fix developer hint visibility

This commit is contained in:
Lightczx
2023-07-07 22:22:43 +08:00
parent 5020621c46
commit f4fa08a939
99 changed files with 379 additions and 449 deletions

1
.gitignore vendored
View File

@@ -4,6 +4,7 @@ desktop.ini
*.pubxml *.pubxml
.vs/ .vs/
src/Snap.Hutao/_ReSharper.Caches
src/Snap.Hutao/Snap.Hutao/bin/ src/Snap.Hutao/Snap.Hutao/bin/
src/Snap.Hutao/Snap.Hutao/obj/ src/Snap.Hutao/Snap.Hutao/obj/

View File

@@ -35,6 +35,6 @@ internal sealed partial class AutoHeightBehavior : BehaviorBase<FrameworkElement
private void UpdateElement() private void UpdateElement()
{ {
AssociatedObject.Height = (double)AssociatedObject.ActualWidth * (TargetHeight / TargetWidth); AssociatedObject.Height = AssociatedObject.ActualWidth * (TargetHeight / TargetWidth);
} }
} }

View File

@@ -35,6 +35,6 @@ internal sealed partial class AutoWidthBehavior : BehaviorBase<FrameworkElement>
private void UpdateElement() private void UpdateElement()
{ {
AssociatedObject.Width = (double)AssociatedObject.Height * (TargetWidth / TargetHeight); AssociatedObject.Width = AssociatedObject.Height * (TargetWidth / TargetHeight);
} }
} }

View File

@@ -41,7 +41,7 @@ internal sealed class CachedImage : ImageEx
token.ThrowIfCancellationRequested(); token.ThrowIfCancellationRequested();
// BitmapImage initialize with a uri will increase image quality and loading speed. // BitmapImage initialize with a uri will increase image quality and loading speed.
return new BitmapImage(new(file)); return new BitmapImage(file.ToUri());
} }
catch (COMException) catch (COMException)
{ {
@@ -54,9 +54,5 @@ internal sealed class CachedImage : ImageEx
// task was explicitly canceled // task was explicitly canceled
return null; return null;
} }
catch
{
throw;
}
} }
} }

View File

@@ -34,7 +34,7 @@ internal abstract partial class CompositionImage : Microsoft.UI.Xaml.Controls.Co
/// <summary> /// <summary>
/// 构造一个新的单色图像 /// 构造一个新的单色图像
/// </summary> /// </summary>
public CompositionImage() protected CompositionImage()
{ {
serviceProvider = Ioc.Default; serviceProvider = Ioc.Default;
@@ -74,7 +74,7 @@ internal abstract partial class CompositionImage : Microsoft.UI.Xaml.Controls.Co
protected virtual async Task<LoadedImageSurface> LoadImageSurfaceAsync(string file, CancellationToken token) protected virtual async Task<LoadedImageSurface> LoadImageSurfaceAsync(string file, CancellationToken token)
{ {
TaskCompletionSource loadCompleteTaskSource = new(); TaskCompletionSource loadCompleteTaskSource = new();
LoadedImageSurface surface = LoadedImageSurface.StartLoadFromUri(new(file)); LoadedImageSurface surface = LoadedImageSurface.StartLoadFromUri(file.ToUri());
surface.LoadCompleted += (s, e) => loadCompleteTaskSource.TrySetResult(); surface.LoadCompleted += (s, e) => loadCompleteTaskSource.TrySetResult();
await loadCompleteTaskSource.Task.ConfigureAwait(true); await loadCompleteTaskSource.Task.ConfigureAwait(true);
return surface; return surface;

View File

@@ -20,21 +20,23 @@ internal sealed partial class Gradient : CompositionImage
private double imageAspectRatio; private double imageAspectRatio;
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnUpdateVisual(SpriteVisual spriteVisual) protected override void OnUpdateVisual(SpriteVisual? spriteVisual)
{ {
if (spriteVisual is not null) if (spriteVisual is null)
{ {
Height = Math.Clamp(ActualWidth / imageAspectRatio, 0D, MaxHeight); return;
spriteVisual.Size = ActualSize;
} }
Height = Math.Clamp(ActualWidth / imageAspectRatio, 0D, MaxHeight);
spriteVisual.Size = ActualSize;
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override async Task<LoadedImageSurface> LoadImageSurfaceAsync(string file, CancellationToken token) protected override async Task<LoadedImageSurface> LoadImageSurfaceAsync(string file, CancellationToken token)
{ {
TaskCompletionSource loadCompleteTaskSource = new(); TaskCompletionSource loadCompleteTaskSource = new();
LoadedImageSurface surface = LoadedImageSurface.StartLoadFromUri(new(file)); LoadedImageSurface surface = LoadedImageSurface.StartLoadFromUri(file.ToUri());
surface.LoadCompleted += (s, e) => loadCompleteTaskSource.TrySetResult(); surface.LoadCompleted += (_, _) => loadCompleteTaskSource.TrySetResult();
await loadCompleteTaskSource.Task.ConfigureAwait(true); await loadCompleteTaskSource.Task.ConfigureAwait(true);
imageAspectRatio = surface.NaturalSize.AspectRatio(); imageAspectRatio = surface.NaturalSize.AspectRatio();
return surface; return surface;

View File

@@ -6,8 +6,6 @@ namespace Snap.Hutao.Control.Image;
/// <summary> /// <summary>
/// 渐变锚点 /// 渐变锚点
/// </summary> /// </summary>
/// <param name="Offset">便宜</param>
/// <param name="Color">颜色</param>
[HighQuality] [HighQuality]
internal readonly struct GradientStop internal readonly struct GradientStop
{ {

View File

@@ -29,9 +29,9 @@ internal sealed class MonoChrome : CompositionImage
/// <inheritdoc/> /// <inheritdoc/>
protected override SpriteVisual CompositeSpriteVisual(Compositor compositor, LoadedImageSurface imageSurface) protected override SpriteVisual CompositeSpriteVisual(Compositor compositor, LoadedImageSurface imageSurface)
{ {
CompositionColorBrush blackLayerBursh = compositor.CreateColorBrush(Colors.Black); CompositionColorBrush blackLayerBrush = compositor.CreateColorBrush(Colors.Black);
CompositionSurfaceBrush imageSurfaceBrush = compositor.CompositeSurfaceBrush(imageSurface, stretch: CompositionStretch.Uniform, vRatio: 0f); CompositionSurfaceBrush imageSurfaceBrush = compositor.CompositeSurfaceBrush(imageSurface, stretch: CompositionStretch.Uniform, vRatio: 0f);
CompositionEffectBrush overlayBrush = compositor.CompositeBlendEffectBrush(blackLayerBursh, imageSurfaceBrush, BlendEffectMode.Overlay); CompositionEffectBrush overlayBrush = compositor.CompositeBlendEffectBrush(blackLayerBrush, imageSurfaceBrush, BlendEffectMode.Overlay);
CompositionEffectBrush opacityBrush = compositor.CompositeLuminanceToAlphaEffectBrush(overlayBrush); CompositionEffectBrush opacityBrush = compositor.CompositeLuminanceToAlphaEffectBrush(overlayBrush);
backgroundBrush = compositor.CreateColorBrush(); backgroundBrush = compositor.CreateColorBrush();

View File

@@ -40,7 +40,7 @@ internal struct Rgba32
/// 构造一个新的 RGBA8 颜色 /// 构造一个新的 RGBA8 颜色
/// </summary> /// </summary>
/// <param name="hex">色值字符串</param> /// <param name="hex">色值字符串</param>
public unsafe Rgba32(string hex) public Rgba32(string hex)
: this(Convert.ToUInt32(hex, 16)) : this(Convert.ToUInt32(hex, 16))
{ {
} }

View File

@@ -1,7 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved. // Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license. // Licensed under the MIT license.
using Microsoft.UI.Xaml;
using Windows.Foundation; using Windows.Foundation;
namespace Snap.Hutao.Control.Panel; namespace Snap.Hutao.Control.Panel;
@@ -35,7 +34,7 @@ internal sealed partial class AspectRatio : Microsoft.UI.Xaml.Controls.Control
} }
// 更高 // 更高
else if (ratioAvailable < ratio) if (ratioAvailable < ratio)
{ {
double newHeight = availableSize.Width / ratio; double newHeight = availableSize.Width / ratio;
return new Size(availableSize.Width, newHeight); return new Size(availableSize.Width, newHeight);

View File

@@ -47,19 +47,6 @@ internal static class Property<TOwner>
return DependencyProperty.Register(name, typeof(TProperty), typeof(TOwner), new(defaultValue)); return DependencyProperty.Register(name, typeof(TProperty), typeof(TOwner), new(defaultValue));
} }
/// <summary>
/// 注册依赖属性
/// </summary>
/// <typeparam name="TProperty">属性的类型</typeparam>
/// <param name="name">属性名称</param>
/// <param name="defaultValue">封装的默认值</param>
/// <param name="callback">属性更改回调</param>
/// <returns>注册的依赖属性</returns>
public static DependencyProperty DependBoxed<TProperty>(string name, object defaultValue, Action<DependencyObject, DependencyPropertyChangedEventArgs> callback)
{
return DependencyProperty.Register(name, typeof(TProperty), typeof(TOwner), new(defaultValue, new(callback)));
}
/// <summary> /// <summary>
/// 注册依赖属性 /// 注册依赖属性
/// </summary> /// </summary>

View File

@@ -64,7 +64,7 @@ internal class ScopedPage : Page
/// </summary> /// </summary>
/// <param name="extra">额外内容</param> /// <param name="extra">额外内容</param>
/// <returns>任务</returns> /// <returns>任务</returns>
public async Task NotifyRecipentAsync(INavigationData extra) public async Task NotifyRecipientAsync(INavigationData extra)
{ {
if (extra.Data != null && DataContext is INavigationRecipient recipient) if (extra.Data != null && DataContext is INavigationRecipient recipient)
{ {
@@ -100,7 +100,7 @@ internal class ScopedPage : Page
{ {
if (e.Parameter is INavigationData extra) if (e.Parameter is INavigationData extra)
{ {
NotifyRecipentAsync(extra).SafeForget(); NotifyRecipientAsync(extra).SafeForget();
} }
} }

View File

@@ -78,7 +78,7 @@ internal sealed class DescriptionTextBlock : ContentControl
} }
// color tag // color tag
else if (description.Slice(i, 2).SequenceEqual("<c")) else if (description.Slice(i, 2) is "<c")
{ {
AppendText(text, description[last..i]); AppendText(text, description[last..i]);
Rgba32 color = new(description.Slice(i + 8, 8).ToString()); Rgba32 color = new(description.Slice(i + 8, 8).ToString());
@@ -90,7 +90,7 @@ internal sealed class DescriptionTextBlock : ContentControl
} }
// italic // italic
else if (description.Slice(i, 2).SequenceEqual("<i")) else if (description.Slice(i, 2) is "<i")
{ {
AppendText(text, description[last..i]); AppendText(text, description[last..i]);

View File

@@ -8,7 +8,7 @@ namespace Snap.Hutao.Core.Abstraction;
/// </summary> /// </summary>
/// <typeparam name="TSelf">自身类型</typeparam> /// <typeparam name="TSelf">自身类型</typeparam>
[HighQuality] [HighQuality]
internal interface ICloneable<TSelf> internal interface ICloneable<out TSelf>
{ {
/// <summary> /// <summary>
/// 克隆 /// 克隆

View File

@@ -9,7 +9,7 @@ namespace Snap.Hutao.Core.Abstraction;
/// <typeparam name="T1">元组的第一个类型</typeparam> /// <typeparam name="T1">元组的第一个类型</typeparam>
/// <typeparam name="T2">元组的第二个类型</typeparam> /// <typeparam name="T2">元组的第二个类型</typeparam>
[HighQuality] [HighQuality]
internal interface IDeconstructable<T1, T2> internal interface IDeconstruct<T1, T2>
{ {
/// <summary> /// <summary>
/// 解构 /// 解构

View File

@@ -6,7 +6,7 @@ namespace Snap.Hutao.Core.Annotation;
/// <summary> /// <summary>
/// 指示此方法为命令的调用方法 /// 指示此方法为命令的调用方法
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] [AttributeUsage(AttributeTargets.Method, Inherited = false)]
internal sealed class CommandAttribute : Attribute internal sealed class CommandAttribute : Attribute
{ {
/// <summary> /// <summary>

View File

@@ -6,7 +6,7 @@ namespace Snap.Hutao.Core.Annotation;
/// <summary> /// <summary>
/// 指示此类自动生成构造器 /// 指示此类自动生成构造器
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] [AttributeUsage(AttributeTargets.Class, Inherited = false)]
internal sealed class ConstructorGeneratedAttribute : Attribute internal sealed class ConstructorGeneratedAttribute : Attribute
{ {
/// <summary> /// <summary>

View File

@@ -8,9 +8,8 @@ namespace Snap.Hutao.Core.Caching;
/// <summary> /// <summary>
/// 为图像缓存提供抽象 /// 为图像缓存提供抽象
/// </summary> /// </summary>
/// <typeparam name="T">缓存类型</typeparam>
[HighQuality] [HighQuality]
internal interface IImageCache : ICastableService internal interface IImageCache : ICastService
{ {
/// <summary> /// <summary>
/// Gets the file path containing cached item for given Uri /// Gets the file path containing cached item for given Uri

View File

@@ -46,8 +46,6 @@ internal sealed class ImageCache : IImageCache, IImageCacheFilePathOperation
/// Initializes a new instance of the <see cref="ImageCache"/> class. /// Initializes a new instance of the <see cref="ImageCache"/> class.
/// </summary> /// </summary>
/// <param name="serviceProvider">服务提供器</param> /// <param name="serviceProvider">服务提供器</param>
/// <param name="logger">日志器</param>
/// <param name="httpClientFactory">http客户端工厂</param>
public ImageCache(IServiceProvider serviceProvider) public ImageCache(IServiceProvider serviceProvider)
{ {
logger = serviceProvider.GetRequiredService<ILogger<ImageCache>>(); logger = serviceProvider.GetRequiredService<ILogger<ImageCache>>();
@@ -84,7 +82,7 @@ internal sealed class ImageCache : IImageCache, IImageCacheFilePathOperation
/// <inheritdoc/> /// <inheritdoc/>
public void Remove(in ReadOnlySpan<Uri> uriForCachedItems) public void Remove(in ReadOnlySpan<Uri> uriForCachedItems)
{ {
if (uriForCachedItems == null || uriForCachedItems.Length <= 0) if (uriForCachedItems.Length <= 0)
{ {
return; return;
} }
@@ -112,26 +110,28 @@ internal sealed class ImageCache : IImageCache, IImageCacheFilePathOperation
string fileName = GetCacheFileName(uri); string fileName = GetCacheFileName(uri);
string filePath = Path.Combine(GetCacheFolder(), fileName); string filePath = Path.Combine(GetCacheFolder(), fileName);
if (!File.Exists(filePath) || new FileInfo(filePath).Length == 0) if (File.Exists(filePath) && new FileInfo(filePath).Length != 0)
{ {
TaskCompletionSource taskCompletionSource = new(); return filePath;
try }
{
if (concurrentTasks.TryAdd(fileName, taskCompletionSource.Task))
{
await DownloadFileAsync(uri, filePath).ConfigureAwait(false);
}
else if (concurrentTasks.TryGetValue(fileName, out Task? task))
{
await task.ConfigureAwait(false);
}
concurrentTasks.TryRemove(fileName, out _); TaskCompletionSource taskCompletionSource = new();
} try
finally {
if (concurrentTasks.TryAdd(fileName, taskCompletionSource.Task))
{ {
taskCompletionSource.TrySetResult(); await DownloadFileAsync(uri, filePath).ConfigureAwait(false);
} }
else if (concurrentTasks.TryGetValue(fileName, out Task? task))
{
await task.ConfigureAwait(false);
}
concurrentTasks.TryRemove(fileName, out _);
}
finally
{
taskCompletionSource.TrySetResult();
} }
return filePath; return filePath;
@@ -198,21 +198,24 @@ internal sealed class ImageCache : IImageCache, IImageCacheFilePathOperation
} }
} }
} }
else if (message.StatusCode == HttpStatusCode.NotFound)
switch (message.StatusCode)
{ {
// directly goto https://static.hut.ao case HttpStatusCode.NotFound:
retryCount += 3; // directly goto https://static.hut.ao
} retryCount += 3;
else if (message.StatusCode == HttpStatusCode.TooManyRequests) break;
{ case HttpStatusCode.TooManyRequests:
retryCount++; {
TimeSpan delay = message.Headers.RetryAfter?.Delta ?? RetryCountToDelay[retryCount]; retryCount++;
logger.LogInformation("Retry {uri} after {delay}.", uri, delay); TimeSpan delay = message.Headers.RetryAfter?.Delta ?? RetryCountToDelay[retryCount];
await Task.Delay(delay).ConfigureAwait(false); logger.LogInformation("Retry {uri} after {delay}.", uri, delay);
} await Task.Delay(delay).ConfigureAwait(false);
else break;
{ }
return;
default:
return;
} }
} }
@@ -225,13 +228,15 @@ internal sealed class ImageCache : IImageCache, IImageCacheFilePathOperation
private string GetCacheFolder() private string GetCacheFolder()
{ {
if (cacheFolder == null) if (cacheFolder is not null)
{ {
baseFolder ??= serviceProvider.GetRequiredService<HutaoOptions>().LocalCache; return cacheFolder!;
DirectoryInfo info = Directory.CreateDirectory(Path.Combine(baseFolder, CacheFolderName));
cacheFolder = info.FullName;
} }
baseFolder ??= serviceProvider.GetRequiredService<HutaoOptions>().LocalCache;
DirectoryInfo info = Directory.CreateDirectory(Path.Combine(baseFolder, CacheFolderName));
cacheFolder = info.FullName;
return cacheFolder!; return cacheFolder!;
} }
} }

View File

@@ -58,11 +58,13 @@ internal sealed class CommandLineBuilder
s.Append(WhiteSpace); s.Append(WhiteSpace);
s.Append(key); s.Append(key);
if (!string.IsNullOrEmpty(value)) if (string.IsNullOrEmpty(value))
{ {
s.Append(WhiteSpace); continue;
s.Append(value);
} }
s.Append(WhiteSpace);
s.Append(value);
} }
return s.ToString(); return s.ToString();

View File

@@ -6,6 +6,6 @@ namespace Snap.Hutao.Core.DependencyInjection.Abstraction;
/// <summary> /// <summary>
/// 可转换类型服务 /// 可转换类型服务
/// </summary> /// </summary>
internal interface ICastableService internal interface ICastService
{ {
} }

View File

@@ -4,12 +4,12 @@
namespace Snap.Hutao.Core.DependencyInjection.Abstraction; namespace Snap.Hutao.Core.DependencyInjection.Abstraction;
/// <summary> /// <summary>
/// 海外服/Hoyolab 可区分 /// 海外服/HoYoLAB 可区分
/// </summary> /// </summary>
internal interface IOverseaSupport internal interface IOverseaSupport
{ {
/// <summary> /// <summary>
/// 是否为 海外服/Hoyolab /// 是否为 海外服/HoYoLAB
/// </summary> /// </summary>
public bool IsOversea { get; } public bool IsOversea { get; }
} }

View File

@@ -8,7 +8,7 @@ namespace Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
/// 由源生成器生成注入代码 /// 由源生成器生成注入代码
/// </summary> /// </summary>
[HighQuality] [HighQuality]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] [AttributeUsage(AttributeTargets.Class, Inherited = false)]
internal sealed class HttpClientAttribute : Attribute internal sealed class HttpClientAttribute : Attribute
{ {
/// <summary> /// <summary>

View File

@@ -7,7 +7,7 @@ namespace Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
/// 配置首选Http消息处理器特性 /// 配置首选Http消息处理器特性
/// </summary> /// </summary>
[HighQuality] [HighQuality]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] [AttributeUsage(AttributeTargets.Class, Inherited = false)]
internal sealed class PrimaryHttpMessageHandlerAttribute : Attribute internal sealed class PrimaryHttpMessageHandlerAttribute : Attribute
{ {
/// <inheritdoc cref="System.Net.Http.HttpClientHandler.MaxConnectionsPerServer"/> /// <inheritdoc cref="System.Net.Http.HttpClientHandler.MaxConnectionsPerServer"/>

View File

@@ -9,7 +9,7 @@ namespace Snap.Hutao.Core.DependencyInjection;
/// 对象扩展 /// 对象扩展
/// </summary> /// </summary>
[HighQuality] [HighQuality]
internal static class CastableServiceExtension internal static class CastServiceExtension
{ {
/// <summary> /// <summary>
/// <see langword="as"/> 的链式调用扩展 /// <see langword="as"/> 的链式调用扩展
@@ -17,7 +17,7 @@ internal static class CastableServiceExtension
/// <typeparam name="T">目标转换类型</typeparam> /// <typeparam name="T">目标转换类型</typeparam>
/// <param name="service">对象</param> /// <param name="service">对象</param>
/// <returns>转换类型后的对象</returns> /// <returns>转换类型后的对象</returns>
public static T? As<T>(this ICastableService service) public static T? As<T>(this ICastService service)
where T : class where T : class
{ {
return service as T; return service as T;

View File

@@ -48,7 +48,7 @@ internal static class DependencyInjection
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static IServiceProvider InitializeCulture(this IServiceProvider serviceProvider) private static void InitializeCulture(this IServiceProvider serviceProvider)
{ {
AppOptions appOptions = serviceProvider.GetRequiredService<AppOptions>(); AppOptions appOptions = serviceProvider.GetRequiredService<AppOptions>();
appOptions.PreviousCulture = CultureInfo.CurrentCulture; appOptions.PreviousCulture = CultureInfo.CurrentCulture;
@@ -58,7 +58,5 @@ internal static class DependencyInjection
CultureInfo.CurrentCulture = cultureInfo; CultureInfo.CurrentCulture = cultureInfo;
CultureInfo.CurrentUICulture = cultureInfo; CultureInfo.CurrentUICulture = cultureInfo;
ApplicationLanguages.PrimaryLanguageOverride = cultureInfo.Name; ApplicationLanguages.PrimaryLanguageOverride = cultureInfo.Name;
return serviceProvider;
} }
} }

View File

@@ -25,6 +25,7 @@ internal static partial class IocHttpClientConfiguration
/// <summary> /// <summary>
/// 默认配置 /// 默认配置
/// </summary> /// </summary>
/// <param name="serviceProvider">服务提供器</param>
/// <param name="client">配置后的客户端</param> /// <param name="client">配置后的客户端</param>
private static void DefaultConfiguration(IServiceProvider serviceProvider, HttpClient client) private static void DefaultConfiguration(IServiceProvider serviceProvider, HttpClient client)
{ {
@@ -68,7 +69,7 @@ internal static partial class IocHttpClientConfiguration
/// <summary> /// <summary>
/// 对于需要添加动态密钥的客户端使用此配置 /// 对于需要添加动态密钥的客户端使用此配置
/// Hoyolab app /// HoYoLAB app
/// </summary> /// </summary>
/// <param name="client">配置后的客户端</param> /// <param name="client">配置后的客户端</param>
private static void XRpc3Configuration(HttpClient client) private static void XRpc3Configuration(HttpClient client)
@@ -84,7 +85,7 @@ internal static partial class IocHttpClientConfiguration
/// <summary> /// <summary>
/// 对于需要添加动态密钥的客户端使用此配置 /// 对于需要添加动态密钥的客户端使用此配置
/// Hoyolab web /// HoYoLAB web
/// </summary> /// </summary>
/// <param name="client">配置后的客户端</param> /// <param name="client">配置后的客户端</param>
private static void XRpc4Configuration(HttpClient client) private static void XRpc4Configuration(HttpClient client)

View File

@@ -0,0 +1,22 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Core.Diagnostics;
internal readonly struct MeasureExecutionToken : IDisposable
{
private readonly ValueStopwatch stopwatch;
private readonly ILogger logger;
private readonly string callerName;
public MeasureExecutionToken(in ValueStopwatch stopwatch, ILogger logger, string callerName)
{
this.stopwatch = stopwatch;
this.logger = logger;
this.callerName = callerName;
}
public void Dispose()
{
logger.LogInformation("{caller} toke {time} ms.", callerName, stopwatch.GetElapsedTime().TotalMilliseconds);
}
}

View File

@@ -43,10 +43,10 @@ internal readonly struct ValueStopwatch
/// <param name="logger">日志器</param> /// <param name="logger">日志器</param>
/// <param name="callerName">调用方法名称</param> /// <param name="callerName">调用方法名称</param>
/// <returns>结束测量</returns> /// <returns>结束测量</returns>
public static IDisposable MeasureExecution(ILogger logger, [CallerMemberName] string callerName = default!) public static MeasureExecutionToken MeasureExecution(ILogger logger, [CallerMemberName] string callerName = default!)
{ {
ValueStopwatch stopwatch = StartNew(); ValueStopwatch stopwatch = StartNew();
return new MeasureExecutionDisposable(stopwatch, logger, callerName); return new MeasureExecutionToken(stopwatch, logger, callerName);
} }
/// <summary> /// <summary>
@@ -75,25 +75,4 @@ internal readonly struct ValueStopwatch
{ {
return new TimeSpan(GetElapsedTimestamp()); return new TimeSpan(GetElapsedTimestamp());
} }
}
[SuppressMessage("", "SA1400")]
[SuppressMessage("", "SA1600")]
file readonly struct MeasureExecutionDisposable : IDisposable
{
private readonly ValueStopwatch stopwatch;
private readonly ILogger logger;
private readonly string callerName;
public MeasureExecutionDisposable(in ValueStopwatch stopwatch, ILogger logger, string callerName)
{
this.stopwatch = stopwatch;
this.logger = logger;
this.callerName = callerName;
}
public void Dispose()
{
logger.LogInformation("{caller} toke {time} ms.", callerName, stopwatch.GetElapsedTime().TotalMilliseconds);
}
} }

View File

@@ -35,7 +35,7 @@ internal sealed class ExceptionFormat
} }
builder.AppendLine(SectionSeparator); builder.AppendLine(SectionSeparator);
builder.Append(exception.ToString()); builder.Append(exception);
return builder.ToString(); return builder.ToString();
} }

View File

@@ -21,17 +21,17 @@ internal sealed partial class ClipboardInterop : IClipboardInterop
where T : class where T : class
{ {
await taskContext.SwitchToMainThreadAsync(); await taskContext.SwitchToMainThreadAsync();
DataPackageView view = Windows.ApplicationModel.DataTransfer.Clipboard.GetContent(); DataPackageView view = Clipboard.GetContent();
if (view.Contains(StandardDataFormats.Text)) if (!view.Contains(StandardDataFormats.Text))
{ {
string json = await view.GetTextAsync(); return null;
await taskContext.SwitchToBackgroundAsync();
return JsonSerializer.Deserialize<T>(json, options);
} }
return null; string json = await view.GetTextAsync();
await taskContext.SwitchToBackgroundAsync();
return JsonSerializer.Deserialize<T>(json, options);
} }
/// <inheritdoc/> /// <inheritdoc/>
@@ -41,8 +41,8 @@ internal sealed partial class ClipboardInterop : IClipboardInterop
{ {
DataPackage content = new() { RequestedOperation = DataPackageOperation.Copy }; DataPackage content = new() { RequestedOperation = DataPackageOperation.Copy };
content.SetText(text); content.SetText(text);
Windows.ApplicationModel.DataTransfer.Clipboard.SetContent(content); Clipboard.SetContent(content);
Windows.ApplicationModel.DataTransfer.Clipboard.Flush(); Clipboard.Flush();
return true; return true;
} }
catch catch
@@ -59,8 +59,8 @@ internal sealed partial class ClipboardInterop : IClipboardInterop
RandomAccessStreamReference reference = RandomAccessStreamReference.CreateFromStream(stream); RandomAccessStreamReference reference = RandomAccessStreamReference.CreateFromStream(stream);
DataPackage content = new() { RequestedOperation = DataPackageOperation.Copy }; DataPackage content = new() { RequestedOperation = DataPackageOperation.Copy };
content.SetBitmap(reference); content.SetBitmap(reference);
Windows.ApplicationModel.DataTransfer.Clipboard.SetContent(content); Clipboard.SetContent(content);
Windows.ApplicationModel.DataTransfer.Clipboard.Flush(); Clipboard.Flush();
return true; return true;
} }
catch catch

View File

@@ -20,23 +20,24 @@ internal static class FileOperation
/// <returns>是否发生了移动操作</returns> /// <returns>是否发生了移动操作</returns>
public static bool Move(string sourceFileName, string destFileName, bool overwrite) public static bool Move(string sourceFileName, string destFileName, bool overwrite)
{ {
if (File.Exists(sourceFileName)) if (!File.Exists(sourceFileName))
{ {
if (overwrite) return false;
{
File.Move(sourceFileName, destFileName, true);
return true;
}
else
{
if (!File.Exists(destFileName))
{
File.Move(sourceFileName, destFileName, false);
return true;
}
}
} }
return false; if (overwrite)
{
File.Move(sourceFileName, destFileName, true);
return true;
}
if (File.Exists(destFileName))
{
return false;
}
File.Move(sourceFileName, destFileName, false);
return true;
} }
} }

View File

@@ -37,13 +37,13 @@ internal sealed class IniParameter : IniElement
/// <returns>是否修改了值</returns> /// <returns>是否修改了值</returns>
public bool Set(string value) public bool Set(string value)
{ {
if (Value != value) if (Value == value)
{ {
Value = value; return false;
return true;
} }
return false; Value = value;
return true;
} }
/// <inheritdoc/> /// <inheritdoc/>

View File

@@ -13,7 +13,6 @@ internal sealed class IniSection : IniElement
/// 构造一个新的Ini 节 /// 构造一个新的Ini 节
/// </summary> /// </summary>
/// <param name="name">名称</param> /// <param name="name">名称</param>
/// <param name="elements">元素</param>
public IniSection(string name) public IniSection(string name)
{ {
Name = name; Name = name;

View File

@@ -20,28 +20,28 @@ internal static class IniSerializer
{ {
using (StreamReader reader = new(fileStream)) using (StreamReader reader = new(fileStream))
{ {
while (reader.ReadLine() is string line) while (reader.ReadLine() is { } line)
{ {
if (line.Length > 0) if (line.Length <= 0)
{ {
if (line[0] == '[') continue;
{
yield return new IniSection(line[1..^1]);
}
if (line[0] == ';')
{
yield return new IniComment(line[1..]);
}
if (line.IndexOf('=') > 0)
{
string[] parameters = line.Split('=', 2);
yield return new IniParameter(parameters[0], parameters[1]);
}
} }
continue; if (line[0] == '[')
{
yield return new IniSection(line[1..^1]);
}
if (line[0] == ';')
{
yield return new IniComment(line[1..]);
}
if (line.IndexOf('=') > 0)
{
string[] parameters = line.Split('=', 2);
yield return new IniParameter(parameters[0], parameters[1]);
}
} }
} }
} }

View File

@@ -74,7 +74,6 @@ internal sealed class StreamCopyWorker<TStatus>
/// <param name="source">源</param> /// <param name="source">源</param>
/// <param name="destination">目标</param> /// <param name="destination">目标</param>
/// <param name="statusFactory">状态工厂</param> /// <param name="statusFactory">状态工厂</param>
/// <param name="totalBytes">总字节</param>
/// <param name="bufferSize">字节尺寸</param> /// <param name="bufferSize">字节尺寸</param>
public StreamCopyWorker(Stream source, Stream destination, Func<long, TStatus> statusFactory, int bufferSize = 81920) public StreamCopyWorker(Stream source, Stream destination, Func<long, TStatus> statusFactory, int bufferSize = 81920)
{ {

View File

@@ -36,7 +36,7 @@ internal sealed class TempFile : IDisposable
/// <summary> /// <summary>
/// 路径 /// 路径
/// </summary> /// </summary>
public string Path { get; private set; } public string Path { get; }
/// <summary> /// <summary>
/// 创建临时文件并复制内容 /// 创建临时文件并复制内容
@@ -57,15 +57,6 @@ internal sealed class TempFile : IDisposable
} }
} }
/// <summary>
/// 作为写入模式打开
/// </summary>
/// <returns>文件流</returns>
public FileStream OpenWrite()
{
return File.OpenWrite(Path);
}
/// <summary> /// <summary>
/// 删除临时文件 /// 删除临时文件
/// </summary> /// </summary>

View File

@@ -9,7 +9,7 @@ namespace Snap.Hutao.Core.Json.Annotation;
/// <summary> /// <summary>
/// Json 枚举类型 /// Json 枚举类型
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)] [AttributeUsage(AttributeTargets.Property)]
internal class JsonEnumAttribute : Attribute internal class JsonEnumAttribute : Attribute
{ {
private static readonly Type UnsafeEnumConverterType = typeof(UnsafeEnumConverter<>); private static readonly Type UnsafeEnumConverterType = typeof(UnsafeEnumConverter<>);

View File

@@ -12,7 +12,7 @@ internal class DateTimeOffsetConverter : JsonConverter<DateTimeOffset>
/// <inheritdoc/> /// <inheritdoc/>
public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
if (reader.GetString() is string dataTimeString) if (reader.GetString() is { } dataTimeString)
{ {
return DateTimeOffset.Parse(dataTimeString); return DateTimeOffset.Parse(dataTimeString);
} }

View File

@@ -16,7 +16,7 @@ internal sealed class SeparatorCommaInt32EnumerableConverter : JsonConverter<IEn
/// <inheritdoc/> /// <inheritdoc/>
public override IEnumerable<int> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override IEnumerable<int> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
if (reader.GetString() is string source) if (reader.GetString() is { } source)
{ {
return EnumerateNumbers(source); return EnumerateNumbers(source);
} }
@@ -32,7 +32,7 @@ internal sealed class SeparatorCommaInt32EnumerableConverter : JsonConverter<IEn
private static IEnumerable<int> EnumerateNumbers(string source) private static IEnumerable<int> EnumerateNumbers(string source)
{ {
foreach (StringSegment id in new StringTokenizer(source, Comma.Enumerate().ToArray())) foreach (StringSegment id in new StringTokenizer(source, new[] { Comma }))
{ {
yield return int.Parse(id.AsSpan()); yield return int.Parse(id.AsSpan());
} }

View File

@@ -38,7 +38,7 @@ internal sealed class UnsafeEnumConverter<TEnum> : JsonConverter<TEnum>
return GetEnum(ref reader, enumTypeCode); return GetEnum(ref reader, enumTypeCode);
} }
if (reader.GetString() is string str) if (reader.GetString() is { } str)
{ {
return Enum.Parse<TEnum>(str); return Enum.Parse<TEnum>(str);
} }
@@ -116,6 +116,8 @@ internal sealed class UnsafeEnumConverter<TEnum> : JsonConverter<TEnum>
} }
break; break;
default:
throw new JsonException();
} }
throw new JsonException(); throw new JsonException();

View File

@@ -27,16 +27,20 @@ internal static class JsonTypeInfoResolvers
foreach (JsonPropertyInfo property in typeInfo.Properties) foreach (JsonPropertyInfo property in typeInfo.Properties)
{ {
if (property.PropertyType.IsEnum) if (!property.PropertyType.IsEnum)
{ {
if (property.AttributeProvider is System.Reflection.ICustomAttributeProvider provider) continue;
{ }
object[] attributes = provider.GetCustomAttributes(JsonEnumAttributeType, false);
if (attributes.SingleOrDefault() is JsonEnumAttribute attr) if (property.AttributeProvider is not { } provider)
{ {
property.CustomConverter = attr.CreateConverter(property); continue;
} }
}
object[] attributes = provider.GetCustomAttributes(JsonEnumAttributeType, false);
if (attributes.SingleOrDefault() is JsonEnumAttribute attr)
{
property.CustomConverter = attr.CreateConverter(property);
} }
} }
} }

View File

@@ -21,13 +21,13 @@ internal static class AppActivationArgumentsExtensions
public static bool TryGetProtocolActivatedUri(this AppActivationArguments activatedEventArgs, [NotNullWhen(true)] out Uri? uri) public static bool TryGetProtocolActivatedUri(this AppActivationArguments activatedEventArgs, [NotNullWhen(true)] out Uri? uri)
{ {
uri = null; uri = null;
if (activatedEventArgs.Data is IProtocolActivatedEventArgs protocolArgs) if (activatedEventArgs.Data is not IProtocolActivatedEventArgs protocolArgs)
{ {
uri = protocolArgs.Uri; return false;
return true;
} }
return false; uri = protocolArgs.Uri;
return true;
} }
/// <summary> /// <summary>
@@ -39,12 +39,12 @@ internal static class AppActivationArgumentsExtensions
public static bool TryGetLaunchActivatedArgument(this AppActivationArguments activatedEventArgs, [NotNullWhen(true)] out string? arguments) public static bool TryGetLaunchActivatedArgument(this AppActivationArguments activatedEventArgs, [NotNullWhen(true)] out string? arguments)
{ {
arguments = null; arguments = null;
if (activatedEventArgs.Data is ILaunchActivatedEventArgs launchArgs) if (activatedEventArgs.Data is not ILaunchActivatedEventArgs launchArgs)
{ {
arguments = launchArgs.Arguments.Trim(); return false;
return true;
} }
return false; arguments = launchArgs.Arguments.Trim();
return true;
} }
} }

View File

@@ -15,7 +15,7 @@ namespace Snap.Hutao.Core.LifeCycle;
[HighQuality] [HighQuality]
internal static class AppInstanceExtension internal static class AppInstanceExtension
{ {
private static readonly WaitCallback RunActionWaitCallback = new(RunAction); private static readonly WaitCallback RunActionWaitCallback = RunAction;
// Hold the reference here to prevent memory corruption. // Hold the reference here to prevent memory corruption.
private static HANDLE redirectEventHandle = HANDLE.Null; private static HANDLE redirectEventHandle = HANDLE.Null;

View File

@@ -49,7 +49,7 @@ internal sealed class Feature : ObservableObject
set set
{ {
LocalSetting.Set(settingKey, value); LocalSetting.Set(settingKey, value);
OnPropertyChanged(nameof(Value)); OnPropertyChanged();
} }
} }
} }

View File

@@ -13,7 +13,7 @@ internal sealed class FeatureOptions : IReadOnlyCollection<Feature>
/// <summary> /// <summary>
/// 启用实时便笺无感验证 /// 启用实时便笺无感验证
/// </summary> /// </summary>
public Feature IsDailyNoteSlientVerificationEnabled { get; } = new("IsDailyNoteSlientVerificationEnabled", "启用实时便笺无感验证", "IsDailyNoteSlientVerificationEnabled", true); public Feature IsDailyNoteSilentVerificationEnabled { get; } = new("IsDailyNoteSilentVerificationEnabled", "启用实时便笺无感验证", "IsDailyNoteSilentVerificationEnabled", true);
/// <inheritdoc/> /// <inheritdoc/>
public int Count { get => 1; } public int Count { get => 1; }
@@ -22,7 +22,7 @@ internal sealed class FeatureOptions : IReadOnlyCollection<Feature>
public IEnumerator<Feature> GetEnumerator() public IEnumerator<Feature> GetEnumerator()
{ {
// TODO: Use source generator // TODO: Use source generator
yield return IsDailyNoteSlientVerificationEnabled; yield return IsDailyNoteSilentVerificationEnabled;
} }
/// <inheritdoc/> /// <inheritdoc/>

View File

@@ -18,9 +18,9 @@ internal static class StaticResource
} }
/// <summary> /// <summary>
/// 完成所有合约 /// 取消完成所有合约
/// </summary> /// </summary>
public static void UnfulfillAllContracts() public static void FailAllContracts()
{ {
SetContractsState(false); SetContractsState(false);
} }

View File

@@ -26,7 +26,7 @@ internal sealed class JumpListInterop : IJumpListInterop
list.Items.Clear(); list.Items.Clear();
JumpListItem launchGameItem = JumpListItem.CreateWithArguments(Activation.LaunchGame, SH.CoreJumpListHelperLaunchGameItemDisplayName); JumpListItem launchGameItem = JumpListItem.CreateWithArguments(Activation.LaunchGame, SH.CoreJumpListHelperLaunchGameItemDisplayName);
launchGameItem.Logo = new("ms-appx:///Resource/Icon/UI_GuideIcon_PlayMethod.png"); launchGameItem.Logo = "ms-appx:///Resource/Icon/UI_GuideIcon_PlayMethod.png".ToUri();
list.Items.Add(launchGameItem); list.Items.Add(launchGameItem);

View File

@@ -68,13 +68,15 @@ internal sealed class ScheduleTaskInterop : IScheduleTaskInterop
string tempFolder = ApplicationData.Current.TemporaryFolder.Path; string tempFolder = ApplicationData.Current.TemporaryFolder.Path;
string fullName = Path.Combine(tempFolder, "Script", $"{name}.vbs"); string fullName = Path.Combine(tempFolder, "Script", $"{name}.vbs");
if (!File.Exists(fullName) || forceCreate) if (File.Exists(fullName) && !forceCreate)
{ {
Directory.CreateDirectory(Path.Combine(tempFolder, "Script")); return fullName;
string script = $"""CreateObject("WScript.Shell").Run "cmd /c start {url}", 0, False""";
File.WriteAllText(fullName, script);
} }
Directory.CreateDirectory(Path.Combine(tempFolder, "Script"));
string script = $"""CreateObject("WScript.Shell").Run "cmd /c start {url}", 0, False""";
File.WriteAllText(fullName, script);
return fullName; return fullName;
} }
} }

View File

@@ -18,7 +18,7 @@ internal class ConcurrentCancellationTokenSource
/// 注册取消令牌 /// 注册取消令牌
/// </summary> /// </summary>
/// <returns>取消令牌</returns> /// <returns>取消令牌</returns>
public CancellationToken CancelPreviousOne() public CancellationToken Register()
{ {
source.Cancel(); source.Cancel();
source = new(); source = new();

View File

@@ -16,7 +16,7 @@ internal static class SemaphoreSlimExtension
/// <param name="semaphoreSlim">信号量</param> /// <param name="semaphoreSlim">信号量</param>
/// <param name="token">取消令牌</param> /// <param name="token">取消令牌</param>
/// <returns>可释放的对象,用于释放信号量</returns> /// <returns>可释放的对象,用于释放信号量</returns>
public static async ValueTask<IDisposable> EnterAsync(this SemaphoreSlim semaphoreSlim, CancellationToken token = default) public static async ValueTask<SemaphoreSlimToken> EnterAsync(this SemaphoreSlim semaphoreSlim, CancellationToken token = default)
{ {
try try
{ {
@@ -27,24 +27,6 @@ internal static class SemaphoreSlimExtension
ThrowHelper.OperationCanceled(SH.CoreThreadingSemaphoreSlimDisposed, ex); ThrowHelper.OperationCanceled(SH.CoreThreadingSemaphoreSlimDisposed, ex);
} }
return new SemaphoreSlimReleaser(semaphoreSlim); return new SemaphoreSlimToken(semaphoreSlim);
} }
} }
[SuppressMessage("", "SA1201")]
[SuppressMessage("", "SA1400")]
[SuppressMessage("", "SA1600")]
file readonly struct SemaphoreSlimReleaser : IDisposable
{
private readonly SemaphoreSlim semaphoreSlim;
public SemaphoreSlimReleaser(SemaphoreSlim semaphoreSlim)
{
this.semaphoreSlim = semaphoreSlim;
}
public void Dispose()
{
semaphoreSlim.Release();
}
}

View File

@@ -0,0 +1,17 @@
namespace Snap.Hutao.Core.Threading;
[SuppressMessage("", "SA1201")]
internal readonly struct SemaphoreSlimToken : IDisposable
{
private readonly SemaphoreSlim semaphoreSlim;
public SemaphoreSlimToken(SemaphoreSlim semaphoreSlim)
{
this.semaphoreSlim = semaphoreSlim;
}
public void Dispose()
{
semaphoreSlim.Release();
}
}

View File

@@ -10,7 +10,7 @@ namespace Snap.Hutao.Core.Threading;
/// </summary> /// </summary>
/// <typeparam name="TResult">结果类型</typeparam> /// <typeparam name="TResult">结果类型</typeparam>
/// <typeparam name="TValue">值类型</typeparam> /// <typeparam name="TValue">值类型</typeparam>
internal readonly struct ValueResult<TResult, TValue> : IDeconstructable<TResult, TValue> internal readonly struct ValueResult<TResult, TValue> : IDeconstruct<TResult, TValue>
{ {
/// <summary> /// <summary>
/// 是否成功 /// 是否成功

View File

@@ -29,7 +29,7 @@ internal sealed class ExtendedWindow<TWindow> : IRecipient<FlyoutOpenCloseMessag
{ {
private readonly TWindow window; private readonly TWindow window;
private readonly IServiceProvider serviceProvider; private readonly IServiceProvider serviceProvider;
private readonly WindowSubclass<TWindow> subclass; private readonly WindowSubclass<TWindow>? subclass;
private ExtendedWindow(TWindow window, IServiceProvider serviceProvider) private ExtendedWindow(TWindow window, IServiceProvider serviceProvider)
{ {
@@ -79,7 +79,7 @@ internal sealed class ExtendedWindow<TWindow> : IRecipient<FlyoutOpenCloseMessag
UpdateSystemBackdrop(appOptions.BackdropType); UpdateSystemBackdrop(appOptions.BackdropType);
appOptions.PropertyChanged += OnOptionsPropertyChanged; appOptions.PropertyChanged += OnOptionsPropertyChanged;
bool subClassApplied = subclass.Initialize(); subclass!.Initialize();
serviceProvider.GetRequiredService<IMessenger>().Register(this); serviceProvider.GetRequiredService<IMessenger>().Register(this);
@@ -122,8 +122,8 @@ internal sealed class ExtendedWindow<TWindow> : IRecipient<FlyoutOpenCloseMessag
UpdateTitleButtonColor(); UpdateTitleButtonColor();
UpdateDragRectangles(); UpdateDragRectangles();
options.TitleBar.ActualThemeChanged += (s, e) => UpdateTitleButtonColor(); options.TitleBar.ActualThemeChanged += (_, _) => UpdateTitleButtonColor();
options.TitleBar.SizeChanged += (s, e) => UpdateDragRectangles(); options.TitleBar.SizeChanged += (_, _) => UpdateDragRectangles();
} }
} }

View File

@@ -8,7 +8,6 @@ namespace Snap.Hutao.Core.Windowing;
/// <summary> /// <summary>
/// 为扩展窗体提供必要的选项 /// 为扩展窗体提供必要的选项
/// </summary> /// </summary>
/// <typeparam name="TWindow">窗体类型</typeparam>
internal interface IWindowOptionsSource internal interface IWindowOptionsSource
{ {
/// <summary> /// <summary>

View File

@@ -23,20 +23,20 @@ internal static class Persistence
/// 设置窗体位置 /// 设置窗体位置
/// </summary> /// </summary>
/// <typeparam name="TWindow">窗体类型</typeparam> /// <typeparam name="TWindow">窗体类型</typeparam>
/// <param name="window">选项窗口param> /// <param name="window">选项窗口</param>
public static void RecoverOrInit<TWindow>(TWindow window) public static void RecoverOrInit<TWindow>(TWindow window)
where TWindow : Window, IWindowOptionsSource where TWindow : Window, IWindowOptionsSource
{ {
WindowOptions options = window.WindowOptions; WindowOptions options = window.WindowOptions;
// Set first launch size. // Set first launch size
double scale = GetScaleForWindowHandle(options.Hwnd); double scale = GetScaleForWindowHandle(options.Hwnd);
SizeInt32 transformedSize = options.InitSize.Scale(scale); SizeInt32 transformedSize = options.InitSize.Scale(scale);
RectInt32 rect = StructMarshal.RectInt32(transformedSize); RectInt32 rect = StructMarshal.RectInt32(transformedSize);
if (options.PersistSize) if (options.PersistSize)
{ {
RectInt32 persistedRect = (CompactRect)LocalSetting.Get(SettingKeys.WindowRect, (ulong)(CompactRect)rect); RectInt32 persistedRect = (CompactRect)LocalSetting.Get(SettingKeys.WindowRect, (CompactRect)rect);
if (persistedRect.Size() >= options.InitSize.Size()) if (persistedRect.Size() >= options.InitSize.Size())
{ {
rect = persistedRect; rect = persistedRect;
@@ -108,24 +108,24 @@ internal static class Persistence
rect.Y = workAreaRect.Y + ((workAreaRect.Height - rect.Height) / 2); rect.Y = workAreaRect.Y + ((workAreaRect.Height - rect.Height) / 2);
} }
private struct CompactRect private readonly struct CompactRect
{ {
public short X; private readonly short x;
public short Y; private readonly short y;
public short Width; private readonly short width;
public short Height; private readonly short height;
private CompactRect(int x, int y, int width, int height) private CompactRect(int x, int y, int width, int height)
{ {
X = (short)x; this.x = (short)x;
Y = (short)y; this.y = (short)y;
Width = (short)width; this.width = (short)width;
Height = (short)height; this.height = (short)height;
} }
public static implicit operator RectInt32(CompactRect rect) public static implicit operator RectInt32(CompactRect rect)
{ {
return new(rect.X, rect.Y, rect.Width, rect.Height); return new(rect.x, rect.y, rect.width, rect.height);
} }
public static explicit operator CompactRect(RectInt32 rect) public static explicit operator CompactRect(RectInt32 rect)

View File

@@ -12,7 +12,6 @@ namespace Snap.Hutao.Core.Windowing;
/// <summary> /// <summary>
/// Window 选项 /// Window 选项
/// </summary> /// </summary>
/// <typeparam name="TWindow">窗体类型</typeparam>
internal readonly struct WindowOptions internal readonly struct WindowOptions
{ {
/// <summary> /// <summary>

View File

@@ -43,24 +43,28 @@ internal sealed class WindowSubclass<TWindow> : IDisposable
{ {
WindowOptions options = window.WindowOptions; WindowOptions options = window.WindowOptions;
windowProc = new(OnSubclassProcedure); windowProc = OnSubclassProcedure;
bool windowHooked = SetWindowSubclass(options.Hwnd, windowProc, WindowSubclassId, 0); bool windowHooked = SetWindowSubclass(options.Hwnd, windowProc, WindowSubclassId, 0);
bool titleBarHooked = true; bool titleBarHooked = true;
// only hook up drag bar proc when use legacy Window.ExtendsContentIntoTitleBar // only hook up drag bar proc when use legacy Window.ExtendsContentIntoTitleBar
if (options.UseLegacyDragBarImplementation) if (!options.UseLegacyDragBarImplementation)
{ {
titleBarHooked = false; return windowHooked && titleBarHooked;
HWND hwndDragBar = FindWindowEx(options.Hwnd, default, "DRAG_BAR_WINDOW_CLASS", default);
if (!hwndDragBar.IsNull)
{
dragBarProc = new(OnDragBarProcedure);
titleBarHooked = SetWindowSubclass(hwndDragBar, dragBarProc, DragBarSubclassId, 0);
}
} }
titleBarHooked = false;
HWND hwndDragBar = FindWindowEx(options.Hwnd, default, "DRAG_BAR_WINDOW_CLASS", default);
if (hwndDragBar.IsNull)
{
return windowHooked && titleBarHooked;
}
dragBarProc = OnDragBarProcedure;
titleBarHooked = SetWindowSubclass(hwndDragBar, dragBarProc, DragBarSubclassId, 0);
return windowHooked && titleBarHooked; return windowHooked && titleBarHooked;
} }
@@ -72,11 +76,13 @@ internal sealed class WindowSubclass<TWindow> : IDisposable
RemoveWindowSubclass(options.Hwnd, windowProc, WindowSubclassId); RemoveWindowSubclass(options.Hwnd, windowProc, WindowSubclassId);
windowProc = null; windowProc = null;
if (options.UseLegacyDragBarImplementation) if (!options.UseLegacyDragBarImplementation)
{ {
RemoveWindowSubclass(options.Hwnd, dragBarProc, DragBarSubclassId); return;
dragBarProc = null;
} }
RemoveWindowSubclass(options.Hwnd, dragBarProc, DragBarSubclassId);
dragBarProc = null;
} }
[SuppressMessage("", "SH002")] [SuppressMessage("", "SH002")]

View File

@@ -17,7 +17,7 @@ internal static class DateTimeOffsetExtension
/// <returns>转换的时间</returns> /// <returns>转换的时间</returns>
public static DateTimeOffset FromUnixTime(long? timestamp, in DateTimeOffset defaultValue) public static DateTimeOffset FromUnixTime(long? timestamp, in DateTimeOffset defaultValue)
{ {
if (timestamp is long value) if (timestamp is { } value)
{ {
try try
{ {

View File

@@ -47,7 +47,19 @@ internal static partial class EnumerableExtension
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TSource? FirstOrFirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) public static TSource? FirstOrFirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{ {
return source.FirstOrDefault(predicate) ?? source.FirstOrDefault(); TSource? first = default;
foreach (TSource element in source)
{
first ??= element;
if (predicate(element))
{
return element;
}
}
return first;
} }
/// <summary> /// <summary>
@@ -68,19 +80,11 @@ internal static partial class EnumerableExtension
/// <typeparam name="T">Type of array elements.</typeparam> /// <typeparam name="T">Type of array elements.</typeparam>
/// <param name="collection">Collection to convert. Cannot be <see langword="null"/>.</param> /// <param name="collection">Collection to convert. Cannot be <see langword="null"/>.</param>
/// <param name="separator">Delimiter between elements in the final string.</param> /// <param name="separator">Delimiter between elements in the final string.</param>
/// <param name="defaultValue">A string to be returned if collection has no elements.</param>
/// <returns>Converted collection into string.</returns> /// <returns>Converted collection into string.</returns>
public static string ToString<T>(this IEnumerable<T> collection, string separator, string defaultValue = "") public static string ToString<T>(this IEnumerable<T> collection, char separator = ',')
{ {
string result = string.Join(separator, collection); string result = string.Join(separator, collection);
if (result.Length > 0) return result.Length > 0 ? result : string.Empty;
{
return result;
}
else
{
return defaultValue;
}
} }
/// <summary> /// <summary>
@@ -94,13 +98,6 @@ internal static partial class EnumerableExtension
public static string ToString<T>(this IEnumerable<T> collection, char separator = ',', string defaultValue = "") public static string ToString<T>(this IEnumerable<T> collection, char separator = ',', string defaultValue = "")
{ {
string result = string.Join(separator, collection); string result = string.Join(separator, collection);
if (result.Length > 0) return result.Length > 0 ? result : defaultValue;
{
return result;
}
else
{
return defaultValue;
}
} }
} }

View File

@@ -19,12 +19,12 @@ internal static class MemoryCacheExtension
/// <returns>是否移除成功</returns> /// <returns>是否移除成功</returns>
public static bool TryRemove(this IMemoryCache memoryCache, string key, out object? value) public static bool TryRemove(this IMemoryCache memoryCache, string key, out object? value)
{ {
if (memoryCache.TryGetValue(key, out value)) if (!memoryCache.TryGetValue(key, out value))
{ {
memoryCache.Remove(key); return false;
return true;
} }
return false; memoryCache.Remove(key);
return true;
} }
} }

View File

@@ -1,6 +1,8 @@
// Copyright (c) DGP Studio. All rights reserved. // Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license. // Licensed under the MIT license.
using System.Runtime.CompilerServices;
namespace Snap.Hutao.Extension; namespace Snap.Hutao.Extension;
/// <summary> /// <summary>
@@ -14,8 +16,9 @@ internal static class ObjectExtension
/// <typeparam name="T">数据类型</typeparam> /// <typeparam name="T">数据类型</typeparam>
/// <param name="source">源</param> /// <param name="source">源</param>
/// <returns>数组</returns> /// <returns>数组</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T[] ToArray<T>(this T source) public static T[] ToArray<T>(this T source)
{ {
return new T[] { source }; return new[] { source };
} }
} }

View File

@@ -20,13 +20,6 @@ internal interface IPickerFactory
/// <returns>经过初始化的 <see cref="FileOpenPicker"/></returns> /// <returns>经过初始化的 <see cref="FileOpenPicker"/></returns>
FileOpenPicker GetFileOpenPicker(PickerLocationId location, string commitButton, params string[] fileTypes); FileOpenPicker GetFileOpenPicker(PickerLocationId location, string commitButton, params string[] fileTypes);
/// <summary>
/// 获取 经过初始化的 <see cref="FileSavePicker"/>
/// </summary>
/// <returns>经过初始化的 <see cref="FileSavePicker"/></returns>
[Obsolete]
FileSavePicker GetFileSavePicker();
/// <summary> /// <summary>
/// 获取 经过初始化的 <see cref="FileSavePicker"/> /// 获取 经过初始化的 <see cref="FileSavePicker"/>
/// </summary> /// </summary>

View File

@@ -49,12 +49,6 @@ internal class PickerFactory : IPickerFactory
return picker; return picker;
} }
/// <inheritdoc/>
public FileSavePicker GetFileSavePicker()
{
return GetInitializedPicker<FileSavePicker>();
}
/// <inheritdoc/> /// <inheritdoc/>
public FileSavePicker GetFileSavePicker(PickerLocationId location, string fileName, string commitButton, IDictionary<string, IList<string>> fileTypes) public FileSavePicker GetFileSavePicker(PickerLocationId location, string fileName, string commitButton, IDictionary<string, IList<string>> fileTypes)
{ {

View File

@@ -1,27 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.Windowing;
namespace Snap.Hutao.Message;
/// <summary>
/// 背景类型改变消息
/// </summary>
[HighQuality]
internal sealed class BackdropTypeChangedMessage
{
/// <summary>
/// 构造一个新的背景类型改变消息
/// </summary>
/// <param name="backdropType">背景类型</param>
public BackdropTypeChangedMessage(BackdropType backdropType)
{
BackdropType = backdropType;
}
/// <summary>
/// 背景类型
/// </summary>
public BackdropType BackdropType { get; set; }
}

View File

@@ -23,7 +23,7 @@ internal abstract class ValueChangedMessage<TValue>
/// </summary> /// </summary>
/// <param name="oldValue">旧值</param> /// <param name="oldValue">旧值</param>
/// <param name="newValue">新值</param> /// <param name="newValue">新值</param>
public ValueChangedMessage(TValue? oldValue, TValue? newValue) protected ValueChangedMessage(TValue? oldValue, TValue? newValue)
{ {
OldValue = oldValue; OldValue = oldValue;
NewValue = newValue; NewValue = newValue;

View File

@@ -18,7 +18,7 @@ internal sealed class InventoryReliquaryConfiguration : IEntityTypeConfiguration
builder.Property(e => e.AppendPropIdList) builder.Property(e => e.AppendPropIdList)
.HasColumnType("TEXT") .HasColumnType("TEXT")
.HasConversion( .HasConversion(
list => string.Join(',', list), list => list.ToString(','),
text => text.Split(',', StringSplitOptions.None).Select(x => int.Parse(x)).ToList()); text => text.Split(',', StringSplitOptions.None).Select(int.Parse).ToList());
} }
} }

View File

@@ -17,7 +17,7 @@ internal enum SchemeType
/// <summary> /// <summary>
/// 国服官服 /// 国服官服
/// </summary> /// </summary>
Officical, Official,
/// <summary> /// <summary>
/// 渠道服 /// 渠道服

View File

@@ -67,10 +67,10 @@ internal sealed class User : ISelectable
/// <returns>新创建的用户</returns> /// <returns>新创建的用户</returns>
public static User Create(Cookie cookie, bool isOversea) public static User Create(Cookie cookie, bool isOversea)
{ {
_ = cookie.TryGetSToken(isOversea, out Cookie? stoken); _ = cookie.TryGetSToken(isOversea, out Cookie? sToken);
_ = cookie.TryGetLToken(out Cookie? ltoken); _ = cookie.TryGetLToken(out Cookie? lToken);
_ = cookie.TryGetCookieToken(out Cookie? cookieToken); _ = cookie.TryGetCookieToken(out Cookie? cookieToken);
return new() { SToken = stoken, LToken = ltoken, CookieToken = cookieToken }; return new() { SToken = sToken, LToken = lToken, CookieToken = cookieToken };
} }
} }

View File

@@ -9,7 +9,7 @@ namespace Snap.Hutao.Model.Intrinsic.Immutable;
/// 本地化的不可变的原生枚举 /// 本地化的不可变的原生枚举
/// </summary> /// </summary>
[HighQuality] [HighQuality]
internal static class IntrinsicImmutables internal static class IntrinsicImmutable
{ {
/// <summary> /// <summary>
/// 所属地区 /// 所属地区

View File

@@ -1,7 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved. // Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license. // Licensed under the MIT license.
using Snap.Hutao.Core.Json.Annotation;
using Snap.Hutao.Model.Intrinsic; using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Primitive; using Snap.Hutao.Model.Primitive;

View File

@@ -16,6 +16,6 @@ internal sealed class AvatarBaseValue : BaseValue
public PropertyCurveValue GetPropertyCurveValue(FightProperty fightProperty) public PropertyCurveValue GetPropertyCurveValue(FightProperty fightProperty)
{ {
// TODO: impl // TODO: impl
return null; return default!;
} }
} }

View File

@@ -72,14 +72,9 @@ internal sealed class FetterInfo
{ {
get get
{ {
if (string.IsNullOrEmpty(ConstellationAfter)) return string.IsNullOrEmpty(ConstellationAfter)
{ ? ConstellationBefore
return ConstellationBefore; : ConstellationAfter;
}
else
{
return ConstellationAfter;
}
} }
} }

View File

@@ -21,12 +21,9 @@ internal sealed class AvatarCardConverter : ValueConverter<string, Uri>
/// <returns>链接</returns> /// <returns>链接</returns>
public static Uri IconNameToUri(string name) public static Uri IconNameToUri(string name)
{ {
if (string.IsNullOrEmpty(name)) return string.IsNullOrEmpty(name)
{ ? UIAvatarIconCostumeCard
return UIAvatarIconCostumeCard; : Web.HutaoEndpoints.StaticFile("AvatarCard", $"{name}_Card.png").ToUri();
}
return Web.HutaoEndpoints.StaticFile("AvatarCard", $"{name}_Card.png").ToUri();
} }
/// <inheritdoc/> /// <inheritdoc/>

View File

@@ -103,8 +103,8 @@ internal static class FightPropertyFormat
{ {
return method switch return method switch
{ {
FormatMethod.Integer => $"{Math.Round((double)value, MidpointRounding.AwayFromZero)}", FormatMethod.Integer => $"{MathF.Round(value, MidpointRounding.AwayFromZero)}",
FormatMethod.Percent => string.Format("{0:P1}", value), FormatMethod.Percent => $"{value:P1}",
_ => value.ToString(), _ => value.ToString(),
}; };
} }

View File

@@ -18,14 +18,9 @@ internal sealed class ItemIconConverter : ValueConverter<string, Uri>
/// <returns>链接</returns> /// <returns>链接</returns>
public static Uri IconNameToUri(string name) public static Uri IconNameToUri(string name)
{ {
if (name.StartsWith("UI_RelicIcon_")) return name.StartsWith("UI_RelicIcon_")
{ ? RelicIconConverter.IconNameToUri(name)
return RelicIconConverter.IconNameToUri(name); : Web.HutaoEndpoints.StaticFile("ItemIcon", $"{name}.png").ToUri();
}
else
{
return Web.HutaoEndpoints.StaticFile("ItemIcon", $"{name}.png").ToUri();
}
} }
/// <inheritdoc/> /// <inheritdoc/>

View File

@@ -23,14 +23,9 @@ internal sealed class SkillIconConverter : ValueConverter<string, Uri>
return Web.HutaoEndpoints.UIIconNone; return Web.HutaoEndpoints.UIIconNone;
} }
if (name.StartsWith("UI_Talent_")) return name.StartsWith("UI_Talent_")
{ ? Web.HutaoEndpoints.StaticFile("Talent", $"{name}.png").ToUri()
return Web.HutaoEndpoints.StaticFile("Talent", $"{name}.png").ToUri(); : Web.HutaoEndpoints.StaticFile("Skill", $"{name}.png").ToUri();
}
else
{
return Web.HutaoEndpoints.StaticFile("Skill", $"{name}.png").ToUri();
}
} }
/// <inheritdoc/> /// <inheritdoc/>

View File

@@ -1,15 +1,13 @@
// Copyright (c) DGP Studio. All rights reserved. // Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license. // Licensed under the MIT license.
using System.Text.Json.Serialization.Metadata;
namespace Snap.Hutao.Model.Primitive.Converter; namespace Snap.Hutao.Model.Primitive.Converter;
/// <summary> /// <summary>
/// Id 转换器 /// Id 转换器
/// </summary> /// </summary>
/// <typeparam name="TWrapper">包装类型</typeparam> /// <typeparam name="TWrapper">包装类型</typeparam>
internal unsafe sealed class IdentityConverter<TWrapper> : JsonConverter<TWrapper> internal sealed unsafe class IdentityConverter<TWrapper> : JsonConverter<TWrapper>
where TWrapper : unmanaged where TWrapper : unmanaged
{ {
/// <inheritdoc/> /// <inheritdoc/>
@@ -22,14 +20,14 @@ internal unsafe sealed class IdentityConverter<TWrapper> : JsonConverter<TWrappe
/// <inheritdoc/> /// <inheritdoc/>
public override TWrapper ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override TWrapper ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
if (reader.TokenType == JsonTokenType.PropertyName) if (reader.TokenType is not JsonTokenType.PropertyName)
{ {
string? value = reader.GetString(); throw new JsonException();
_ = uint.TryParse(value,out uint result);
return *(TWrapper*)&result;
} }
throw new JsonException(); string? value = reader.GetString();
_ = uint.TryParse(value, out uint result);
return *(TWrapper*)&result;
} }
/// <inheritdoc/> /// <inheritdoc/>

View File

@@ -82,7 +82,6 @@ internal struct GachaLogFetchContext
/// <summary> /// <summary>
/// 为下一个物品页面重置 /// 为下一个物品页面重置
/// </summary> /// </summary>
/// <param name="configType">卡池类型</param>
public void ResetForProcessingPage() public void ResetForProcessingPage()
{ {
FetchStatus = new(CurrentType); FetchStatus = new(CurrentType);

View File

@@ -109,8 +109,8 @@ internal readonly struct GachaLogServiceContext
uint place = id.Place(); uint place = id.Place();
return place switch return place switch
{ {
8U => IdAvatarMap![id], 8U => IdAvatarMap[id],
5U => IdWeaponMap![id], 5U => IdWeaponMap[id],
_ => throw Must.NeverHappen($"Id places: {place}"), _ => throw Must.NeverHappen($"Id places: {place}"),
}; };
} }

View File

@@ -33,7 +33,8 @@ internal sealed partial class GachaLogQueryWebCacheProvider : IGachaLogQueryProv
? GameConstants.GenshinImpactData ? GameConstants.GenshinImpactData
: GameConstants.YuanShenData; : GameConstants.YuanShenData;
return Path.Combine(Path.GetDirectoryName(path)!, dataFolder, @"webCaches\Cache\Cache_Data\data_2"); // TODO: make sure how the cache file located.
return Path.Combine(Path.GetDirectoryName(path)!, dataFolder, @"webCaches\2.13.0.1\Cache\Cache_Data\data_2");
} }
/// <inheritdoc/> /// <inheritdoc/>

View File

@@ -22,7 +22,7 @@ internal enum RefreshOption
WebCache, WebCache,
/// <summary> /// <summary>
/// 通过Stoken刷新 /// 通过SToken刷新
/// </summary> /// </summary>
SToken, SToken,

View File

@@ -29,14 +29,11 @@ namespace Snap.Hutao.Service.Game;
[Injection(InjectAs.Singleton, typeof(IGameService))] [Injection(InjectAs.Singleton, typeof(IGameService))]
internal sealed partial class GameService : IGameService internal sealed partial class GameService : IGameService
{ {
private const string GamePathKey = $"{nameof(GameService)}.Cache.{SettingEntry.GamePath}";
private readonly PackageConverter packageConverter; private readonly PackageConverter packageConverter;
private readonly IServiceProvider serviceProvider; private readonly IServiceProvider serviceProvider;
private readonly LaunchOptions launchOptions; private readonly LaunchOptions launchOptions;
private readonly HutaoOptions hutaoOptions; private readonly HutaoOptions hutaoOptions;
private readonly ITaskContext taskContext; private readonly ITaskContext taskContext;
private readonly IMemoryCache memoryCache;
private readonly AppOptions appOptions; private readonly AppOptions appOptions;
private volatile int runningGamesCounter; private volatile int runningGamesCounter;
@@ -277,12 +274,12 @@ internal sealed partial class GameService : IGameService
try try
{ {
bool isfirstInstance = Interlocked.Increment(ref runningGamesCounter) == 1; bool isFirstInstance = Interlocked.Increment(ref runningGamesCounter) == 1;
game.Start(); game.Start();
bool isAdvancedOptionsAllowed = hutaoOptions.IsElevated && appOptions.IsAdvancedLaunchOptionsEnabled; bool isAdvancedOptionsAllowed = hutaoOptions.IsElevated && appOptions.IsAdvancedLaunchOptionsEnabled;
if (isAdvancedOptionsAllowed && launchOptions.MultipleInstances && !isfirstInstance) if (isAdvancedOptionsAllowed && launchOptions.MultipleInstances && !isFirstInstance)
{ {
ProcessInterop.DisableProtection(game, gamePath); ProcessInterop.DisableProtection(game, gamePath);
} }

View File

@@ -146,14 +146,15 @@ internal sealed class LaunchOptions : DbStoreOptions
/// <summary> /// <summary>
/// 目标帧率 /// 目标帧率
/// </summary> /// </summary>
[AllowNull]
public NameValue<int> Monitor public NameValue<int> Monitor
{ {
get => GetOption(ref monitor, SettingEntry.LaunchMonitor, index => Monitors[int.Parse(index) - 1], Monitors[0]); get => GetOption(ref monitor, SettingEntry.LaunchMonitor, index => Monitors[int.Parse(index) - 1], Monitors[0]);
set set
{ {
if (value != null) if (value is not null)
{ {
SetOption(ref monitor, SettingEntry.LaunchMonitor, value, selected => selected.Value.ToString() ?? "1"); SetOption(ref monitor, SettingEntry.LaunchMonitor, value, selected => selected.Value.ToString());
} }
} }
} }

View File

@@ -27,16 +27,16 @@ internal sealed partial class UnityLogGameLocator : IGameLocator
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string logFilePathChinese = Path.Combine(appDataPath, @"..\LocalLow\miHoYo\原神\output_log.txt"); string logFilePathChinese = Path.Combine(appDataPath, @"..\LocalLow\miHoYo\原神\output_log.txt");
string logFilePathOvsesea = Path.Combine(appDataPath, @"..\LocalLow\miHoYo\Genshin Impact\output_log.txt"); string logFilePathOversea = Path.Combine(appDataPath, @"..\LocalLow\miHoYo\Genshin Impact\output_log.txt");
// Fallback to the CN server. // Fallback to the CN server.
string logFilePathFinal = File.Exists(logFilePathOvsesea) ? logFilePathOvsesea : logFilePathChinese; string logFilePathFinal = File.Exists(logFilePathOversea) ? logFilePathOversea : logFilePathChinese;
using (TempFile? tempFile = TempFile.CopyFrom(logFilePathFinal)) using (TempFile? tempFile = TempFile.CopyFrom(logFilePathFinal))
{ {
if (tempFile != null) if (tempFile != null)
{ {
string content = File.ReadAllText(tempFile.Path); string content = await File.ReadAllTextAsync(tempFile.Path).ConfigureAwait(false);
Match matchResult = WarmupFileLine().Match(content); Match matchResult = WarmupFileLine().Match(content);
if (!matchResult.Success) if (!matchResult.Success)

View File

@@ -322,11 +322,13 @@ internal sealed partial class PackageConverter
{ {
while (await reader.ReadLineAsync().ConfigureAwait(false) is string raw) while (await reader.ReadLineAsync().ConfigureAwait(false) is string raw)
{ {
if (!string.IsNullOrEmpty(raw)) if (string.IsNullOrEmpty(raw))
{ {
VersionItem item = JsonSerializer.Deserialize<VersionItem>(raw, options)!; continue;
results.Add(item.RemoteName, item);
} }
VersionItem item = JsonSerializer.Deserialize<VersionItem>(raw, options)!;
results.Add(item.RemoteName, item);
} }
} }
@@ -341,25 +343,27 @@ internal sealed partial class PackageConverter
{ {
while (await reader.ReadLineAsync().ConfigureAwait(false) is string raw) while (await reader.ReadLineAsync().ConfigureAwait(false) is string raw)
{ {
if (!string.IsNullOrEmpty(raw)) if (string.IsNullOrEmpty(raw))
{ {
VersionItem item = JsonSerializer.Deserialize<VersionItem>(raw, options)!; continue;
string remoteName = item.RemoteName;
// 我们已经提前重命名了整个 Data 文件夹 所以需要将 RemoteName 中的 Data 同样替换
if (remoteName.StartsWith(YuanShenData) || remoteName.StartsWith(GenshinImpactData))
{
remoteName = direction switch
{
ConvertDirection.OverseaToChinese => $"{YuanShenData}{remoteName[GenshinImpactData.Length..]}",
ConvertDirection.ChineseToOversea => $"{GenshinImpactData}{remoteName[YuanShenData.Length..]}",
_ => remoteName,
};
}
results.Add(remoteName, item);
} }
VersionItem item = JsonSerializer.Deserialize<VersionItem>(raw, options)!;
string remoteName = item.RemoteName;
// 我们已经提前重命名了整个 Data 文件夹 所以需要将 RemoteName 中的 Data 同样替换
if (remoteName.StartsWith(YuanShenData) || remoteName.StartsWith(GenshinImpactData))
{
remoteName = direction switch
{
ConvertDirection.OverseaToChinese => $"{YuanShenData}{remoteName[GenshinImpactData.Length..]}",
ConvertDirection.ChineseToOversea => $"{GenshinImpactData}{remoteName[YuanShenData.Length..]}",
_ => remoteName,
};
}
results.Add(remoteName, item);
} }
} }

View File

@@ -90,9 +90,9 @@ internal static class RegistryInterop
{ {
string paths = Environment.GetEnvironmentVariable("Path")!; string paths = Environment.GetEnvironmentVariable("Path")!;
foreach (StringSegment path in new StringTokenizer(paths, ';'.Enumerate().ToArray())) foreach (StringSegment path in new StringTokenizer(paths, ';'.ToArray()))
{ {
if (path.HasValue && path.Length > 0) if (path is { HasValue: true, Length: > 0 })
{ {
if (path.Value.Contains("WindowsPowerShell", StringComparison.OrdinalIgnoreCase)) if (path.Value.Contains("WindowsPowerShell", StringComparison.OrdinalIgnoreCase))
{ {

View File

@@ -21,7 +21,6 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker
{ {
private readonly Process gameProcess; private readonly Process gameProcess;
private readonly LaunchOptions launchOptions; private readonly LaunchOptions launchOptions;
private readonly ILogger<GameFpsUnlocker> logger;
private readonly UnlockerStatus status = new(); private readonly UnlockerStatus status = new();
/// <summary> /// <summary>
@@ -37,7 +36,6 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker
public GameFpsUnlocker(IServiceProvider serviceProvider, Process gameProcess) public GameFpsUnlocker(IServiceProvider serviceProvider, Process gameProcess)
{ {
launchOptions = serviceProvider.GetRequiredService<LaunchOptions>(); launchOptions = serviceProvider.GetRequiredService<LaunchOptions>();
logger = serviceProvider.GetRequiredService<ILogger<GameFpsUnlocker>>();
this.gameProcess = gameProcess; this.gameProcess = gameProcess;
} }
@@ -65,14 +63,14 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker
memory = new VirtualMemory(unityPlayer.Size + userAssembly.Size); memory = new VirtualMemory(unityPlayer.Size + userAssembly.Size);
byte* lpBuffer = (byte*)memory.Pointer; byte* lpBuffer = (byte*)memory.Pointer;
return ReadProcessMemory((HANDLE)process.Handle, (void*)unityPlayer.Address, lpBuffer, unityPlayer.Size, default) return ReadProcessMemory((HANDLE)process.Handle, (void*)unityPlayer.Address, lpBuffer, unityPlayer.Size)
&& ReadProcessMemory((HANDLE)process.Handle, (void*)userAssembly.Address, lpBuffer + unityPlayer.Size, userAssembly.Size, default); && ReadProcessMemory((HANDLE)process.Handle, (void*)userAssembly.Address, lpBuffer + unityPlayer.Size, userAssembly.Size);
} }
private static unsafe bool UnsafeReadProcessMemory(Process process, nuint baseAddress, out nuint value) private static unsafe bool UnsafeReadProcessMemory(Process process, nuint baseAddress, out nuint value)
{ {
ulong temp = 0; ulong temp = 0;
bool result = ReadProcessMemory((HANDLE)process.Handle, (void*)baseAddress, (byte*)&temp, 8, default); bool result = ReadProcessMemory((HANDLE)process.Handle, (void*)baseAddress, (byte*)&temp, 8);
if (!result) if (!result)
{ {
ThrowHelper.InvalidOperation(SH.ServiceGameUnlockerReadProcessMemoryPointerAddressFailed, null); ThrowHelper.InvalidOperation(SH.ServiceGameUnlockerReadProcessMemoryPointerAddressFailed, null);
@@ -84,13 +82,13 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker
private static unsafe bool UnsafeWriteProcessMemory(Process process, nuint baseAddress, int value) private static unsafe bool UnsafeWriteProcessMemory(Process process, nuint baseAddress, int value)
{ {
return WriteProcessMemory((HANDLE)process.Handle, (void*)baseAddress, &value, sizeof(int), default); return WriteProcessMemory((HANDLE)process.Handle, (void*)baseAddress, &value, sizeof(int));
} }
private static unsafe FindModuleResult UnsafeTryFindModule(in HANDLE hProcess, in ReadOnlySpan<char> moduleName, out Module module) private static unsafe FindModuleResult UnsafeTryFindModule(in HANDLE hProcess, in ReadOnlySpan<char> moduleName, out Module module)
{ {
HMODULE[] buffer = new HMODULE[128]; HMODULE[] buffer = new HMODULE[128];
uint actualSize = 0; uint actualSize;
fixed (HMODULE* pBuffer = buffer) fixed (HMODULE* pBuffer = buffer)
{ {
if (!K32EnumProcessModules(hProcess, pBuffer, unchecked((uint)(buffer.Length * sizeof(HMODULE))), out actualSize)) if (!K32EnumProcessModules(hProcess, pBuffer, unchecked((uint)(buffer.Length * sizeof(HMODULE))), out actualSize))
@@ -158,7 +156,7 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker
return -1; return -1;
} }
private static unsafe FindModuleResult UnsafeGetGameModuleInfo(in HANDLE hProcess, out GameModule info) private static FindModuleResult UnsafeGetGameModuleInfo(in HANDLE hProcess, out GameModule info)
{ {
FindModuleResult unityPlayerResult = UnsafeTryFindModule(hProcess, "UnityPlayer.dll", out Module unityPlayer); FindModuleResult unityPlayerResult = UnsafeTryFindModule(hProcess, "UnityPlayer.dll", out Module unityPlayer);
FindModuleResult userAssemblyResult = UnsafeTryFindModule(hProcess, "UserAssembly.dll", out Module userAssembly); FindModuleResult userAssemblyResult = UnsafeTryFindModule(hProcess, "UserAssembly.dll", out Module userAssembly);

View File

@@ -80,7 +80,7 @@ internal sealed partial class HutaoService : IHutaoService
{ {
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>(); AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
if (appDbContext.ObjectCache.SingleOrDefault(e => e.Key == key) is ObjectCacheEntry entry) if (appDbContext.ObjectCache.SingleOrDefault(e => e.Key == key) is { } entry)
{ {
if (entry.IsExpired) if (entry.IsExpired)
{ {
@@ -105,14 +105,14 @@ internal sealed partial class HutaoService : IHutaoService
{ {
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>(); AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
appDbContext.ObjectCache.AddAndSave(new() await appDbContext.ObjectCache.AddAndSaveAsync(new()
{ {
Key = key, Key = key,
// We hold the cache for 4 hours // We hold the cache for 4 hours
ExpireTime = DateTimeOffset.Now.Add(CacheExpireTime), ExpireTime = DateTimeOffset.Now.Add(CacheExpireTime),
Value = JsonSerializer.Serialize(data, options), Value = JsonSerializer.Serialize(data, options),
}); }).ConfigureAwait(false);
} }
} }
} }

View File

@@ -44,7 +44,7 @@ internal sealed partial class HutaoUserService : IHutaoUserService, IHutaoUserSe
options.LoginSucceed(userName, response.Data); options.LoginSucceed(userName, response.Data);
await taskContext.SwitchToBackgroundAsync(); await taskContext.SwitchToBackgroundAsync();
Web.Response.Response<UserInfo> userInfoResponse = await passportClient.GetUserInfoAsync(response.Data).ConfigureAwait(false); Web.Response.Response<UserInfo> userInfoResponse = await passportClient.GetUserInfoAsync(response.Data, token).ConfigureAwait(false);
if (userInfoResponse.IsOk()) if (userInfoResponse.IsOk())
{ {
await taskContext.SwitchToMainThreadAsync(); await taskContext.SwitchToMainThreadAsync();

View File

@@ -8,7 +8,7 @@ namespace Snap.Hutao.Service.Hutao;
/// <summary> /// <summary>
/// 胡桃用户服务 /// 胡桃用户服务
/// </summary> /// </summary>
internal interface IHutaoUserService : ICastableService internal interface IHutaoUserService : ICastService
{ {
/// <summary> /// <summary>
/// 异步初始化 /// 异步初始化

View File

@@ -19,7 +19,7 @@ namespace Snap.Hutao.Service.Metadata;
/// </summary> /// </summary>
[HighQuality] [HighQuality]
[SuppressMessage("", "SA1124")] [SuppressMessage("", "SA1124")]
internal interface IMetadataService : ICastableService internal interface IMetadataService : ICastService
{ {
/// <summary> /// <summary>
/// 异步初始化服务,尝试更新元数据 /// 异步初始化服务,尝试更新元数据

View File

@@ -10,7 +10,7 @@ namespace Snap.Hutao.Service.Navigation;
/// 导航服务 /// 导航服务
/// </summary> /// </summary>
[HighQuality] [HighQuality]
internal interface INavigationService : ICastableService internal interface INavigationService : ICastService
{ {
/// <summary> /// <summary>
/// 导航到指定类型的页面 /// 导航到指定类型的页面

View File

@@ -116,7 +116,7 @@ internal sealed partial class NavigationService : INavigationService, INavigatio
{ {
if (frame!.Content is ScopedPage scopedPage) if (frame!.Content is ScopedPage scopedPage)
{ {
await scopedPage.NotifyRecipentAsync((INavigationData)data).ConfigureAwait(false); await scopedPage.NotifyRecipientAsync((INavigationData)data).ConfigureAwait(false);
} }
} }

View File

@@ -92,7 +92,8 @@
Margin="0,6,0,0" Margin="0,6,0,0"
HorizontalTextAlignment="Center" HorizontalTextAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}" Style="{StaticResource CaptionTextBlockStyle}"
Text="{shcm:ResourceString Name=ViewPageGachaLogHutaoCloudDeveloperHint}"/> Text="{shcm:ResourceString Name=ViewPageGachaLogHutaoCloudDeveloperHint}"
Visibility="{Binding HutaoCloudViewModel.Options.IsLicensedDeveloper, Converter={StaticResource BoolToVisibilityConverter}}"/>
</StackPanel> </StackPanel>
</Grid> </Grid>
<ScrollViewer Grid.Row="1" Margin="0,0,0,8"> <ScrollViewer Grid.Row="1" Margin="0,0,0,8">

View File

@@ -185,7 +185,7 @@ internal sealed partial class CultivationViewModel : Abstraction.ViewModel
if (SelectedProject != null) if (SelectedProject != null)
{ {
await taskContext.SwitchToBackgroundAsync(); await taskContext.SwitchToBackgroundAsync();
CancellationToken token = StatisticsCancellationTokenSource.CancelPreviousOne(); CancellationToken token = StatisticsCancellationTokenSource.Register();
ObservableCollection<StatisticsCultivateItem> statistics; ObservableCollection<StatisticsCultivateItem> statistics;
try try
{ {

View File

@@ -192,7 +192,7 @@ internal sealed partial class SettingViewModel : Abstraction.ViewModel
[Command("ResetStaticResourceCommand")] [Command("ResetStaticResourceCommand")]
private void ResetStaticResource() private void ResetStaticResource()
{ {
StaticResource.UnfulfillAllContracts(); StaticResource.FailAllContracts();
AppInstance.Restart(string.Empty); AppInstance.Restart(string.Empty);
} }

View File

@@ -36,31 +36,31 @@ internal static class AvatarFilter
continue; continue;
} }
if (IntrinsicImmutables.ElementNames.Contains(value)) if (IntrinsicImmutable.ElementNames.Contains(value))
{ {
matches.Add(avatar.FetterInfo.VisionBefore == value); matches.Add(avatar.FetterInfo.VisionBefore == value);
continue; continue;
} }
if (IntrinsicImmutables.AssociationTypes.Contains(value)) if (IntrinsicImmutable.AssociationTypes.Contains(value))
{ {
matches.Add(avatar.FetterInfo.Association.GetLocalizedDescriptionOrDefault() == value); matches.Add(avatar.FetterInfo.Association.GetLocalizedDescriptionOrDefault() == value);
continue; continue;
} }
if (IntrinsicImmutables.WeaponTypes.Contains(value)) if (IntrinsicImmutable.WeaponTypes.Contains(value))
{ {
matches.Add(avatar.Weapon.GetLocalizedDescriptionOrDefault() == value); matches.Add(avatar.Weapon.GetLocalizedDescriptionOrDefault() == value);
continue; continue;
} }
if (IntrinsicImmutables.ItemQualities.Contains(value)) if (IntrinsicImmutable.ItemQualities.Contains(value))
{ {
matches.Add(avatar.Quality.GetLocalizedDescriptionOrDefault() == value); matches.Add(avatar.Quality.GetLocalizedDescriptionOrDefault() == value);
continue; continue;
} }
if (IntrinsicImmutables.BodyTypes.Contains(value)) if (IntrinsicImmutable.BodyTypes.Contains(value))
{ {
matches.Add(avatar.Body.GetLocalizedDescriptionOrDefault() == value); matches.Add(avatar.Body.GetLocalizedDescriptionOrDefault() == value);
continue; continue;

View File

@@ -36,19 +36,19 @@ internal static class WeaponFilter
continue; continue;
} }
if (IntrinsicImmutables.WeaponTypes.Contains(value)) if (IntrinsicImmutable.WeaponTypes.Contains(value))
{ {
matches.Add(weapon.WeaponType.GetLocalizedDescriptionOrDefault() == value); matches.Add(weapon.WeaponType.GetLocalizedDescriptionOrDefault() == value);
continue; continue;
} }
if (IntrinsicImmutables.ItemQualities.Contains(value)) if (IntrinsicImmutable.ItemQualities.Contains(value))
{ {
matches.Add(weapon.Quality.GetLocalizedDescriptionOrDefault() == value); matches.Add(weapon.Quality.GetLocalizedDescriptionOrDefault() == value);
continue; continue;
} }
if (IntrinsicImmutables.FightProperties.Contains(value)) if (IntrinsicImmutable.FightProperties.Contains(value))
{ {
matches.Add(weapon.GrowCurves.ElementAtOrDefault(1)?.Type.GetLocalizedDescriptionOrDefault() == value); matches.Add(weapon.GrowCurves.ElementAtOrDefault(1)?.Type.GetLocalizedDescriptionOrDefault() == value);
continue; continue;