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
|
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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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()
|
||||||
{
|
{
|
||||||
|
|||||||
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;
|
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));
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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.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;
|
||||||
|
|||||||
Reference in New Issue
Block a user