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

View File

@@ -35,6 +35,6 @@ internal sealed partial class AutoHeightBehavior : BehaviorBase<FrameworkElement
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()
{
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();
// BitmapImage initialize with a uri will increase image quality and loading speed.
return new BitmapImage(new(file));
return new BitmapImage(file.ToUri());
}
catch (COMException)
{
@@ -54,9 +54,5 @@ internal sealed class CachedImage : ImageEx
// task was explicitly canceled
return null;
}
catch
{
throw;
}
}
}

View File

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

View File

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

View File

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

View File

@@ -29,9 +29,9 @@ internal sealed class MonoChrome : CompositionImage
/// <inheritdoc/>
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);
CompositionEffectBrush overlayBrush = compositor.CompositeBlendEffectBrush(blackLayerBursh, imageSurfaceBrush, BlendEffectMode.Overlay);
CompositionEffectBrush overlayBrush = compositor.CompositeBlendEffectBrush(blackLayerBrush, imageSurfaceBrush, BlendEffectMode.Overlay);
CompositionEffectBrush opacityBrush = compositor.CompositeLuminanceToAlphaEffectBrush(overlayBrush);
backgroundBrush = compositor.CreateColorBrush();

View File

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

View File

@@ -1,7 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
using Windows.Foundation;
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;
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));
}
/// <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>

View File

@@ -64,7 +64,7 @@ internal class ScopedPage : Page
/// </summary>
/// <param name="extra">额外内容</param>
/// <returns>任务</returns>
public async Task NotifyRecipentAsync(INavigationData extra)
public async Task NotifyRecipientAsync(INavigationData extra)
{
if (extra.Data != null && DataContext is INavigationRecipient recipient)
{
@@ -100,7 +100,7 @@ internal class ScopedPage : Page
{
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
else if (description.Slice(i, 2).SequenceEqual("<c"))
else if (description.Slice(i, 2) is "<c")
{
AppendText(text, description[last..i]);
Rgba32 color = new(description.Slice(i + 8, 8).ToString());
@@ -90,7 +90,7 @@ internal sealed class DescriptionTextBlock : ContentControl
}
// italic
else if (description.Slice(i, 2).SequenceEqual("<i"))
else if (description.Slice(i, 2) is "<i")
{
AppendText(text, description[last..i]);

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,9 +8,8 @@ namespace Snap.Hutao.Core.Caching;
/// <summary>
/// 为图像缓存提供抽象
/// </summary>
/// <typeparam name="T">缓存类型</typeparam>
[HighQuality]
internal interface IImageCache : ICastableService
internal interface IImageCache : ICastService
{
/// <summary>
/// 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.
/// </summary>
/// <param name="serviceProvider">服务提供器</param>
/// <param name="logger">日志器</param>
/// <param name="httpClientFactory">http客户端工厂</param>
public ImageCache(IServiceProvider serviceProvider)
{
logger = serviceProvider.GetRequiredService<ILogger<ImageCache>>();
@@ -84,7 +82,7 @@ internal sealed class ImageCache : IImageCache, IImageCacheFilePathOperation
/// <inheritdoc/>
public void Remove(in ReadOnlySpan<Uri> uriForCachedItems)
{
if (uriForCachedItems == null || uriForCachedItems.Length <= 0)
if (uriForCachedItems.Length <= 0)
{
return;
}
@@ -112,26 +110,28 @@ internal sealed class ImageCache : IImageCache, IImageCacheFilePathOperation
string fileName = GetCacheFileName(uri);
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();
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);
}
return filePath;
}
concurrentTasks.TryRemove(fileName, out _);
}
finally
TaskCompletionSource taskCompletionSource = new();
try
{
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;
@@ -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
retryCount += 3;
}
else if (message.StatusCode == HttpStatusCode.TooManyRequests)
{
retryCount++;
TimeSpan delay = message.Headers.RetryAfter?.Delta ?? RetryCountToDelay[retryCount];
logger.LogInformation("Retry {uri} after {delay}.", uri, delay);
await Task.Delay(delay).ConfigureAwait(false);
}
else
{
return;
case HttpStatusCode.NotFound:
// directly goto https://static.hut.ao
retryCount += 3;
break;
case HttpStatusCode.TooManyRequests:
{
retryCount++;
TimeSpan delay = message.Headers.RetryAfter?.Delta ?? RetryCountToDelay[retryCount];
logger.LogInformation("Retry {uri} after {delay}.", uri, delay);
await Task.Delay(delay).ConfigureAwait(false);
break;
}
default:
return;
}
}
@@ -225,13 +228,15 @@ internal sealed class ImageCache : IImageCache, IImageCacheFilePathOperation
private string GetCacheFolder()
{
if (cacheFolder == null)
if (cacheFolder is not null)
{
baseFolder ??= serviceProvider.GetRequiredService<HutaoOptions>().LocalCache;
DirectoryInfo info = Directory.CreateDirectory(Path.Combine(baseFolder, CacheFolderName));
cacheFolder = info.FullName;
return cacheFolder!;
}
baseFolder ??= serviceProvider.GetRequiredService<HutaoOptions>().LocalCache;
DirectoryInfo info = Directory.CreateDirectory(Path.Combine(baseFolder, CacheFolderName));
cacheFolder = info.FullName;
return cacheFolder!;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -25,6 +25,7 @@ internal static partial class IocHttpClientConfiguration
/// <summary>
/// 默认配置
/// </summary>
/// <param name="serviceProvider">服务提供器</param>
/// <param name="client">配置后的客户端</param>
private static void DefaultConfiguration(IServiceProvider serviceProvider, HttpClient client)
{
@@ -68,7 +69,7 @@ internal static partial class IocHttpClientConfiguration
/// <summary>
/// 对于需要添加动态密钥的客户端使用此配置
/// Hoyolab app
/// HoYoLAB app
/// </summary>
/// <param name="client">配置后的客户端</param>
private static void XRpc3Configuration(HttpClient client)
@@ -84,7 +85,7 @@ internal static partial class IocHttpClientConfiguration
/// <summary>
/// 对于需要添加动态密钥的客户端使用此配置
/// Hoyolab web
/// HoYoLAB web
/// </summary>
/// <param name="client">配置后的客户端</param>
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="callerName">调用方法名称</param>
/// <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();
return new MeasureExecutionDisposable(stopwatch, logger, callerName);
return new MeasureExecutionToken(stopwatch, logger, callerName);
}
/// <summary>
@@ -75,25 +75,4 @@ internal readonly struct ValueStopwatch
{
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.Append(exception.ToString());
builder.Append(exception);
return builder.ToString();
}

View File

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

View File

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

View File

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

View File

@@ -20,28 +20,28 @@ internal static class IniSerializer
{
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] == '[')
{
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;
}
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="destination">目标</param>
/// <param name="statusFactory">状态工厂</param>
/// <param name="totalBytes">总字节</param>
/// <param name="bufferSize">字节尺寸</param>
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>
public string Path { get; private set; }
public string Path { get; }
/// <summary>
/// 创建临时文件并复制内容
@@ -57,15 +57,6 @@ internal sealed class TempFile : IDisposable
}
}
/// <summary>
/// 作为写入模式打开
/// </summary>
/// <returns>文件流</returns>
public FileStream OpenWrite()
{
return File.OpenWrite(Path);
}
/// <summary>
/// 删除临时文件
/// </summary>

View File

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

View File

@@ -12,7 +12,7 @@ internal class DateTimeOffsetConverter : JsonConverter<DateTimeOffset>
/// <inheritdoc/>
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);
}

View File

@@ -16,7 +16,7 @@ internal sealed class SeparatorCommaInt32EnumerableConverter : JsonConverter<IEn
/// <inheritdoc/>
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);
}
@@ -32,7 +32,7 @@ internal sealed class SeparatorCommaInt32EnumerableConverter : JsonConverter<IEn
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());
}

View File

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

View File

@@ -27,16 +27,20 @@ internal static class JsonTypeInfoResolvers
foreach (JsonPropertyInfo property in typeInfo.Properties)
{
if (property.PropertyType.IsEnum)
if (!property.PropertyType.IsEnum)
{
if (property.AttributeProvider is System.Reflection.ICustomAttributeProvider provider)
{
object[] attributes = provider.GetCustomAttributes(JsonEnumAttributeType, false);
if (attributes.SingleOrDefault() is JsonEnumAttribute attr)
{
property.CustomConverter = attr.CreateConverter(property);
}
}
continue;
}
if (property.AttributeProvider is not { } provider)
{
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)
{
uri = null;
if (activatedEventArgs.Data is IProtocolActivatedEventArgs protocolArgs)
if (activatedEventArgs.Data is not IProtocolActivatedEventArgs protocolArgs)
{
uri = protocolArgs.Uri;
return true;
return false;
}
return false;
uri = protocolArgs.Uri;
return true;
}
/// <summary>
@@ -39,12 +39,12 @@ internal static class AppActivationArgumentsExtensions
public static bool TryGetLaunchActivatedArgument(this AppActivationArguments activatedEventArgs, [NotNullWhen(true)] out string? arguments)
{
arguments = null;
if (activatedEventArgs.Data is ILaunchActivatedEventArgs launchArgs)
if (activatedEventArgs.Data is not ILaunchActivatedEventArgs launchArgs)
{
arguments = launchArgs.Arguments.Trim();
return true;
return false;
}
return false;
arguments = launchArgs.Arguments.Trim();
return true;
}
}
}

View File

@@ -15,7 +15,7 @@ namespace Snap.Hutao.Core.LifeCycle;
[HighQuality]
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.
private static HANDLE redirectEventHandle = HANDLE.Null;

View File

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

View File

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

View File

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

View File

@@ -26,7 +26,7 @@ internal sealed class JumpListInterop : IJumpListInterop
list.Items.Clear();
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);

View File

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

View File

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

View File

@@ -16,7 +16,7 @@ internal static class SemaphoreSlimExtension
/// <param name="semaphoreSlim">信号量</param>
/// <param name="token">取消令牌</param>
/// <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
{
@@ -27,24 +27,6 @@ internal static class SemaphoreSlimExtension
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>
/// <typeparam name="TResult">结果类型</typeparam>
/// <typeparam name="TValue">值类型</typeparam>
internal readonly struct ValueResult<TResult, TValue> : IDeconstructable<TResult, TValue>
internal readonly struct ValueResult<TResult, TValue> : IDeconstruct<TResult, TValue>
{
/// <summary>
/// 是否成功

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -47,7 +47,19 @@ internal static partial class EnumerableExtension
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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>
@@ -68,19 +80,11 @@ internal static partial class EnumerableExtension
/// <typeparam name="T">Type of array elements.</typeparam>
/// <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="defaultValue">A string to be returned if collection has no elements.</param>
/// <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);
if (result.Length > 0)
{
return result;
}
else
{
return defaultValue;
}
return result.Length > 0 ? result : string.Empty;
}
/// <summary>
@@ -94,13 +98,6 @@ internal static partial class EnumerableExtension
public static string ToString<T>(this IEnumerable<T> collection, char separator = ',', string defaultValue = "")
{
string result = string.Join(separator, collection);
if (result.Length > 0)
{
return result;
}
else
{
return defaultValue;
}
return result.Length > 0 ? result : defaultValue;
}
}

View File

@@ -19,12 +19,12 @@ internal static class MemoryCacheExtension
/// <returns>是否移除成功</returns>
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 true;
return false;
}
return false;
memoryCache.Remove(key);
return true;
}
}

View File

@@ -1,6 +1,8 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using System.Runtime.CompilerServices;
namespace Snap.Hutao.Extension;
/// <summary>
@@ -14,8 +16,9 @@ internal static class ObjectExtension
/// <typeparam name="T">数据类型</typeparam>
/// <param name="source">源</param>
/// <returns>数组</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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>
FileOpenPicker GetFileOpenPicker(PickerLocationId location, string commitButton, params string[] fileTypes);
/// <summary>
/// 获取 经过初始化的 <see cref="FileSavePicker"/>
/// </summary>
/// <returns>经过初始化的 <see cref="FileSavePicker"/></returns>
[Obsolete]
FileSavePicker GetFileSavePicker();
/// <summary>
/// 获取 经过初始化的 <see cref="FileSavePicker"/>
/// </summary>

View File

@@ -49,12 +49,6 @@ internal class PickerFactory : IPickerFactory
return picker;
}
/// <inheritdoc/>
public FileSavePicker GetFileSavePicker()
{
return GetInitializedPicker<FileSavePicker>();
}
/// <inheritdoc/>
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>
/// <param name="oldValue">旧值</param>
/// <param name="newValue">新值</param>
public ValueChangedMessage(TValue? oldValue, TValue? newValue)
protected ValueChangedMessage(TValue? oldValue, TValue? newValue)
{
OldValue = oldValue;
NewValue = newValue;

View File

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

View File

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

View File

@@ -67,10 +67,10 @@ internal sealed class User : ISelectable
/// <returns>新创建的用户</returns>
public static User Create(Cookie cookie, bool isOversea)
{
_ = cookie.TryGetSToken(isOversea, out Cookie? stoken);
_ = cookie.TryGetLToken(out Cookie? ltoken);
_ = cookie.TryGetSToken(isOversea, out Cookie? sToken);
_ = cookie.TryGetLToken(out Cookie? lToken);
_ = 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>
[HighQuality]
internal static class IntrinsicImmutables
internal static class IntrinsicImmutable
{
/// <summary>
/// 所属地区

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -33,7 +33,8 @@ internal sealed partial class GachaLogQueryWebCacheProvider : IGachaLogQueryProv
? GameConstants.GenshinImpactData
: 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/>

View File

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

View File

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

View File

@@ -146,14 +146,15 @@ internal sealed class LaunchOptions : DbStoreOptions
/// <summary>
/// 目标帧率
/// </summary>
[AllowNull]
public NameValue<int> Monitor
{
get => GetOption(ref monitor, SettingEntry.LaunchMonitor, index => Monitors[int.Parse(index) - 1], Monitors[0]);
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 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.
string logFilePathFinal = File.Exists(logFilePathOvsesea) ? logFilePathOvsesea : logFilePathChinese;
string logFilePathFinal = File.Exists(logFilePathOversea) ? logFilePathOversea : logFilePathChinese;
using (TempFile? tempFile = TempFile.CopyFrom(logFilePathFinal))
{
if (tempFile != null)
{
string content = File.ReadAllText(tempFile.Path);
string content = await File.ReadAllTextAsync(tempFile.Path).ConfigureAwait(false);
Match matchResult = WarmupFileLine().Match(content);
if (!matchResult.Success)

View File

@@ -322,11 +322,13 @@ internal sealed partial class PackageConverter
{
while (await reader.ReadLineAsync().ConfigureAwait(false) is string raw)
{
if (!string.IsNullOrEmpty(raw))
if (string.IsNullOrEmpty(raw))
{
VersionItem item = JsonSerializer.Deserialize<VersionItem>(raw, options)!;
results.Add(item.RemoteName, item);
continue;
}
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)
{
if (!string.IsNullOrEmpty(raw))
if (string.IsNullOrEmpty(raw))
{
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);
continue;
}
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")!;
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))
{

View File

@@ -21,7 +21,6 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker
{
private readonly Process gameProcess;
private readonly LaunchOptions launchOptions;
private readonly ILogger<GameFpsUnlocker> logger;
private readonly UnlockerStatus status = new();
/// <summary>
@@ -37,7 +36,6 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker
public GameFpsUnlocker(IServiceProvider serviceProvider, Process gameProcess)
{
launchOptions = serviceProvider.GetRequiredService<LaunchOptions>();
logger = serviceProvider.GetRequiredService<ILogger<GameFpsUnlocker>>();
this.gameProcess = gameProcess;
}
@@ -65,14 +63,14 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker
memory = new VirtualMemory(unityPlayer.Size + userAssembly.Size);
byte* lpBuffer = (byte*)memory.Pointer;
return ReadProcessMemory((HANDLE)process.Handle, (void*)unityPlayer.Address, lpBuffer, unityPlayer.Size, default)
&& ReadProcessMemory((HANDLE)process.Handle, (void*)userAssembly.Address, lpBuffer + unityPlayer.Size, userAssembly.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);
}
private static unsafe bool UnsafeReadProcessMemory(Process process, nuint baseAddress, out nuint value)
{
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)
{
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)
{
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)
{
HMODULE[] buffer = new HMODULE[128];
uint actualSize = 0;
uint actualSize;
fixed (HMODULE* pBuffer = buffer)
{
if (!K32EnumProcessModules(hProcess, pBuffer, unchecked((uint)(buffer.Length * sizeof(HMODULE))), out actualSize))
@@ -158,7 +156,7 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker
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 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>();
if (appDbContext.ObjectCache.SingleOrDefault(e => e.Key == key) is ObjectCacheEntry entry)
if (appDbContext.ObjectCache.SingleOrDefault(e => e.Key == key) is { } entry)
{
if (entry.IsExpired)
{
@@ -105,14 +105,14 @@ internal sealed partial class HutaoService : IHutaoService
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
appDbContext.ObjectCache.AddAndSave(new()
await appDbContext.ObjectCache.AddAndSaveAsync(new()
{
Key = key,
// We hold the cache for 4 hours
ExpireTime = DateTimeOffset.Now.Add(CacheExpireTime),
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);
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())
{
await taskContext.SwitchToMainThreadAsync();

View File

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

View File

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

View File

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

View File

@@ -116,7 +116,7 @@ internal sealed partial class NavigationService : INavigationService, INavigatio
{
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"
HorizontalTextAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{shcm:ResourceString Name=ViewPageGachaLogHutaoCloudDeveloperHint}"/>
Text="{shcm:ResourceString Name=ViewPageGachaLogHutaoCloudDeveloperHint}"
Visibility="{Binding HutaoCloudViewModel.Options.IsLicensedDeveloper, Converter={StaticResource BoolToVisibilityConverter}}"/>
</StackPanel>
</Grid>
<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)
{
await taskContext.SwitchToBackgroundAsync();
CancellationToken token = StatisticsCancellationTokenSource.CancelPreviousOne();
CancellationToken token = StatisticsCancellationTokenSource.Register();
ObservableCollection<StatisticsCultivateItem> statistics;
try
{

View File

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

View File

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

View File

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