mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
locale zh-cn
This commit is contained in:
@@ -25,4 +25,4 @@
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -8,6 +8,8 @@ namespace Snap.Hutao.Control;
|
||||
/// <summary>
|
||||
/// 绑定探针
|
||||
/// 用于处理特定情况下需要穿透数据上下文的工作
|
||||
/// DependencyObject will dispose inner ReferenceTracker in any time
|
||||
/// when object is not used anymore.
|
||||
/// </summary>
|
||||
public class BindingProxy : DependencyObject
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Snap.Hutao.Control.Extension;
|
||||
/// <summary>
|
||||
/// 对话框扩展
|
||||
/// </summary>
|
||||
internal static class ContentDialogExtensions
|
||||
internal static class ContentDialogExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// 阻止用户交互
|
||||
@@ -32,7 +32,7 @@ public class CachedImage : ImageEx
|
||||
|
||||
try
|
||||
{
|
||||
Verify.Operation(imageUri.Host != string.Empty, "无效的Uri");
|
||||
Verify.Operation(imageUri.Host != string.Empty, SH.ControlImageCachedImageInvalidResourceUri);
|
||||
string file = await imageCache.GetFileFromCacheAsync(imageUri).ConfigureAwait(true);
|
||||
|
||||
// check token state to determine whether the operation should be canceled.
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.WinUI.UI.Animations;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.UI.Composition;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Hosting;
|
||||
@@ -11,6 +12,7 @@ using Snap.Hutao.Extension;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Snap.Hutao.Control.Image;
|
||||
@@ -24,7 +26,7 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control
|
||||
private static readonly DependencyProperty SourceProperty = Property<CompositionImage>.Depend(nameof(Source), default(Uri), OnSourceChanged);
|
||||
private static readonly ConcurrentCancellationTokenSource<CompositionImage> LoadingTokenSource = new();
|
||||
|
||||
private readonly IImageCache imageCache;
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
|
||||
private SpriteVisual? spriteVisual;
|
||||
private bool isShow = true;
|
||||
@@ -34,8 +36,15 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control
|
||||
/// </summary>
|
||||
public CompositionImage()
|
||||
{
|
||||
imageCache = Ioc.Default.GetRequiredService<IImageCache>();
|
||||
serviceProvider = Ioc.Default.GetRequiredService<IServiceProvider>();
|
||||
|
||||
AllowFocusOnInteraction = false;
|
||||
IsDoubleTapEnabled = false;
|
||||
IsHitTestVisible = false;
|
||||
IsHoldingEnabled = false;
|
||||
IsRightTapEnabled = false;
|
||||
IsTabStop = false;
|
||||
|
||||
SizeChanged += OnSizeChanged;
|
||||
}
|
||||
|
||||
@@ -86,11 +95,11 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control
|
||||
|
||||
if (exception is HttpRequestException httpRequestException)
|
||||
{
|
||||
infoBarService.Error(httpRequestException, $"GET {uri}");
|
||||
infoBarService.Error(httpRequestException, string.Format(SH.ControlImageCompositionImageHttpRequest, uri));
|
||||
}
|
||||
else
|
||||
{
|
||||
infoBarService.Error(exception, $"应用 {nameof(CompositionImage)} 的源时发生异常");
|
||||
infoBarService.Error(exception.GetBaseException(), SH.ControlImageCompositionImageSystemException);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,26 +133,20 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control
|
||||
|
||||
if (uri != null)
|
||||
{
|
||||
if (uri.Scheme == "ms-appx")
|
||||
{
|
||||
imageSurface = LoadedImageSurface.StartLoadFromUri(uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
string storageFile = await imageCache.GetFileFromCacheAsync(uri).ConfigureAwait(true);
|
||||
IImageCache imageCache = serviceProvider.GetRequiredService<IImageCache>();
|
||||
string file = await imageCache.GetFileFromCacheAsync(uri).ConfigureAwait(true);
|
||||
|
||||
try
|
||||
{
|
||||
imageSurface = await LoadImageSurfaceAsync(storageFile, token).ConfigureAwait(true);
|
||||
}
|
||||
catch (COMException)
|
||||
{
|
||||
imageCache.Remove(uri.Enumerate());
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
imageCache.Remove(uri.Enumerate());
|
||||
}
|
||||
try
|
||||
{
|
||||
imageSurface = await LoadImageSurfaceAsync(file, token).ConfigureAwait(true);
|
||||
}
|
||||
catch (COMException)
|
||||
{
|
||||
imageCache.Remove(uri.Enumerate());
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
imageCache.Remove(uri.Enumerate());
|
||||
}
|
||||
|
||||
if (imageSurface != null)
|
||||
|
||||
@@ -6,6 +6,7 @@ using Microsoft.UI;
|
||||
using Microsoft.UI.Composition;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Snap.Hutao.Control.Theme;
|
||||
|
||||
namespace Snap.Hutao.Control.Image;
|
||||
|
||||
@@ -49,12 +50,7 @@ public class MonoChrome : CompositionImage
|
||||
|
||||
private void SetBackgroundColor(CompositionColorBrush backgroundBrush)
|
||||
{
|
||||
ApplicationTheme theme = ActualTheme switch
|
||||
{
|
||||
ElementTheme.Light => ApplicationTheme.Light,
|
||||
ElementTheme.Dark => ApplicationTheme.Dark,
|
||||
_ => Ioc.Default.GetRequiredService<App>().RequestedTheme,
|
||||
};
|
||||
ApplicationTheme theme = ThemeHelper.ElementToApplication(ActualTheme);
|
||||
|
||||
backgroundBrush.Color = theme switch
|
||||
{
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml.Markup;
|
||||
|
||||
namespace Snap.Hutao.Control.Markup;
|
||||
|
||||
/// <summary>
|
||||
/// Xaml extension to return a <see cref="string"/> value from resource file associated with a resource key
|
||||
/// </summary>
|
||||
[MarkupExtensionReturnType(ReturnType = typeof(string))]
|
||||
public sealed class ResourceStringExtension : MarkupExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets associated ID from resource strings.
|
||||
/// </summary>
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string value from resource file associated with a resource key.
|
||||
/// </summary>
|
||||
/// <param name="name">Resource key name.</param>
|
||||
/// <returns>A string value from resource file associated with a resource key.</returns>
|
||||
public static string GetValue(string name)
|
||||
{
|
||||
// This function is needed to accomodate compiled function usage without second paramater,
|
||||
// which doesn't work with optional values.
|
||||
return SH.ResourceManager.GetString(name)!;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override object ProvideValue()
|
||||
{
|
||||
return GetValue(Name ?? string.Empty);
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,10 @@ namespace Snap.Hutao.Control.Panel;
|
||||
/// <summary>
|
||||
/// 纵横比控件
|
||||
/// </summary>
|
||||
internal class AspectRatio : Microsoft.UI.Xaml.Controls.ContentControl
|
||||
internal class AspectRatio : Microsoft.UI.Xaml.Controls.Control
|
||||
{
|
||||
private const double Epsilon = 2.2204460492503131e-016;
|
||||
|
||||
private static readonly DependencyProperty TargetWidthProperty = Property<AspectRatio>.Depend(nameof(TargetWidth), 1D);
|
||||
private static readonly DependencyProperty TargetHeightProperty = Property<AspectRatio>.Depend(nameof(TargetHeight), 1D);
|
||||
|
||||
@@ -38,6 +40,11 @@ internal class AspectRatio : Microsoft.UI.Xaml.Controls.ContentControl
|
||||
double ratio = TargetWidth / TargetHeight;
|
||||
double ratioAvailable = availableSize.Width / availableSize.Height;
|
||||
|
||||
if (Math.Abs(ratioAvailable - ratio) < Epsilon)
|
||||
{
|
||||
return availableSize;
|
||||
}
|
||||
|
||||
// 更宽
|
||||
if (ratioAvailable > ratio)
|
||||
{
|
||||
|
||||
@@ -21,12 +21,12 @@
|
||||
Click="RadioMenuFlyoutItemClick"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
Tag="List"
|
||||
Text="列表"/>
|
||||
Text="{shcm:ResourceString Name=ControlPanelPanelSelectorDropdownListName}"/>
|
||||
<RadioMenuFlyoutItem
|
||||
Click="RadioMenuFlyoutItemClick"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
Tag="Grid"
|
||||
Text="网格"/>
|
||||
Text="{shcm:ResourceString Name=ControlPanelPanelSelectorDropdownGridName}"/>
|
||||
</MenuFlyout>
|
||||
</SplitButton.Flyout>
|
||||
</SplitButton>
|
||||
|
||||
@@ -16,16 +16,34 @@ namespace Snap.Hutao.Control;
|
||||
[SuppressMessage("", "CA1001")]
|
||||
public class ScopedPage : Page
|
||||
{
|
||||
// Allow GC to Collect the IServiceScope
|
||||
private static readonly WeakReference<IServiceScope> PreviousScopeReference = new(null!);
|
||||
|
||||
private readonly CancellationTokenSource viewCancellationTokenSource = new();
|
||||
private readonly IServiceScope serviceScope;
|
||||
private readonly IServiceScope currentScope;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的页面
|
||||
/// </summary>
|
||||
public ScopedPage()
|
||||
{
|
||||
serviceScope = Ioc.Default.CreateScope();
|
||||
serviceScope.Track();
|
||||
Unloaded += OnScopedPageUnloaded;
|
||||
currentScope = Ioc.Default.CreateScope();
|
||||
DisposePreviousScope();
|
||||
|
||||
// track current
|
||||
PreviousScopeReference.SetTarget(currentScope);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放上个范围
|
||||
/// </summary>
|
||||
public static void DisposePreviousScope()
|
||||
{
|
||||
if (PreviousScopeReference.TryGetTarget(out IServiceScope? scope))
|
||||
{
|
||||
scope.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -36,7 +54,7 @@ public class ScopedPage : Page
|
||||
public void InitializeWith<TViewModel>()
|
||||
where TViewModel : class, IViewModel
|
||||
{
|
||||
IViewModel viewModel = serviceScope.ServiceProvider.GetRequiredService<TViewModel>();
|
||||
IViewModel viewModel = currentScope.ServiceProvider.GetRequiredService<TViewModel>();
|
||||
viewModel.CancellationToken = viewCancellationTokenSource.Token;
|
||||
DataContext = viewModel;
|
||||
}
|
||||
@@ -59,7 +77,6 @@ public class ScopedPage : Page
|
||||
/// <inheritdoc/>
|
||||
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
|
||||
{
|
||||
base.OnNavigatingFrom(e);
|
||||
using (viewCancellationTokenSource)
|
||||
{
|
||||
// Cancel all tasks executed by the view model
|
||||
@@ -73,7 +90,7 @@ public class ScopedPage : Page
|
||||
viewModel.IsViewDisposed = true;
|
||||
|
||||
// Dispose the scope
|
||||
serviceScope.Dispose();
|
||||
currentScope.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,11 +98,15 @@ public class ScopedPage : Page
|
||||
/// <inheritdoc/>
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
if (e.Parameter is INavigationData extra)
|
||||
{
|
||||
NotifyRecipentAsync(extra).SafeForget();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnScopedPageUnloaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
DataContext = null;
|
||||
Unloaded -= OnScopedPageUnloaded;
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Documents;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Snap.Hutao.Control.Media;
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Control.Theme;
|
||||
using Windows.UI;
|
||||
|
||||
namespace Snap.Hutao.Control.Text;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
using Microsoft.UI.Composition.SystemBackdrops;
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
namespace Snap.Hutao.Core;
|
||||
namespace Snap.Hutao.Control.Theme;
|
||||
|
||||
/// <summary>
|
||||
/// 主题帮助工具类
|
||||
@@ -42,6 +42,21 @@ public static class ThemeHelper
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从 <see cref="ElementTheme"/> 转换到 <see cref="ApplicationTheme"/>
|
||||
/// </summary>
|
||||
/// <param name="applicationTheme">元素主题</param>
|
||||
/// <returns>应用主题</returns>
|
||||
public static ApplicationTheme ElementToApplication(ElementTheme applicationTheme)
|
||||
{
|
||||
return applicationTheme switch
|
||||
{
|
||||
ElementTheme.Light => ApplicationTheme.Light,
|
||||
ElementTheme.Dark => ApplicationTheme.Dark,
|
||||
_ => Ioc.Default.GetRequiredService<App>().RequestedTheme,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从 <see cref="ElementTheme"/> 转换到 <see cref="SystemBackdropTheme"/>
|
||||
/// </summary>
|
||||
@@ -70,6 +70,11 @@ internal static class CoreEnvironment
|
||||
/// </summary>
|
||||
public static readonly string FamilyName;
|
||||
|
||||
/// <summary>
|
||||
/// 安装位置
|
||||
/// </summary>
|
||||
public static readonly string InstalledLocation;
|
||||
|
||||
/// <summary>
|
||||
/// 数据文件夹
|
||||
/// </summary>
|
||||
@@ -98,9 +103,10 @@ internal static class CoreEnvironment
|
||||
|
||||
static CoreEnvironment()
|
||||
{
|
||||
DataFolder = GetDocumentsHutaoPath();
|
||||
DataFolder = GetDatafolderPath();
|
||||
Version = Package.Current.Id.Version.ToVersion();
|
||||
FamilyName = Package.Current.Id.FamilyName;
|
||||
InstalledLocation = Package.Current.InstalledLocation.Path;
|
||||
CommonUA = $"Snap Hutao/{Version}";
|
||||
|
||||
// simply assign a random guid
|
||||
@@ -115,7 +121,7 @@ internal static class CoreEnvironment
|
||||
return Convert.ToMd5HexString($"{userName}{machineGuid}");
|
||||
}
|
||||
|
||||
private static string GetDocumentsHutaoPath()
|
||||
private static string GetDatafolderPath()
|
||||
{
|
||||
string myDocument = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
|
||||
#if RELEASE
|
||||
|
||||
@@ -18,7 +18,7 @@ internal static class IocConfiguration
|
||||
/// </summary>
|
||||
/// <param name="services">集合</param>
|
||||
/// <returns>可继续操作的集合</returns>
|
||||
public static IServiceCollection AddJsonSerializerOptions(this IServiceCollection services)
|
||||
public static IServiceCollection AddJsonOptions(this IServiceCollection services)
|
||||
{
|
||||
return services.AddSingleton(CoreEnvironment.JsonOptions);
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Snap.Hutao.Core.DependencyInjection;
|
||||
|
||||
/// <summary>
|
||||
/// 服务范围扩展
|
||||
/// </summary>
|
||||
public static class ServiceScopeExtension
|
||||
{
|
||||
// Allow GC to Collect the IServiceScope
|
||||
private static readonly WeakReference<IServiceScope> ScopeReference = new(null!);
|
||||
|
||||
/// <summary>
|
||||
/// 追踪服务范围
|
||||
/// </summary>
|
||||
/// <param name="scope">范围</param>
|
||||
public static void Track(this IServiceScope scope)
|
||||
{
|
||||
DisposeLast();
|
||||
ScopeReference.SetTarget(scope);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放上个范围
|
||||
/// </summary>
|
||||
public static void DisposeLast()
|
||||
{
|
||||
if (ScopeReference.TryGetTarget(out IServiceScope? scope))
|
||||
{
|
||||
scope.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Service.Game.Package;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Snap.Hutao.Core.ExceptionService;
|
||||
|
||||
/// <summary>
|
||||
/// 帮助更好的抛出异常
|
||||
/// </summary>
|
||||
[System.Diagnostics.StackTraceHidden]
|
||||
internal static class ThrowHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 操作取消
|
||||
/// </summary>
|
||||
/// <param name="message">消息</param>
|
||||
/// <param name="inner">内部错误</param>
|
||||
/// <exception cref="OperationCanceledException">操作取消异常</exception>
|
||||
/// <returns>nothing</returns>
|
||||
[DoesNotReturn]
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static OperationCanceledException OperationCanceled(string message, Exception? inner)
|
||||
{
|
||||
throw new OperationCanceledException(message, inner);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 包转换错误
|
||||
/// </summary>
|
||||
/// <param name="message">消息</param>
|
||||
/// <param name="inner">内部错误</param>
|
||||
/// <returns>nothing</returns>
|
||||
/// <exception cref="PackageConvertException">包转换错误异常</exception>
|
||||
[DoesNotReturn]
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static PackageConvertException PackageConvert(string message, Exception inner)
|
||||
{
|
||||
throw new PackageConvertException(message, inner);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用户数据损坏
|
||||
/// </summary>
|
||||
/// <param name="message">消息</param>
|
||||
/// <param name="inner">内部错误</param>
|
||||
/// <exception cref="UserdataCorruptedException">数据损坏</exception>
|
||||
/// <returns>nothing</returns>
|
||||
[DoesNotReturn]
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static UserdataCorruptedException UserdataCorrupted(string message, Exception inner)
|
||||
{
|
||||
throw new UserdataCorruptedException(message, inner);
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ internal class UserdataCorruptedException : Exception
|
||||
/// <param name="message">消息</param>
|
||||
/// <param name="innerException">内部错误</param>
|
||||
public UserdataCorruptedException(string message, Exception innerException)
|
||||
: base($"用户数据已损坏: {message}", innerException)
|
||||
: base(string.Format(SH.CoreExceptionServiceUserdataCorruptedMessage, message), innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ internal class BitsJob : DisposableObject, IBackgroundCopyCallback
|
||||
private readonly object lockObj = new();
|
||||
|
||||
private IBackgroundCopyJob? nativeJob;
|
||||
private System.Exception? jobException;
|
||||
private Exception? jobException;
|
||||
private BG_JOB_PROGRESS progress;
|
||||
private BG_JOB_STATE state;
|
||||
private bool isJobComplete;
|
||||
@@ -79,7 +79,7 @@ internal class BitsJob : DisposableObject, IBackgroundCopyCallback
|
||||
UpdateJobState();
|
||||
CompleteOrCancel();
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.LogInformation("Failed to job transfer: {message}", ex.Message);
|
||||
}
|
||||
@@ -101,7 +101,7 @@ internal class BitsJob : DisposableObject, IBackgroundCopyCallback
|
||||
CompleteOrCancel();
|
||||
log.LogInformation(jobException, "Job Exception:");
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
log?.LogInformation("Failed to handle job error: {message}", ex.Message);
|
||||
}
|
||||
@@ -141,7 +141,7 @@ internal class BitsJob : DisposableObject, IBackgroundCopyCallback
|
||||
CompleteOrCancel();
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.LogInformation(ex, "message");
|
||||
}
|
||||
@@ -283,7 +283,7 @@ internal class BitsJob : DisposableObject, IBackgroundCopyCallback
|
||||
{
|
||||
action();
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.LogInformation("{name} failed. {exception}", displayName, ex);
|
||||
if (throwOnFailure)
|
||||
|
||||
@@ -39,7 +39,8 @@ internal class BitsManager
|
||||
public async Task<ValueResult<bool, TempFile>> DownloadAsync(Uri uri, IProgress<ProgressUpdateStatus> progress, CancellationToken token = default)
|
||||
{
|
||||
TempFile tempFile = new(true);
|
||||
bool result = await Task.Run(() => DownloadCore(uri, tempFile.Path, progress.Report, token), token).ConfigureAwait(false);
|
||||
await ThreadHelper.SwitchToBackgroundAsync();
|
||||
bool result = DownloadCore(uri, tempFile.Path, progress.Report, token);
|
||||
return new(result, tempFile);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Snap.Hutao.Core.IO.Bits;
|
||||
|
||||
/// <summary>
|
||||
/// 进度更新状态
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{BytesRead}/{TotalBytes}")]
|
||||
public class ProgressUpdateStatus
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -20,7 +20,6 @@ internal static class Clipboard
|
||||
public static async Task<T?> DeserializeTextAsync<T>(JsonSerializerOptions options)
|
||||
where T : class
|
||||
{
|
||||
await ThreadHelper.SwitchToMainThreadAsync();
|
||||
DataPackageView view = Windows.ApplicationModel.DataTransfer.Clipboard.GetContent();
|
||||
string json = await view.GetTextAsync();
|
||||
return JsonSerializer.Deserialize<T>(json, options);
|
||||
|
||||
@@ -32,13 +32,7 @@ internal static class PickerExtension
|
||||
}
|
||||
else
|
||||
{
|
||||
if (exception != null)
|
||||
{
|
||||
Ioc.Default
|
||||
.GetRequiredService<Service.Abstraction.IInfoBarService>()
|
||||
.Warning("无法打开文件选择器", $"请勿在管理员模式下使用此功能 {exception.Message}");
|
||||
}
|
||||
|
||||
InfoBarWaringPickerException(exception);
|
||||
return new(false, null!);
|
||||
}
|
||||
}
|
||||
@@ -64,14 +58,20 @@ internal static class PickerExtension
|
||||
}
|
||||
else
|
||||
{
|
||||
if (exception != null)
|
||||
{
|
||||
Ioc.Default
|
||||
.GetRequiredService<Service.Abstraction.IInfoBarService>()
|
||||
.Warning("无法打开文件选择器", $"请勿在管理员模式下使用此功能 {exception.Message}");
|
||||
}
|
||||
|
||||
InfoBarWaringPickerException(exception);
|
||||
return new(false, null!);
|
||||
}
|
||||
}
|
||||
|
||||
private static void InfoBarWaringPickerException(Exception? exception)
|
||||
{
|
||||
if (exception != null)
|
||||
{
|
||||
Ioc.Default
|
||||
.GetRequiredService<Service.Abstraction.IInfoBarService>()
|
||||
.Warning(
|
||||
SH.CoreIOPickerExtensionPickerExceptionInfoBarTitle,
|
||||
string.Format(SH.CoreIOPickerExtensionPickerExceptionInfoBarMessage, exception.Message));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,8 +23,8 @@ public static class JumpListHelper
|
||||
|
||||
list.Items.Clear();
|
||||
|
||||
JumpListItem launchGameItem = JumpListItem.CreateWithArguments(Activation.LaunchGame, "启动游戏");
|
||||
launchGameItem.GroupName = "快捷操作";
|
||||
JumpListItem launchGameItem = JumpListItem.CreateWithArguments(Activation.LaunchGame, SH.CoreJumpListHelperLaunchGameItemDisplayName);
|
||||
launchGameItem.GroupName = SH.CoreJumpListHelperLaunchGameItemGroupName;
|
||||
launchGameItem.Logo = new("ms-appx:///Resource/Icon/UI_GuideIcon_PlayMethod.png");
|
||||
|
||||
list.Items.Add(launchGameItem);
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
|
||||
namespace Snap.Hutao.Core.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for the <see cref="ILoggerFactory"/> class.
|
||||
/// </summary>
|
||||
public static class DatabaseLoggerFactoryExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds a debug logger named 'Debug' to the factory.
|
||||
/// </summary>
|
||||
/// <param name="builder">The extension method argument.</param>
|
||||
/// <returns>日志构造器</returns>
|
||||
public static ILoggingBuilder AddDatabase(this ILoggingBuilder builder)
|
||||
{
|
||||
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, DatebaseLoggerProvider>());
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Core.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// A logger that writes messages in the database table
|
||||
/// </summary>
|
||||
internal sealed partial class DatebaseLogger : ILogger
|
||||
{
|
||||
private readonly string name;
|
||||
private readonly LogEntryQueue logEntryQueue;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DatebaseLogger"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the logger.</param>
|
||||
/// <param name="logEntryQueue">日志队列</param>
|
||||
public DatebaseLogger(string name, LogEntryQueue logEntryQueue)
|
||||
{
|
||||
this.name = name;
|
||||
this.logEntryQueue = logEntryQueue;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDisposable BeginScope<TState>(TState state)
|
||||
where TState : notnull
|
||||
{
|
||||
return new NullScope();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
return logLevel != LogLevel.None;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, System.Exception? exception, Func<TState, System.Exception?, string> formatter)
|
||||
{
|
||||
if (!IsEnabled(logLevel))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string message = formatter(state, exception);
|
||||
|
||||
if (string.IsNullOrEmpty(message))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LogEntry entry = new()
|
||||
{
|
||||
Time = DateTimeOffset.Now,
|
||||
Category = name,
|
||||
LogLevel = logLevel,
|
||||
EventId = eventId.Id,
|
||||
Message = message,
|
||||
Exception = exception?.ToString(),
|
||||
};
|
||||
|
||||
logEntryQueue.Enqueue(entry);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An empty scope without any logic
|
||||
/// </summary>
|
||||
private struct NullScope : IDisposable
|
||||
{
|
||||
public NullScope()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Core.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// The provider for the <see cref="DatebaseLogger"/>.
|
||||
/// </summary>
|
||||
[ProviderAlias("Database")]
|
||||
public sealed class DatebaseLoggerProvider : ILoggerProvider
|
||||
{
|
||||
private readonly LogEntryQueue logEntryQueue = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ILogger CreateLogger(string name)
|
||||
{
|
||||
return new DatebaseLogger(name, logEntryQueue);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
logEntryQueue.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Snap.Hutao.Core.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// 数据库日志入口点
|
||||
/// </summary>
|
||||
[Table("logs")]
|
||||
public class LogEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// 内部Id
|
||||
/// </summary>
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid InnerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 日志时间
|
||||
/// </summary>
|
||||
public DateTimeOffset Time { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 类别
|
||||
/// </summary>
|
||||
public string Category { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 日志等级
|
||||
/// </summary>
|
||||
public LogLevel LogLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 事件Id
|
||||
/// </summary>
|
||||
public int EventId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 消息
|
||||
/// </summary>
|
||||
public string Message { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 可能的异常
|
||||
/// </summary>
|
||||
public string? Exception { get; set; }
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Snap.Hutao.Model.Entity.Database;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Snap.Hutao.Core.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// 日志队列
|
||||
/// </summary>
|
||||
public sealed class LogEntryQueue : IDisposable
|
||||
{
|
||||
private readonly ConcurrentQueue<LogEntry> entryQueue = new();
|
||||
private readonly CancellationTokenSource disposeTokenSource = new();
|
||||
private readonly TaskCompletionSource writeDbCompletionSource = new();
|
||||
private readonly LogDbContext logDbContext;
|
||||
|
||||
private bool disposed;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的日志队列
|
||||
/// </summary>
|
||||
public LogEntryQueue()
|
||||
{
|
||||
logDbContext = InitializeDbContext();
|
||||
|
||||
Task.Run(() => WritePendingLogsAsync(disposeTokenSource.Token)).SafeForget();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将日志消息存入队列
|
||||
/// </summary>
|
||||
/// <param name="logEntry">日志</param>
|
||||
public void Enqueue(LogEntry logEntry)
|
||||
{
|
||||
entryQueue.Enqueue(logEntry);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[SuppressMessage("", "VSTHRD002")]
|
||||
public void Dispose()
|
||||
{
|
||||
if (disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// notify the write task to complete.
|
||||
disposeTokenSource.Cancel();
|
||||
|
||||
// Wait the db operation complete.
|
||||
writeDbCompletionSource.Task.GetAwaiter().GetResult();
|
||||
|
||||
logDbContext.Dispose();
|
||||
disposed = true;
|
||||
}
|
||||
|
||||
private static LogDbContext InitializeDbContext()
|
||||
{
|
||||
string logDbName = System.IO.Path.Combine(CoreEnvironment.DataFolder, "Log.db");
|
||||
LogDbContext logDbContext = LogDbContext.Create($"Data Source={logDbName}");
|
||||
if (logDbContext.Database.GetPendingMigrations().Any())
|
||||
{
|
||||
Debug.WriteLine("[Debug] Performing LogDbContext Migrations");
|
||||
logDbContext.Database.Migrate();
|
||||
}
|
||||
|
||||
// only raw sql can pass
|
||||
logDbContext.Logs.Where(log => log.Exception == null).ExecuteDelete();
|
||||
return logDbContext;
|
||||
}
|
||||
|
||||
private async Task WritePendingLogsAsync(CancellationToken token)
|
||||
{
|
||||
bool hasAdded = false;
|
||||
while (true)
|
||||
{
|
||||
if (entryQueue.TryDequeue(out LogEntry? logEntry))
|
||||
{
|
||||
logDbContext.Logs.Add(logEntry);
|
||||
hasAdded = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hasAdded)
|
||||
{
|
||||
logDbContext.SaveChanges();
|
||||
hasAdded = false;
|
||||
}
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
writeDbCompletionSource.TrySetResult();
|
||||
break;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await Task.Delay(5000, token).ConfigureAwait(false);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ internal static class ScheduleTaskHelper
|
||||
}
|
||||
|
||||
TaskDefinition task = TaskService.Instance.NewTask();
|
||||
task.RegistrationInfo.Description = "胡桃实时便笺刷新任务 | 请勿编辑或删除。";
|
||||
task.RegistrationInfo.Description = SH.CoreScheduleTaskHelperDailyNoteRefreshTaskDescription;
|
||||
task.Triggers.Add(new TimeTrigger() { Repetition = new(TimeSpan.FromSeconds(interval), TimeSpan.Zero), });
|
||||
task.Actions.Add("explorer", "hutao://DailyNote/Refresh");
|
||||
TaskService.Instance.RootFolder.RegisterTaskDefinition(DailyNoteRefreshTaskName, task);
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
|
||||
namespace Snap.Hutao.Core.Threading;
|
||||
|
||||
/// <summary>
|
||||
/// 信号量扩展
|
||||
/// </summary>
|
||||
public static class SemaphoreSlimExtensions
|
||||
public static class SemaphoreSlimExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// 异步进入信号量
|
||||
@@ -22,7 +24,7 @@ public static class SemaphoreSlimExtensions
|
||||
}
|
||||
catch (ObjectDisposedException ex)
|
||||
{
|
||||
throw new OperationCanceledException("信号量已经被释放,操作取消", ex);
|
||||
ThrowHelper.OperationCanceled(SH.CoreThreadingSemaphoreSlimDisposed, ex);
|
||||
}
|
||||
|
||||
return new SemaphoreSlimReleaser(semaphoreSlim);
|
||||
@@ -22,7 +22,7 @@ public static class TaskExtensions
|
||||
{
|
||||
await task.ConfigureAwait(false);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(ex);
|
||||
}
|
||||
@@ -39,11 +39,11 @@ public static class TaskExtensions
|
||||
{
|
||||
await task.ConfigureAwait(false);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
catch (System.Exception e)
|
||||
catch (Exception e)
|
||||
{
|
||||
logger?.LogError(EventIds.TaskException, e, "{caller}:\r\n{exception}", nameof(SafeForget), e.GetBaseException());
|
||||
}
|
||||
@@ -55,17 +55,17 @@ public static class TaskExtensions
|
||||
/// <param name="task">任务</param>
|
||||
/// <param name="logger">日志器</param>
|
||||
/// <param name="onException">发生异常时调用</param>
|
||||
public static async void SafeForget(this Task task, ILogger? logger = null, Action<System.Exception>? onException = null)
|
||||
public static async void SafeForget(this Task task, ILogger? logger = null, Action<Exception>? onException = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await task.ConfigureAwait(false);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
catch (System.Exception e)
|
||||
catch (Exception e)
|
||||
{
|
||||
logger?.LogError(EventIds.TaskException, e, "{caller}:\r\n{exception}", nameof(SafeForget), e.GetBaseException());
|
||||
onException?.Invoke(e);
|
||||
@@ -79,17 +79,17 @@ public static class TaskExtensions
|
||||
/// <param name="logger">日志器</param>
|
||||
/// <param name="onCanceled">任务取消时调用</param>
|
||||
/// <param name="onException">发生异常时调用</param>
|
||||
public static async void SafeForget(this Task task, ILogger? logger = null, Action? onCanceled = null, Action<System.Exception>? onException = null)
|
||||
public static async void SafeForget(this Task task, ILogger? logger = null, Action? onCanceled = null, Action<Exception>? onException = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await task.ConfigureAwait(false);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
onCanceled?.Invoke();
|
||||
}
|
||||
catch (System.Exception e)
|
||||
catch (Exception e)
|
||||
{
|
||||
logger?.LogError(EventIds.TaskException, e, "{caller}:\r\n{exception}", nameof(SafeForget), e.GetBaseException());
|
||||
onException?.Invoke(e);
|
||||
|
||||
@@ -30,13 +30,13 @@ public readonly struct ThreadPoolSwitchOperation : IAwaitable<ThreadPoolSwitchOp
|
||||
/// <inheritdoc/>
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
QueueContinuation(continuation, flowContext: true);
|
||||
QueueContinuation(continuation, true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void UnsafeOnCompleted(Action continuation)
|
||||
{
|
||||
QueueContinuation(continuation, flowContext: false);
|
||||
QueueContinuation(continuation, false);
|
||||
}
|
||||
|
||||
private static void QueueContinuation(Action continuation, bool flowContext)
|
||||
|
||||
@@ -44,7 +44,7 @@ public static class Must
|
||||
/// <param name="context">上下文</param>
|
||||
/// <returns>Nothing. This method always throws.</returns>
|
||||
[DoesNotReturn]
|
||||
public static System.Exception NeverHappen(string? context = null)
|
||||
public static Exception NeverHappen(string? context = null)
|
||||
{
|
||||
throw new NotSupportedException(context);
|
||||
}
|
||||
@@ -62,21 +62,4 @@ public static class Must
|
||||
{
|
||||
return value ?? throw new ArgumentNullException(parameterName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws an <see cref="ArgumentNullException"/> if the specified parameter's value is IntPtr.Zero.
|
||||
/// </summary>
|
||||
/// <param name="value">The value of the argument.</param>
|
||||
/// <param name="parameterName">The name of the parameter to include in any thrown exception.</param>
|
||||
/// <returns>The value of the parameter.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown if <paramref name="value"/> is <see cref="IntPtr.Zero"/>.</exception>
|
||||
public static Windows.Win32.Foundation.HWND NotNull(Windows.Win32.Foundation.HWND value, [CallerArgumentExpression("value")] string? parameterName = null)
|
||||
{
|
||||
if (value == default)
|
||||
{
|
||||
throw new ArgumentNullException(parameterName);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ internal abstract class WebView2Helper
|
||||
{
|
||||
private static bool hasEverDetected;
|
||||
private static bool isSupported;
|
||||
private static string version = "未检测到 WebView2 运行时";
|
||||
private static string version = SH.CoreWebView2HelperVersionUndetected;
|
||||
|
||||
/// <summary>
|
||||
/// 检测 WebView2 是否存在
|
||||
@@ -36,7 +36,7 @@ internal abstract class WebView2Helper
|
||||
catch (FileNotFoundException ex)
|
||||
{
|
||||
ILogger<WebView2Helper> logger = Ioc.Default.GetRequiredService<ILogger<WebView2Helper>>();
|
||||
logger.LogError(EventIds.WebView2EnvironmentException, ex, "WebView2 运行时未安装");
|
||||
logger.LogError(EventIds.WebView2EnvironmentException, ex, "WebView2 Runtime not installed.");
|
||||
isSupported = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,8 +124,8 @@ internal sealed class ExtendedWindow<TWindow> : IRecipient<BackdropTypeChangedMe
|
||||
|
||||
private void InitializeWindow()
|
||||
{
|
||||
appWindow.Title = "胡桃";
|
||||
appWindow.SetIcon(Path.Combine(Package.Current.InstalledLocation.Path, "Assets/Logos/Logo.ico"));
|
||||
appWindow.Title = string.Format(SH.AppNameAndVersion, CoreEnvironment.Version);
|
||||
appWindow.SetIcon(Path.Combine(CoreEnvironment.InstalledLocation, "Assets/Logo.ico"));
|
||||
ExtendsContentIntoTitleBar();
|
||||
|
||||
Persistence.RecoverOrInit(appWindow, window.PersistSize, window.InitSize);
|
||||
|
||||
@@ -5,6 +5,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.UI.Composition;
|
||||
using Microsoft.UI.Composition.SystemBackdrops;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Snap.Hutao.Control.Theme;
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.Entity.Database;
|
||||
@@ -140,7 +141,7 @@ public class SystemBackdrop
|
||||
/// <summary>
|
||||
/// 确保系统调度队列控制器存在
|
||||
/// </summary>
|
||||
public void Ensure()
|
||||
public unsafe void Ensure()
|
||||
{
|
||||
if (DispatcherQueue.GetForCurrentThread() != null)
|
||||
{
|
||||
@@ -152,7 +153,7 @@ public class SystemBackdrop
|
||||
{
|
||||
DispatcherQueueOptions options = new()
|
||||
{
|
||||
DwSize = Marshal.SizeOf<DispatcherQueueOptions>(),
|
||||
DwSize = sizeof(DispatcherQueueOptions),
|
||||
ThreadType = 2, // DQTYPE_THREAD_CURRENT
|
||||
ApartmentType = 2, // DQTAT_COM_STA
|
||||
};
|
||||
|
||||
@@ -37,7 +37,7 @@ internal class WindowSubclassManager<TWindow> : IDisposable
|
||||
public WindowSubclassManager(TWindow window, HWND hwnd, bool isLegacyDragBar)
|
||||
{
|
||||
this.window = window;
|
||||
this.hwnd = Must.NotNull(hwnd);
|
||||
this.hwnd = hwnd;
|
||||
this.isLegacyDragBar = isLegacyDragBar;
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ internal class WindowSubclassManager<TWindow> : IDisposable
|
||||
/// 尝试设置窗体子类
|
||||
/// </summary>
|
||||
/// <returns>是否设置成功</returns>
|
||||
public bool TrySetWindowSubclass()
|
||||
public unsafe bool TrySetWindowSubclass()
|
||||
{
|
||||
windowProc = new(OnSubclassProcedure);
|
||||
bool windowHooked = SetWindowSubclass(hwnd, windowProc, WindowSubclassId, 0);
|
||||
|
||||
@@ -8,22 +8,6 @@ namespace Snap.Hutao.Extension;
|
||||
/// </summary>
|
||||
public static class DateTimeOffsetExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the current <see cref="DateTimeOffset"/> to a <see cref="DateTimeOffset"/> that represents the local time.
|
||||
/// </summary>
|
||||
/// <param name="dateTimeOffset">时间偏移</param>
|
||||
/// <param name="keepTicks">保留主时间部分</param>
|
||||
/// <returns>A <see cref="DateTimeOffset"/> that represents the local time.</returns>
|
||||
public static DateTimeOffset ToLocalTime(this DateTimeOffset dateTimeOffset, bool keepTicks)
|
||||
{
|
||||
if (keepTicks)
|
||||
{
|
||||
dateTimeOffset -= TimeZoneInfo.Local.GetUtcOffset(DateTimeOffset.Now);
|
||||
}
|
||||
|
||||
return dateTimeOffset.ToLocalTime();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从Unix时间戳转换
|
||||
/// </summary>
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Extension;
|
||||
|
||||
/// <summary>
|
||||
/// 日志器扩展
|
||||
/// </summary>
|
||||
[SuppressMessage("", "CA2254")]
|
||||
public static class LoggerExtension
|
||||
{
|
||||
/// <inheritdoc cref="LoggerExtensions.LogWarning(ILogger, string?, object?[])"/>
|
||||
public static T LogWarning<T>(this ILogger logger, string message, params object?[] param)
|
||||
{
|
||||
logger.LogWarning(message, param);
|
||||
return default!;
|
||||
}
|
||||
}
|
||||
@@ -33,17 +33,4 @@ public static class StringBuilderExtensions
|
||||
{
|
||||
return condition ? sb.Append(value) : sb;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当条件符合时执行 <see cref="StringBuilder.Append(string?)"/>
|
||||
/// </summary>
|
||||
/// <param name="sb">字符串建造器</param>
|
||||
/// <param name="condition">条件</param>
|
||||
/// <param name="trueValue">条件符合时附加的字符串</param>
|
||||
/// <param name="falseValue">条件不符合时附加的字符串</param>
|
||||
/// <returns>同一个字符串建造器</returns>
|
||||
public static StringBuilder AppendIfElse(this StringBuilder sb, bool condition, string? trueValue, string? falseValue)
|
||||
{
|
||||
return condition ? sb.Append(trueValue) : sb.Append(falseValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ internal class ContentDialogFactory : IContentDialogFactory
|
||||
Title = title,
|
||||
Content = content,
|
||||
DefaultButton = ContentDialogButton.Primary,
|
||||
PrimaryButtonText = "确认",
|
||||
PrimaryButtonText = SH.FactoryContentDialogFactoryConfirmPrimaryButtonText,
|
||||
};
|
||||
|
||||
return dialog;
|
||||
@@ -75,8 +75,8 @@ internal class ContentDialogFactory : IContentDialogFactory
|
||||
Title = title,
|
||||
Content = content,
|
||||
DefaultButton = defaultButton,
|
||||
PrimaryButtonText = "确认",
|
||||
CloseButtonText = "取消",
|
||||
PrimaryButtonText = SH.FactoryContentDialogFactoryConfirmPrimaryButtonText,
|
||||
CloseButtonText = SH.FactoryContentDialogFactoryCancelCloseButtonText,
|
||||
};
|
||||
|
||||
return dialog;
|
||||
|
||||
@@ -13,6 +13,7 @@ global using Snap.Hutao.Core.DependencyInjection;
|
||||
global using Snap.Hutao.Core.DependencyInjection.Annotation;
|
||||
global using Snap.Hutao.Core.Threading;
|
||||
global using Snap.Hutao.Core.Validation;
|
||||
global using Snap.Hutao.Resource.Localization;
|
||||
|
||||
// Runtime
|
||||
global using System;
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Snap.Hutao.Model.Entity.Database;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Snap.Hutao.Migrations.LogDb
|
||||
{
|
||||
[DbContext(typeof(LogDbContext))]
|
||||
[Migration("20220720121521_Logs")]
|
||||
partial class Logs
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "6.0.7");
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Core.Logging.LogEntry", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Category")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EventId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Exception")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("LogLevel")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("logs");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
// <auto-generated />
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Snap.Hutao.Migrations.LogDb
|
||||
{
|
||||
public partial class Logs : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "logs",
|
||||
columns: table => new
|
||||
{
|
||||
InnerId = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||
Category = table.Column<string>(type: "TEXT", nullable: false),
|
||||
LogLevel = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
EventId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Message = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Exception = table.Column<string>(type: "TEXT", nullable: true),
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_logs", x => x.InnerId);
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "logs");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Snap.Hutao.Model.Entity.Database;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Snap.Hutao.Migrations.LogDb
|
||||
{
|
||||
[DbContext(typeof(LogDbContext))]
|
||||
[Migration("20220903071033_LogTime")]
|
||||
partial class LogTime
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "6.0.8");
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Core.Logging.LogEntry", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Category")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EventId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Exception")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("LogLevel")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("Time")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("logs");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
// <auto-generated />
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Snap.Hutao.Migrations.LogDb
|
||||
{
|
||||
public partial class LogTime : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<DateTimeOffset>(
|
||||
name: "Time",
|
||||
table: "logs",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)));
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Time",
|
||||
table: "logs");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Snap.Hutao.Model.Entity.Database;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Snap.Hutao.Migrations.LogDb
|
||||
{
|
||||
[DbContext(typeof(LogDbContext))]
|
||||
partial class LogDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "6.0.8");
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Core.Logging.LogEntry", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Category")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EventId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Exception")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("LogLevel")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("Time")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("logs");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,9 +15,9 @@ public class LaunchScheme
|
||||
/// </summary>
|
||||
public static readonly ImmutableList<LaunchScheme> KnownSchemes = new List<LaunchScheme>()
|
||||
{
|
||||
new LaunchScheme("官方服 | 天空岛", "eYd89JmJ", "18", "1", "1"),
|
||||
new LaunchScheme("渠道服 | 世界树", "KAtdSsoQ", "17", "14", "0"),
|
||||
new LaunchScheme("国际服 | 部分支持", "gcStgarh", "10", "1", "0"),
|
||||
new LaunchScheme("官方服", "eYd89JmJ", "18", "1", "1"),
|
||||
new LaunchScheme("渠道服", "KAtdSsoQ", "17", "14", "0"),
|
||||
new LaunchScheme("国际服", "gcStgarh", "10", "1", "0"),
|
||||
}.ToImmutableList();
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Snap.Hutao.Core.Logging;
|
||||
|
||||
namespace Snap.Hutao.Model.Entity.Database;
|
||||
|
||||
/// <summary>
|
||||
/// 日志数据库上下文
|
||||
/// 由于写入日志的行为需要锁定数据库上下文
|
||||
/// 所以将日志单独分离出来进行读写
|
||||
/// </summary>
|
||||
public class LogDbContext : DbContext
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建一个新的
|
||||
/// </summary>
|
||||
/// <param name="options">选项</param>
|
||||
private LogDbContext(DbContextOptions<LogDbContext> options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 日志记录
|
||||
/// </summary>
|
||||
public DbSet<LogEntry> Logs { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个临时的日志数据库上下文
|
||||
/// </summary>
|
||||
/// <param name="sqlConnectionString">连接字符串</param>
|
||||
/// <returns>日志数据库上下文</returns>
|
||||
public static LogDbContext Create(string sqlConnectionString)
|
||||
{
|
||||
return new(new DbContextOptionsBuilder<LogDbContext>().UseSqlite(sqlConnectionString).Options);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Design;
|
||||
using Snap.Hutao.Model.Entity.Database;
|
||||
|
||||
namespace Snap.Hutao.Context.Database;
|
||||
|
||||
/// <summary>
|
||||
/// 此类只用于在生成迁移时提供数据库上下文
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public class LogDbContextDesignTimeFactory : IDesignTimeDbContextFactory<LogDbContext>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public LogDbContext CreateDbContext(string[] args)
|
||||
{
|
||||
string logDbName = System.IO.Path.Combine(Core.CoreEnvironment.DataFolder, "Log.db");
|
||||
return LogDbContext.Create($"Data Source={logDbName}");
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
<DeveloperAccountType>MSA</DeveloperAccountType>
|
||||
<GeneratePackageHash>http://www.w3.org/2001/04/xmlenc#sha256</GeneratePackageHash>
|
||||
<SupportedLocales>
|
||||
<Language Code="en-us" InMinimumRequirementSet="true" />
|
||||
<Language Code="zh-cn" InMinimumRequirementSet="true" />
|
||||
</SupportedLocales>
|
||||
<ProductReservedInfo>
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
<Resource Language="zh-CN"/>
|
||||
<Resource Language="en-us"/>
|
||||
<Resource Language="zh-cn"/>
|
||||
</Resources>
|
||||
|
||||
<Applications>
|
||||
|
||||
@@ -5,7 +5,6 @@ using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.Windows.AppLifecycle;
|
||||
using Snap.Hutao.Core.Logging;
|
||||
using System.Runtime.InteropServices;
|
||||
using WinRT;
|
||||
|
||||
@@ -33,7 +32,7 @@ public static partial class Program
|
||||
// In a Desktop app this runs a message pump internally,
|
||||
// and does not return until the application shuts down.
|
||||
Application.Start(InitializeApp);
|
||||
ServiceScopeExtension.DisposeLast();
|
||||
Control.ScopedPage.DisposePreviousScope();
|
||||
}
|
||||
|
||||
AppInstance.GetCurrent().UnregisterKey();
|
||||
@@ -54,11 +53,11 @@ public static partial class Program
|
||||
ServiceProvider services = new ServiceCollection()
|
||||
|
||||
// Microsoft extension
|
||||
.AddLogging(builder => builder.AddDebug().AddDatabase())
|
||||
.AddLogging(builder => builder.AddDebug())
|
||||
.AddMemoryCache()
|
||||
|
||||
// Hutao extensions
|
||||
.AddJsonSerializerOptions()
|
||||
.AddJsonOptions()
|
||||
.AddDatebase()
|
||||
.AddInjections()
|
||||
.AddHttpClients()
|
||||
@@ -66,7 +65,7 @@ public static partial class Program
|
||||
// Discrete services
|
||||
.AddSingleton<IMessenger>(WeakReferenceMessenger.Default)
|
||||
|
||||
.BuildServiceProvider();
|
||||
.BuildServiceProvider(true);
|
||||
|
||||
Ioc.Default.ConfigureServices(services);
|
||||
return services;
|
||||
|
||||
648
src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs
generated
Normal file
648
src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs
generated
Normal file
@@ -0,0 +1,648 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 此代码由工具生成。
|
||||
// 运行时版本:4.0.30319.42000
|
||||
//
|
||||
// 对此文件的更改可能会导致不正确的行为,并且如果
|
||||
// 重新生成代码,这些更改将会丢失。
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Snap.Hutao.Resource.Localization {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 一个强类型的资源类,用于查找本地化的字符串等。
|
||||
/// </summary>
|
||||
// 此类是由 StronglyTypedResourceBuilder
|
||||
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
|
||||
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
|
||||
// (以 /str 作为命令选项),或重新生成 VS 项目。
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class SH {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal SH() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回此类使用的缓存的 ResourceManager 实例。
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Snap.Hutao.Resource.Localization.SH", typeof(SH).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重写当前线程的 CurrentUICulture 属性,对
|
||||
/// 使用此强类型资源类的所有资源查找执行重写。
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 胡桃 Dev {0} 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string AppDevNameAndVersion {
|
||||
get {
|
||||
return ResourceManager.GetString("AppDevNameAndVersion", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 胡桃 {0} 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string AppNameAndVersion {
|
||||
get {
|
||||
return ResourceManager.GetString("AppNameAndVersion", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 无效的 Uri 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ControlImageCachedImageInvalidResourceUri {
|
||||
get {
|
||||
return ResourceManager.GetString("ControlImageCachedImageInvalidResourceUri", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 HTTP GET {0} 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ControlImageCompositionImageHttpRequest {
|
||||
get {
|
||||
return ResourceManager.GetString("ControlImageCompositionImageHttpRequest", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 应用 CompositionImage 的源时发生异常 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ControlImageCompositionImageSystemException {
|
||||
get {
|
||||
return ResourceManager.GetString("ControlImageCompositionImageSystemException", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 网格 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ControlPanelPanelSelectorDropdownGridName {
|
||||
get {
|
||||
return ResourceManager.GetString("ControlPanelPanelSelectorDropdownGridName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 列表 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ControlPanelPanelSelectorDropdownListName {
|
||||
get {
|
||||
return ResourceManager.GetString("ControlPanelPanelSelectorDropdownListName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 用户数据已损坏: {0} 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string CoreExceptionServiceUserdataCorruptedMessage {
|
||||
get {
|
||||
return ResourceManager.GetString("CoreExceptionServiceUserdataCorruptedMessage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 请勿在管理员模式下使用此功能 {0} 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string CoreIOPickerExtensionPickerExceptionInfoBarMessage {
|
||||
get {
|
||||
return ResourceManager.GetString("CoreIOPickerExtensionPickerExceptionInfoBarMessage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 无法打开文件选择器 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string CoreIOPickerExtensionPickerExceptionInfoBarTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("CoreIOPickerExtensionPickerExceptionInfoBarTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 启动游戏 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string CoreJumpListHelperLaunchGameItemDisplayName {
|
||||
get {
|
||||
return ResourceManager.GetString("CoreJumpListHelperLaunchGameItemDisplayName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 快捷操作 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string CoreJumpListHelperLaunchGameItemGroupName {
|
||||
get {
|
||||
return ResourceManager.GetString("CoreJumpListHelperLaunchGameItemGroupName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 胡桃实时便笺刷新任务 | 请勿编辑或删除。 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string CoreScheduleTaskHelperDailyNoteRefreshTaskDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("CoreScheduleTaskHelperDailyNoteRefreshTaskDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 信号量已经被释放,操作取消 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string CoreThreadingSemaphoreSlimDisposed {
|
||||
get {
|
||||
return ResourceManager.GetString("CoreThreadingSemaphoreSlimDisposed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 未检测到 WebView2 运行时 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string CoreWebView2HelperVersionUndetected {
|
||||
get {
|
||||
return ResourceManager.GetString("CoreWebView2HelperVersionUndetected", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 取消 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string FactoryContentDialogFactoryCancelCloseButtonText {
|
||||
get {
|
||||
return ResourceManager.GetString("FactoryContentDialogFactoryCancelCloseButtonText", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 确认 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string FactoryContentDialogFactoryConfirmPrimaryButtonText {
|
||||
get {
|
||||
return ResourceManager.GetString("FactoryContentDialogFactoryConfirmPrimaryButtonText", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 单个成就存档内发现多个相同的成就 Id 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceAchievementUserdataCorruptedInnerIdNotUnique {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceAchievementUserdataCorruptedInnerIdNotUnique", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 开始游戏 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceDailyNoteNotifierActionLaunchGameButton {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceDailyNoteNotifierActionLaunchGameButton", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 我知道了 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceDailyNoteNotifierActionLaunchGameDismiss {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceDailyNoteNotifierActionLaunchGameDismiss", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 请求异常 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceDailyNoteNotifierAttribution {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceDailyNoteNotifierAttribution", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 每日委托 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceDailyNoteNotifierDailyTask {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceDailyNoteNotifierDailyTask", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 奖励未领取 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceDailyNoteNotifierDailyTaskHint {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceDailyNoteNotifierDailyTaskHint", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 探索派遣 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceDailyNoteNotifierExpedition {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceDailyNoteNotifierExpedition", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 已完成 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceDailyNoteNotifierExpeditionAdaptiveHint {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceDailyNoteNotifierExpeditionAdaptiveHint", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 探索派遣已完成 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceDailyNoteNotifierExpeditionHint {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceDailyNoteNotifierExpeditionHint", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 洞天宝钱 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceDailyNoteNotifierHomeCoin {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceDailyNoteNotifierHomeCoin", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 当前洞天宝钱:{0} 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceDailyNoteNotifierHomeCoinCurrent {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceDailyNoteNotifierHomeCoinCurrent", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 多个提醒项达到设定值 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceDailyNoteNotifierMultiValueReached {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceDailyNoteNotifierMultiValueReached", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 原粹树脂 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceDailyNoteNotifierResin {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceDailyNoteNotifierResin", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 当前原粹树脂:{0} 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceDailyNoteNotifierResinCurrent {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceDailyNoteNotifierResinCurrent", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 实时便笺提醒 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceDailyNoteNotifierTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceDailyNoteNotifierTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 参量质变仪 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceDailyNoteNotifierTransformer {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceDailyNoteNotifierTransformer", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 准备完成 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceDailyNoteNotifierTransformerAdaptiveHint {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceDailyNoteNotifierTransformerAdaptiveHint", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 参量质变仪已准备完成 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceDailyNoteNotifierTransformerHint {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceDailyNoteNotifierTransformerHint", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 无法获取祈愿记录: {0} 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceGachaLogArchiveCollectionUserdataCorruptedMessage {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceGachaLogArchiveCollectionUserdataCorruptedMessage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 无法获取祈愿记录 End Id 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceGachaLogEndIdUserdataCorruptedMessage {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceGachaLogEndIdUserdataCorruptedMessage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 角色活动 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceGachaLogFactoryAvatarWishName {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceGachaLogFactoryAvatarWishName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 奔行世间 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceGachaLogFactoryPermanentWishName {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceGachaLogFactoryPermanentWishName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 神铸赋形 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceGachaLogFactoryWeaponWishName {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceGachaLogFactoryWeaponWishName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 请求验证密钥失败 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceGachaLogUrlProviderAuthkeyRequestFailed {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceGachaLogUrlProviderAuthkeyRequestFailed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 未正确提供原神路径,或当前设置的路径不正确 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceGachaLogUrlProviderCachePathInvalid {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceGachaLogUrlProviderCachePathInvalid", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 找不到原神内置浏览器缓存路径:\n{0} 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceGachaLogUrlProviderCachePathNotFound {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceGachaLogUrlProviderCachePathNotFound", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 未找到可用的 Url 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceGachaLogUrlProviderCacheUrlNotFound {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceGachaLogUrlProviderCacheUrlNotFound", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 提供的Url无效 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceGachaLogUrlProviderManualInputInvalid {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceGachaLogUrlProviderManualInputInvalid", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 存在多个匹配账号,请删除重复的账号 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceGameDetectGameAccountMultiMatched {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceGameDetectGameAccountMultiMatched", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 查询游戏资源信息 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceGameEnsureGameResourceQueryResourceInformation {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceGameEnsureGameResourceQueryResourceInformation", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 游戏文件操作失败: {0} 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceGameFileOperationExceptionMessage {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceGameFileOperationExceptionMessage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 选择游戏本体 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceGameLocatorFileOpenPickerCommitText {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceGameLocatorFileOpenPickerCommitText", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 找不到 Unity 日志文件 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceGameLocatorUnityLogFileNotFound {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceGameLocatorUnityLogFileNotFound", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 在 Unity 日志文件中找不到游戏路径 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceGameLocatorUnityLogGamePathNotFound {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceGameLocatorUnityLogGamePathNotFound", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 获取 Package Version 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceGamePackageRequestPackageVerion {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceGamePackageRequestPackageVerion", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 获取 Package Version 失败 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceGamePackageRequestPackageVerionFailed {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceGamePackageRequestPackageVerionFailed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 无法找到游戏路径,请前往设置修改 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceGamePathLocateFailed {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceGamePathLocateFailed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 找不到游戏配置文件 {0} 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceGameSetMultiChannelConfigFileNotFound {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceGameSetMultiChannelConfigFileNotFound", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 无法读取或保存配置文件,请以管理员模式重试 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceGameSetMultiChannelUnauthorizedAccess {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceGameSetMultiChannelUnauthorizedAccess", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 元数据服务尚未初始化,或初始化失败 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceMetadataNotInitialized {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceMetadataNotInitialized", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 元数据校验文件解析失败 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceMetadataParseFailed {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceMetadataParseFailed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 元数据校验文件下载失败 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceMetadataRequestFailed {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceMetadataRequestFailed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 尚未选择任何用户以及角色 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceUserAndRoleUnselected {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceUserAndRoleUnselected", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 多个用户记录为选中状态 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceUserCurrentMultiMatched {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceUserCurrentMultiMatched", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 用户 {0} 状态保存失败 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceUserCurrentUpdateAndSaveFailed {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceUserCurrentUpdateAndSaveFailed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 输入的 Cookie 必须包含 Mid 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceUserProcessCookieNoMid {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceUserProcessCookieNoMid", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 输入的 Cookie 必须包含 Stoken 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceUserProcessCookieNoStoken {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceUserProcessCookieNoStoken", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 输入的 Cookie 无法获取用户信息 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ServiceUserProcessCookieRequestUserInfoFailed {
|
||||
get {
|
||||
return ResourceManager.GetString("ServiceUserProcessCookieRequestUserInfoFailed", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
315
src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
Normal file
315
src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
Normal file
@@ -0,0 +1,315 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="AppDevNameAndVersion" xml:space="preserve">
|
||||
<value>胡桃 Dev {0}</value>
|
||||
</data>
|
||||
<data name="AppNameAndVersion" xml:space="preserve">
|
||||
<value>胡桃 {0}</value>
|
||||
</data>
|
||||
<data name="ControlImageCachedImageInvalidResourceUri" xml:space="preserve">
|
||||
<value>无效的 Uri</value>
|
||||
</data>
|
||||
<data name="ControlImageCompositionImageHttpRequest" xml:space="preserve">
|
||||
<value>HTTP GET {0}</value>
|
||||
</data>
|
||||
<data name="ControlImageCompositionImageSystemException" xml:space="preserve">
|
||||
<value>应用 CompositionImage 的源时发生异常</value>
|
||||
</data>
|
||||
<data name="ControlPanelPanelSelectorDropdownGridName" xml:space="preserve">
|
||||
<value>网格</value>
|
||||
</data>
|
||||
<data name="ControlPanelPanelSelectorDropdownListName" xml:space="preserve">
|
||||
<value>列表</value>
|
||||
</data>
|
||||
<data name="CoreExceptionServiceUserdataCorruptedMessage" xml:space="preserve">
|
||||
<value>用户数据已损坏: {0}</value>
|
||||
</data>
|
||||
<data name="CoreIOPickerExtensionPickerExceptionInfoBarMessage" xml:space="preserve">
|
||||
<value>请勿在管理员模式下使用此功能 {0}</value>
|
||||
</data>
|
||||
<data name="CoreIOPickerExtensionPickerExceptionInfoBarTitle" xml:space="preserve">
|
||||
<value>无法打开文件选择器</value>
|
||||
</data>
|
||||
<data name="CoreJumpListHelperLaunchGameItemDisplayName" xml:space="preserve">
|
||||
<value>启动游戏</value>
|
||||
</data>
|
||||
<data name="CoreJumpListHelperLaunchGameItemGroupName" xml:space="preserve">
|
||||
<value>快捷操作</value>
|
||||
</data>
|
||||
<data name="CoreScheduleTaskHelperDailyNoteRefreshTaskDescription" xml:space="preserve">
|
||||
<value>胡桃实时便笺刷新任务 | 请勿编辑或删除。</value>
|
||||
</data>
|
||||
<data name="CoreThreadingSemaphoreSlimDisposed" xml:space="preserve">
|
||||
<value>信号量已经被释放,操作取消</value>
|
||||
</data>
|
||||
<data name="CoreWebView2HelperVersionUndetected" xml:space="preserve">
|
||||
<value>未检测到 WebView2 运行时</value>
|
||||
</data>
|
||||
<data name="FactoryContentDialogFactoryCancelCloseButtonText" xml:space="preserve">
|
||||
<value>取消</value>
|
||||
</data>
|
||||
<data name="FactoryContentDialogFactoryConfirmPrimaryButtonText" xml:space="preserve">
|
||||
<value>确认</value>
|
||||
</data>
|
||||
<data name="ServiceAchievementUserdataCorruptedInnerIdNotUnique" xml:space="preserve">
|
||||
<value>单个成就存档内发现多个相同的成就 Id</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierActionLaunchGameButton" xml:space="preserve">
|
||||
<value>开始游戏</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierActionLaunchGameDismiss" xml:space="preserve">
|
||||
<value>我知道了</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierAttribution" xml:space="preserve">
|
||||
<value>请求异常</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierDailyTask" xml:space="preserve">
|
||||
<value>每日委托</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierDailyTaskHint" xml:space="preserve">
|
||||
<value>奖励未领取</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierExpedition" xml:space="preserve">
|
||||
<value>探索派遣</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierExpeditionAdaptiveHint" xml:space="preserve">
|
||||
<value>已完成</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierExpeditionHint" xml:space="preserve">
|
||||
<value>探索派遣已完成</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierHomeCoin" xml:space="preserve">
|
||||
<value>洞天宝钱</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierHomeCoinCurrent" xml:space="preserve">
|
||||
<value>当前洞天宝钱:{0}</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierMultiValueReached" xml:space="preserve">
|
||||
<value>多个提醒项达到设定值</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierResin" xml:space="preserve">
|
||||
<value>原粹树脂</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierResinCurrent" xml:space="preserve">
|
||||
<value>当前原粹树脂:{0}</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierTitle" xml:space="preserve">
|
||||
<value>实时便笺提醒</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierTransformer" xml:space="preserve">
|
||||
<value>参量质变仪</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierTransformerAdaptiveHint" xml:space="preserve">
|
||||
<value>准备完成</value>
|
||||
</data>
|
||||
<data name="ServiceDailyNoteNotifierTransformerHint" xml:space="preserve">
|
||||
<value>参量质变仪已准备完成</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogArchiveCollectionUserdataCorruptedMessage" xml:space="preserve">
|
||||
<value>无法获取祈愿记录: {0}</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogEndIdUserdataCorruptedMessage" xml:space="preserve">
|
||||
<value>无法获取祈愿记录 End Id</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogFactoryAvatarWishName" xml:space="preserve">
|
||||
<value>角色活动</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogFactoryPermanentWishName" xml:space="preserve">
|
||||
<value>奔行世间</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogFactoryWeaponWishName" xml:space="preserve">
|
||||
<value>神铸赋形</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogUrlProviderAuthkeyRequestFailed" xml:space="preserve">
|
||||
<value>请求验证密钥失败</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogUrlProviderCachePathInvalid" xml:space="preserve">
|
||||
<value>未正确提供原神路径,或当前设置的路径不正确</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogUrlProviderCachePathNotFound" xml:space="preserve">
|
||||
<value>找不到原神内置浏览器缓存路径:\n{0}</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogUrlProviderCacheUrlNotFound" xml:space="preserve">
|
||||
<value>未找到可用的 Url</value>
|
||||
</data>
|
||||
<data name="ServiceGachaLogUrlProviderManualInputInvalid" xml:space="preserve">
|
||||
<value>提供的Url无效</value>
|
||||
</data>
|
||||
<data name="ServiceGameDetectGameAccountMultiMatched" xml:space="preserve">
|
||||
<value>存在多个匹配账号,请删除重复的账号</value>
|
||||
</data>
|
||||
<data name="ServiceGameEnsureGameResourceQueryResourceInformation" xml:space="preserve">
|
||||
<value>查询游戏资源信息</value>
|
||||
</data>
|
||||
<data name="ServiceGameFileOperationExceptionMessage" xml:space="preserve">
|
||||
<value>游戏文件操作失败: {0}</value>
|
||||
</data>
|
||||
<data name="ServiceGameLocatorFileOpenPickerCommitText" xml:space="preserve">
|
||||
<value>选择游戏本体</value>
|
||||
</data>
|
||||
<data name="ServiceGameLocatorUnityLogFileNotFound" xml:space="preserve">
|
||||
<value>找不到 Unity 日志文件</value>
|
||||
</data>
|
||||
<data name="ServiceGameLocatorUnityLogGamePathNotFound" xml:space="preserve">
|
||||
<value>在 Unity 日志文件中找不到游戏路径</value>
|
||||
</data>
|
||||
<data name="ServiceGamePackageRequestPackageVerion" xml:space="preserve">
|
||||
<value>获取 Package Version</value>
|
||||
</data>
|
||||
<data name="ServiceGamePackageRequestPackageVerionFailed" xml:space="preserve">
|
||||
<value>获取 Package Version 失败</value>
|
||||
</data>
|
||||
<data name="ServiceGamePathLocateFailed" xml:space="preserve">
|
||||
<value>无法找到游戏路径,请前往设置修改</value>
|
||||
</data>
|
||||
<data name="ServiceGameSetMultiChannelConfigFileNotFound" xml:space="preserve">
|
||||
<value>找不到游戏配置文件 {0}</value>
|
||||
</data>
|
||||
<data name="ServiceGameSetMultiChannelUnauthorizedAccess" xml:space="preserve">
|
||||
<value>无法读取或保存配置文件,请以管理员模式重试</value>
|
||||
</data>
|
||||
<data name="ServiceMetadataNotInitialized" xml:space="preserve">
|
||||
<value>元数据服务尚未初始化,或初始化失败</value>
|
||||
</data>
|
||||
<data name="ServiceMetadataParseFailed" xml:space="preserve">
|
||||
<value>元数据校验文件解析失败</value>
|
||||
</data>
|
||||
<data name="ServiceMetadataRequestFailed" xml:space="preserve">
|
||||
<value>元数据校验文件下载失败</value>
|
||||
</data>
|
||||
<data name="ServiceUserAndRoleUnselected" xml:space="preserve">
|
||||
<value>尚未选择任何用户以及角色</value>
|
||||
</data>
|
||||
<data name="ServiceUserCurrentMultiMatched" xml:space="preserve">
|
||||
<value>多个用户记录为选中状态</value>
|
||||
</data>
|
||||
<data name="ServiceUserCurrentUpdateAndSaveFailed" xml:space="preserve">
|
||||
<value>用户 {0} 状态保存失败</value>
|
||||
</data>
|
||||
<data name="ServiceUserProcessCookieNoMid" xml:space="preserve">
|
||||
<value>输入的 Cookie 必须包含 Mid</value>
|
||||
</data>
|
||||
<data name="ServiceUserProcessCookieNoStoken" xml:space="preserve">
|
||||
<value>输入的 Cookie 必须包含 Stoken</value>
|
||||
</data>
|
||||
<data name="ServiceUserProcessCookieRequestUserInfoFailed" xml:space="preserve">
|
||||
<value>输入的 Cookie 无法获取用户信息</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -114,14 +114,14 @@ internal class AchievementService : IAchievementService
|
||||
List<BindingAchievement> results = new();
|
||||
foreach (MetadataAchievement meta in metadata)
|
||||
{
|
||||
EntityAchievement? entity;
|
||||
EntityAchievement? entity = null;
|
||||
try
|
||||
{
|
||||
entity = entities.SingleOrDefault(e => e.Id == meta.Id);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
throw new UserdataCorruptedException("单个成就存档内发现多个相同的成就 Id", ex);
|
||||
ThrowHelper.UserdataCorrupted(SH.ServiceAchievementUserdataCorruptedInnerIdNotUnique, ex);
|
||||
}
|
||||
|
||||
entity ??= EntityAchievement.Create(archiveId, meta.Id);
|
||||
@@ -154,7 +154,6 @@ internal class AchievementService : IAchievementService
|
||||
/// <inheritdoc/>
|
||||
public async Task<ImportResult> ImportFromUIAFAsync(EntityArchive archive, List<UIAFItem> list, ImportStrategy strategy)
|
||||
{
|
||||
// return Task.Run(() => ImportFromUIAF(archive, list, strategy));
|
||||
await ThreadHelper.SwitchToBackgroundAsync();
|
||||
|
||||
Guid archiveId = archive.InnerId;
|
||||
@@ -190,7 +189,7 @@ internal class AchievementService : IAchievementService
|
||||
public void SaveAchievements(EntityArchive archive, IList<BindingAchievement> achievements)
|
||||
{
|
||||
string name = archive.Name;
|
||||
logger.LogInformation(EventIds.Achievement, "Begin saving achievements for [{name}]", name);
|
||||
logger.LogInformation("Begin saving achievements for [{name}]", name);
|
||||
ValueStopwatch stopwatch = ValueStopwatch.StartNew();
|
||||
|
||||
IEnumerable<EntityAchievement> newData = achievements
|
||||
@@ -200,8 +199,8 @@ internal class AchievementService : IAchievementService
|
||||
ImportResult result = achievementDbOperation.Overwrite(archive.InnerId, newData);
|
||||
|
||||
double time = stopwatch.GetElapsedTime().TotalMilliseconds;
|
||||
logger.LogInformation(EventIds.Achievement, "{add} added, {update} updated, {remove} removed", result.Add, result.Update, result.Remove);
|
||||
logger.LogInformation(EventIds.Achievement, "Save achievements for [{name}] completed in {time}ms", name, time);
|
||||
logger.LogInformation("{add} added, {update} updated, {remove} removed", result.Add, result.Update, result.Remove);
|
||||
logger.LogInformation("Save achievements for [{name}] completed in {time}ms", name, time);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -57,7 +57,7 @@ internal partial class AnnouncementService : IAnnouncementService
|
||||
Dictionary<int, string> contentMap = contents
|
||||
.ToDictionary(id => id.AnnId, content => content.Content);
|
||||
|
||||
// 将活动公告置于上方
|
||||
// 将活动公告置于前方
|
||||
wrapper.List.Reverse();
|
||||
|
||||
// 将公告内容联入公告列表
|
||||
|
||||
@@ -35,11 +35,10 @@ internal class SummaryFactoryImplementation
|
||||
return new()
|
||||
{
|
||||
Player = SummaryHelper.CreatePlayer(playerInfo),
|
||||
Avatars = avatarInfos.Where(a => !AvatarIds.IsPlayer(a.AvatarId)).Select(a =>
|
||||
{
|
||||
SummaryAvatarFactory summaryAvatarFactory = new(metadataContext, a);
|
||||
return summaryAvatarFactory.CreateAvatar();
|
||||
}).ToList(),
|
||||
Avatars = avatarInfos
|
||||
.Where(a => !AvatarIds.IsPlayer(a.AvatarId))
|
||||
.Select(a => new SummaryAvatarFactory(metadataContext, a).CreateAvatar())
|
||||
.ToList(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,10 +54,10 @@ internal class DailyNoteNotifier
|
||||
if (!entry.ResinNotifySuppressed)
|
||||
{
|
||||
notifyInfos.Add(new(
|
||||
"原粹树脂",
|
||||
SH.ServiceDailyNoteNotifierResin,
|
||||
"ms-appx:///Resource/Icon/UI_ItemIcon_210_256.png",
|
||||
$"{entry.DailyNote.CurrentResin}",
|
||||
$"当前原粹树脂:{entry.DailyNote.CurrentResin}"));
|
||||
string.Format(SH.ServiceDailyNoteNotifierResinCurrent, entry.DailyNote.CurrentResin)));
|
||||
entry.ResinNotifySuppressed = true;
|
||||
}
|
||||
}
|
||||
@@ -71,10 +71,10 @@ internal class DailyNoteNotifier
|
||||
if (!entry.HomeCoinNotifySuppressed)
|
||||
{
|
||||
notifyInfos.Add(new(
|
||||
"洞天宝钱",
|
||||
SH.ServiceDailyNoteNotifierHomeCoin,
|
||||
"ms-appx:///Resource/Icon/UI_ItemIcon_204.png",
|
||||
$"{entry.DailyNote.CurrentHomeCoin}",
|
||||
$"当前洞天宝钱:{entry.DailyNote.CurrentHomeCoin}"));
|
||||
string.Format(SH.ServiceDailyNoteNotifierHomeCoinCurrent, entry.DailyNote.CurrentHomeCoin)));
|
||||
entry.HomeCoinNotifySuppressed = true;
|
||||
}
|
||||
}
|
||||
@@ -88,9 +88,9 @@ internal class DailyNoteNotifier
|
||||
if (!entry.DailyTaskNotifySuppressed)
|
||||
{
|
||||
notifyInfos.Add(new(
|
||||
"每日委托",
|
||||
SH.ServiceDailyNoteNotifierDailyTask,
|
||||
"ms-appx:///Resource/Icon/UI_MarkQuest_Events_Proce.png",
|
||||
$"奖励待领取",
|
||||
SH.ServiceDailyNoteNotifierDailyTaskHint,
|
||||
entry.DailyNote.ExtraTaskRewardDescription));
|
||||
entry.DailyTaskNotifySuppressed = true;
|
||||
}
|
||||
@@ -105,10 +105,10 @@ internal class DailyNoteNotifier
|
||||
if (!entry.TransformerNotifySuppressed)
|
||||
{
|
||||
notifyInfos.Add(new(
|
||||
"参量质变仪",
|
||||
SH.ServiceDailyNoteNotifierTransformer,
|
||||
"ms-appx:///Resource/Icon/UI_ItemIcon_220021.png",
|
||||
$"准备完成",
|
||||
"参量质变仪已准备完成"));
|
||||
SH.ServiceDailyNoteNotifierTransformerAdaptiveHint,
|
||||
SH.ServiceDailyNoteNotifierTransformerHint));
|
||||
entry.TransformerNotifySuppressed = true;
|
||||
}
|
||||
}
|
||||
@@ -122,10 +122,10 @@ internal class DailyNoteNotifier
|
||||
if (!entry.ExpeditionNotifySuppressed)
|
||||
{
|
||||
notifyInfos.Add(new(
|
||||
"探索派遣",
|
||||
AvatarIconConverter.IconNameToUri("UI_AvatarIcon_Side_None.png").ToString(),
|
||||
$"已完成",
|
||||
"探索派遣已完成"));
|
||||
SH.ServiceDailyNoteNotifierExpedition,
|
||||
Web.HutaoEndpoints.UIAvatarIconSideNone.ToString(), // TODO: embed this
|
||||
SH.ServiceDailyNoteNotifierExpeditionAdaptiveHint,
|
||||
SH.ServiceDailyNoteNotifierExpeditionHint));
|
||||
entry.ExpeditionNotifySuppressed = true;
|
||||
}
|
||||
}
|
||||
@@ -150,7 +150,7 @@ internal class DailyNoteNotifier
|
||||
.GetActionTicketByStokenAsync("game_role", entry.User)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
string? attribution = "请求异常";
|
||||
string? attribution = SH.ServiceDailyNoteNotifierAttribution;
|
||||
if (actionTicketResponse.IsOk())
|
||||
{
|
||||
Response<ListWrapper<UserGameRole>> rolesResponse = await scope.ServiceProvider
|
||||
@@ -166,10 +166,13 @@ internal class DailyNoteNotifier
|
||||
}
|
||||
|
||||
ToastContentBuilder builder = new ToastContentBuilder()
|
||||
.AddHeader("DAILYNOTE", "实时便笺提醒", "DAILYNOTE")
|
||||
.AddHeader("DAILYNOTE", SH.ServiceDailyNoteNotifierTitle, "DAILYNOTE")
|
||||
.AddAttributionText(attribution)
|
||||
.AddButton(new ToastButton().SetContent("开始游戏").AddArgument("Action", "LaunchGame").AddArgument("Uid", entry.Uid))
|
||||
.AddButton(new ToastButtonDismiss("我知道了"));
|
||||
.AddButton(new ToastButton()
|
||||
.SetContent(SH.ServiceDailyNoteNotifierActionLaunchGameButton)
|
||||
.AddArgument("Action", Core.LifeCycle.Activation.LaunchGame)
|
||||
.AddArgument("Uid", entry.Uid))
|
||||
.AddButton(new ToastButtonDismiss(SH.ServiceDailyNoteNotifierActionLaunchGameDismiss));
|
||||
|
||||
if (appDbContext.Settings.SingleOrAdd(SettingEntry.DailyNoteReminderNotify, SettingEntryHelper.FalseString).GetBoolean())
|
||||
{
|
||||
@@ -178,7 +181,7 @@ internal class DailyNoteNotifier
|
||||
|
||||
if (notifyInfos.Count > 2)
|
||||
{
|
||||
builder.AddText("多个提醒项达到设定值");
|
||||
builder.AddText(SH.ServiceDailyNoteNotifierMultiValueReached);
|
||||
|
||||
// Desktop and Mobile started supporting adaptive toasts in API contract 3 (Anniversary Update)
|
||||
if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 3))
|
||||
|
||||
@@ -65,9 +65,9 @@ internal class GachaStatisticsFactory : IGachaStatisticsFactory
|
||||
Dictionary<WeaponId, Weapon> weaponMap,
|
||||
bool isEmptyHistoryWishVisible)
|
||||
{
|
||||
TypedWishSummaryBuilder permanentWishBuilder = new("奔行世间", TypedWishSummaryBuilder.IsPermanentWish, 90, 10);
|
||||
TypedWishSummaryBuilder avatarWishBuilder = new("角色活动", TypedWishSummaryBuilder.IsAvatarEventWish, 90, 10);
|
||||
TypedWishSummaryBuilder weaponWishBuilder = new("神铸赋形", TypedWishSummaryBuilder.IsWeaponEventWish, 80, 10);
|
||||
TypedWishSummaryBuilder permanentWishBuilder = new(SH.ServiceGachaLogFactoryPermanentWishName, TypedWishSummaryBuilder.IsPermanentWish, 90, 10);
|
||||
TypedWishSummaryBuilder avatarWishBuilder = new(SH.ServiceGachaLogFactoryAvatarWishName, TypedWishSummaryBuilder.IsAvatarEventWish, 90, 10);
|
||||
TypedWishSummaryBuilder weaponWishBuilder = new(SH.ServiceGachaLogFactoryWeaponWishName, TypedWishSummaryBuilder.IsWeaponEventWish, 80, 10);
|
||||
|
||||
Dictionary<Avatar, int> orangeAvatarCounter = new();
|
||||
Dictionary<Avatar, int> purpleAvatarCounter = new();
|
||||
@@ -136,6 +136,7 @@ internal class GachaStatisticsFactory : IGachaStatisticsFactory
|
||||
else
|
||||
{
|
||||
// ItemId place not correct.
|
||||
// TODO: check items id when importing
|
||||
Must.NeverHappen();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Core.Diagnostics;
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
using Snap.Hutao.Core.Logging;
|
||||
using Snap.Hutao.Extension;
|
||||
using Snap.Hutao.Model.Binding.Gacha;
|
||||
@@ -119,12 +120,14 @@ internal class GachaLogService : IGachaLogService
|
||||
await ThreadHelper.SwitchToMainThreadAsync();
|
||||
try
|
||||
{
|
||||
return archiveCollection ??= appDbContext.GachaArchives.AsNoTracking().ToObservableCollection();
|
||||
archiveCollection ??= appDbContext.GachaArchives.AsNoTracking().ToObservableCollection();
|
||||
}
|
||||
catch (SqliteException ex)
|
||||
{
|
||||
throw new Core.ExceptionService.UserdataCorruptedException($"无法获取祈愿记录: {ex.Message}", ex);
|
||||
ThrowHelper.UserdataCorrupted(string.Format(SH.ServiceGachaLogArchiveCollectionUserdataCorruptedMessage, ex.Message), ex);
|
||||
}
|
||||
|
||||
return archiveCollection;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -349,7 +352,7 @@ internal class GachaLogService : IGachaLogService
|
||||
}
|
||||
catch (SqliteException ex)
|
||||
{
|
||||
throw new Core.ExceptionService.UserdataCorruptedException("无法获取祈愿记录 End Id", ex);
|
||||
ThrowHelper.UserdataCorrupted(SH.ServiceGachaLogEndIdUserdataCorruptedMessage, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ internal class GachaLogUrlManualInputProvider : IGachaLogUrlProvider
|
||||
}
|
||||
else
|
||||
{
|
||||
return new(false, "提供的Url无效");
|
||||
return new(false, SH.ServiceGachaLogUrlProviderManualInputInvalid);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Binding.User;
|
||||
using Snap.Hutao.Service.User;
|
||||
using Snap.Hutao.Web.Hoyolab;
|
||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||
using Snap.Hutao.Web.Response;
|
||||
|
||||
namespace Snap.Hutao.Service.GachaLog;
|
||||
|
||||
@@ -34,32 +35,23 @@ internal class GachaLogUrlStokenProvider : IGachaLogUrlProvider
|
||||
/// <inheritdoc/>
|
||||
public async Task<ValueResult<bool, string>> GetQueryAsync()
|
||||
{
|
||||
Model.Binding.User.User? user = userService.Current;
|
||||
if (user != null && user.SelectedUserGameRole != null)
|
||||
if (UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid))
|
||||
{
|
||||
if (user.Stoken != null)
|
||||
{
|
||||
PlayerUid uid = (PlayerUid)user.SelectedUserGameRole;
|
||||
GenAuthKeyData data = GenAuthKeyData.CreateForWebViewGacha(uid);
|
||||
GenAuthKeyData data = GenAuthKeyData.CreateForWebViewGacha(userAndUid.Uid);
|
||||
Response<GameAuthKey> authkeyResponse = await bindingClient2.GenerateAuthenticationKeyAsync(userAndUid.User, data).ConfigureAwait(false);
|
||||
|
||||
Web.Response.Response<GameAuthKey> authkeyResponse = await bindingClient2.GenerateAuthenticationKeyAsync(user.Entity, data).ConfigureAwait(false);
|
||||
if (authkeyResponse.IsOk())
|
||||
{
|
||||
return new(true, GachaLogConfigration.AsQuery(data, authkeyResponse.Data));
|
||||
}
|
||||
else
|
||||
{
|
||||
return new(false, "请求验证密钥失败");
|
||||
}
|
||||
if (authkeyResponse.IsOk())
|
||||
{
|
||||
return new(true, GachaLogConfigration.AsQuery(data, authkeyResponse.Data));
|
||||
}
|
||||
else
|
||||
{
|
||||
return new(false, "当前用户的 Cookie 不包含 Stoken");
|
||||
return new(false, SH.ServiceGachaLogUrlProviderAuthkeyRequestFailed);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return new(false, "尚未选择要刷新的用户以及角色");
|
||||
return new(false, SH.ServiceUserAndRoleUnselected);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@ internal class GachaLogUrlWebCacheProvider : IGachaLogUrlProvider
|
||||
{
|
||||
if (tempFile == null)
|
||||
{
|
||||
return new(false, $"找不到原神内置浏览器缓存路径:\n{cacheFile}");
|
||||
return new(false, string.Format(SH.ServiceGachaLogUrlProviderCachePathNotFound, cacheFile));
|
||||
}
|
||||
|
||||
using (FileStream fileStream = new(tempFile.Path, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
@@ -65,14 +65,14 @@ internal class GachaLogUrlWebCacheProvider : IGachaLogUrlProvider
|
||||
{
|
||||
await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false);
|
||||
string? result = Match(memoryStream, cacheFile.Contains(GameConstants.GenshinImpactData));
|
||||
return new(!string.IsNullOrEmpty(result), result ?? "未找到可用的 Url");
|
||||
return new(!string.IsNullOrEmpty(result), result ?? SH.ServiceGachaLogUrlProviderCacheUrlNotFound);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return new(false, "未正确提供原神路径,或当前设置的路径不正确");
|
||||
return new(false, SH.ServiceGachaLogUrlProviderCachePathInvalid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ internal class GameFileOperationException : Exception
|
||||
/// <param name="message">消息</param>
|
||||
/// <param name="innerException">内部错误</param>
|
||||
public GameFileOperationException(string message, Exception innerException)
|
||||
: base($"游戏文件操作失败: {message}", innerException)
|
||||
: base(string.Format(SH.ServiceGameFileOperationExceptionMessage, message), innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
using Snap.Hutao.Core.IO.Ini;
|
||||
using Snap.Hutao.Extension;
|
||||
using Snap.Hutao.Model.Binding.LaunchGame;
|
||||
@@ -92,7 +93,7 @@ internal class GameService : IGameService
|
||||
}
|
||||
else
|
||||
{
|
||||
return new(false, "请启动游戏后再次尝试");
|
||||
return new(false, SH.ServiceGamePathLocateFailed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,15 +182,15 @@ internal class GameService : IGameService
|
||||
}
|
||||
catch (FileNotFoundException ex)
|
||||
{
|
||||
throw new GameFileOperationException($"找不到游戏配置文件 {configPath}", ex);
|
||||
throw new GameFileOperationException(string.Format(SH.ServiceGameSetMultiChannelConfigFileNotFound, configPath), ex);
|
||||
}
|
||||
catch (DirectoryNotFoundException ex)
|
||||
{
|
||||
throw new GameFileOperationException($"找不到游戏配置文件 {configPath}", ex);
|
||||
throw new GameFileOperationException(string.Format(SH.ServiceGameSetMultiChannelConfigFileNotFound, configPath), ex);
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
throw new GameFileOperationException($"无法读取或保存配置文件,请以管理员模式重试。", ex);
|
||||
throw new GameFileOperationException(SH.ServiceGameSetMultiChannelUnauthorizedAccess, ex);
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
@@ -236,7 +237,7 @@ internal class GameService : IGameService
|
||||
string gameFolder = Path.GetDirectoryName(gamePath)!;
|
||||
string gameFileName = Path.GetFileName(gamePath);
|
||||
|
||||
progress.Report(new("查询游戏资源信息"));
|
||||
progress.Report(new(SH.ServiceGameEnsureGameResourceQueryResourceInformation));
|
||||
Response<GameResource> response = await Ioc.Default
|
||||
.GetRequiredService<ResourceClient>()
|
||||
.GetResourceAsync(launchScheme)
|
||||
@@ -321,6 +322,7 @@ internal class GameService : IGameService
|
||||
}
|
||||
|
||||
// https://docs.unity.cn/cn/current/Manual/PlayerCommandLineArguments.html
|
||||
// TODO: impl monitor option.
|
||||
string commandLine = new CommandLineBuilder()
|
||||
.AppendIf("-popupwindow", configuration.IsBorderless)
|
||||
.Append("-screen-fullscreen", configuration.IsFullScreen ? 1 : 0)
|
||||
@@ -343,31 +345,24 @@ internal class GameService : IGameService
|
||||
|
||||
using (await gameSemaphore.EnterAsync().ConfigureAwait(false))
|
||||
{
|
||||
try
|
||||
if (configuration.UnlockFPS)
|
||||
{
|
||||
if (configuration.UnlockFPS)
|
||||
{
|
||||
IGameFpsUnlocker unlocker = new GameFpsUnlocker(game, configuration.TargetFPS);
|
||||
IGameFpsUnlocker unlocker = new GameFpsUnlocker(game, configuration.TargetFPS);
|
||||
|
||||
TimeSpan findModuleDelay = TimeSpan.FromMilliseconds(100);
|
||||
TimeSpan findModuleLimit = TimeSpan.FromMilliseconds(10000);
|
||||
TimeSpan adjustFpsDelay = TimeSpan.FromMilliseconds(2000);
|
||||
if (game.Start())
|
||||
{
|
||||
await unlocker.UnlockAsync(findModuleDelay, findModuleLimit, adjustFpsDelay).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
TimeSpan findModuleDelay = TimeSpan.FromMilliseconds(100);
|
||||
TimeSpan findModuleLimit = TimeSpan.FromMilliseconds(10000);
|
||||
TimeSpan adjustFpsDelay = TimeSpan.FromMilliseconds(2000);
|
||||
if (game.Start())
|
||||
{
|
||||
if (game.Start())
|
||||
{
|
||||
await game.WaitForExitAsync().ConfigureAwait(false);
|
||||
}
|
||||
await unlocker.UnlockAsync(findModuleDelay, findModuleLimit, adjustFpsDelay).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Win32Exception)
|
||||
else
|
||||
{
|
||||
// 通常是用户取消了UAC
|
||||
if (game.Start())
|
||||
{
|
||||
await game.WaitForExitAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -380,14 +375,14 @@ internal class GameService : IGameService
|
||||
string? registrySdk = RegistryInterop.Get();
|
||||
if (!string.IsNullOrEmpty(registrySdk))
|
||||
{
|
||||
GameAccount? account;
|
||||
GameAccount? account = null;
|
||||
try
|
||||
{
|
||||
account = gameAccounts.SingleOrDefault(a => a.MihoyoSDK == registrySdk);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
throw new Core.ExceptionService.UserdataCorruptedException("已存在多个匹配账号,请先删除重复的账号", ex);
|
||||
ThrowHelper.UserdataCorrupted(SH.ServiceGameDetectGameAccountMultiMatched, ex);
|
||||
}
|
||||
|
||||
if (account == null)
|
||||
|
||||
@@ -36,7 +36,11 @@ internal class ManualGameLocator : IGameLocator
|
||||
|
||||
private async Task<ValueResult<bool, string>> LocateInternalAsync(List<string> fileNames)
|
||||
{
|
||||
FileOpenPicker picker = pickerFactory.GetFileOpenPicker(PickerLocationId.Desktop, "选择游戏本体", ".exe");
|
||||
FileOpenPicker picker = pickerFactory.GetFileOpenPicker(
|
||||
PickerLocationId.Desktop,
|
||||
SH.ServiceGameLocatorFileOpenPickerCommitText,
|
||||
".exe");
|
||||
|
||||
(bool isPickerOk, FilePath file) = await picker.TryPickSingleFileAsync().ConfigureAwait(false);
|
||||
|
||||
if (isPickerOk)
|
||||
|
||||
@@ -20,6 +20,7 @@ internal partial class UnityLogGameLocator : IGameLocator
|
||||
public async Task<ValueResult<bool, string>> LocateGamePathAsync()
|
||||
{
|
||||
await ThreadHelper.SwitchToBackgroundAsync();
|
||||
|
||||
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
string logFilePathChinese = Path.Combine(appDataPath, @"..\LocalLow\miHoYo\原神\output_log.txt");
|
||||
string logFilePathOvsesea = Path.Combine(appDataPath, @"..\LocalLow\miHoYo\Genshin Impact\output_log.txt");
|
||||
@@ -36,7 +37,7 @@ internal partial class UnityLogGameLocator : IGameLocator
|
||||
Match matchResult = WarmupFileLine().Match(content);
|
||||
if (!matchResult.Success)
|
||||
{
|
||||
return new(false, $"在 Unity 日志文件中找不到游戏路径");
|
||||
return new(false, SH.ServiceGameLocatorUnityLogGamePathNotFound);
|
||||
}
|
||||
|
||||
string entryName = matchResult.Groups[0].Value.Replace("_Data", ".exe");
|
||||
@@ -45,7 +46,7 @@ internal partial class UnityLogGameLocator : IGameLocator
|
||||
}
|
||||
else
|
||||
{
|
||||
return new(false, $"找不到 Unity 日志文件");
|
||||
return new(false, SH.ServiceGameLocatorUnityLogFileNotFound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
using Snap.Hutao.Core.IO;
|
||||
using Snap.Hutao.Model.Binding.LaunchGame;
|
||||
using Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher;
|
||||
@@ -49,8 +50,8 @@ internal class PackageConverter
|
||||
Uri pkgVersionUri = new($"{scatteredFilesUrl}/pkg_version");
|
||||
ConvertDirection direction = targetScheme.IsOversea ? ConvertDirection.ChineseToOversea : ConvertDirection.OverseaToChinese;
|
||||
|
||||
progress.Report(new("获取 Package Version"));
|
||||
Dictionary<string, VersionItem> remoteItems;
|
||||
progress.Report(new(SH.ServiceGamePackageRequestPackageVerion));
|
||||
Dictionary<string, VersionItem> remoteItems = default!;
|
||||
try
|
||||
{
|
||||
using (Stream remoteSteam = await httpClient.GetStreamAsync(pkgVersionUri).ConfigureAwait(false))
|
||||
@@ -60,7 +61,7 @@ internal class PackageConverter
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
throw new PackageConvertException("下载 Package Version 失败", ex);
|
||||
ThrowHelper.PackageConvert(SH.ServiceGamePackageRequestPackageVerionFailed, ex);
|
||||
}
|
||||
|
||||
Dictionary<string, VersionItem> localItems;
|
||||
|
||||
@@ -33,7 +33,7 @@ internal class GameFpsUnlocker : IGameFpsUnlocker
|
||||
/// <param name="targetFPS">目标fps</param>
|
||||
public GameFpsUnlocker(Process gameProcess, int targetFPS)
|
||||
{
|
||||
Must.Range(targetFPS >= 30 && targetFPS <= 2000, "目标FPS超过允许值");
|
||||
Must.Range(targetFPS >= 30 && targetFPS <= 2000, "Target FPS threshold exceeded");
|
||||
|
||||
TargetFps = targetFPS;
|
||||
this.gameProcess = gameProcess;
|
||||
@@ -45,12 +45,12 @@ internal class GameFpsUnlocker : IGameFpsUnlocker
|
||||
/// <inheritdoc/>
|
||||
public async Task UnlockAsync(TimeSpan findModuleDelay, TimeSpan findModuleLimit, TimeSpan adjustFpsDelay)
|
||||
{
|
||||
Verify.Operation(isValid, "此解锁器已经失效");
|
||||
Verify.Operation(isValid, "This Unlocker is invalid");
|
||||
|
||||
MODULEENTRY32 unityPlayer = await FindModuleAsync(findModuleDelay, findModuleLimit).ConfigureAwait(false);
|
||||
|
||||
// Read UnityPlayer.dll
|
||||
TryReadModuleMemoryFindFpsAddress(unityPlayer);
|
||||
UnsafeTryReadModuleMemoryFindFpsAddress(unityPlayer);
|
||||
|
||||
// When player switch between scenes, we have to re adjust the fps
|
||||
// So we keep a loop here
|
||||
@@ -73,7 +73,7 @@ internal class GameFpsUnlocker : IGameFpsUnlocker
|
||||
return WriteProcessMemory(process.SafeHandle, (void*)baseAddress, lpBuffer, sizeof(int), null);
|
||||
}
|
||||
|
||||
private static unsafe MODULEENTRY32 FindModule(int processId, string moduleName)
|
||||
private static unsafe MODULEENTRY32 UnsafeFindModule(int processId, string moduleName)
|
||||
{
|
||||
HANDLE snapshot = CreateToolhelp32Snapshot(CREATE_TOOLHELP_SNAPSHOT_FLAGS.TH32CS_SNAPMODULE, (uint)processId);
|
||||
try
|
||||
@@ -109,7 +109,7 @@ internal class GameFpsUnlocker : IGameFpsUnlocker
|
||||
|
||||
while (true)
|
||||
{
|
||||
MODULEENTRY32 module = FindModule(gameProcess.Id, "UnityPlayer.dll");
|
||||
MODULEENTRY32 module = UnsafeFindModule(gameProcess.Id, "UnityPlayer.dll");
|
||||
if (!StructMarshal.IsDefault(module))
|
||||
{
|
||||
return module;
|
||||
@@ -145,7 +145,7 @@ internal class GameFpsUnlocker : IGameFpsUnlocker
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void TryReadModuleMemoryFindFpsAddress(MODULEENTRY32 unityPlayer)
|
||||
private unsafe void UnsafeTryReadModuleMemoryFindFpsAddress(MODULEENTRY32 unityPlayer)
|
||||
{
|
||||
bool readOk = UnsafeReadModuleMemory(gameProcess, unityPlayer, out Span<byte> image);
|
||||
Verify.Operation(readOk, "读取内存失败");
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Diagnostics;
|
||||
using Snap.Hutao.Win32;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.System.Diagnostics.ToolHelp;
|
||||
using static Windows.Win32.PInvoke;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Unlocker;
|
||||
|
||||
/// <summary>
|
||||
/// 游戏帧率解锁器异常
|
||||
/// </summary>
|
||||
internal class GameFpsUnlockerException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造一个新的用户数据损坏异常
|
||||
/// </summary>
|
||||
/// <param name="message">消息</param>
|
||||
/// <param name="innerException">内部错误</param>
|
||||
public GameFpsUnlockerException(Exception innerException)
|
||||
: base($"解锁帧率失败: {innerException.Message}", innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -94,13 +94,13 @@ internal partial class MetadataService : IMetadataService, IMetadataServiceIniti
|
||||
|
||||
if (metaMd5Map is null)
|
||||
{
|
||||
infoBarService.Error("元数据校验文件解析失败");
|
||||
infoBarService.Error(SH.ServiceMetadataParseFailed);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
infoBarService.Error(ex, "元数据校验文件下载失败");
|
||||
infoBarService.Error(ex, SH.ServiceMetadataRequestFailed);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ internal partial class MetadataService : IMetadataService, IMetadataServiceIniti
|
||||
|
||||
if (!skip)
|
||||
{
|
||||
logger.LogInformation(EventIds.MetadataFileMD5Check, "MD5 of {file} not matched, begin downloading", fileFullName);
|
||||
logger.LogInformation("MD5 of {file} not matched, begin downloading", fileFullName);
|
||||
|
||||
await DownloadMetadataAsync(fileFullName, token).ConfigureAwait(false);
|
||||
}
|
||||
@@ -172,7 +172,7 @@ internal partial class MetadataService : IMetadataService, IMetadataServiceIniti
|
||||
private async ValueTask<T> FromCacheOrFileAsync<T>(string fileName, CancellationToken token)
|
||||
where T : class
|
||||
{
|
||||
Verify.Operation(isInitialized, "元数据服务尚未初始化,或初始化失败");
|
||||
Verify.Operation(isInitialized, SH.ServiceMetadataNotInitialized);
|
||||
string cacheKey = $"{nameof(MetadataService)}.Cache.{fileName}";
|
||||
|
||||
if (memoryCache.TryGetValue(cacheKey, out object? value))
|
||||
|
||||
@@ -48,6 +48,7 @@ public interface INavigationService
|
||||
|
||||
/// <summary>
|
||||
/// 导航到指定类型的页面
|
||||
/// 若已经处于当前页面不会向页面发送消息
|
||||
/// </summary>
|
||||
/// <typeparam name="T">指定的页面类型</typeparam>
|
||||
/// <param name="data">要传递的数据</param>
|
||||
@@ -58,6 +59,7 @@ public interface INavigationService
|
||||
|
||||
/// <summary>
|
||||
/// 异步的导航到指定类型的页面
|
||||
/// 若已经处于当前页面则会向页面发送消息
|
||||
/// </summary>
|
||||
/// <typeparam name="TPage">指定的页面类型</typeparam>
|
||||
/// <param name="data">要传递的数据</param>
|
||||
|
||||
@@ -102,7 +102,7 @@ internal class NavigationService : INavigationService
|
||||
|
||||
if (currentType == pageType)
|
||||
{
|
||||
logger.LogInformation(EventIds.NavigationHistory, "Navigate to {pageType} : succeed, already in", pageType);
|
||||
logger.LogInformation("Navigate to {pageType} : succeed, already in", pageType);
|
||||
return NavigationResult.AlreadyNavigatedTo;
|
||||
}
|
||||
|
||||
@@ -112,11 +112,11 @@ internal class NavigationService : INavigationService
|
||||
try
|
||||
{
|
||||
navigated = Frame?.Navigate(pageType, data) ?? false;
|
||||
logger.LogInformation(EventIds.NavigationHistory, "Navigate to {pageType} : {result}", pageType, navigated ? "succeed" : "failed");
|
||||
logger.LogInformation("Navigate to {pageType} : {result}", pageType, navigated ? "succeed" : "failed");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(EventIds.NavigationFailed, ex, "An error occurred while navigating to {pageType}", pageType);
|
||||
logger.LogError(ex, "An error occurred while navigating to {pageType}", pageType);
|
||||
infoBarService.Error(ex);
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ internal class NavigationService : INavigationService
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(EventIds.NavigationFailed, ex, "异步导航时发生异常");
|
||||
logger.LogError(ex, "异步导航时发生异常");
|
||||
return NavigationResult.Failed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
using Snap.Hutao.Extension;
|
||||
using Snap.Hutao.Message;
|
||||
using Snap.Hutao.Model.Entity.Database;
|
||||
@@ -82,7 +83,7 @@ internal class UserService : IUserService
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
throw new Core.ExceptionService.UserdataCorruptedException($"用户 {currentUser.UserInfo?.Uid} 状态保存失败", ex);
|
||||
ThrowHelper.UserdataCorrupted(string.Format(SH.ServiceUserCurrentUpdateAndSaveFailed, currentUser.UserInfo?.Uid), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,15 +105,10 @@ internal class UserService : IUserService
|
||||
await ThreadHelper.SwitchToBackgroundAsync();
|
||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||
{
|
||||
try
|
||||
{
|
||||
// Note: cascade deleted dailynotes
|
||||
await scope.ServiceProvider.GetRequiredService<AppDbContext>().Users.RemoveAndSaveAsync(user.Entity).ConfigureAwait(false);
|
||||
}
|
||||
catch (DbUpdateConcurrencyException ex)
|
||||
{
|
||||
throw new Core.ExceptionService.UserdataCorruptedException("用户已被其他功能删除", ex);
|
||||
}
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
await appDbContext.Users
|
||||
.ExecuteDeleteWhereAsync(u => u.InnerId == user.Entity.InnerId)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
messenger.Send(new UserRemovedMessage(user.Entity));
|
||||
@@ -144,7 +140,7 @@ internal class UserService : IUserService
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
throw new Core.ExceptionService.UserdataCorruptedException("无法设置当前用户", ex);
|
||||
throw new UserdataCorruptedException(SH.ServiceUserCurrentMultiMatched, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,7 +197,7 @@ internal class UserService : IUserService
|
||||
|
||||
if (mid == null)
|
||||
{
|
||||
return new(UserOptionResult.Invalid, "输入的Cookie无法获取用户信息");
|
||||
return new(UserOptionResult.Invalid, SH.ServiceUserProcessCookieNoMid);
|
||||
}
|
||||
|
||||
// 检查 mid 对应用户是否存在
|
||||
@@ -222,7 +218,7 @@ internal class UserService : IUserService
|
||||
}
|
||||
else
|
||||
{
|
||||
return new(UserOptionResult.Invalid, "必须包含 Stoken");
|
||||
return new(UserOptionResult.Invalid, SH.ServiceUserProcessCookieNoStoken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -266,7 +262,7 @@ internal class UserService : IUserService
|
||||
}
|
||||
else
|
||||
{
|
||||
return new(UserOptionResult.Invalid, "输入的 Cookie 无法获取用户信息");
|
||||
return new(UserOptionResult.Invalid, SH.ServiceUserProcessCookieRequestUserInfoFailed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,13 +174,13 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Validation" Version="17.0.65" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Validation" Version="17.6.4-alpha" />
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.2.188-beta">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.755" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.2.221209.1" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.2.230118.102" />
|
||||
<PackageReference Include="StyleCop.Analyzers.Unstable" Version="1.2.0.435">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
@@ -438,4 +438,20 @@
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Control\Extension\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Resource\Localization\SH.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>SH.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Resource\Localization\SH.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>SH.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -5,7 +5,7 @@ using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using Snap.Hutao.Control;
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Control.Theme;
|
||||
using Snap.Hutao.Service.Navigation;
|
||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
|
||||
using Windows.System;
|
||||
|
||||
@@ -100,7 +100,6 @@
|
||||
<wsc:Setting Content="{Binding Overview.SpiralAbyssBattleAverage}" Header="平均战斗次数"/>
|
||||
</wsc:SettingsGroup>
|
||||
</StackPanel>
|
||||
|
||||
</Flyout>
|
||||
</AppBarButton.Flyout>
|
||||
</AppBarButton>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<Page
|
||||
<shc:ScopedPage
|
||||
x:Class="Snap.Hutao.View.Page.SettingPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:shc="using:Snap.Hutao.Control"
|
||||
xmlns:shv="using:Snap.Hutao.ViewModel"
|
||||
xmlns:wsc="using:WinUICommunity.SettingsUI.Controls"
|
||||
d:DataContext="{d:DesignInstance shv:SettingViewModel}"
|
||||
@@ -214,4 +215,4 @@
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Page>
|
||||
</shc:ScopedPage>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using Snap.Hutao.Control;
|
||||
using Snap.Hutao.Service.Navigation;
|
||||
using Snap.Hutao.ViewModel;
|
||||
|
||||
@@ -10,25 +11,14 @@ namespace Snap.Hutao.View.Page;
|
||||
/// <summary>
|
||||
/// 设置页面
|
||||
/// </summary>
|
||||
public sealed partial class SettingPage : Microsoft.UI.Xaml.Controls.Page
|
||||
public sealed partial class SettingPage : ScopedPage
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造新的设置页面
|
||||
/// </summary>
|
||||
public SettingPage()
|
||||
{
|
||||
DataContext = Ioc.Default.GetRequiredService<SettingViewModel>();
|
||||
InitializeWith<SettingViewModel>();
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
if (e.Parameter is INavigationData data)
|
||||
{
|
||||
data.NotifyNavigationCompleted();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<Page
|
||||
<shc:ScopedPage
|
||||
x:Class="Snap.Hutao.View.Page.WikiAvatarPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
@@ -7,6 +7,7 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:mxic="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:shc="using:Snap.Hutao.Control"
|
||||
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
|
||||
xmlns:shci="using:Snap.Hutao.Control.Image"
|
||||
xmlns:shcm="using:Snap.Hutao.Control.Markup"
|
||||
@@ -602,4 +603,4 @@
|
||||
</Grid>
|
||||
<shvc:LoadingView IsLoading="{Binding Avatars, Converter={StaticResource EmptyObjectToBoolRevertConverter}}"/>
|
||||
</Grid>
|
||||
</Page>
|
||||
</shc:ScopedPage>
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using Snap.Hutao.Control;
|
||||
using Snap.Hutao.Service.Navigation;
|
||||
using Snap.Hutao.ViewModel;
|
||||
|
||||
@@ -10,25 +11,14 @@ namespace Snap.Hutao.View.Page;
|
||||
/// <summary>
|
||||
/// 角色资料页
|
||||
/// </summary>
|
||||
public sealed partial class WikiAvatarPage : Microsoft.UI.Xaml.Controls.Page
|
||||
public sealed partial class WikiAvatarPage : ScopedPage
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造一个新的角色资料页
|
||||
/// </summary>
|
||||
public WikiAvatarPage()
|
||||
{
|
||||
DataContext = Ioc.Default.GetRequiredService<WikiAvatarViewModel>();
|
||||
InitializeWith<WikiAvatarViewModel>();
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
if (e.Parameter is INavigationData data)
|
||||
{
|
||||
data.NotifyNavigationCompleted();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,9 +26,9 @@ public sealed partial class TitleView : UserControl
|
||||
public string Title
|
||||
{
|
||||
#if DEBUG
|
||||
get => $"胡桃 Dev Build";
|
||||
get => string.Format(SH.AppDevNameAndVersion, Core.CoreEnvironment.Version);
|
||||
#else
|
||||
get => $"胡桃 {Core.CoreEnvironment.Version}";
|
||||
get => string.Format(SH.AppNameAndVersion, Core.CoreEnvironment.Version);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
|
||||
xmlns:shv="using:Snap.Hutao.ViewModel"
|
||||
d:DataContext="{d:DesignInstance shv:WelcomeViewModel}"
|
||||
Unloaded="OnUnloaded"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<mxi:Interaction.Behaviors>
|
||||
@@ -34,6 +35,7 @@
|
||||
<ItemsControl.ItemContainerTransitions>
|
||||
<EntranceThemeTransition IsStaggeringEnabled="False"/>
|
||||
<AddDeleteThemeTransition/>
|
||||
<RepositionThemeTransition/>
|
||||
</ItemsControl.ItemContainerTransitions>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Snap.Hutao.ViewModel;
|
||||
|
||||
@@ -11,12 +12,21 @@ namespace Snap.Hutao.View;
|
||||
/// </summary>
|
||||
public sealed partial class WelcomeView : UserControl
|
||||
{
|
||||
private readonly IServiceScope serviceScope;
|
||||
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD>µĻ<C2B5>ӭ<EFBFBD><D3AD>ͼ
|
||||
/// </summary>
|
||||
public WelcomeView()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = Ioc.Default.GetRequiredService<WelcomeViewModel>();
|
||||
serviceScope = Ioc.Default.CreateScope();
|
||||
DataContext = serviceScope.ServiceProvider.GetRequiredService<WelcomeViewModel>();
|
||||
}
|
||||
|
||||
private void OnUnloaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
DataContext = null;
|
||||
serviceScope.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,6 +370,12 @@ public class MiHoYoJSInterface
|
||||
}
|
||||
}
|
||||
|
||||
private IJsResult? LogUnhandledMessage([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string message, params object?[] param)
|
||||
{
|
||||
logger.LogWarning(message, param);
|
||||
return default;
|
||||
}
|
||||
|
||||
private async Task<IJsResult?> TryGetJsResultFromJsParamAsync(JsParam param)
|
||||
{
|
||||
try
|
||||
@@ -391,7 +397,7 @@ public class MiHoYoJSInterface
|
||||
"login" => null,
|
||||
"pushPage" => await PushPageAsync(param).ConfigureAwait(false),
|
||||
"showLoading" => null,
|
||||
_ => logger.LogWarning<IJsResult>("Unhandled Message Type: {method}", param.Method),
|
||||
_ => LogUnhandledMessage("Unhandled Message Type: {method}", param.Method),
|
||||
};
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
|
||||
@@ -116,6 +116,11 @@ internal static class HutaoEndpoints
|
||||
/// </summary>
|
||||
public static readonly Uri UIItemIconNone = new(StaticFile("Bg", "UI_ItemIcon_None.png"));
|
||||
|
||||
/// <summary>
|
||||
/// UI_AvatarIcon_Side_None
|
||||
/// </summary>
|
||||
public static readonly Uri UIAvatarIconSideNone = new(StaticFile("AvatarIcon", "UI_AvatarIcon_Side_None.png"));
|
||||
|
||||
/// <summary>
|
||||
/// 压缩包资源
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user