fix up oversea launcher support

This commit is contained in:
DismissedLight
2023-01-10 15:05:21 +08:00
parent abdc8e2e9f
commit 267f285101
20 changed files with 112 additions and 233 deletions

View File

@@ -14,9 +14,9 @@ csharp_style_expression_bodied_accessors = when_on_single_line:silent
csharp_style_expression_bodied_lambdas = when_on_single_line:silent
csharp_style_expression_bodied_local_functions = false:silent
csharp_style_conditional_delegate_call = true:suggestion
csharp_style_var_for_built_in_types = false:silent
csharp_style_var_when_type_is_apparent = false:silent
csharp_style_var_elsewhere = false:silent
csharp_style_var_for_built_in_types = false:warning
csharp_style_var_when_type_is_apparent = false:warning
csharp_style_var_elsewhere = false:warning
csharp_prefer_simple_using_statement = false:suggestion
csharp_prefer_braces = true:silent
csharp_style_namespace_declarations = file_scoped:silent
@@ -24,11 +24,11 @@ csharp_prefer_static_local_function = false:suggestion
[*.{cs,vb}]
end_of_line = crlf
dotnet_style_qualification_for_field = false:silent
dotnet_style_qualification_for_property = false:silent
dotnet_style_qualification_for_method = false:silent
dotnet_style_qualification_for_event = false:silent
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_event = false:suggestion
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
dotnet_code_quality_unused_parameters = non_public:suggestion
dotnet_style_readonly_field = true:suggestion
indent_size = 4
@@ -47,8 +47,8 @@ dotnet_style_prefer_auto_properties = false:silent
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
dotnet_style_prefer_conditional_expression_over_return = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
@@ -165,6 +165,7 @@ dotnet_diagnostic.CA1805.severity = suggestion
dotnet_diagnostic.VSTHRD111.severity = suggestion
csharp_style_prefer_top_level_statements = true:silent
csharp_style_prefer_readonly_struct = true:suggestion
csharp_style_prefer_utf8_string_literals = true:suggestion
[*.vb]
#### 命名样式 ####

View File

@@ -1,52 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;
using System.Collections.Immutable;
namespace Snap.Hutao.SourceGeneration;
/// <summary>
/// 高亮TODO
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal class TodoAnalyzer : DiagnosticAnalyzer
{
private static readonly DiagnosticDescriptor Descriptor =
new("SH0001", "TODO 项尚未实现", "此 TODO 项需要实现", "Standard", DiagnosticSeverity.Info, true);
/// <inheritdoc/>
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get => ImmutableArray.Create(Descriptor); }
/// <inheritdoc/>
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterSyntaxTreeAction(HandleSyntaxTree);
}
private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context)
{
SyntaxNode root = context.Tree.GetCompilationUnitRoot(context.CancellationToken);
foreach (SyntaxTrivia node in root.DescendantTrivia(descendIntoTrivia: true))
{
switch (node.Kind())
{
case SyntaxKind.SingleLineCommentTrivia:
case SyntaxKind.MultiLineCommentTrivia:
string text = node.ToString().ToLowerInvariant();
if (text.Contains("todo:"))
{
string hint = node.ToString().Substring(text.IndexOf("todo:") + 6);
DiagnosticDescriptor descriptor = new("SH0001", "TODO 项尚未实现", hint, "Standard", DiagnosticSeverity.Info, true);
context.ReportDiagnostic(Diagnostic.Create(descriptor, node.GetLocation()));
}
break;
}
}
}
}

View File

@@ -153,10 +153,11 @@ public class User : ObservableObject
using (IServiceScope scope = Ioc.Default.CreateScope())
{
UserInfo = await scope.ServiceProvider
Web.Response.Response<UserFullInfoWrapper> response = await scope.ServiceProvider
.GetRequiredService<UserClient2>()
.GetUserFullInfoAsync(Entity, token)
.ConfigureAwait(false);
UserInfo = response.Data?.UserInfo;
// 自动填充 Ltoken
if (Ltoken == null)

View File

@@ -4,6 +4,7 @@
using Microsoft.Extensions.Caching.Memory;
using Snap.Hutao.Service.Abstraction;
using Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
using Snap.Hutao.Web.Response;
using System.Text.RegularExpressions;
namespace Snap.Hutao.Service;
@@ -34,29 +35,39 @@ internal partial class AnnouncementService : IAnnouncementService
// 缓存中存在记录,直接返回
if (memoryCache.TryGetValue(CacheKey, out object? cache))
{
return Must.NotNull((AnnouncementWrapper)cache!);
return (AnnouncementWrapper)cache!;
}
await ThreadHelper.SwitchToBackgroundAsync();
AnnouncementWrapper? wrapper = await announcementClient
Response<AnnouncementWrapper> announcementWrapperResponse = await announcementClient
.GetAnnouncementsAsync(cancellationToken)
.ConfigureAwait(false);
List<AnnouncementContent> contents = await announcementClient
.GetAnnouncementContentsAsync(cancellationToken)
.ConfigureAwait(false);
Dictionary<int, string> contentMap = contents
.ToDictionary(id => id.AnnId, content => content.Content);
if (announcementWrapperResponse.IsOk())
{
AnnouncementWrapper wrapper = announcementWrapperResponse.Data;
Response<ListWrapper<AnnouncementContent>> announcementContentResponse = await announcementClient
.GetAnnouncementContentsAsync(cancellationToken)
.ConfigureAwait(false);
Must.NotNull(wrapper!);
if (announcementContentResponse.IsOk())
{
List<AnnouncementContent> contents = announcementContentResponse.Data.List;
// 将活动公告置于上方
wrapper.List.Reverse();
Dictionary<int, string> contentMap = contents
.ToDictionary(id => id.AnnId, content => content.Content);
// 将公告内容联入公告列表
JoinAnnouncements(contentMap, wrapper.List);
// 将活动公告置于上方
wrapper.List.Reverse();
return memoryCache.Set(CacheKey, wrapper, TimeSpan.FromMinutes(30));
// 将公告内容联入公告列表
JoinAnnouncements(contentMap, wrapper.List);
return memoryCache.Set(CacheKey, wrapper, TimeSpan.FromMinutes(30));
}
}
return null!;
}
private static void JoinAnnouncements(Dictionary<int, string> contentMap, List<AnnouncementListWrapper> announcementListWrappers)

View File

@@ -36,16 +36,10 @@ internal class GachaLogUrlWebCacheProvider : IGachaLogUrlProvider
public static string GetCacheFile(string path)
{
string folder = Path.GetDirectoryName(path) ?? string.Empty;
var cacheDataPath = Path.Combine(folder, @"YuanShen_Data\webCaches\Cache\Cache_Data\data_2");
var cacheDataPathIntl = Path.Combine(folder, @"GenshinImpact_Data\webCaches\Cache\Cache_Data\data_2");
if (File.Exists(cacheDataPath))
{
return cacheDataPath;
}
else
{
return cacheDataPathIntl;
}
string cacheDataPathChinese = Path.Combine(folder, @"YuanShen_Data\webCaches\Cache\Cache_Data\data_2");
string cacheDataPathOversea = Path.Combine(folder, @"GenshinImpact_Data\webCaches\Cache\Cache_Data\data_2");
return File.Exists(cacheDataPathChinese) ? cacheDataPathChinese : cacheDataPathOversea;
}
/// <inheritdoc/>

View File

@@ -200,6 +200,7 @@ internal class GameService : IGameService, IDisposable
}
/// <inheritdoc/>
[SuppressMessage("", "IDE0046")]
public bool IsGameRunning()
{
if (gameSemaphore.CurrentCount == 0)
@@ -207,7 +208,8 @@ internal class GameService : IGameService, IDisposable
return true;
}
return Process.GetProcessesByName("YuanShen.exe").Any() || Process.GetProcessesByName("GenshinImpact.exe").Any();
return Process.GetProcessesByName("YuanShen.exe").Any()
|| Process.GetProcessesByName("GenshinImpact.exe").Any();
}
/// <inheritdoc/>

View File

@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.IO;
using Snap.Hutao.Factory.Abstraction;
using Windows.Storage;
using Windows.Storage.Pickers;
@@ -30,26 +31,21 @@ internal class ManualGameLocator : IGameLocator
/// <inheritdoc/>
public Task<ValueResult<bool, string>> LocateGamePathAsync()
{
List<string> filenames = new List<string>()
{
"YuanShen.exe",
"GenshinImpact.exe",
};
List<string> filenames = new(2) { "YuanShen.exe", "GenshinImpact.exe", };
return LocateInternalAsync(filenames);
}
private async Task<ValueResult<bool, string>> LocateInternalAsync(List<string> fileNames)
{
FileOpenPicker picker = pickerFactory.GetFileOpenPicker(PickerLocationId.Desktop, "选择游戏本体", ".exe");
if (await picker.PickSingleFileAsync() is StorageFile file)
(bool isPickerOk, FilePath file) = await picker.TryPickSingleFileAsync().ConfigureAwait(false);
if (isPickerOk)
{
string path = file.Path;
foreach (string fileName in fileNames)
string fileName = System.IO.Path.GetFileName(file);
if (fileNames.Contains(fileName))
{
if (path.Contains(fileName))
{
return new(true, path);
}
return new(true, file);
}
}

View File

@@ -21,10 +21,13 @@ internal partial class UnityLogGameLocator : IGameLocator
{
await ThreadHelper.SwitchToBackgroundAsync();
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string logFilePath = Path.Combine(appDataPath, @"..\LocalLow\miHoYo\原神\output_log.txt");
string logFilePathIntl = Path.Combine(appDataPath, @"..\LocalLow\miHoYo\Genshin Impact\output_log.txt");
string logFilePathChinese = Path.Combine(appDataPath, @"..\LocalLow\miHoYo\原神\output_log.txt");
string logFilePathOvsesea = Path.Combine(appDataPath, @"..\LocalLow\miHoYo\Genshin Impact\output_log.txt");
using (TempFile? tempFile = TempFile.CreateFromFileCopy(logFilePath), tempFileIntl = TempFile.CreateFromFileCopy(logFilePathIntl))
// We need to fallback the the cn server rather than os server.
string logFilePathFinal = File.Exists(logFilePathOvsesea) ? logFilePathOvsesea : logFilePathChinese;
using (TempFile? tempFile = TempFile.CreateFromFileCopy(logFilePathFinal))
{
if (tempFile != null)
{
@@ -40,30 +43,13 @@ internal partial class UnityLogGameLocator : IGameLocator
string fullPath = Path.GetFullPath(Path.Combine(matchResult.Value, "..", entryName));
return new(true, fullPath);
}
else if (tempFileIntl != null)
{
string content = File.ReadAllText(tempFileIntl.Path);
Match matchResult = WarmupFileLineIntl().Match(content);
if (!matchResult.Success)
{
return new(false, $"在 Unity 日志文件中找不到游戏路径");
}
string entryName = matchResult.Groups[0].Value.Replace("_Data", ".exe");
string fullPath = Path.GetFullPath(Path.Combine(matchResult.Value, "..", entryName));
return new(true, fullPath);
}
else
{
return new(false, $"找不到 Unity 日志文件\n{logFilePath}\n{logFilePathIntl}");
return new(false, $"找不到 Unity 日志文件");
}
}
}
[GeneratedRegex(@"(?m).:/.+YuanShen_Data")]
[GeneratedRegex(@"(?m).:/.+(GenshinImpact_Data|YuanShen_Data)")]
private static partial Regex WarmupFileLine();
[GeneratedRegex(@"(?m).:/.+GenshinImpact_Data")]
private static partial Regex WarmupFileLineIntl();
}

View File

@@ -87,7 +87,7 @@ internal class AnnouncementViewModel : ObservableObject, ISupportCancellation
else
{
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
infoBarService.Warning("尚未安装 WebView2 运行时。");
infoBarService.Warning("尚未安装 WebView2 Runtime");
}
}
}

View File

@@ -61,7 +61,7 @@ internal class SettingViewModel : ObservableObject
Experimental = experimental;
isEmptyHistoryWishVisibleEntry = appDbContext.Settings.SingleOrAdd(SettingEntry.IsEmptyHistoryWishVisible, true.ToString());
isEmptyHistoryWishVisibleEntry = appDbContext.Settings.SingleOrAdd(SettingEntry.IsEmptyHistoryWishVisible, SettingEntryHelper.TrueString);
IsEmptyHistoryWishVisible = bool.Parse(isEmptyHistoryWishVisibleEntry.Value!);
selectedBackdropTypeEntry = appDbContext.Settings.SingleOrAdd(SettingEntry.SystemBackdropType, BackdropType.Mica.ToString());

View File

@@ -40,7 +40,7 @@ internal class EnkaClient
/// <param name="playerUid">玩家Uid</param>
/// <param name="token">取消令牌</param>
/// <returns>Enka API 响应</returns>
public Task<EnkaResponse?> GetForwardDataAsync(PlayerUid playerUid, CancellationToken token)
public Task<EnkaResponse?> GetForwardDataAsync(PlayerUid playerUid, CancellationToken token = default)
{
return httpClient.TryCatchGetFromJsonAsync<EnkaResponse>(string.Format(EnkaAPIHutaoForward, playerUid.Value), options, logger, token);
}
@@ -51,8 +51,8 @@ internal class EnkaClient
/// <param name="playerUid">玩家Uid</param>
/// <param name="token">取消令牌</param>
/// <returns>Enka API 响应</returns>
public Task<EnkaResponse?> GetDataAsync(PlayerUid playerUid, CancellationToken token)
public Task<EnkaResponse?> GetDataAsync(PlayerUid playerUid, CancellationToken token = default)
{
return httpClient.TryCatchGetFromJsonAsync<EnkaResponse>(string.Format(EnkaAPI, playerUid.Value), options, logger, token);
}
}
}

View File

@@ -43,7 +43,7 @@ internal class AccountClient
/// <param name="token">取消令牌</param>
/// <returns>用户角色信息</returns>
[ApiInformation(Cookie = CookieType.Stoken, Salt = SaltType.K2)]
public async Task<GameAuthKey?> GenerateAuthenticationKeyAsync(User user, GenAuthKeyData data, CancellationToken token = default)
public async Task<Response<GameAuthKey>> GenerateAuthenticationKeyAsync(User user, GenAuthKeyData data, CancellationToken token = default)
{
Response<GameAuthKey>? resp = await httpClient
.SetUser(user, CookieType.Stoken)
@@ -53,6 +53,6 @@ internal class AccountClient
.TryCatchPostAsJsonAsync<GenAuthKeyData, Response<GameAuthKey>>(ApiEndpoints.AppAuthGenAuthKey, data, options, logger, token)
.ConfigureAwait(false);
return resp?.Data;
return Response.Response.DefaultIfNull(resp);
}
}

View File

@@ -1,70 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
using Snap.Hutao.Web.Hoyolab.Annotation;
using Snap.Hutao.Web.Response;
using System.Net.Http;
namespace Snap.Hutao.Web.Hoyolab.Bbs.User;
/// <summary>
/// 用户信息客户端
/// </summary>
[HttpClient(HttpClientConfigration.XRpc)]
internal class UserClient
{
private readonly HttpClient httpClient;
private readonly JsonSerializerOptions options;
private readonly ILogger<UserClient> logger;
/// <summary>
/// 构造一个新的用户信息客户端
/// </summary>
/// <param name="httpClient">http客户端</param>
/// <param name="options">Json序列化选项</param>
/// <param name="logger">日志器</param>
public UserClient(HttpClient httpClient, JsonSerializerOptions options, ILogger<UserClient> logger)
{
this.httpClient = httpClient;
this.options = options;
this.logger = logger;
}
/// <summary>
/// 获取当前用户详细信息
/// </summary>
/// <param name="user">用户</param>
/// <param name="token">取消令牌</param>
/// <returns>详细信息</returns>
[ApiInformation(Cookie = CookieType.Ltoken)]
public async Task<UserInfo?> GetUserFullInfoAsync(Model.Entity.User user, CancellationToken token = default)
{
Response<UserFullInfoWrapper>? resp = await httpClient
.SetUser(user, CookieType.Ltoken)
.SetReferer(ApiEndpoints.BbsReferer) // Otherwise HTTP 403
.TryCatchGetFromJsonAsync<Response<UserFullInfoWrapper>>(ApiEndpoints.UserFullInfo, options, logger, token)
.ConfigureAwait(false);
return resp?.Data?.UserInfo;
}
/// <summary>
/// 获取当前用户详细信息
/// </summary>
/// <param name="user">用户</param>
/// <param name="token">取消令牌</param>
/// <returns>详细信息</returns>
[ApiInformation(Cookie = CookieType.Ltoken)]
public async Task<UserInfo?> GetUserFullInfoByUidAsync(Model.Entity.User user, CancellationToken token = default)
{
Response<UserFullInfoWrapper>? resp = await httpClient
// .SetUser(user, CookieType.Cookie)
.SetReferer(ApiEndpoints.BbsReferer) // Otherwise HTTP 403
.TryCatchGetFromJsonAsync<Response<UserFullInfoWrapper>>(ApiEndpoints.UserFullInfoQuery(user.Aid!), options, logger, token)
.ConfigureAwait(false);
return resp?.Data?.UserInfo;
}
}

View File

@@ -40,7 +40,7 @@ internal class UserClient2
/// <param name="token">取消令牌</param>
/// <returns>详细信息</returns>
[ApiInformation(Cookie = CookieType.Stoken, Salt = SaltType.K2)]
public async Task<UserInfo?> GetUserFullInfoAsync(Model.Entity.User user, CancellationToken token = default)
public async Task<Response<UserFullInfoWrapper>> GetUserFullInfoAsync(Model.Entity.User user, CancellationToken token = default)
{
Response<UserFullInfoWrapper>? resp = await httpClient
.SetUser(user, CookieType.Stoken)
@@ -49,6 +49,6 @@ internal class UserClient2
.TryCatchGetFromJsonAsync<Response<UserFullInfoWrapper>>(ApiEndpoints.UserFullInfoQuery(user.Aid!), options, logger, token)
.ConfigureAwait(false);
return resp?.Data?.UserInfo;
return Response.Response.DefaultIfNull(resp);
}
}

View File

@@ -5,6 +5,7 @@ namespace Snap.Hutao.Web.Hoyolab.DynamicSecret;
/// <summary>
/// 指示此客户端使用动态密钥
/// 会为依附类自动生成相关代码
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
internal class UseDynamicSecretAttribute : Attribute

View File

@@ -36,13 +36,13 @@ internal class AnnouncementClient
/// </summary>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>公告列表</returns>
public async Task<AnnouncementWrapper?> GetAnnouncementsAsync(CancellationToken cancellationToken = default)
public async Task<Response<AnnouncementWrapper>> GetAnnouncementsAsync(CancellationToken cancellationToken = default)
{
Response<AnnouncementWrapper>? resp = await httpClient
.TryCatchGetFromJsonAsync<Response<AnnouncementWrapper>>(ApiEndpoints.AnnList, jsonSerializerOptions, logger, cancellationToken)
.ConfigureAwait(false);
return resp?.Data;
return Response.Response.DefaultIfNull(resp);
}
/// <summary>
@@ -50,13 +50,12 @@ internal class AnnouncementClient
/// </summary>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>公告内容列表</returns>
public async Task<List<AnnouncementContent>> GetAnnouncementContentsAsync(CancellationToken cancellationToken = default)
public async Task<Response<ListWrapper<AnnouncementContent>>> GetAnnouncementContentsAsync(CancellationToken cancellationToken = default)
{
// Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Response<ListWrapper<AnnouncementContent>>? resp = await httpClient
.TryCatchGetFromJsonAsync<Response<ListWrapper<AnnouncementContent>>>(ApiEndpoints.AnnContent, jsonSerializerOptions, logger, cancellationToken)
.ConfigureAwait(false);
return EnumerableExtension.EmptyIfNull(resp?.Data?.List);
return Response.Response.DefaultIfNull(resp);
}
}

View File

@@ -28,7 +28,7 @@ internal static class HttpClientExtensions
logger.LogWarning(EventIds.HttpException, ex, "请求异常已忽略");
return null;
}
catch (SocketException ex)
catch (IOException ex)
{
logger.LogWarning(EventIds.HttpException, ex, "请求异常已忽略");
return null;
@@ -38,7 +38,7 @@ internal static class HttpClientExtensions
logger.LogWarning(EventIds.HttpException, ex, "请求异常已忽略");
return null;
}
catch (IOException ex)
catch (SocketException ex)
{
logger.LogWarning(EventIds.HttpException, ex, "请求异常已忽略");
return null;
@@ -59,7 +59,7 @@ internal static class HttpClientExtensions
logger.LogWarning(EventIds.HttpException, ex, "请求异常已忽略");
return null;
}
catch (SocketException ex)
catch (IOException ex)
{
logger.LogWarning(EventIds.HttpException, ex, "请求异常已忽略");
return null;
@@ -69,7 +69,7 @@ internal static class HttpClientExtensions
logger.LogWarning(EventIds.HttpException, ex, "请求异常已忽略");
return null;
}
catch (IOException ex)
catch (SocketException ex)
{
logger.LogWarning(EventIds.HttpException, ex, "请求异常已忽略");
return null;
@@ -89,7 +89,7 @@ internal static class HttpClientExtensions
{
return null;
}
catch (SocketException)
catch (IOException)
{
return null;
}
@@ -97,7 +97,7 @@ internal static class HttpClientExtensions
{
return null;
}
catch (IOException)
catch (SocketException)
{
return null;
}

View File

@@ -22,13 +22,9 @@ public class Response : ISupportValidation
{
ReturnCode = returnCode;
Message = message;
if (!Validate())
{
Ioc.Default.GetRequiredService<IInfoBarService>().Error(ToString());
}
#if DEBUG
Ioc.Default.GetRequiredService<ILogger<Response>>().LogInformation("Response [{resp}]", ToString());
#endif
}
/// <summary>
@@ -43,6 +39,18 @@ public class Response : ISupportValidation
[JsonPropertyName("message")]
public string Message { get; set; } = default!;
/// <summary>
/// 返回本体或带有消息提示的默认值
/// </summary>
/// <typeparam name="TData">类型</typeparam>
/// <param name="response">本体</param>
/// <returns>本体或默认值,当本体为 null 时 返回默认值</returns>
public static Response<TData> DefaultIfNull<TData>(Response<TData>? response)
{
// 0x26F19335 is a magic number that hashed from "Snap.Hutao"
return response ?? new(0x26F19335, "请求异常", default);
}
/// <inheritdoc/>
public bool Validate()
{
@@ -90,7 +98,17 @@ public class Response<TData> : Response, IJsResult
[MemberNotNullWhen(true, nameof(Data))]
public bool IsOk()
{
return ReturnCode == 0;
if (ReturnCode == 0)
{
#pragma warning disable CS8775
return true;
#pragma warning restore CS8775
}
else
{
Ioc.Default.GetRequiredService<IInfoBarService>().Error(ToString());
return false;
}
}
/// <inheritdoc/>

View File

@@ -9,20 +9,22 @@ namespace Snap.Hutao.Win32;
/// <summary>
/// 内存拓展 for <see cref="Memory{T}"/> and <see cref="Span{T}"/>
/// </summary>
internal static class MemoryExtensions
internal static class MemoryExtension
{
/// <summary>
/// 将 __CHAR_256 转换到 字符串
/// </summary>
/// <param name="char256">目标字符数组</param>
/// <returns>结果字符串</returns>
public static unsafe string AsString(this __CHAR_256 char256)
public static unsafe string AsString(this ref __CHAR_256 char256)
{
byte* pszModule = (byte*)&char256;
return Encoding.UTF8.GetString(pszModule, StringLength(pszModule));
fixed (CHAR* pszModule = &char256._0)
{
return Encoding.UTF8.GetString((byte*)pszModule, StringLength(pszModule));
}
}
private static unsafe int StringLength(byte* pszStr)
private static unsafe int StringLength(CHAR* pszStr)
{
int len = 0;
while (*pszStr++ != 0)

View File

@@ -63,14 +63,4 @@ internal static class StructMarshal
{
return moduleEntry32.dwSize == 0;
}
/// <summary>
/// 判断结构实例是否为默认结构
/// </summary>
/// <param name="uid">待测试的结构</param>
/// <returns>是否为默认结构</returns>
public static bool IsDefault(PlayerUid uid)
{
return uid.Value == default;
}
}