This commit is contained in:
DismissedLight
2024-06-23 17:18:11 +08:00
parent b4f7bf934e
commit b02f2b47c8
25 changed files with 154 additions and 168 deletions

View File

@@ -13,15 +13,22 @@ namespace Snap.Hutao.Core.IO;
internal static class DirectoryOperation internal static class DirectoryOperation
{ {
public static bool Move(string sourceDirName, string destDirName) public static bool TryMove(string sourceDirName, string destDirName)
{ {
if (!Directory.Exists(sourceDirName)) if (!Directory.Exists(sourceDirName))
{ {
return false; return false;
} }
FileSystem.MoveDirectory(sourceDirName, destDirName, true); try
return true; {
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) public static unsafe bool UnsafeRename(string path, string name, FILEOPERATION_FLAGS flags = FILEOPERATION_FLAGS.FOF_ALLOWUNDO | FILEOPERATION_FLAGS.FOF_NOCONFIRMMKDIR)

View File

@@ -32,8 +32,15 @@ internal static class FileOperation
if (overwrite) if (overwrite)
{ {
File.Move(sourceFileName, destFileName, true); try
return true; {
File.Move(sourceFileName, destFileName, true);
return true;
}
catch
{
return false;
}
} }
if (File.Exists(destFileName)) if (File.Exists(destFileName))
@@ -41,8 +48,15 @@ internal static class FileOperation
return false; return false;
} }
File.Move(sourceFileName, destFileName, false); try
return true; {
File.Move(sourceFileName, destFileName, false);
return true;
}
catch
{
return false;
}
} }
public static bool Delete(string path) public static bool Delete(string path)

View File

@@ -9,6 +9,7 @@ using System.Net.Http;
namespace Snap.Hutao.Core.IO.Http.Sharding; namespace Snap.Hutao.Core.IO.Http.Sharding;
// TODO: refactor to use tree structure to calculate shards
internal sealed class HttpShardCopyWorker<TStatus> : IDisposable internal sealed class HttpShardCopyWorker<TStatus> : IDisposable
{ {
private const int ShardSize = 4 * 1024 * 1024; private const int ShardSize = 4 * 1024 * 1024;

View File

@@ -3,7 +3,15 @@
namespace Snap.Hutao.Core.IO; namespace Snap.Hutao.Core.IO;
/// <summary> internal sealed class StreamCopyStatus
/// 流复制状态 {
/// </summary> public StreamCopyStatus(long bytesCopied, long totalBytes)
internal sealed record StreamCopyStatus(long BytesCopied, long TotalBytes); {
BytesCopied = bytesCopied;
TotalBytes = totalBytes;
}
public long BytesCopied { get; }
public long TotalBytes { get; }
}

View File

@@ -12,15 +12,8 @@ namespace Snap.Hutao.Core.IO;
[HighQuality] [HighQuality]
internal readonly struct TempFile : IDisposable internal readonly struct TempFile : IDisposable
{ {
/// <summary>
/// 路径
/// </summary>
public readonly string Path; public readonly string Path;
/// <summary>
/// 构造一个新的临时文件
/// </summary>
/// <param name="delete">是否在创建时删除文件</param>
private TempFile(bool delete) private TempFile(bool delete)
{ {
try try
@@ -38,11 +31,6 @@ internal readonly struct TempFile : IDisposable
} }
} }
/// <summary>
/// 创建临时文件并复制内容
/// </summary>
/// <param name="file">源文件</param>
/// <returns>临时文件</returns>
public static TempFile? CopyFrom(string file) public static TempFile? CopyFrom(string file)
{ {
TempFile temporaryFile = new(false); TempFile temporaryFile = new(false);
@@ -57,9 +45,6 @@ internal readonly struct TempFile : IDisposable
} }
} }
/// <summary>
/// 删除临时文件
/// </summary>
public void Dispose() public void Dispose()
{ {
try try

View File

@@ -5,76 +5,58 @@ using System.IO;
namespace Snap.Hutao.Core.IO; namespace Snap.Hutao.Core.IO;
/// <summary>
/// 临时文件流
/// </summary>
internal sealed class TempFileStream : Stream internal sealed class TempFileStream : Stream
{ {
private readonly string path; private readonly string path;
private readonly FileStream stream; private readonly FileStream stream;
/// <summary>
/// 构造一个新的临时的文件流
/// </summary>
/// <param name="mode">文件模式</param>
/// <param name="access">访问方式</param>
public TempFileStream(FileMode mode, FileAccess access) public TempFileStream(FileMode mode, FileAccess access)
{ {
path = Path.GetTempFileName(); path = Path.GetTempFileName();
stream = File.Open(path, mode, access); stream = File.Open(path, mode, access);
} }
/// <inheritdoc/>
public override bool CanRead { get => stream.CanRead; } public override bool CanRead { get => stream.CanRead; }
/// <inheritdoc/>
public override bool CanSeek { get => stream.CanSeek; } public override bool CanSeek { get => stream.CanSeek; }
/// <inheritdoc/>
public override bool CanWrite { get => stream.CanWrite; } public override bool CanWrite { get => stream.CanWrite; }
/// <inheritdoc/>
public override long Length { get => stream.Length; } public override long Length { get => stream.Length; }
/// <inheritdoc/>
public override long Position { get => stream.Position; set => stream.Position = value; } public override long Position { get => stream.Position; set => stream.Position = value; }
/// <inheritdoc/>
public override void Flush() public override void Flush()
{ {
stream.Flush(); stream.Flush();
} }
/// <inheritdoc/>
public override int Read(byte[] buffer, int offset, int count) public override int Read(byte[] buffer, int offset, int count)
{ {
return stream.Read(buffer, offset, count); return stream.Read(buffer, offset, count);
} }
/// <inheritdoc/>
public override long Seek(long offset, SeekOrigin origin) public override long Seek(long offset, SeekOrigin origin)
{ {
return stream.Seek(offset, origin); return stream.Seek(offset, origin);
} }
/// <inheritdoc/>
public override void SetLength(long value) public override void SetLength(long value)
{ {
stream.SetLength(value); stream.SetLength(value);
} }
/// <inheritdoc/>
public override void Write(byte[] buffer, int offset, int count) public override void Write(byte[] buffer, int offset, int count)
{ {
stream.Write(buffer, offset, count); stream.Write(buffer, offset, count);
} }
/// <inheritdoc/>
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
base.Dispose(disposing); if (disposing)
{
stream.Dispose(); stream.Dispose();
File.Delete(path); File.Delete(path);
}
} }
} }

View File

@@ -5,17 +5,10 @@ using System.IO;
namespace Snap.Hutao.Core.IO; namespace Snap.Hutao.Core.IO;
/// <summary>
/// 文件路径
/// </summary>
internal readonly struct ValueFile internal readonly struct ValueFile
{ {
private readonly string value; 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) private ValueFile(string value)
{ {
this.value = value; this.value = value;
@@ -31,55 +24,6 @@ internal readonly struct ValueFile
return new(value); 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")] [SuppressMessage("", "CA1307")]
public override int GetHashCode() public override int GetHashCode()
{ {

View 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;
}
}
}

View File

@@ -5,17 +5,12 @@ using System.Globalization;
namespace Snap.Hutao.Core.Json.Converter; namespace Snap.Hutao.Core.Json.Converter;
/// <summary> /// 此转换器无法实现无损往返 必须在反序列化后调整 Offset
/// 实现日期的转换
/// 此转换器无法实现无损往返
/// 必须在反序列化后调整 Offset
/// </summary>
[HighQuality] [HighQuality]
internal class DateTimeOffsetConverter : JsonConverter<DateTimeOffset> internal class DateTimeOffsetConverter : JsonConverter<DateTimeOffset>
{ {
private const string Format = "yyyy-MM-dd HH:mm:ss"; private const string Format = "yyyy-MM-dd HH:mm:ss";
/// <inheritdoc/>
public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
if (reader.GetString() is { } dataTimeString) if (reader.GetString() is { } dataTimeString)
@@ -29,7 +24,6 @@ internal class DateTimeOffsetConverter : JsonConverter<DateTimeOffset>
return default; return default;
} }
/// <inheritdoc/>
public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options) public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)
{ {
writer.WriteStringValue(value.DateTime.ToString(Format, CultureInfo.InvariantCulture)); writer.WriteStringValue(value.DateTime.ToString(Format, CultureInfo.InvariantCulture));

View File

@@ -6,15 +6,11 @@ using System.Globalization;
namespace Snap.Hutao.Core.Json.Converter; namespace Snap.Hutao.Core.Json.Converter;
/// <summary>
/// 逗号分隔列表转换器
/// </summary>
[HighQuality] [HighQuality]
internal sealed class SeparatorCommaInt32EnumerableConverter : JsonConverter<IEnumerable<int>> internal sealed class SeparatorCommaInt32EnumerableConverter : JsonConverter<IEnumerable<int>>
{ {
private const char Comma = ','; private const char Comma = ',';
/// <inheritdoc/>
public override IEnumerable<int> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override IEnumerable<int> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
if (reader.GetString() is { } source) if (reader.GetString() is { } source)
@@ -25,7 +21,6 @@ internal sealed class SeparatorCommaInt32EnumerableConverter : JsonConverter<IEn
return []; return [];
} }
/// <inheritdoc/>
public override void Write(Utf8JsonWriter writer, IEnumerable<int> value, JsonSerializerOptions options) public override void Write(Utf8JsonWriter writer, IEnumerable<int> value, JsonSerializerOptions options)
{ {
writer.WriteStringValue(string.Join(Comma, value)); writer.WriteStringValue(string.Join(Comma, value));

View File

@@ -6,10 +6,6 @@ using System.Runtime.CompilerServices;
namespace Snap.Hutao.Core.Json.Converter; namespace Snap.Hutao.Core.Json.Converter;
/// <summary>
/// 枚举转换器
/// </summary>
/// <typeparam name="TEnum">枚举的类型</typeparam>
[HighQuality] [HighQuality]
internal sealed class UnsafeEnumConverter<TEnum> : JsonConverter<TEnum> internal sealed class UnsafeEnumConverter<TEnum> : JsonConverter<TEnum>
where TEnum : struct, Enum where TEnum : struct, Enum
@@ -19,18 +15,12 @@ internal sealed class UnsafeEnumConverter<TEnum> : JsonConverter<TEnum>
private readonly JsonSerializeType readAs; private readonly JsonSerializeType readAs;
private readonly JsonSerializeType writeAs; private readonly JsonSerializeType writeAs;
/// <summary>
/// 构造一个新的枚举转换器
/// </summary>
/// <param name="readAs">读取</param>
/// <param name="writeAs">写入</param>
public UnsafeEnumConverter(JsonSerializeType readAs, JsonSerializeType writeAs) public UnsafeEnumConverter(JsonSerializeType readAs, JsonSerializeType writeAs)
{ {
this.readAs = readAs; this.readAs = readAs;
this.writeAs = writeAs; this.writeAs = writeAs;
} }
/// <inheritdoc/>
public override TEnum Read(ref Utf8JsonReader reader, Type typeToConverTEnum, JsonSerializerOptions options) public override TEnum Read(ref Utf8JsonReader reader, Type typeToConverTEnum, JsonSerializerOptions options)
{ {
if (readAs == JsonSerializeType.Number) if (readAs == JsonSerializeType.Number)
@@ -46,13 +36,12 @@ internal sealed class UnsafeEnumConverter<TEnum> : JsonConverter<TEnum>
throw new JsonException(); throw new JsonException();
} }
/// <inheritdoc/>
public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options) public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options)
{ {
switch (writeAs) switch (writeAs)
{ {
case JsonSerializeType.Number: case JsonSerializeType.Number:
WriteEnumValue(writer, value, enumTypeCode); WriteNumberValue(writer, value, enumTypeCode);
break; break;
case JsonSerializeType.NumberString: case JsonSerializeType.NumberString:
writer.WriteStringValue(value.ToString("D")); writer.WriteStringValue(value.ToString("D"));
@@ -123,7 +112,7 @@ internal sealed class UnsafeEnumConverter<TEnum> : JsonConverter<TEnum>
throw new JsonException(); 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) switch (typeCode)
{ {

View File

@@ -6,14 +6,8 @@ using System.Text.Json.Serialization.Metadata;
namespace Snap.Hutao.Core.Json; namespace Snap.Hutao.Core.Json;
/// <summary>
/// Json 选项
/// </summary>
internal static class JsonOptions internal static class JsonOptions
{ {
/// <summary>
/// 默认的Json序列化选项
/// </summary>
public static readonly JsonSerializerOptions Default = new() public static readonly JsonSerializerOptions Default = new()
{ {
AllowTrailingCommas = true, AllowTrailingCommas = true,

View File

@@ -6,18 +6,10 @@ using System.Text.Json.Serialization.Metadata;
namespace Snap.Hutao.Core.Json; namespace Snap.Hutao.Core.Json;
/// <summary>
/// Json 类型信息解析器
/// </summary>
[HighQuality]
internal static class JsonTypeInfoResolvers internal static class JsonTypeInfoResolvers
{ {
private static readonly Type JsonEnumAttributeType = typeof(JsonEnumAttribute); private static readonly Type JsonEnumAttributeType = typeof(JsonEnumAttribute);
/// <summary>
/// 解析枚举类型
/// </summary>
/// <param name="typeInfo">Json 类型信息</param>
public static void ResolveEnumType(JsonTypeInfo typeInfo) public static void ResolveEnumType(JsonTypeInfo typeInfo)
{ {
if (typeInfo.Kind != JsonTypeInfoKind.Object) if (typeInfo.Kind != JsonTypeInfoKind.Object)

View File

@@ -18,12 +18,10 @@ using Snap.Hutao.Service.Metadata;
using Snap.Hutao.Service.Navigation; using Snap.Hutao.Service.Navigation;
using Snap.Hutao.ViewModel.Guide; using Snap.Hutao.ViewModel.Guide;
using System.Diagnostics; using System.Diagnostics;
using System.Text.RegularExpressions;
namespace Snap.Hutao.Core.LifeCycle; namespace Snap.Hutao.Core.LifeCycle;
/// <summary>
/// 激活
/// </summary>
[HighQuality] [HighQuality]
[ConstructorGenerated] [ConstructorGenerated]
[Injection(InjectAs.Singleton, typeof(IAppActivation))] [Injection(InjectAs.Singleton, typeof(IAppActivation))]
@@ -44,7 +42,6 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi
private readonly SemaphoreSlim activateSemaphore = new(1); private readonly SemaphoreSlim activateSemaphore = new(1);
/// <inheritdoc/>
public void Activate(HutaoActivationArguments args) public void Activate(HutaoActivationArguments args)
{ {
HandleActivationExclusiveAsync(args).SafeForget(); HandleActivationExclusiveAsync(args).SafeForget();
@@ -90,7 +87,6 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi
HandleAppNotificationActivationAsync(args.Arguments, false).SafeForget(); HandleAppNotificationActivationAsync(args.Arguments, false).SafeForget();
} }
/// <inheritdoc/>
public void PostInitialization() public void PostInitialization()
{ {
RunPostInitializationAsync().SafeForget(); RunPostInitializationAsync().SafeForget();
@@ -210,10 +206,13 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi
await taskContext.SwitchToMainThreadAsync(); await taskContext.SwitchToMainThreadAsync();
INavigationAwaiter navigationAwaiter = new NavigationExtra(ImportUIAFFromClipboard); 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>() .GetRequiredService<INavigationService>()
.NavigateAsync<View.Page.AchievementPage>(navigationAwaiter, true) .Navigate<View.Page.AchievementPage>(navigationAwaiter, true);
.ConfigureAwait(false); #pragma warning restore CA1849
break; break;
} }
} }

View File

@@ -5,6 +5,18 @@ using System.Runtime.InteropServices;
namespace Snap.Hutao.Core.LifeCycle.InterProcess; 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)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct PipePacketHeader internal struct PipePacketHeader
{ {

View File

@@ -35,7 +35,6 @@ internal static class PipeStreamExtension
} }
} }
[SkipLocalsInit]
public static unsafe void ReadPacket(this PipeStream stream, out PipePacketHeader header) public static unsafe void ReadPacket(this PipeStream stream, out PipePacketHeader header)
{ {
fixed (PipePacketHeader* pHeader = &header) fixed (PipePacketHeader* pHeader = &header)

View File

@@ -89,6 +89,7 @@ internal sealed partial class PrivateNamedPipeServer : IDisposable
serverStream.WritePacketWithJsonContent(PrivateNamedPipe.Version, PipePacketType.Response, PipePacketCommand.ResponseElevationStatus, resp); serverStream.WritePacketWithJsonContent(PrivateNamedPipe.Version, PipePacketType.Response, PipePacketCommand.ResponseElevationStatus, resp);
serverStream.Flush(); serverStream.Flush();
break; break;
case (PipePacketType.Request, PipePacketCommand.RedirectActivation): case (PipePacketType.Request, PipePacketCommand.RedirectActivation):
HutaoActivationArguments? hutaoArgs = serverStream.ReadJsonContent<HutaoActivationArguments>(in header); HutaoActivationArguments? hutaoArgs = serverStream.ReadJsonContent<HutaoActivationArguments>(in header);
if (hutaoArgs is not null) if (hutaoArgs is not null)
@@ -98,6 +99,7 @@ internal sealed partial class PrivateNamedPipeServer : IDisposable
messageDispatcher.RedirectActivation(hutaoArgs); messageDispatcher.RedirectActivation(hutaoArgs);
break; break;
case (PipePacketType.SessionTermination, _): case (PipePacketType.SessionTermination, _):
serverStream.Disconnect(); serverStream.Disconnect();
if (header.Command is PipePacketCommand.Exit) if (header.Command is PipePacketCommand.Exit)

View File

@@ -41,6 +41,7 @@ internal sealed partial class SettingEntry
public const string LaunchScreenHeight = "Launch.ScreenHeight"; public const string LaunchScreenHeight = "Launch.ScreenHeight";
public const string LaunchIsScreenHeightEnabled = "Launch.IsScreenHeightEnabled"; public const string LaunchIsScreenHeightEnabled = "Launch.IsScreenHeightEnabled";
public const string LaunchUnlockFps = "Launch.UnlockFps"; public const string LaunchUnlockFps = "Launch.UnlockFps";
public const string LaunchUnlockerKind = "Launch.UnlockerKind";
public const string LaunchTargetFps = "Launch.TargetFps"; public const string LaunchTargetFps = "Launch.TargetFps";
public const string LaunchMonitor = "Launch.Monitor"; public const string LaunchMonitor = "Launch.Monitor";
public const string LaunchIsMonitorEnabled = "Launch.IsMonitorEnabled"; public const string LaunchIsMonitorEnabled = "Launch.IsMonitorEnabled";

View File

@@ -18,6 +18,18 @@ internal abstract partial class DbStoreOptions : ObservableObject
{ {
private readonly IServiceProvider serviceProvider; 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 = "") protected string GetOption(ref string? storage, string key, string defaultValue = "")
{ {
return GetOption(ref storage, key, () => defaultValue); return GetOption(ref storage, key, () => defaultValue);

View File

@@ -84,16 +84,4 @@ internal sealed partial class AppOptions : DbStoreOptions
get => GetOption(ref geetestCustomCompositeUrl, SettingEntry.GeetestCustomCompositeUrl); get => GetOption(ref geetestCustomCompositeUrl, SettingEntry.GeetestCustomCompositeUrl);
set => SetOption(ref geetestCustomCompositeUrl, SettingEntry.GeetestCustomCompositeUrl, value); 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();
}
} }

View File

@@ -7,6 +7,7 @@ using Snap.Hutao.Model;
using Snap.Hutao.Model.Entity; using Snap.Hutao.Model.Entity;
using Snap.Hutao.Service.Abstraction; using Snap.Hutao.Service.Abstraction;
using Snap.Hutao.Service.Game.PathAbstraction; using Snap.Hutao.Service.Game.PathAbstraction;
using Snap.Hutao.Service.Game.Unlocker;
using Snap.Hutao.Win32.Graphics.Gdi; using Snap.Hutao.Win32.Graphics.Gdi;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Globalization; using System.Globalization;
@@ -38,6 +39,7 @@ internal sealed class LaunchOptions : DbStoreOptions
private int? screenHeight; private int? screenHeight;
private bool? isScreenHeightEnabled; private bool? isScreenHeightEnabled;
private bool? unlockFps; private bool? unlockFps;
private GameFpsUnlockerKind? unlockerKind;
private int? targetFps; private int? targetFps;
private NameValue<int>? monitor; private NameValue<int>? monitor;
private bool? isMonitorEnabled; private bool? isMonitorEnabled;
@@ -167,6 +169,12 @@ internal sealed class LaunchOptions : DbStoreOptions
set => SetOption(ref unlockFps, SettingEntry.LaunchUnlockFps, value); 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 public int TargetFps
{ {
get => GetOption(ref targetFps, SettingEntry.LaunchTargetFps, primaryScreenFps); get => GetOption(ref targetFps, SettingEntry.LaunchTargetFps, primaryScreenFps);

View File

@@ -25,7 +25,12 @@ internal sealed class LaunchExecutionUnlockFpsHandler : ILaunchExecutionDelegate
return; 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 try
{ {

View File

@@ -304,7 +304,7 @@ internal sealed partial class PackageConverter
try try
{ {
progress.Report(new(SH.FormatServiceGamePackageConvertMoveFileRenameFormat(context.FromDataFolderName, context.ToDataFolderName))); progress.Report(new(SH.FormatServiceGamePackageConvertMoveFileRenameFormat(context.FromDataFolderName, context.ToDataFolderName)));
DirectoryOperation.Move(context.FromDataFolder, context.ToDataFolder); DirectoryOperation.TryMove(context.FromDataFolder, context.ToDataFolder);
} }
catch (IOException ex) catch (IOException ex)
{ {

View File

@@ -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,
}

View File

@@ -6,12 +6,14 @@ using Snap.Hutao.Core;
using Snap.Hutao.Core.Database; using Snap.Hutao.Core.Database;
using Snap.Hutao.Core.Diagnostics.CodeAnalysis; using Snap.Hutao.Core.Diagnostics.CodeAnalysis;
using Snap.Hutao.Core.ExceptionService; using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Model;
using Snap.Hutao.Model.Entity; using Snap.Hutao.Model.Entity;
using Snap.Hutao.Service; using Snap.Hutao.Service;
using Snap.Hutao.Service.Game; using Snap.Hutao.Service.Game;
using Snap.Hutao.Service.Game.Locator; using Snap.Hutao.Service.Game.Locator;
using Snap.Hutao.Service.Game.PathAbstraction; using Snap.Hutao.Service.Game.PathAbstraction;
using Snap.Hutao.Service.Game.Scheme; using Snap.Hutao.Service.Game.Scheme;
using Snap.Hutao.Service.Game.Unlocker;
using Snap.Hutao.Service.Notification; using Snap.Hutao.Service.Notification;
using Snap.Hutao.Service.User; using Snap.Hutao.Service.User;
using Snap.Hutao.UI.Xaml.Data; 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(); public List<LaunchScheme> KnownSchemes { get; } = KnownLaunchSchemes.Get();
[AlsoAsyncSets(nameof(GamePackage), nameof(GameAccountsView))]
public LaunchScheme? SelectedScheme public LaunchScheme? SelectedScheme
{ {
get => selectedScheme; get => selectedScheme;