From 69dc8355adb548b0b40f68f63ede639c7191944e Mon Sep 17 00:00:00 2001
From: Lightczx <1686188646@qq.com>
Date: Sun, 30 Jul 2023 21:32:15 +0800
Subject: [PATCH] fix #811
---
.../Resource/Localization/SH.Designer.cs | 9 +
.../Snap.Hutao/Resource/Localization/SH.resx | 3 +
.../Service/Game/Package/ItemOperationInfo.cs | 26 +-
.../Service/Game/Package/ItemOperationType.cs | 8 +-
.../Game/Package/PackageConvertContext.cs | 65 ++++
.../Service/Game/Package/PackageConverter.cs | 289 +++++++++---------
.../Service/Game/Package/VersionItem.cs | 2 +-
7 files changed, 226 insertions(+), 176 deletions(-)
create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Game/Package/PackageConvertContext.cs
diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs
index b4c36d8b..b97a6c26 100644
--- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs
@@ -1500,6 +1500,15 @@ namespace Snap.Hutao.Resource.Localization {
}
}
+ ///
+ /// 查找类似 下载客户端文件失败:{0} 的本地化字符串。
+ ///
+ internal static string ServiceGamePackageRequestScatteredFileFailed {
+ get {
+ return ResourceManager.GetString("ServiceGamePackageRequestScatteredFileFailed", resourceCulture);
+ }
+ }
+
///
/// 查找类似 无法找到游戏路径,请前往设置修改 的本地化字符串。
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
index b918c340..d0ccd76b 100644
--- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
+++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
@@ -2078,4 +2078,7 @@
UIGF 文件的语言:{0} 与胡桃的语言:{1} 不匹配,请切换到对应语言重试
+
+ 下载客户端文件失败:{0}
+
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/ItemOperationInfo.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/ItemOperationInfo.cs
index e2261157..bb4acb4e 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/ItemOperationInfo.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/ItemOperationInfo.cs
@@ -20,35 +20,23 @@ internal readonly struct ItemOperationInfo
///
/// 目标文件
///
- public readonly string Target;
+ public readonly VersionItem Remote;
///
/// 移动至中时的名称
///
- public readonly string MoveTo;
-
- ///
- /// 文件的目标Md5
- ///
- public readonly string Md5;
-
- ///
- /// 文件的目标大小 Byte
- ///
- public readonly long TotalBytes;
+ public readonly VersionItem Local;
///
/// 构造一个新的包操作
///
/// 操作类型
- /// 目标
- /// 缓存
- public ItemOperationInfo(ItemOperationType type, VersionItem target, VersionItem moveTo)
+ /// 远程
+ /// 本地
+ public ItemOperationInfo(ItemOperationType type, VersionItem remote, VersionItem local)
{
Type = type;
- Target = target.RemoteName;
- MoveTo = moveTo.RemoteName;
- Md5 = target.Md5;
- TotalBytes = target.FileSize;
+ Remote = remote;
+ Local = local;
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/ItemOperationType.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/ItemOperationType.cs
index e3ca31f3..19b8a299 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/ItemOperationType.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/ItemOperationType.cs
@@ -10,9 +10,9 @@ namespace Snap.Hutao.Service.Game.Package;
internal enum ItemOperationType
{
///
- /// 添加
+ /// 需要备份
///
- Add = 0,
+ Backup = 0,
///
/// 替换
@@ -20,7 +20,7 @@ internal enum ItemOperationType
Replace = 1,
///
- /// 需要备份
+ /// 添加
///
- Backup = 2,
+ Add = 2,
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/PackageConvertContext.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/PackageConvertContext.cs
new file mode 100644
index 00000000..605c6906
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/PackageConvertContext.cs
@@ -0,0 +1,65 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.IO;
+using static Snap.Hutao.Service.Game.GameConstants;
+
+namespace Snap.Hutao.Service.Game.Package;
+
+internal readonly struct PackageConvertContext
+{
+ public readonly string GameFolder;
+ public readonly string ServerCacheFolder;
+
+ public readonly string ServerCacheBackupFolder; // From
+ public readonly string ServerCacheTargetFolder; // To
+
+ public readonly string FromDataFolderName;
+ public readonly string ToDataFolderName;
+ public readonly string FromDataFolder;
+ public readonly string ToDataFolder;
+
+ public readonly string ScatteredFilesUrl;
+ public readonly string PkgVersionUrl;
+
+ public PackageConvertContext(bool isTargetOversea, string dataFolder, string gameFolder, string scatteredFilesUrl)
+ {
+ GameFolder = gameFolder;
+ ServerCacheFolder = Path.Combine(dataFolder, "ServerCache");
+
+ string serverCacheOversea = Path.Combine(ServerCacheFolder, "Oversea");
+ string serverCacheChinese = Path.Combine(ServerCacheFolder, "Chinese");
+ (ServerCacheBackupFolder, ServerCacheTargetFolder) = isTargetOversea
+ ? (serverCacheChinese, serverCacheOversea)
+ : (serverCacheOversea, serverCacheChinese);
+
+ (FromDataFolderName, ToDataFolderName) = isTargetOversea
+ ? (YuanShenData, GenshinImpactData)
+ : (GenshinImpactData, YuanShenData);
+
+ (FromDataFolder, ToDataFolder) = (Path.Combine(GameFolder, FromDataFolderName), Path.Combine(GameFolder, ToDataFolderName));
+
+ ScatteredFilesUrl = scatteredFilesUrl;
+ PkgVersionUrl = $"{scatteredFilesUrl}/pkg_version";
+ }
+
+ public readonly string GetScatteredFilesUrl(string file)
+ {
+ return $"{ScatteredFilesUrl}/{file}";
+ }
+
+ public readonly string GetServerCacheBackupFilePath(string filePath)
+ {
+ return Path.Combine(ServerCacheBackupFolder, filePath);
+ }
+
+ public readonly string GetServerCacheTargetFilePath(string filePath)
+ {
+ return Path.Combine(ServerCacheTargetFolder, filePath);
+ }
+
+ public readonly string GetGameFolderFilePath(string filePath)
+ {
+ return Path.Combine(GameFolder, filePath);
+ }
+}
\ No newline at end of file
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 7284e4c0..5a21643a 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/PackageConverter.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/PackageConverter.cs
@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Snap.Hutao.Core;
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Core.IO;
@@ -10,6 +11,7 @@ using Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher;
using System.IO;
using System.IO.Compression;
using System.Net.Http;
+using System.Text.RegularExpressions;
using static Snap.Hutao.Service.Game.GameConstants;
namespace Snap.Hutao.Service.Game.Package;
@@ -23,9 +25,11 @@ namespace Snap.Hutao.Service.Game.Package;
internal sealed partial class PackageConverter
{
private const string PackageVersion = "pkg_version";
-
+ private const string OverseaFolder = "Oversea";
+ private const string ChineseFolder = "Chinese";
private readonly IServiceProvider serviceProvider;
private readonly JsonSerializerOptions options;
+ private readonly RuntimeOptions runtimeOptions;
private readonly HttpClient httpClient;
///
@@ -57,15 +61,26 @@ internal sealed partial class PackageConverter
// 4. 全部资源下载完成后,根据操作信息项,进行文件替换
// 处理顺序:备份/替换/新增
// 替换操作等于 先备份国服文件,随后新增国际服文件
+
+ // 准备下载链接
string scatteredFilesUrl = gameResource.Game.Latest.DecompressedPath;
- Uri pkgVersionUri = $"{scatteredFilesUrl}/{PackageVersion}".ToUri();
- ConvertDirection direction = targetScheme.IsOversea ? ConvertDirection.ChineseToOversea : ConvertDirection.OverseaToChinese;
+ string pkgVersionUrl = $"{scatteredFilesUrl}/{PackageVersion}";
+ PackageConvertContext context = new(targetScheme.IsOversea, runtimeOptions.DataFolder, gameFolder, scatteredFilesUrl);
+
+ // Step 1
progress.Report(new(SH.ServiceGamePackageRequestPackageVerion));
- Dictionary remoteItems = await GetRemoteItemsAsync(pkgVersionUri).ConfigureAwait(false);
- Dictionary localItems = await GetLocalItemsAsync(gameFolder, direction).ConfigureAwait(false);
+ Dictionary remoteItems = await GetRemoteItemsAsync(pkgVersionUrl).ConfigureAwait(false);
+ Dictionary localItems = await GetLocalItemsAsync(gameFolder).ConfigureAwait(false);
- IEnumerable diffOperations = GetItemOperationInfos(remoteItems, localItems).OrderBy(i => i.Type);
+ // Step 2
+ List diffOperations = GetItemOperationInfos(remoteItems, localItems).ToList();
+ diffOperations.SortBy(i => i.Type);
+
+ // Step 3
+ await PrepareCacheFilesAsync(diffOperations, direction, scatteredFilesUrl, cacheFolder, progress).ConfigureAwait(false);
+
+ // Step 4
return await ReplaceGameResourceAsync(diffOperations, gameFolder, scatteredFilesUrl, direction, progress).ConfigureAwait(false);
}
@@ -158,46 +173,39 @@ internal sealed partial class PackageConverter
}
}
- private static void TryRenameDataFolder(string gameFolder, ConvertDirection direction)
- {
- string yuanShenData = Path.Combine(gameFolder, YuanShenData);
- string genshinImpactData = Path.Combine(gameFolder, GenshinImpactData);
-
- try
- {
- _ = direction == ConvertDirection.ChineseToOversea
- ? DirectoryOperation.Move(yuanShenData, genshinImpactData)
- : DirectoryOperation.Move(genshinImpactData, yuanShenData);
- }
- catch (IOException ex)
- {
- // Access to the path is denied.
- // When user install the game in special folder like 'Program Files'
- throw ThrowHelper.GameFileOperation(SH.ServiceGamePackageRenameDataFolderFailed, ex);
- }
- }
-
private static void MoveToCache(string cacheFilePath, string targetFullPath)
{
Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath)!);
File.Move(targetFullPath, cacheFilePath, true);
}
- private async ValueTask> GetLocalItemsAsync(string gameFolder, ConvertDirection direction)
+ [GeneratedRegex("^(?:YuanShen_Data|GenshinImpact_Data)(?=/)")]
+ private static partial Regex DataFolderRegex();
+
+ private async ValueTask> GetVersionItemsAsync(Stream stream)
{
- using (FileStream localSteam = File.OpenRead(Path.Combine(gameFolder, PackageVersion)))
+ Dictionary results = new();
+ using (StreamReader reader = new(stream))
{
- return await GetLocalVersionItemsAsync(localSteam, direction).ConfigureAwait(false);
+ Regex dataFolderRegex = DataFolderRegex();
+ while (await reader.ReadLineAsync().ConfigureAwait(false) is { } row && !string.IsNullOrEmpty(row))
+ {
+ VersionItem item = JsonSerializer.Deserialize(row, options)!;
+ item.RelativePath = dataFolderRegex.Replace(item.RelativePath, "{0}");
+ results.Add(item.RelativePath, item);
+ }
}
+
+ return results;
}
- private async ValueTask> GetRemoteItemsAsync(Uri pkgVersionUri)
+ private async ValueTask> GetRemoteItemsAsync(string pkgVersionUrl)
{
try
{
- using (Stream remoteSteam = await httpClient.GetStreamAsync(pkgVersionUri).ConfigureAwait(false))
+ using (Stream remoteSteam = await httpClient.GetStreamAsync(pkgVersionUrl).ConfigureAwait(false))
{
- return await GetRemoteVersionItemsAsync(remoteSteam).ConfigureAwait(false);
+ return await GetVersionItemsAsync(remoteSteam).ConfigureAwait(false);
}
}
catch (IOException ex)
@@ -206,104 +214,136 @@ internal sealed partial class PackageConverter
}
}
- private async ValueTask ReplaceGameResourceAsync(IEnumerable operations, string gameFolder, string scatteredFilesUrl, ConvertDirection direction, IProgress progress)
+ private async ValueTask> GetLocalItemsAsync(string gameFolder)
{
- // 重命名 _Data 目录
- TryRenameDataFolder(gameFolder, direction);
+ using (FileStream localSteam = File.OpenRead(Path.Combine(gameFolder, PackageVersion)))
+ {
+ return await GetVersionItemsAsync(localSteam).ConfigureAwait(false);
+ }
+ }
- // Cache folder
- Core.RuntimeOptions runtimeOptions = serviceProvider.GetRequiredService();
- string cacheFolder = Path.Combine(runtimeOptions.DataFolder, "ServerCache");
-
- // 执行下载与移动操作
+ private async ValueTask PrepareCacheFilesAsync(List operations, PackageConvertContext context, IProgress progress)
+ {
foreach (ItemOperationInfo info in operations)
{
- progress.Report(new($"{info.Target}"));
-
- string targetFilePath = Path.Combine(gameFolder, info.Target);
- string cacheFilePath = Path.Combine(cacheFolder, info.Target);
- string moveToFilePath = Path.Combine(cacheFolder, info.MoveTo);
-
switch (info.Type)
{
case ItemOperationType.Backup:
- MoveToCache(moveToFilePath, targetFilePath);
- break;
+ continue;
case ItemOperationType.Replace:
- MoveToCache(moveToFilePath, targetFilePath);
- await ReplaceFromCacheOrWebAsync(cacheFilePath, targetFilePath, scatteredFilesUrl, info, progress).ConfigureAwait(false);
- break;
case ItemOperationType.Add:
- await ReplaceFromCacheOrWebAsync(cacheFilePath, targetFilePath, scatteredFilesUrl, info, progress).ConfigureAwait(false);
- break;
- default:
+ await SkipOrDownloadAsync(info, context, progress).ConfigureAwait(false);
break;
}
}
-
- // 重新下载所有 *pkg_version 文件
- await ReplacePackageVersionFilesAsync(scatteredFilesUrl, gameFolder).ConfigureAwait(false);
- return true;
}
- private async ValueTask ReplaceFromCacheOrWebAsync(string cacheFilePath, string targetFilePath, string scatteredFilesUrl, ItemOperationInfo info, IProgress progress)
+ private async ValueTask SkipOrDownloadAsync(ItemOperationInfo info, PackageConvertContext context, IProgress progress)
{
- if (File.Exists(cacheFilePath))
+ // 还原正确的远程地址
+ string remoteName = string.Format(info.Remote.RelativePath, context.ToDataFolderName);
+ string cacheFile = context.GetServerCacheTargetFilePath(remoteName);
+
+ if (File.Exists(cacheFile))
{
- string remoteMd5 = await MD5.HashFileAsync(cacheFilePath).ConfigureAwait(false);
- if (info.Md5 == remoteMd5.ToLowerInvariant() && new FileInfo(cacheFilePath).Length == info.TotalBytes)
+ if (info.Remote.FileSize == new FileInfo(cacheFile).Length)
{
- // Valid, move it to target path
- // There shouldn't be any file in the path/name
- File.Move(cacheFilePath, targetFilePath, false);
- return;
- }
- else
- {
- // Invalid file, delete it
- File.Delete(cacheFilePath);
+ string cacheMd5 = await MD5.HashFileAsync(cacheFile).ConfigureAwait(false);
+ if (info.Remote.Md5.Equals(cacheMd5, StringComparison.OrdinalIgnoreCase))
+ {
+ return;
+ }
}
+
+ // Invalid file, delete it
+ File.Delete(cacheFile);
}
- // Cache no item, download it anyway.
- while (true)
+ // Cache no matching item, download
+ using (FileStream fileStream = File.Create(cacheFile))
{
- using (FileStream fileStream = File.Create(targetFilePath))
+ string remoteUrl = context.GetScatteredFilesUrl(remoteName);
+ using (HttpResponseMessage response = await httpClient.GetAsync(remoteUrl, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false))
{
- using (HttpResponseMessage response = await httpClient.GetAsync($"{scatteredFilesUrl}/{info.Target}", HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false))
+ // This stream's length is incorrect,
+ // so we use length in the header
+ long totalBytes = response.Content.Headers.ContentLength ?? 0;
+ using (Stream webStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
{
- long totalBytes = response.Content.Headers.ContentLength ?? 0;
- using (Stream webStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
+ try
{
- try
+ StreamCopyWorker streamCopyWorker = new(webStream, fileStream, bytesRead => new(remoteName, bytesRead, totalBytes));
+ await streamCopyWorker.CopyAsync(progress).ConfigureAwait(false);
+ fileStream.Position = 0;
+ string cacheMd5 = await MD5.HashAsync(fileStream).ConfigureAwait(false);
+ if (string.Equals(info.Remote.Md5, cacheMd5, StringComparison.OrdinalIgnoreCase))
{
- StreamCopyWorker streamCopyWorker = new(webStream, fileStream, bytesRead => new(info.Target, bytesRead, totalBytes));
- await streamCopyWorker.CopyAsync(progress).ConfigureAwait(false);
- fileStream.Position = 0;
- string remoteMd5 = await MD5.HashAsync(fileStream).ConfigureAwait(false);
- if (string.Equals(info.Md5, remoteMd5, StringComparison.OrdinalIgnoreCase))
- {
- return;
- }
- }
- catch (Exception ex)
- {
- // System.IO.IOException: The response ended prematurely.
- // System.IO.IOException: Received an unexpected EOF or 0 bytes from the transport stream.
-
- // We want to retry forever.
- serviceProvider.GetRequiredService().Error(ex);
- await Delay.FromSeconds(2).ConfigureAwait(false);
+ return;
}
}
+ catch (Exception ex)
+ {
+ // System.IO.IOException: The response ended prematurely.
+ // System.IO.IOException: Received an unexpected EOF or 0 bytes from the transport stream.
+ ThrowHelper.PackageConvert(string.Format(SH.ServiceGamePackageRequestScatteredFileFailed, remoteName), ex);
+ }
}
}
}
}
- private async ValueTask ReplacePackageVersionFilesAsync(string scatteredFilesUrl, string gameFolder)
+ private async ValueTask ReplaceGameResourceAsync(List operations, PackageConvertContext context, IProgress progress)
{
- foreach (string versionFilePath in Directory.EnumerateFiles(gameFolder, "*pkg_version"))
+ // 重命名 _Data 目录
+ try
+ {
+ DirectoryOperation.Move(context.FromDataFolder, context.ToDataFolder);
+ }
+ catch (IOException ex)
+ {
+ // Access to the path is denied.
+ // When user install the game in special folder like 'Program Files'
+ throw ThrowHelper.GameFileOperation(SH.ServiceGamePackageRenameDataFolderFailed, ex);
+ }
+
+ // 执行下载与移动操作
+ foreach (ItemOperationInfo info in operations)
+ {
+ progress.Report(new($"{info.Remote}"));
+
+ (bool backup, bool target) = info.Type switch
+ {
+ ItemOperationType.Backup => (true, false),
+ ItemOperationType.Replace => (true, true),
+ ItemOperationType.Add => (false, true),
+ _ => (false, false),
+ };
+
+ if (backup)
+ {
+ string localFileName = string.Format(info.Local.RelativePath, context.FromDataFolder);
+ string localFilePath = context.GetGameFolderFilePath(localFileName);
+ Directory.CreateDirectory(Path.GetDirectoryName(localFilePath)!);
+ File.Move(localFilePath, context.GetServerCacheBackupFilePath(localFileName), true);
+ }
+
+ if (target)
+ {
+ string targetFileName = string.Format(info.Remote.RelativePath, context.ToDataFolder);
+ string targetFilePath = context.GetGameFolderFilePath(targetFileName);
+ Directory.CreateDirectory(Path.GetDirectoryName(targetFilePath)!);
+ File.Move(context.GetServerCacheTargetFilePath(targetFileName), targetFilePath, true);
+ }
+ }
+
+ // 重新下载所有 *pkg_version 文件
+ await ReplacePackageVersionFilesAsync(context).ConfigureAwait(false);
+ return true;
+ }
+
+ private async ValueTask ReplacePackageVersionFilesAsync(PackageConvertContext context)
+ {
+ foreach (string versionFilePath in Directory.EnumerateFiles(context.GameFolder, "*pkg_version"))
{
string versionFileName = Path.GetFileName(versionFilePath);
@@ -316,66 +356,11 @@ internal sealed partial class PackageConverter
using (FileStream versionFileStream = File.Create(versionFilePath))
{
- using (Stream webStream = await httpClient.GetStreamAsync($"{scatteredFilesUrl}/{versionFileName}").ConfigureAwait(false))
+ using (Stream webStream = await httpClient.GetStreamAsync(context.GetScatteredFilesUrl(versionFileName)).ConfigureAwait(false))
{
await webStream.CopyToAsync(versionFileStream).ConfigureAwait(false);
}
}
}
}
-
- private async ValueTask> GetRemoteVersionItemsAsync(Stream stream)
- {
- Dictionary results = new();
- using (StreamReader reader = new(stream))
- {
- while (await reader.ReadLineAsync().ConfigureAwait(false) is { } raw)
- {
- if (string.IsNullOrEmpty(raw))
- {
- continue;
- }
-
- VersionItem item = JsonSerializer.Deserialize(raw, options)!;
- results.Add(item.RemoteName, item);
- }
- }
-
- return results;
- }
-
- private async ValueTask> GetLocalVersionItemsAsync(Stream stream, ConvertDirection direction)
- {
- Dictionary results = new();
-
- using (StreamReader reader = new(stream))
- {
- while (await reader.ReadLineAsync().ConfigureAwait(false) is { } row)
- {
- if (string.IsNullOrEmpty(row))
- {
- continue;
- }
-
- VersionItem item = JsonSerializer.Deserialize(row, options)!;
-
- string remoteName = item.RemoteName;
-
- // 我们已经提前重命名了整个 Data 文件夹 所以需要将 RemoteName 中的 Data 同样替换
- if (remoteName.StartsWith(YuanShenData) || remoteName.StartsWith(GenshinImpactData))
- {
- remoteName = direction switch
- {
- ConvertDirection.OverseaToChinese => $"{YuanShenData}{remoteName[GenshinImpactData.Length..]}",
- ConvertDirection.ChineseToOversea => $"{GenshinImpactData}{remoteName[YuanShenData.Length..]}",
- _ => remoteName,
- };
- }
-
- results.Add(remoteName, item);
- }
- }
-
- return results;
- }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/VersionItem.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/VersionItem.cs
index 49205e31..a508c9d7 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/VersionItem.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/VersionItem.cs
@@ -13,7 +13,7 @@ internal sealed class VersionItem
/// 服务器上的名称
///
[JsonPropertyName("remoteName")]
- public string RemoteName { get; set; } = default!;
+ public string RelativePath { get; set; } = default!;
///
/// MD5校验值