From f2fea5a0e6a89cea7bf6cecff30f90b11ea08f22 Mon Sep 17 00:00:00 2001
From: DismissedLight <1686188646@qq.com>
Date: Sun, 20 Aug 2023 21:50:39 +0800
Subject: [PATCH] fix game package convert
---
src/Snap.Hutao/Snap.Hutao/App.xaml | 8 +--
.../Snap.Hutao/Core/IO/DirectoryOperation.cs | 5 +-
.../Snap.Hutao/Core/RuntimeOptions.cs | 4 +-
.../Resource/Localization/SH.Designer.cs | 27 +++++++
.../Snap.Hutao/Resource/Localization/SH.resx | 9 +++
.../Snap.Hutao/Service/Game/GameService.cs | 4 +-
.../Service/Game/LaunchScheme.KnownSchemes.cs | 10 +++
.../Service/Game/Package/PackageConverter.cs | 70 +++++++++++--------
.../Game/Package/PackageReplaceStatus.cs | 33 +++++----
.../LaunchGamePackageConvertDialog.xaml | 21 ++++--
.../ViewModel/Game/LaunchGameViewModel.cs | 4 +-
11 files changed, 138 insertions(+), 57 deletions(-)
diff --git a/src/Snap.Hutao/Snap.Hutao/App.xaml b/src/Snap.Hutao/Snap.Hutao/App.xaml
index 1f3b166d..fd4bf633 100644
--- a/src/Snap.Hutao/Snap.Hutao/App.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/App.xaml
@@ -44,14 +44,12 @@
0,6,6,0
0,0,6,6
2
-
- 212
-
- 304
+
288
+ 212
+ 304
180
-
64
0.2
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/DirectoryOperation.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/DirectoryOperation.cs
index c881bb77..39132ba2 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/IO/DirectoryOperation.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/DirectoryOperation.cs
@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Microsoft.VisualBasic.FileIO;
using System.IO;
namespace Snap.Hutao.Core.IO;
@@ -14,7 +15,7 @@ internal static class DirectoryOperation
return false;
}
- Directory.Move(sourceDirName, destDirName);
+ FileSystem.MoveDirectory(sourceDirName, destDirName, true);
return true;
}
-}
\ No newline at end of file
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/RuntimeOptions.cs b/src/Snap.Hutao/Snap.Hutao/Core/RuntimeOptions.cs
index ad3a4916..30aa3f95 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/RuntimeOptions.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/RuntimeOptions.cs
@@ -131,15 +131,13 @@ internal sealed class RuntimeOptions : IOptions
private static bool GetElevated()
{
-#if DEBUG_AS_FAKE_ELEVATED
return true;
-#else
+
using (WindowsIdentity identity = WindowsIdentity.GetCurrent())
{
WindowsPrincipal principal = new(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
-#endif
}
private void DetectWebView2Environment(ref string webView2Version, ref bool isWebView2Supported)
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 e5e68acb..368a76a5 100644
--- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs
@@ -1473,6 +1473,33 @@ namespace Snap.Hutao.Resource.Localization {
}
}
+ ///
+ /// 查找类似 备份:{0} 的本地化字符串。
+ ///
+ internal static string ServiceGamePackageConvertMoveFileBackupFormat {
+ get {
+ return ResourceManager.GetString("ServiceGamePackageConvertMoveFileBackupFormat", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 重命名:{0} 到:{1} 的本地化字符串。
+ ///
+ internal static string ServiceGamePackageConvertMoveFileRenameFormat {
+ get {
+ return ResourceManager.GetString("ServiceGamePackageConvertMoveFileRenameFormat", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 替换:{0} 的本地化字符串。
+ ///
+ internal static string ServiceGamePackageConvertMoveFileRestoreFormat {
+ get {
+ return ResourceManager.GetString("ServiceGamePackageConvertMoveFileRestoreFormat", resourceCulture);
+ }
+ }
+
///
/// 查找类似 重命名数据文件夹名称失败 的本地化字符串。
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
index 09b35f8a..5f8b287a 100644
--- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
+++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
@@ -644,6 +644,15 @@
在 Unity 日志文件中找不到游戏路径
+
+ 备份:{0}
+
+
+ 重命名:{0} 到:{1}
+
+
+ 替换:{0}
+
重命名数据文件夹名称失败
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs
index 2ce14a2f..8a0cea37 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs
@@ -163,7 +163,7 @@ internal sealed partial class GameService : IGameService
}
}
- return changed;
+ return changed || !LaunchSchemeMatchesExecutable(scheme, Path.GetFileName(gamePath));
}
///
@@ -244,7 +244,7 @@ internal sealed partial class GameService : IGameService
}
string gamePath = appOptions.GamePath;
- ArgumentNullException.ThrowIfNullOrEmpty(gamePath);
+ ArgumentException.ThrowIfNullOrEmpty(gamePath);
using (Process game = ProcessInterop.InitializeGameProcess(launchOptions, gamePath))
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchScheme.KnownSchemes.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchScheme.KnownSchemes.cs
index 99833640..4527cb62 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchScheme.KnownSchemes.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchScheme.KnownSchemes.cs
@@ -18,6 +18,15 @@ internal sealed partial class LaunchScheme
private const string SdkStaticLauncherBilibiliKey = "KAtdSsoQ";
private const string SdkStaticLauncherGlobalKey = "gcStgarh";
+ private static readonly LaunchScheme ServerChineseChannelOfficialSubChannelDefault = new()
+ {
+ LauncherId = SdkStaticLauncherChineseId,
+ Key = SdkStaticLauncherChineseKey,
+ Channel = ChannelType.Official,
+ SubChannel = SubChannelType.Default,
+ IsOversea = false,
+ };
+
private static readonly LaunchScheme ServerChineseChannelOfficialSubChannelOfficial = new()
{
LauncherId = SdkStaticLauncherChineseId,
@@ -81,6 +90,7 @@ internal sealed partial class LaunchScheme
return new List()
{
// 官服
+ ServerChineseChannelOfficialSubChannelDefault,
ServerChineseChannelOfficialSubChannelOfficial,
ServerChineseChannelOfficialSubChannelNoTapTap,
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 7321a6f5..a01a8f22 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/PackageConverter.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/PackageConverter.cs
@@ -27,6 +27,7 @@ internal sealed partial class PackageConverter
private readonly JsonSerializerOptions options;
private readonly RuntimeOptions runtimeOptions;
private readonly HttpClient httpClient;
+ private readonly ILogger logger;
///
/// 异步检查替换游戏资源
@@ -253,7 +254,9 @@ internal sealed partial class PackageConverter
}
// Cache no matching item, download
- Directory.CreateDirectory(context.ServerCacheTargetFolder);
+ string? directory = Path.GetDirectoryName(cacheFile);
+ ArgumentException.ThrowIfNullOrEmpty(directory);
+ Directory.CreateDirectory(directory);
using (FileStream fileStream = File.Create(cacheFile))
{
string remoteUrl = context.GetScatteredFilesUrl(remoteName);
@@ -288,24 +291,10 @@ internal sealed partial class PackageConverter
private async ValueTask ReplaceGameResourceAsync(List operations, PackageConvertContext context, IProgress progress)
{
- // 重命名 _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
+ (bool moveToBackup, bool moveToTarget) = info.Type switch
{
ItemOperationType.Backup => (true, false),
ItemOperationType.Replace => (true, true),
@@ -313,27 +302,52 @@ internal sealed partial class PackageConverter
_ => (false, false),
};
- if (backup)
+ // 先备份
+ if (moveToBackup)
{
- string localFileName = info.Local.RelativePath.Format(context.FromDataFolder);
+ string localFileName = info.Local.RelativePath.Format(context.FromDataFolderName);
+ progress.Report(new(SH.ServiceGamePackageConvertMoveFileBackupFormat.Format(localFileName)));
+
string localFilePath = context.GetGameFolderFilePath(localFileName);
- string? directory = Path.GetDirectoryName(localFilePath);
- ArgumentException.ThrowIfNullOrEmpty(directory);
- Directory.CreateDirectory(directory);
- File.Move(localFilePath, context.GetServerCacheBackupFilePath(localFileName), true);
+ string cacheFilePath = context.GetServerCacheBackupFilePath(localFileName);
+ string? cacheFileDirectory = Path.GetDirectoryName(cacheFilePath);
+ ArgumentException.ThrowIfNullOrEmpty(cacheFileDirectory);
+ Directory.CreateDirectory(cacheFileDirectory);
+
+ logger.LogInformation("Backing file from:{Src} to:{Dst}", localFilePath, cacheFilePath);
+ FileOperation.Move(localFilePath, cacheFilePath, true);
}
- if (target)
+ // 后替换
+ if (moveToTarget)
{
- string targetFileName = info.Remote.RelativePath.Format(context.ToDataFolder);
+ string targetFileName = info.Remote.RelativePath.Format(context.ToDataFolderName);
+ progress.Report(new(SH.ServiceGamePackageConvertMoveFileRestoreFormat.Format(targetFileName)));
+
string targetFilePath = context.GetGameFolderFilePath(targetFileName);
- string? directory = Path.GetDirectoryName(targetFilePath);
- ArgumentException.ThrowIfNullOrEmpty(directory);
- Directory.CreateDirectory(directory);
- File.Move(context.GetServerCacheTargetFilePath(targetFileName), targetFilePath, true);
+ string? targetFileDirectory = Path.GetDirectoryName(targetFilePath);
+ string cacheFilePath = context.GetServerCacheTargetFilePath(targetFileName);
+ ArgumentException.ThrowIfNullOrEmpty(targetFileDirectory);
+ Directory.CreateDirectory(targetFileDirectory);
+
+ logger.LogInformation("Restoring file from:{Src} to:{Dst}", cacheFilePath, targetFilePath);
+ FileOperation.Move(cacheFilePath, targetFilePath, true);
}
}
+ // 重命名 _Data 目录
+ try
+ {
+ progress.Report(new(SH.ServiceGamePackageConvertMoveFileRenameFormat.Format(context.FromDataFolderName, context.ToDataFolderName)));
+ 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);
+ }
+
// 重新下载所有 *pkg_version 文件
await ReplacePackageVersionFilesAsync(context).ConfigureAwait(false);
return true;
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/PackageReplaceStatus.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/PackageReplaceStatus.cs
index 0e49cfa2..1998f152 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/PackageReplaceStatus.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/PackageReplaceStatus.cs
@@ -14,11 +14,11 @@ internal sealed class PackageReplaceStatus : ICloneable
///
/// 构造一个新的包更新状态
///
- /// 描述
- public PackageReplaceStatus(string description)
+ /// 描述
+ public PackageReplaceStatus(string name)
{
- Name = default!;
- Description = description;
+ Name = name;
+ Description = default!;
}
///
@@ -34,23 +34,27 @@ internal sealed class PackageReplaceStatus : ICloneable
Description = $"{Converters.ToFileSizeString(bytesRead)}/{Converters.ToFileSizeString(totalBytes)}";
}
- public string Name { get; set; }
+ private PackageReplaceStatus()
+ {
+ }
+
+ public string Name { get; set; } = default!;
///
/// 描述
///
- public string Description { get; set; }
-
- ///
- /// 是否有进度
- ///
- public bool IsIndeterminate { get => Percent < 0; }
+ public string Description { get; set; } = default!;
///
/// 进度
///
public double Percent { get; set; } = -1;
+ ///
+ /// 是否有进度
+ ///
+ public bool IsIndeterminate { get => Percent < 0; }
+
///
/// 克隆
///
@@ -58,6 +62,11 @@ internal sealed class PackageReplaceStatus : ICloneable
public PackageReplaceStatus Clone()
{
// 进度需要在主线程上创建
- return new(Description) { Percent = Percent };
+ return new()
+ {
+ Name = Name,
+ Description = Description,
+ Percent = Percent,
+ };
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/LaunchGamePackageConvertDialog.xaml b/src/Snap.Hutao/Snap.Hutao/View/Dialog/LaunchGamePackageConvertDialog.xaml
index c8bca679..9118449b 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Dialog/LaunchGamePackageConvertDialog.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/LaunchGamePackageConvertDialog.xaml
@@ -21,9 +21,22 @@
IsIndeterminate="{Binding State.IsIndeterminate}"
Maximum="1"
Value="{Binding State.Percent}"/>
-
+
+
+
+
+
+
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Game/LaunchGameViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Game/LaunchGameViewModel.cs
index 6e6fb91d..85487db3 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Game/LaunchGameViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Game/LaunchGameViewModel.cs
@@ -10,6 +10,7 @@ using Snap.Hutao.Factory.Abstraction;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Service;
using Snap.Hutao.Service.Game;
+using Snap.Hutao.Service.Game.Package;
using Snap.Hutao.Service.Navigation;
using Snap.Hutao.Service.Notification;
using Snap.Hutao.Service.User;
@@ -188,7 +189,7 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel
{
// Channel changed, we need to change local file.
LaunchGamePackageConvertDialog dialog = await contentDialogFactory.CreateInstanceAsync().ConfigureAwait(false);
- Progress progress = new(state => dialog.State = state.Clone());
+ IProgress progress = taskContext.CreateProgressForMainThread(state => dialog.State = state/*.Clone()*/);
using (await dialog.BlockAsync(taskContext).ConfigureAwait(false))
{
if (!await gameService.EnsureGameResourceAsync(SelectedScheme, progress).ConfigureAwait(false))
@@ -212,6 +213,7 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel
}
catch (Exception ex)
{
+ System.Diagnostics.Debug.WriteLine(ExceptionFormat.Format(ex));
infoBarService.Error(ex);
}
}