From b02f2b47c831e4b308c7eb2ad2b7302ccdd40e9a Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Sun, 23 Jun 2024 17:18:11 +0800 Subject: [PATCH] fix #1763 --- .../Snap.Hutao/Core/IO/DirectoryOperation.cs | 13 ++++- .../Snap.Hutao/Core/IO/FileOperation.cs | 22 ++++++-- .../IO/Http/Sharding/HttpShardCopyWorker.cs | 1 + .../Snap.Hutao/Core/IO/StreamCopyStatus.cs | 16 ++++-- src/Snap.Hutao/Snap.Hutao/Core/IO/TempFile.cs | 15 ----- .../Snap.Hutao/Core/IO/TempFileStream.cs | 28 ++-------- .../Snap.Hutao/Core/IO/ValueFile.cs | 56 ------------------- .../Snap.Hutao/Core/IO/ValueFileExtension.cs | 44 +++++++++++++++ .../Json/Converter/DateTimeOffsetConverter.cs | 8 +-- .../SeparatorCommaInt32EnumerableConverter.cs | 5 -- .../Json/Converter/UnsafeEnumConverter.cs | 15 +---- .../Snap.Hutao/Core/Json/JsonOptions.cs | 6 -- .../Core/Json/JsonTypeInfoResolvers.cs | 8 --- .../Core/LifeCycle/AppActivation.cs | 15 +++-- .../InterProcess/PipePacketHeader.cs | 12 ++++ .../InterProcess/PipeStreamExtension.cs | 1 - .../InterProcess/PrivateNamedPipeServer.cs | 2 + .../Model/Entity/SettingEntry.Constant.cs | 1 + .../Service/Abstraction/DbStoreOptions.cs | 12 ++++ .../Snap.Hutao/Service/AppOptions.cs | 12 ---- .../Snap.Hutao/Service/Game/LaunchOptions.cs | 8 +++ .../LaunchExecutionUnlockFpsHandler.cs | 7 ++- .../Service/Game/Package/PackageConverter.cs | 2 +- .../Game/Unlocker/GameFpsUnlockerKind.cs | 10 ++++ .../ViewModel/Game/LaunchGameViewModel.cs | 3 +- 25 files changed, 154 insertions(+), 168 deletions(-) create mode 100644 src/Snap.Hutao/Snap.Hutao/Core/IO/ValueFileExtension.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlockerKind.cs diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/DirectoryOperation.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/DirectoryOperation.cs index 2e3894ad..ec378745 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/IO/DirectoryOperation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/DirectoryOperation.cs @@ -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) diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/FileOperation.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/FileOperation.cs index 7ea0f3b1..09df12cb 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/IO/FileOperation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/FileOperation.cs @@ -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) diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Sharding/HttpShardCopyWorker.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Sharding/HttpShardCopyWorker.cs index 12bcd97b..b7f2d109 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Sharding/HttpShardCopyWorker.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Sharding/HttpShardCopyWorker.cs @@ -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 : IDisposable { private const int ShardSize = 4 * 1024 * 1024; diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/StreamCopyStatus.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/StreamCopyStatus.cs index 465e3047..23d12d18 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/IO/StreamCopyStatus.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/StreamCopyStatus.cs @@ -3,7 +3,15 @@ namespace Snap.Hutao.Core.IO; -/// -/// 流复制状态 -/// -internal sealed record StreamCopyStatus(long BytesCopied, long TotalBytes); \ No newline at end of file +internal sealed class StreamCopyStatus +{ + public StreamCopyStatus(long bytesCopied, long totalBytes) + { + BytesCopied = bytesCopied; + TotalBytes = totalBytes; + } + + public long BytesCopied { get; } + + public long TotalBytes { get; } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/TempFile.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/TempFile.cs index f70daead..2f711160 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/IO/TempFile.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/TempFile.cs @@ -12,15 +12,8 @@ namespace Snap.Hutao.Core.IO; [HighQuality] internal readonly struct TempFile : IDisposable { - /// - /// 路径 - /// public readonly string Path; - /// - /// 构造一个新的临时文件 - /// - /// 是否在创建时删除文件 private TempFile(bool delete) { try @@ -38,11 +31,6 @@ internal readonly struct TempFile : IDisposable } } - /// - /// 创建临时文件并复制内容 - /// - /// 源文件 - /// 临时文件 public static TempFile? CopyFrom(string file) { TempFile temporaryFile = new(false); @@ -57,9 +45,6 @@ internal readonly struct TempFile : IDisposable } } - /// - /// 删除临时文件 - /// public void Dispose() { try diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/TempFileStream.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/TempFileStream.cs index 6bd69425..0c2d3266 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/IO/TempFileStream.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/TempFileStream.cs @@ -5,76 +5,58 @@ using System.IO; namespace Snap.Hutao.Core.IO; -/// -/// 临时文件流 -/// internal sealed class TempFileStream : Stream { private readonly string path; private readonly FileStream stream; - /// - /// 构造一个新的临时的文件流 - /// - /// 文件模式 - /// 访问方式 public TempFileStream(FileMode mode, FileAccess access) { path = Path.GetTempFileName(); stream = File.Open(path, mode, access); } - /// public override bool CanRead { get => stream.CanRead; } - /// public override bool CanSeek { get => stream.CanSeek; } - /// public override bool CanWrite { get => stream.CanWrite; } - /// public override long Length { get => stream.Length; } - /// public override long Position { get => stream.Position; set => stream.Position = value; } - /// public override void Flush() { stream.Flush(); } - /// public override int Read(byte[] buffer, int offset, int count) { return stream.Read(buffer, offset, count); } - /// public override long Seek(long offset, SeekOrigin origin) { return stream.Seek(offset, origin); } - /// public override void SetLength(long value) { stream.SetLength(value); } - /// public override void Write(byte[] buffer, int offset, int count) { stream.Write(buffer, offset, count); } - /// protected override void Dispose(bool disposing) { - base.Dispose(disposing); - - stream.Dispose(); - File.Delete(path); + if (disposing) + { + stream.Dispose(); + File.Delete(path); + } } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/ValueFile.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/ValueFile.cs index cdd0b610..de7b8c3d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/IO/ValueFile.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/ValueFile.cs @@ -5,17 +5,10 @@ using System.IO; namespace Snap.Hutao.Core.IO; -/// -/// 文件路径 -/// internal readonly struct ValueFile { private readonly string value; - /// - /// Initializes a new instance of the struct. - /// - /// value private ValueFile(string value) { this.value = value; @@ -31,55 +24,6 @@ internal readonly struct ValueFile return new(value); } - /// - /// 异步反序列化文件中的内容 - /// - /// 内容的类型 - /// 序列化选项 - /// 操作是否成功,反序列化后的内容 - public async ValueTask> DeserializeFromJsonAsync(JsonSerializerOptions options) - where T : class - { - try - { - using (FileStream stream = File.OpenRead(value)) - { - T? t = await JsonSerializer.DeserializeAsync(stream, options).ConfigureAwait(false); - return new(true, t); - } - } - catch (Exception ex) - { - _ = ex; - return new(false, null); - } - } - - /// - /// 将对象异步序列化入文件 - /// - /// 对象的类型 - /// 对象 - /// 序列化选项 - /// 操作是否成功 - public async ValueTask SerializeToJsonAsync(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; - } - } - - /// [SuppressMessage("", "CA1307")] public override int GetHashCode() { diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/ValueFileExtension.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/ValueFileExtension.cs new file mode 100644 index 00000000..c2f393c1 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/ValueFileExtension.cs @@ -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> DeserializeFromJsonAsync(this ValueFile file, JsonSerializerOptions options) + where T : class + { + try + { + using (FileStream stream = File.OpenRead(file)) + { + T? t = await JsonSerializer.DeserializeAsync(stream, options).ConfigureAwait(false); + return new(true, t); + } + } + catch (Exception ex) + { + _ = ex; + return new(false, null); + } + } + + public static async ValueTask SerializeToJsonAsync(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; + } + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/DateTimeOffsetConverter.cs b/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/DateTimeOffsetConverter.cs index 1d67f9ab..23694c2a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/DateTimeOffsetConverter.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/DateTimeOffsetConverter.cs @@ -5,17 +5,12 @@ using System.Globalization; namespace Snap.Hutao.Core.Json.Converter; -/// -/// 实现日期的转换 -/// 此转换器无法实现无损往返 -/// 必须在反序列化后调整 Offset -/// +/// 此转换器无法实现无损往返 必须在反序列化后调整 Offset [HighQuality] internal class DateTimeOffsetConverter : JsonConverter { private const string Format = "yyyy-MM-dd HH:mm:ss"; - /// public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.GetString() is { } dataTimeString) @@ -29,7 +24,6 @@ internal class DateTimeOffsetConverter : JsonConverter return default; } - /// public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options) { writer.WriteStringValue(value.DateTime.ToString(Format, CultureInfo.InvariantCulture)); diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/SeparatorCommaInt32EnumerableConverter.cs b/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/SeparatorCommaInt32EnumerableConverter.cs index 49e1773d..4cdafdab 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/SeparatorCommaInt32EnumerableConverter.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/SeparatorCommaInt32EnumerableConverter.cs @@ -6,15 +6,11 @@ using System.Globalization; namespace Snap.Hutao.Core.Json.Converter; -/// -/// 逗号分隔列表转换器 -/// [HighQuality] internal sealed class SeparatorCommaInt32EnumerableConverter : JsonConverter> { private const char Comma = ','; - /// public override IEnumerable Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.GetString() is { } source) @@ -25,7 +21,6 @@ internal sealed class SeparatorCommaInt32EnumerableConverter : JsonConverter public override void Write(Utf8JsonWriter writer, IEnumerable value, JsonSerializerOptions options) { writer.WriteStringValue(string.Join(Comma, value)); diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/UnsafeEnumConverter.cs b/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/UnsafeEnumConverter.cs index 65066e8e..82586061 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/UnsafeEnumConverter.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/UnsafeEnumConverter.cs @@ -6,10 +6,6 @@ using System.Runtime.CompilerServices; namespace Snap.Hutao.Core.Json.Converter; -/// -/// 枚举转换器 -/// -/// 枚举的类型 [HighQuality] internal sealed class UnsafeEnumConverter : JsonConverter where TEnum : struct, Enum @@ -19,18 +15,12 @@ internal sealed class UnsafeEnumConverter : JsonConverter private readonly JsonSerializeType readAs; private readonly JsonSerializeType writeAs; - /// - /// 构造一个新的枚举转换器 - /// - /// 读取 - /// 写入 public UnsafeEnumConverter(JsonSerializeType readAs, JsonSerializeType writeAs) { this.readAs = readAs; this.writeAs = writeAs; } - /// public override TEnum Read(ref Utf8JsonReader reader, Type typeToConverTEnum, JsonSerializerOptions options) { if (readAs == JsonSerializeType.Number) @@ -46,13 +36,12 @@ internal sealed class UnsafeEnumConverter : JsonConverter throw new JsonException(); } - /// 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 : JsonConverter 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) { diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Json/JsonOptions.cs b/src/Snap.Hutao/Snap.Hutao/Core/Json/JsonOptions.cs index 33c06adc..91fb7d6e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Json/JsonOptions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Json/JsonOptions.cs @@ -6,14 +6,8 @@ using System.Text.Json.Serialization.Metadata; namespace Snap.Hutao.Core.Json; -/// -/// Json 选项 -/// internal static class JsonOptions { - /// - /// 默认的Json序列化选项 - /// public static readonly JsonSerializerOptions Default = new() { AllowTrailingCommas = true, diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Json/JsonTypeInfoResolvers.cs b/src/Snap.Hutao/Snap.Hutao/Core/Json/JsonTypeInfoResolvers.cs index 348e6627..04aafb4f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Json/JsonTypeInfoResolvers.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Json/JsonTypeInfoResolvers.cs @@ -6,18 +6,10 @@ using System.Text.Json.Serialization.Metadata; namespace Snap.Hutao.Core.Json; -/// -/// Json 类型信息解析器 -/// -[HighQuality] internal static class JsonTypeInfoResolvers { private static readonly Type JsonEnumAttributeType = typeof(JsonEnumAttribute); - /// - /// 解析枚举类型 - /// - /// Json 类型信息 public static void ResolveEnumType(JsonTypeInfo typeInfo) { if (typeInfo.Kind != JsonTypeInfoKind.Object) diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivation.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivation.cs index 9eb7cf3a..34070474 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivation.cs @@ -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; -/// -/// 激活 -/// [HighQuality] [ConstructorGenerated] [Injection(InjectAs.Singleton, typeof(IAppActivation))] @@ -44,7 +42,6 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi private readonly SemaphoreSlim activateSemaphore = new(1); - /// public void Activate(HutaoActivationArguments args) { HandleActivationExclusiveAsync(args).SafeForget(); @@ -90,7 +87,6 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi HandleAppNotificationActivationAsync(args.Arguments, false).SafeForget(); } - /// 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() - .NavigateAsync(navigationAwaiter, true) - .ConfigureAwait(false); + .Navigate(navigationAwaiter, true); +#pragma warning restore CA1849 break; } } diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/InterProcess/PipePacketHeader.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/InterProcess/PipePacketHeader.cs index 1c9b3bf5..5c763241 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/InterProcess/PipePacketHeader.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/InterProcess/PipePacketHeader.cs @@ -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 { diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/InterProcess/PipeStreamExtension.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/InterProcess/PipeStreamExtension.cs index 81fd8263..fc6c7fcf 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/InterProcess/PipeStreamExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/InterProcess/PipeStreamExtension.cs @@ -35,7 +35,6 @@ internal static class PipeStreamExtension } } - [SkipLocalsInit] public static unsafe void ReadPacket(this PipeStream stream, out PipePacketHeader header) { fixed (PipePacketHeader* pHeader = &header) diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/InterProcess/PrivateNamedPipeServer.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/InterProcess/PrivateNamedPipeServer.cs index 172ee9d5..8a207d72 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/InterProcess/PrivateNamedPipeServer.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/InterProcess/PrivateNamedPipeServer.cs @@ -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(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) diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.Constant.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.Constant.cs index a34712f1..0e07b71d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.Constant.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.Constant.cs @@ -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"; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/DbStoreOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/DbStoreOptions.cs index cb5747b1..9e9aa78a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/DbStoreOptions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/DbStoreOptions.cs @@ -18,6 +18,18 @@ internal abstract partial class DbStoreOptions : ObservableObject { private readonly IServiceProvider serviceProvider; + protected static T? EnumParse(string input) + where T : struct, Enum + { + return Enum.Parse(input); + } + + protected static string EnumToStringOrEmpty(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); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs index 4b701632..86165cb8 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs @@ -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(string input) - where T : struct, Enum - { - return Enum.Parse(input); - } - - private static string EnumToStringOrEmpty(T? input) - where T : struct, Enum - { - return input.ToStringOrEmpty(); - } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs index 4b614ef3..ce2b1c19 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs @@ -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? 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.Legacy).Value; + set => SetOption(ref unlockerKind, SettingEntry.LaunchUnlockerKind, value, EnumToStringOrEmpty); + } + public int TargetFps { get => GetOption(ref targetFps, SettingEntry.LaunchTargetFps, primaryScreenFps); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Handler/LaunchExecutionUnlockFpsHandler.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Handler/LaunchExecutionUnlockFpsHandler.cs index 89de1653..5b4d44ec 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Handler/LaunchExecutionUnlockFpsHandler.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Handler/LaunchExecutionUnlockFpsHandler.cs @@ -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 { diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/PackageConverter.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/PackageConverter.cs index 977609bc..91d23eab 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/PackageConverter.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/PackageConverter.cs @@ -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) { diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlockerKind.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlockerKind.cs new file mode 100644 index 00000000..079ea5fa --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlockerKind.cs @@ -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, +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Game/LaunchGameViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Game/LaunchGameViewModel.cs index 0e4e4a06..244333b6 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Game/LaunchGameViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Game/LaunchGameViewModel.cs @@ -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 KnownSchemes { get; } = KnownLaunchSchemes.Get(); - [AlsoAsyncSets(nameof(GamePackage), nameof(GameAccountsView))] public LaunchScheme? SelectedScheme { get => selectedScheme;