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