mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
fix #1763
This commit is contained in:
@@ -13,15 +13,22 @@ namespace Snap.Hutao.Core.IO;
|
||||
|
||||
internal static class DirectoryOperation
|
||||
{
|
||||
public static bool Move(string sourceDirName, string destDirName)
|
||||
public static bool TryMove(string sourceDirName, string destDirName)
|
||||
{
|
||||
if (!Directory.Exists(sourceDirName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FileSystem.MoveDirectory(sourceDirName, destDirName, true);
|
||||
return true;
|
||||
try
|
||||
{
|
||||
FileSystem.MoveDirectory(sourceDirName, destDirName, true);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe bool UnsafeRename(string path, string name, FILEOPERATION_FLAGS flags = FILEOPERATION_FLAGS.FOF_ALLOWUNDO | FILEOPERATION_FLAGS.FOF_NOCONFIRMMKDIR)
|
||||
|
||||
@@ -32,8 +32,15 @@ internal static class FileOperation
|
||||
|
||||
if (overwrite)
|
||||
{
|
||||
File.Move(sourceFileName, destFileName, true);
|
||||
return true;
|
||||
try
|
||||
{
|
||||
File.Move(sourceFileName, destFileName, true);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (File.Exists(destFileName))
|
||||
@@ -41,8 +48,15 @@ internal static class FileOperation
|
||||
return false;
|
||||
}
|
||||
|
||||
File.Move(sourceFileName, destFileName, false);
|
||||
return true;
|
||||
try
|
||||
{
|
||||
File.Move(sourceFileName, destFileName, false);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool Delete(string path)
|
||||
|
||||
@@ -9,6 +9,7 @@ using System.Net.Http;
|
||||
|
||||
namespace Snap.Hutao.Core.IO.Http.Sharding;
|
||||
|
||||
// TODO: refactor to use tree structure to calculate shards
|
||||
internal sealed class HttpShardCopyWorker<TStatus> : IDisposable
|
||||
{
|
||||
private const int ShardSize = 4 * 1024 * 1024;
|
||||
|
||||
@@ -3,7 +3,15 @@
|
||||
|
||||
namespace Snap.Hutao.Core.IO;
|
||||
|
||||
/// <summary>
|
||||
/// 流复制状态
|
||||
/// </summary>
|
||||
internal sealed record StreamCopyStatus(long BytesCopied, long TotalBytes);
|
||||
internal sealed class StreamCopyStatus
|
||||
{
|
||||
public StreamCopyStatus(long bytesCopied, long totalBytes)
|
||||
{
|
||||
BytesCopied = bytesCopied;
|
||||
TotalBytes = totalBytes;
|
||||
}
|
||||
|
||||
public long BytesCopied { get; }
|
||||
|
||||
public long TotalBytes { get; }
|
||||
}
|
||||
@@ -12,15 +12,8 @@ namespace Snap.Hutao.Core.IO;
|
||||
[HighQuality]
|
||||
internal readonly struct TempFile : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// 路径
|
||||
/// </summary>
|
||||
public readonly string Path;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的临时文件
|
||||
/// </summary>
|
||||
/// <param name="delete">是否在创建时删除文件</param>
|
||||
private TempFile(bool delete)
|
||||
{
|
||||
try
|
||||
@@ -38,11 +31,6 @@ internal readonly struct TempFile : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建临时文件并复制内容
|
||||
/// </summary>
|
||||
/// <param name="file">源文件</param>
|
||||
/// <returns>临时文件</returns>
|
||||
public static TempFile? CopyFrom(string file)
|
||||
{
|
||||
TempFile temporaryFile = new(false);
|
||||
@@ -57,9 +45,6 @@ internal readonly struct TempFile : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除临时文件
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
|
||||
@@ -5,76 +5,58 @@ using System.IO;
|
||||
|
||||
namespace Snap.Hutao.Core.IO;
|
||||
|
||||
/// <summary>
|
||||
/// 临时文件流
|
||||
/// </summary>
|
||||
internal sealed class TempFileStream : Stream
|
||||
{
|
||||
private readonly string path;
|
||||
private readonly FileStream stream;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的临时的文件流
|
||||
/// </summary>
|
||||
/// <param name="mode">文件模式</param>
|
||||
/// <param name="access">访问方式</param>
|
||||
public TempFileStream(FileMode mode, FileAccess access)
|
||||
{
|
||||
path = Path.GetTempFileName();
|
||||
stream = File.Open(path, mode, access);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool CanRead { get => stream.CanRead; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool CanSeek { get => stream.CanSeek; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool CanWrite { get => stream.CanWrite; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override long Length { get => stream.Length; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override long Position { get => stream.Position; set => stream.Position = value; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Flush()
|
||||
{
|
||||
stream.Flush();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
return stream.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
return stream.Seek(offset, origin);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
stream.SetLength(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
stream.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
stream.Dispose();
|
||||
File.Delete(path);
|
||||
if (disposing)
|
||||
{
|
||||
stream.Dispose();
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,17 +5,10 @@ using System.IO;
|
||||
|
||||
namespace Snap.Hutao.Core.IO;
|
||||
|
||||
/// <summary>
|
||||
/// 文件路径
|
||||
/// </summary>
|
||||
internal readonly struct ValueFile
|
||||
{
|
||||
private readonly string value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ValueFile"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="value">value</param>
|
||||
private ValueFile(string value)
|
||||
{
|
||||
this.value = value;
|
||||
@@ -31,55 +24,6 @@ internal readonly struct ValueFile
|
||||
return new(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步反序列化文件中的内容
|
||||
/// </summary>
|
||||
/// <typeparam name="T">内容的类型</typeparam>
|
||||
/// <param name="options">序列化选项</param>
|
||||
/// <returns>操作是否成功,反序列化后的内容</returns>
|
||||
public async ValueTask<ValueResult<bool, T?>> DeserializeFromJsonAsync<T>(JsonSerializerOptions options)
|
||||
where T : class
|
||||
{
|
||||
try
|
||||
{
|
||||
using (FileStream stream = File.OpenRead(value))
|
||||
{
|
||||
T? t = await JsonSerializer.DeserializeAsync<T>(stream, options).ConfigureAwait(false);
|
||||
return new(true, t);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_ = ex;
|
||||
return new(false, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将对象异步序列化入文件
|
||||
/// </summary>
|
||||
/// <typeparam name="T">对象的类型</typeparam>
|
||||
/// <param name="obj">对象</param>
|
||||
/// <param name="options">序列化选项</param>
|
||||
/// <returns>操作是否成功</returns>
|
||||
public async ValueTask<bool> SerializeToJsonAsync<T>(T obj, JsonSerializerOptions options)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (FileStream stream = File.Create(value))
|
||||
{
|
||||
await JsonSerializer.SerializeAsync(stream, obj, options).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[SuppressMessage("", "CA1307")]
|
||||
public override int GetHashCode()
|
||||
{
|
||||
|
||||
44
src/Snap.Hutao/Snap.Hutao/Core/IO/ValueFileExtension.cs
Normal file
44
src/Snap.Hutao/Snap.Hutao/Core/IO/ValueFileExtension.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace Snap.Hutao.Core.IO;
|
||||
|
||||
internal static class ValueFileExtension
|
||||
{
|
||||
public static async ValueTask<ValueResult<bool, T?>> DeserializeFromJsonAsync<T>(this ValueFile file, JsonSerializerOptions options)
|
||||
where T : class
|
||||
{
|
||||
try
|
||||
{
|
||||
using (FileStream stream = File.OpenRead(file))
|
||||
{
|
||||
T? t = await JsonSerializer.DeserializeAsync<T>(stream, options).ConfigureAwait(false);
|
||||
return new(true, t);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_ = ex;
|
||||
return new(false, null);
|
||||
}
|
||||
}
|
||||
|
||||
public static async ValueTask<bool> SerializeToJsonAsync<T>(this ValueFile file, T obj, JsonSerializerOptions options)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (FileStream stream = File.Create(file))
|
||||
{
|
||||
await JsonSerializer.SerializeAsync(stream, obj, options).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,17 +5,12 @@ using System.Globalization;
|
||||
|
||||
namespace Snap.Hutao.Core.Json.Converter;
|
||||
|
||||
/// <summary>
|
||||
/// 实现日期的转换
|
||||
/// 此转换器无法实现无损往返
|
||||
/// 必须在反序列化后调整 Offset
|
||||
/// </summary>
|
||||
/// 此转换器无法实现无损往返 必须在反序列化后调整 Offset
|
||||
[HighQuality]
|
||||
internal class DateTimeOffsetConverter : JsonConverter<DateTimeOffset>
|
||||
{
|
||||
private const string Format = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.GetString() is { } dataTimeString)
|
||||
@@ -29,7 +24,6 @@ internal class DateTimeOffsetConverter : JsonConverter<DateTimeOffset>
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.DateTime.ToString(Format, CultureInfo.InvariantCulture));
|
||||
|
||||
@@ -6,15 +6,11 @@ using System.Globalization;
|
||||
|
||||
namespace Snap.Hutao.Core.Json.Converter;
|
||||
|
||||
/// <summary>
|
||||
/// 逗号分隔列表转换器
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
internal sealed class SeparatorCommaInt32EnumerableConverter : JsonConverter<IEnumerable<int>>
|
||||
{
|
||||
private const char Comma = ',';
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override IEnumerable<int> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.GetString() is { } source)
|
||||
@@ -25,7 +21,6 @@ internal sealed class SeparatorCommaInt32EnumerableConverter : JsonConverter<IEn
|
||||
return [];
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(Utf8JsonWriter writer, IEnumerable<int> value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(string.Join(Comma, value));
|
||||
|
||||
@@ -6,10 +6,6 @@ using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Snap.Hutao.Core.Json.Converter;
|
||||
|
||||
/// <summary>
|
||||
/// 枚举转换器
|
||||
/// </summary>
|
||||
/// <typeparam name="TEnum">枚举的类型</typeparam>
|
||||
[HighQuality]
|
||||
internal sealed class UnsafeEnumConverter<TEnum> : JsonConverter<TEnum>
|
||||
where TEnum : struct, Enum
|
||||
@@ -19,18 +15,12 @@ internal sealed class UnsafeEnumConverter<TEnum> : JsonConverter<TEnum>
|
||||
private readonly JsonSerializeType readAs;
|
||||
private readonly JsonSerializeType writeAs;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的枚举转换器
|
||||
/// </summary>
|
||||
/// <param name="readAs">读取</param>
|
||||
/// <param name="writeAs">写入</param>
|
||||
public UnsafeEnumConverter(JsonSerializeType readAs, JsonSerializeType writeAs)
|
||||
{
|
||||
this.readAs = readAs;
|
||||
this.writeAs = writeAs;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override TEnum Read(ref Utf8JsonReader reader, Type typeToConverTEnum, JsonSerializerOptions options)
|
||||
{
|
||||
if (readAs == JsonSerializeType.Number)
|
||||
@@ -46,13 +36,12 @@ internal sealed class UnsafeEnumConverter<TEnum> : JsonConverter<TEnum>
|
||||
throw new JsonException();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options)
|
||||
{
|
||||
switch (writeAs)
|
||||
{
|
||||
case JsonSerializeType.Number:
|
||||
WriteEnumValue(writer, value, enumTypeCode);
|
||||
WriteNumberValue(writer, value, enumTypeCode);
|
||||
break;
|
||||
case JsonSerializeType.NumberString:
|
||||
writer.WriteStringValue(value.ToString("D"));
|
||||
@@ -123,7 +112,7 @@ internal sealed class UnsafeEnumConverter<TEnum> : JsonConverter<TEnum>
|
||||
throw new JsonException();
|
||||
}
|
||||
|
||||
private static void WriteEnumValue(Utf8JsonWriter writer, TEnum value, TypeCode typeCode)
|
||||
private static void WriteNumberValue(Utf8JsonWriter writer, TEnum value, TypeCode typeCode)
|
||||
{
|
||||
switch (typeCode)
|
||||
{
|
||||
|
||||
@@ -6,14 +6,8 @@ using System.Text.Json.Serialization.Metadata;
|
||||
|
||||
namespace Snap.Hutao.Core.Json;
|
||||
|
||||
/// <summary>
|
||||
/// Json 选项
|
||||
/// </summary>
|
||||
internal static class JsonOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// 默认的Json序列化选项
|
||||
/// </summary>
|
||||
public static readonly JsonSerializerOptions Default = new()
|
||||
{
|
||||
AllowTrailingCommas = true,
|
||||
|
||||
@@ -6,18 +6,10 @@ using System.Text.Json.Serialization.Metadata;
|
||||
|
||||
namespace Snap.Hutao.Core.Json;
|
||||
|
||||
/// <summary>
|
||||
/// Json 类型信息解析器
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
internal static class JsonTypeInfoResolvers
|
||||
{
|
||||
private static readonly Type JsonEnumAttributeType = typeof(JsonEnumAttribute);
|
||||
|
||||
/// <summary>
|
||||
/// 解析枚举类型
|
||||
/// </summary>
|
||||
/// <param name="typeInfo">Json 类型信息</param>
|
||||
public static void ResolveEnumType(JsonTypeInfo typeInfo)
|
||||
{
|
||||
if (typeInfo.Kind != JsonTypeInfoKind.Object)
|
||||
|
||||
@@ -18,12 +18,10 @@ using Snap.Hutao.Service.Metadata;
|
||||
using Snap.Hutao.Service.Navigation;
|
||||
using Snap.Hutao.ViewModel.Guide;
|
||||
using System.Diagnostics;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Snap.Hutao.Core.LifeCycle;
|
||||
|
||||
/// <summary>
|
||||
/// 激活
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[ConstructorGenerated]
|
||||
[Injection(InjectAs.Singleton, typeof(IAppActivation))]
|
||||
@@ -44,7 +42,6 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi
|
||||
|
||||
private readonly SemaphoreSlim activateSemaphore = new(1);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Activate(HutaoActivationArguments args)
|
||||
{
|
||||
HandleActivationExclusiveAsync(args).SafeForget();
|
||||
@@ -90,7 +87,6 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi
|
||||
HandleAppNotificationActivationAsync(args.Arguments, false).SafeForget();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void PostInitialization()
|
||||
{
|
||||
RunPostInitializationAsync().SafeForget();
|
||||
@@ -210,10 +206,13 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
|
||||
INavigationAwaiter navigationAwaiter = new NavigationExtra(ImportUIAFFromClipboard);
|
||||
await serviceProvider
|
||||
#pragma warning disable CA1849
|
||||
// We can't await here to navigate to Achievment Page, the Achievement
|
||||
// ViewModel requires the Metadata Service to be initialized.
|
||||
serviceProvider
|
||||
.GetRequiredService<INavigationService>()
|
||||
.NavigateAsync<View.Page.AchievementPage>(navigationAwaiter, true)
|
||||
.ConfigureAwait(false);
|
||||
.Navigate<View.Page.AchievementPage>(navigationAwaiter, true);
|
||||
#pragma warning restore CA1849
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,18 @@ using System.Runtime.InteropServices;
|
||||
|
||||
namespace Snap.Hutao.Core.LifeCycle.InterProcess;
|
||||
|
||||
// Layout:
|
||||
// 0 1 2 3 4 Bytes
|
||||
// ┌─────────┬──────┬─────────┬─────────────┐
|
||||
// │ Version │ Type │ Command │ ContentType │
|
||||
// ├─────────┴──────┴─────────┴─────────────┤ 4 Bytes
|
||||
// │ ContentLength │
|
||||
// ├────────────────────────────────────────┤ 8 Bytes
|
||||
// │ │
|
||||
// │─────────────── Checksum ───────────────│
|
||||
// │ │
|
||||
// └────────────────────────────────────────┘ 16 Bytes
|
||||
// Any content will be placed after the header.
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
internal struct PipePacketHeader
|
||||
{
|
||||
|
||||
@@ -35,7 +35,6 @@ internal static class PipeStreamExtension
|
||||
}
|
||||
}
|
||||
|
||||
[SkipLocalsInit]
|
||||
public static unsafe void ReadPacket(this PipeStream stream, out PipePacketHeader header)
|
||||
{
|
||||
fixed (PipePacketHeader* pHeader = &header)
|
||||
|
||||
@@ -89,6 +89,7 @@ internal sealed partial class PrivateNamedPipeServer : IDisposable
|
||||
serverStream.WritePacketWithJsonContent(PrivateNamedPipe.Version, PipePacketType.Response, PipePacketCommand.ResponseElevationStatus, resp);
|
||||
serverStream.Flush();
|
||||
break;
|
||||
|
||||
case (PipePacketType.Request, PipePacketCommand.RedirectActivation):
|
||||
HutaoActivationArguments? hutaoArgs = serverStream.ReadJsonContent<HutaoActivationArguments>(in header);
|
||||
if (hutaoArgs is not null)
|
||||
@@ -98,6 +99,7 @@ internal sealed partial class PrivateNamedPipeServer : IDisposable
|
||||
|
||||
messageDispatcher.RedirectActivation(hutaoArgs);
|
||||
break;
|
||||
|
||||
case (PipePacketType.SessionTermination, _):
|
||||
serverStream.Disconnect();
|
||||
if (header.Command is PipePacketCommand.Exit)
|
||||
|
||||
@@ -41,6 +41,7 @@ internal sealed partial class SettingEntry
|
||||
public const string LaunchScreenHeight = "Launch.ScreenHeight";
|
||||
public const string LaunchIsScreenHeightEnabled = "Launch.IsScreenHeightEnabled";
|
||||
public const string LaunchUnlockFps = "Launch.UnlockFps";
|
||||
public const string LaunchUnlockerKind = "Launch.UnlockerKind";
|
||||
public const string LaunchTargetFps = "Launch.TargetFps";
|
||||
public const string LaunchMonitor = "Launch.Monitor";
|
||||
public const string LaunchIsMonitorEnabled = "Launch.IsMonitorEnabled";
|
||||
|
||||
@@ -18,6 +18,18 @@ internal abstract partial class DbStoreOptions : ObservableObject
|
||||
{
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
|
||||
protected static T? EnumParse<T>(string input)
|
||||
where T : struct, Enum
|
||||
{
|
||||
return Enum.Parse<T>(input);
|
||||
}
|
||||
|
||||
protected static string EnumToStringOrEmpty<T>(T? input)
|
||||
where T : struct, Enum
|
||||
{
|
||||
return input.ToStringOrEmpty();
|
||||
}
|
||||
|
||||
protected string GetOption(ref string? storage, string key, string defaultValue = "")
|
||||
{
|
||||
return GetOption(ref storage, key, () => defaultValue);
|
||||
|
||||
@@ -84,16 +84,4 @@ internal sealed partial class AppOptions : DbStoreOptions
|
||||
get => GetOption(ref geetestCustomCompositeUrl, SettingEntry.GeetestCustomCompositeUrl);
|
||||
set => SetOption(ref geetestCustomCompositeUrl, SettingEntry.GeetestCustomCompositeUrl, value);
|
||||
}
|
||||
|
||||
private static T? EnumParse<T>(string input)
|
||||
where T : struct, Enum
|
||||
{
|
||||
return Enum.Parse<T>(input);
|
||||
}
|
||||
|
||||
private static string EnumToStringOrEmpty<T>(T? input)
|
||||
where T : struct, Enum
|
||||
{
|
||||
return input.ToStringOrEmpty();
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ using Snap.Hutao.Model;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
using Snap.Hutao.Service.Game.PathAbstraction;
|
||||
using Snap.Hutao.Service.Game.Unlocker;
|
||||
using Snap.Hutao.Win32.Graphics.Gdi;
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
@@ -38,6 +39,7 @@ internal sealed class LaunchOptions : DbStoreOptions
|
||||
private int? screenHeight;
|
||||
private bool? isScreenHeightEnabled;
|
||||
private bool? unlockFps;
|
||||
private GameFpsUnlockerKind? unlockerKind;
|
||||
private int? targetFps;
|
||||
private NameValue<int>? monitor;
|
||||
private bool? isMonitorEnabled;
|
||||
@@ -167,6 +169,12 @@ internal sealed class LaunchOptions : DbStoreOptions
|
||||
set => SetOption(ref unlockFps, SettingEntry.LaunchUnlockFps, value);
|
||||
}
|
||||
|
||||
public GameFpsUnlockerKind UnlockerKind
|
||||
{
|
||||
get => GetOption(ref unlockerKind, SettingEntry.LaunchUnlockerKind, EnumParse<GameFpsUnlockerKind>, GameFpsUnlockerKind.Legacy).Value;
|
||||
set => SetOption(ref unlockerKind, SettingEntry.LaunchUnlockerKind, value, EnumToStringOrEmpty);
|
||||
}
|
||||
|
||||
public int TargetFps
|
||||
{
|
||||
get => GetOption(ref targetFps, SettingEntry.LaunchTargetFps, primaryScreenFps);
|
||||
|
||||
@@ -25,7 +25,12 @@ internal sealed class LaunchExecutionUnlockFpsHandler : ILaunchExecutionDelegate
|
||||
return;
|
||||
}
|
||||
|
||||
IslandGameFpsUnlocker unlocker = new(context.ServiceProvider, context.Process, new(gameFileSystem, 100, 20000, 2000), progress);
|
||||
UnlockOptions unlockOptions = new(gameFileSystem, 100, 20000, 2000);
|
||||
IGameFpsUnlocker unlocker = context.Options.UnlockerKind switch
|
||||
{
|
||||
GameFpsUnlockerKind.Island => new IslandGameFpsUnlocker(context.ServiceProvider, context.Process, unlockOptions, progress),
|
||||
_ => new DefaultGameFpsUnlocker(context.ServiceProvider, context.Process, unlockOptions, progress),
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -304,7 +304,7 @@ internal sealed partial class PackageConverter
|
||||
try
|
||||
{
|
||||
progress.Report(new(SH.FormatServiceGamePackageConvertMoveFileRenameFormat(context.FromDataFolderName, context.ToDataFolderName)));
|
||||
DirectoryOperation.Move(context.FromDataFolder, context.ToDataFolder);
|
||||
DirectoryOperation.TryMove(context.FromDataFolder, context.ToDataFolder);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Unlocker;
|
||||
|
||||
internal enum GameFpsUnlockerKind
|
||||
{
|
||||
Legacy,
|
||||
Island,
|
||||
}
|
||||
@@ -6,12 +6,14 @@ using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Core.Diagnostics.CodeAnalysis;
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
using Snap.Hutao.Model;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Service;
|
||||
using Snap.Hutao.Service.Game;
|
||||
using Snap.Hutao.Service.Game.Locator;
|
||||
using Snap.Hutao.Service.Game.PathAbstraction;
|
||||
using Snap.Hutao.Service.Game.Scheme;
|
||||
using Snap.Hutao.Service.Game.Unlocker;
|
||||
using Snap.Hutao.Service.Notification;
|
||||
using Snap.Hutao.Service.User;
|
||||
using Snap.Hutao.UI.Xaml.Data;
|
||||
@@ -69,7 +71,6 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel, IView
|
||||
|
||||
public List<LaunchScheme> KnownSchemes { get; } = KnownLaunchSchemes.Get();
|
||||
|
||||
[AlsoAsyncSets(nameof(GamePackage), nameof(GameAccountsView))]
|
||||
public LaunchScheme? SelectedScheme
|
||||
{
|
||||
get => selectedScheme;
|
||||
|
||||
Reference in New Issue
Block a user