Compare commits

..

1 Commits

Author SHA1 Message Date
qhy040404
a02162f76d refresh data folder size after deleting server cache 2024-01-05 23:30:16 +08:00
27 changed files with 170 additions and 307 deletions

View File

@@ -124,6 +124,9 @@ dotnet_diagnostic.SA1623.severity = none
# SA1636: File header copyright text should match
dotnet_diagnostic.SA1636.severity = none
# SA1414: Tuple types in signatures should have element names
dotnet_diagnostic.SA1414.severity = none
# SA0001: XML comment analysis disabled
dotnet_diagnostic.SA0001.severity = none
csharp_style_prefer_parameter_null_checking = true:suggestion
@@ -322,6 +325,7 @@ dotnet_diagnostic.CA2227.severity = suggestion
# CA2251: 使用 “string.Equals”
dotnet_diagnostic.CA2251.severity = suggestion
csharp_style_prefer_primary_constructors = true:suggestion
dotnet_diagnostic.SA1010.severity = none
[*.vb]
#### 命名样式 ####

View File

@@ -11,14 +11,11 @@ namespace Snap.Hutao.Core.IO.Ini;
[HighQuality]
internal static class IniSerializer
{
public static List<IniElement> DeserializeFromFile(string filePath)
{
using (FileStream readStream = File.OpenRead(filePath))
{
return Deserialize(readStream);
}
}
/// <summary>
/// 反序列化
/// </summary>
/// <param name="fileStream">文件流</param>
/// <returns>Ini 元素集合</returns>
public static List<IniElement> Deserialize(FileStream fileStream)
{
List<IniElement> results = [];
@@ -53,14 +50,11 @@ internal static class IniSerializer
return results;
}
public static void SerializeToFile(string filePath, IEnumerable<IniElement> elements)
{
using (FileStream writeStream = File.Create(filePath))
{
Serialize(writeStream, elements);
}
}
/// <summary>
/// 序列化
/// </summary>
/// <param name="fileStream">写入的流</param>
/// <param name="elements">元素</param>
public static void Serialize(FileStream fileStream, IEnumerable<IniElement> elements)
{
using (StreamWriter writer = new(fileStream))

View File

@@ -69,9 +69,4 @@ internal static class StructMarshal
{
return new(point.X, point.Y, size.Width, size.Height);
}
public static SizeInt32 SizeInt32(RectInt32 rect)
{
return new(rect.Width, rect.Height);
}
}

View File

@@ -1,19 +0,0 @@
<Window
x:Class="Snap.Hutao.IdentifyMonitorWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:shcm="using:Snap.Hutao.Control.Markup"
mc:Ignorable="d">
<StackPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"
Spacing="3">
<TextBlock Text="{shcm:ResourceString Name=WindowIdentifyMonitorHeader}"/>
<TextBlock
Style="{StaticResource SubtitleTextBlockStyle}"
Text="{x:Bind Monitor}"
TextAlignment="Center"/>
</StackPanel>
</Window>

View File

@@ -1,30 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Snap.Hutao.Core;
using Windows.Graphics;
namespace Snap.Hutao;
internal sealed partial class IdentifyMonitorWindow : Window
{
public IdentifyMonitorWindow(DisplayArea displayArea, int index)
{
InitializeComponent();
Monitor = $"{displayArea.DisplayId.Value:X8}:{index}";
OverlappedPresenter presenter = OverlappedPresenter.Create();
presenter.SetBorderAndTitleBar(false, false);
presenter.IsAlwaysOnTop = true;
presenter.IsResizable = false;
AppWindow.SetPresenter(presenter);
PointInt32 point = new(40, 32);
SizeInt32 size = StructMarshal.SizeInt32(displayArea.WorkArea).Scale(0.1);
AppWindow.MoveAndResize(StructMarshal.RectInt32(point, size), displayArea);
}
public string Monitor { get; private set; }
}

View File

@@ -1559,9 +1559,6 @@
<data name="ViewModelLaunchGameEnsureGameResourceFail" xml:space="preserve">
<value>切换服务器失败</value>
</data>
<data name="ViewModelLaunchGameIdentifyMonitorsAction" xml:space="preserve">
<value>识别显示器</value>
</data>
<data name="ViewModelLaunchGameMultiChannelReadFail" xml:space="preserve">
<value>无法读取游戏配置文件: {0},可能是文件不存在或权限不足</value>
</data>
@@ -2969,7 +2966,4 @@
<data name="WebResponseRequestExceptionFormat" xml:space="preserve">
<value>[{0}] 中的 [{1}] 网络请求异常,请稍后再试</value>
</data>
<data name="WindowIdentifyMonitorHeader" xml:space="preserve">
<value>显示器编号</value>
</data>
</root>

View File

@@ -29,9 +29,10 @@ internal readonly struct ChannelOptions
/// </summary>
public readonly bool IsOversea;
public readonly ChannelOptionsErrorKind ErrorKind;
public readonly string? FilePath;
/// <summary>
/// 配置文件路径 当不为 null 时则存在文件读写问题
/// </summary>
public readonly string? ConfigFilePath;
public ChannelOptions(ChannelType channel, SubChannelType subChannel, bool isOversea)
{
@@ -47,20 +48,15 @@ internal readonly struct ChannelOptions
IsOversea = isOversea;
}
private ChannelOptions(ChannelOptionsErrorKind errorKind, string? filePath)
private ChannelOptions(bool isOversea, string? configFilePath)
{
ErrorKind = errorKind;
FilePath = filePath;
IsOversea = isOversea;
ConfigFilePath = configFilePath;
}
public static ChannelOptions ConfigurationFileNotFound(string filePath)
public static ChannelOptions FileNotFound(bool isOversea, string configFilePath)
{
return new(ChannelOptionsErrorKind.ConfigurationFileNotFound, filePath);
}
public static ChannelOptions GamePathNullOrEmpty()
{
return new(ChannelOptionsErrorKind.GamePathNullOrEmpty, string.Empty);
return new(isOversea, configFilePath);
}
/// <inheritdoc/>

View File

@@ -1,11 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Service.Game.Configuration;
internal enum ChannelOptionsErrorKind
{
None,
ConfigurationFileNotFound,
GamePathNullOrEmpty,
}

View File

@@ -1,9 +1,11 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Core.IO.Ini;
using Snap.Hutao.Service.Game.Scheme;
using System.IO;
using static Snap.Hutao.Service.Game.GameConstants;
namespace Snap.Hutao.Service.Game.Configuration;
@@ -15,22 +17,25 @@ internal sealed partial class GameChannelOptionsService : IGameChannelOptionsSer
public ChannelOptions GetChannelOptions()
{
if (!launchOptions.TryGetGameFileSystem(out GameFileSystem? gameFileSystem))
if (!launchOptions.TryGetGamePathAndFilePathByName(ConfigFileName, out string gamePath, out string? configPath))
{
return ChannelOptions.GamePathNullOrEmpty();
throw ThrowHelper.InvalidOperation($"Invalid game path: {gamePath}");
}
bool isOversea = LaunchScheme.ExecutableIsOversea(gameFileSystem.GameFileName);
bool isOversea = LaunchScheme.ExecutableIsOversea(Path.GetFileName(gamePath));
if (!File.Exists(gameFileSystem.GameConfigFilePath))
if (!File.Exists(configPath))
{
return ChannelOptions.ConfigurationFileNotFound(gameFileSystem.GameConfigFilePath);
return ChannelOptions.FileNotFound(isOversea, configPath);
}
List<IniParameter> parameters = IniSerializer.DeserializeFromFile(gameFileSystem.GameConfigFilePath).OfType<IniParameter>().ToList();
string? channel = parameters.FirstOrDefault(p => p.Key == ChannelOptions.ChannelName)?.Value;
string? subChannel = parameters.FirstOrDefault(p => p.Key == ChannelOptions.SubChannelName)?.Value;
using (FileStream stream = File.OpenRead(configPath))
{
List<IniParameter> parameters = IniSerializer.Deserialize(stream).OfType<IniParameter>().ToList();
string? channel = parameters.FirstOrDefault(p => p.Key == ChannelOptions.ChannelName)?.Value;
string? subChannel = parameters.FirstOrDefault(p => p.Key == ChannelOptions.SubChannelName)?.Value;
return new(channel, subChannel, isOversea);
return new(channel, subChannel, isOversea);
}
}
}

View File

@@ -10,7 +10,6 @@ namespace Snap.Hutao.Service.Game;
internal static class GameConstants
{
public const string ConfigFileName = "config.ini";
public const string PCGameSDKFilePath = @"YuanShen_Data\Plugins\PCGameSDK.dll";
public const string YuanShenFileName = "YuanShen.exe";
public const string YuanShenFileNameUpper = "YUANSHEN.EXE";
public const string GenshinImpactFileName = "GenshinImpact.exe";

View File

@@ -1,39 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using System.IO;
namespace Snap.Hutao.Service.Game;
internal sealed class GameFileSystem
{
private readonly string gameFilePath;
private string? gameFileName;
private string? gameDirectory;
private string? gameConfigFilePath;
private string? pcGameSDKFilePath;
public GameFileSystem(string gameFilePath)
{
this.gameFilePath = gameFilePath;
}
public string GameFilePath { get => gameFilePath; }
public string GameFileName { get => gameFileName ??= Path.GetFileName(gameFilePath); }
public string GameDirectory
{
get
{
gameDirectory ??= Path.GetDirectoryName(gameFilePath);
ArgumentException.ThrowIfNullOrEmpty(gameDirectory);
return gameDirectory;
}
}
public string GameConfigFilePath { get => gameConfigFilePath ??= Path.Combine(GameDirectory, GameConstants.ConfigFileName); }
public string PCGameSDKFilePath { get => pcGameSDKFilePath ??= Path.Combine(GameDirectory, GameConstants.PCGameSDKFilePath); }
}

View File

@@ -3,25 +3,70 @@
using Snap.Hutao.Service.Game.PathAbstraction;
using System.Collections.Immutable;
using System.IO;
namespace Snap.Hutao.Service.Game;
internal static class LaunchOptionsExtension
{
public static bool TryGetGameFileSystem(this LaunchOptions options, [NotNullWhen(true)] out GameFileSystem? fileSystem)
public static bool TryGetGamePathAndGameDirectory(this LaunchOptions options, out string gamePath, [NotNullWhen(true)] out string? gameDirectory)
{
string gamePath = options.GamePath;
gamePath = options.GamePath;
if (string.IsNullOrEmpty(gamePath))
gameDirectory = Path.GetDirectoryName(gamePath);
if (string.IsNullOrEmpty(gameDirectory))
{
fileSystem = default;
return false;
}
fileSystem = new GameFileSystem(gamePath);
return true;
}
public static bool TryGetGameDirectoryAndGameFileName(this LaunchOptions options, [NotNullWhen(true)] out string? gameDirectory, [NotNullWhen(true)] out string? gameFileName)
{
string gamePath = options.GamePath;
gameDirectory = Path.GetDirectoryName(gamePath);
if (string.IsNullOrEmpty(gameDirectory))
{
gameFileName = default;
return false;
}
gameFileName = Path.GetFileName(gamePath);
if (string.IsNullOrEmpty(gameFileName))
{
return false;
}
return true;
}
public static bool TryGetGamePathAndGameFileName(this LaunchOptions options, out string gamePath, [NotNullWhen(true)] out string? gameFileName)
{
gamePath = options.GamePath;
gameFileName = Path.GetFileName(gamePath);
if (string.IsNullOrEmpty(gameFileName))
{
return false;
}
return true;
}
public static bool TryGetGamePathAndFilePathByName(this LaunchOptions options, string fileName, out string gamePath, [NotNullWhen(true)] out string? filePath)
{
if (options.TryGetGamePathAndGameDirectory(out gamePath, out string? gameDirectory))
{
filePath = Path.Combine(gameDirectory, fileName);
return true;
}
filePath = default;
return false;
}
public static ImmutableList<GamePathEntry> GetGamePathEntries(this LaunchOptions options, out GamePathEntry? entry)
{
string gamePath = options.GamePath;

View File

@@ -5,7 +5,6 @@ using Microsoft.Win32.SafeHandles;
using Snap.Hutao.Control.Extension;
using Snap.Hutao.Factory.ContentDialog;
using Snap.Hutao.Factory.Progress;
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Service.Game.Package;
using Snap.Hutao.Service.Game.PathAbstraction;
using Snap.Hutao.View.Dialog;
@@ -20,68 +19,38 @@ internal sealed class LaunchExecutionEnsureGameResourceHandler : ILaunchExecutio
{
public async ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next)
{
if (!context.TryGetGameFileSystem(out GameFileSystem? gameFileSystem))
IServiceProvider serviceProvider = context.ServiceProvider;
IContentDialogFactory contentDialogFactory = serviceProvider.GetRequiredService<IContentDialogFactory>();
IProgressFactory progressFactory = serviceProvider.GetRequiredService<IProgressFactory>();
LaunchGamePackageConvertDialog dialog = await contentDialogFactory.CreateInstanceAsync<LaunchGamePackageConvertDialog>().ConfigureAwait(false);
IProgress<PackageConvertStatus> convertProgress = progressFactory.CreateForMainThread<PackageConvertStatus>(state => dialog.State = state);
using (await dialog.BlockAsync(context.TaskContext).ConfigureAwait(false))
{
return;
}
if (ShouldConvert(context, gameFileSystem))
{
IServiceProvider serviceProvider = context.ServiceProvider;
IContentDialogFactory contentDialogFactory = serviceProvider.GetRequiredService<IContentDialogFactory>();
IProgressFactory progressFactory = serviceProvider.GetRequiredService<IProgressFactory>();
LaunchGamePackageConvertDialog dialog = await contentDialogFactory.CreateInstanceAsync<LaunchGamePackageConvertDialog>().ConfigureAwait(false);
IProgress<PackageConvertStatus> convertProgress = progressFactory.CreateForMainThread<PackageConvertStatus>(state => dialog.State = state);
using (await dialog.BlockAsync(context.TaskContext).ConfigureAwait(false))
if (!await EnsureGameResourceAsync(context, convertProgress).ConfigureAwait(false))
{
if (!await EnsureGameResourceAsync(context, gameFileSystem, convertProgress).ConfigureAwait(false))
{
// context.Result is set in EnsureGameResourceAsync
return;
}
await context.TaskContext.SwitchToMainThreadAsync();
ImmutableList<GamePathEntry> gamePathEntries = context.Options.GetGamePathEntries(out GamePathEntry? selected);
context.ViewModel.SetGamePathEntriesAndSelectedGamePathEntry(gamePathEntries, selected);
// context.Result is set in EnsureGameResourceAsync
return;
}
await context.TaskContext.SwitchToMainThreadAsync();
ImmutableList<GamePathEntry> gamePathEntries = context.Options.GetGamePathEntries(out GamePathEntry? selected);
context.ViewModel.SetGamePathEntriesAndSelectedGamePathEntry(gamePathEntries, selected);
}
await next().ConfigureAwait(false);
}
private static bool ShouldConvert(LaunchExecutionContext context, GameFileSystem gameFileSystem)
private static async ValueTask<bool> EnsureGameResourceAsync(LaunchExecutionContext context, IProgress<PackageConvertStatus> progress)
{
// Configuration file changed
if (context.ChannelOptionsChanged)
if (!context.Options.TryGetGameDirectoryAndGameFileName(out string? gameFolder, out string? gameFileName))
{
return true;
context.Result.Kind = LaunchExecutionResultKind.NoActiveGamePath;
context.Result.ErrorMessage = SH.ServiceGameLaunchExecutionGamePathNotValid;
return false;
}
// Executable name not match
if (!context.Scheme.ExecutableMatches(gameFileSystem.GameFileName))
{
return true;
}
if (!context.Scheme.IsOversea)
{
// [It's Bilibili channel xor PCGameSDK.dll exists] means we need to convert
if (context.Scheme.Channel is ChannelType.Bili ^ File.Exists(gameFileSystem.PCGameSDKFilePath))
{
return true;
}
}
return false;
}
private static async ValueTask<bool> EnsureGameResourceAsync(LaunchExecutionContext context, GameFileSystem gameFileSystem, IProgress<PackageConvertStatus> progress)
{
string gameFolder = gameFileSystem.GameDirectory;
string gameFileName = gameFileSystem.GameFileName;
context.Logger.LogInformation("Game folder: {GameFolder}", gameFolder);
if (!CheckDirectoryPermissions(gameFolder))

View File

@@ -3,7 +3,7 @@
namespace Snap.Hutao.Service.Game.Launching.Handler;
internal sealed class LaunchExecutionEnsureSchemeHandler : ILaunchExecutionDelegateHandler
internal sealed class LaunchExecutionEnsureSchemeNotExistsHandler : ILaunchExecutionDelegateHandler
{
public async ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next)
{
@@ -14,7 +14,7 @@ internal sealed class LaunchExecutionEnsureSchemeHandler : ILaunchExecutionDeleg
return;
}
context.Logger.LogInformation("Scheme [{Scheme}] is selected", context.Scheme.DisplayName);
context.Logger.LogInformation("Scheme[{Scheme}] is selected", context.Scheme.DisplayName);
await next().ConfigureAwait(false);
}
}

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Snap.Hutao.Core;
using System.IO;
namespace Snap.Hutao.Service.Game.Launching.Handler;
@@ -9,19 +10,21 @@ internal sealed class LaunchExecutionGameProcessInitializationHandler : ILaunchE
{
public async ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next)
{
if (!context.TryGetGameFileSystem(out GameFileSystem? gameFileSystem))
if (!context.Options.TryGetGamePathAndGameFileName(out string gamePath, out string? gameFileName))
{
context.Result.Kind = LaunchExecutionResultKind.NoActiveGamePath;
context.Result.ErrorMessage = SH.ServiceGameLaunchExecutionGamePathNotValid;
return;
}
context.Progress.Report(new(LaunchPhase.ProcessInitializing, SH.ServiceGameLaunchPhaseProcessInitializing));
using (context.Process = InitializeGameProcess(context, gameFileSystem))
using (context.Process = InitializeGameProcess(context, gamePath))
{
await next().ConfigureAwait(false);
}
}
private static System.Diagnostics.Process InitializeGameProcess(LaunchExecutionContext context, GameFileSystem gameFileSystem)
private static System.Diagnostics.Process InitializeGameProcess(LaunchExecutionContext context, string gamePath)
{
LaunchOptions launchOptions = context.Options;
@@ -48,10 +51,10 @@ internal sealed class LaunchExecutionGameProcessInitializationHandler : ILaunchE
StartInfo = new()
{
Arguments = commandLine,
FileName = gameFileSystem.GameFilePath,
FileName = gamePath,
UseShellExecute = true,
Verb = "runas",
WorkingDirectory = gameFileSystem.GameDirectory,
WorkingDirectory = Path.GetDirectoryName(gamePath),
},
};
}

View File

@@ -11,19 +11,22 @@ internal sealed class LaunchExecutionSetChannelOptionsHandler : ILaunchExecution
{
public async ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next)
{
if (!context.TryGetGameFileSystem(out GameFileSystem? gameFileSystem))
if (!context.Options.TryGetGamePathAndFilePathByName(GameConstants.ConfigFileName, out string gamePath, out string? configPath))
{
// context.Result is set in TryGetGameFileSystem
context.Result.Kind = LaunchExecutionResultKind.NoActiveGamePath;
context.Result.ErrorMessage = SH.ServiceGameLaunchExecutionGamePathNotValid;
return;
}
string configPath = gameFileSystem.GameConfigFilePath;
context.Logger.LogInformation("Game config file path: {ConfigPath}", configPath);
List<IniElement> elements = default!;
try
{
elements = [.. IniSerializer.DeserializeFromFile(configPath)];
using (FileStream readStream = File.OpenRead(configPath))
{
elements = [.. IniSerializer.Deserialize(readStream)];
}
}
catch (FileNotFoundException)
{
@@ -44,27 +47,32 @@ internal sealed class LaunchExecutionSetChannelOptionsHandler : ILaunchExecution
return;
}
bool changed = false;
foreach (IniElement element in elements)
{
if (element is IniParameter parameter)
{
if (parameter.Key is ChannelOptions.ChannelName)
{
context.ChannelOptionsChanged = parameter.Set(context.Scheme.Channel.ToString("D")) || context.ChannelOptionsChanged;
changed = parameter.Set(context.Scheme.Channel.ToString("D")) || changed;
continue;
}
if (parameter.Key is ChannelOptions.SubChannelName)
{
context.ChannelOptionsChanged = parameter.Set(context.Scheme.SubChannel.ToString("D")) || context.ChannelOptionsChanged;
changed = parameter.Set(context.Scheme.SubChannel.ToString("D")) || changed;
continue;
}
}
}
if (context.ChannelOptionsChanged)
if (changed)
{
IniSerializer.SerializeToFile(configPath, elements);
using (FileStream writeStream = File.Create(configPath))
{
IniSerializer.Serialize(writeStream, elements);
}
}
await next().ConfigureAwait(false);

View File

@@ -15,8 +15,6 @@ internal sealed partial class LaunchExecutionContext
private readonly ITaskContext taskContext;
private readonly LaunchOptions options;
private GameFileSystem? gameFileSystem;
[SuppressMessage("", "SH007")]
public LaunchExecutionContext(IServiceProvider serviceProvider, IViewModelSupportLaunchExecution viewModel, LaunchScheme? scheme, GameAccount? account)
: this(serviceProvider)
@@ -38,34 +36,13 @@ internal sealed partial class LaunchExecutionContext
public LaunchOptions Options { get => options; }
public IViewModelSupportLaunchExecution ViewModel { get; private set; } = default!;
public IViewModelSupportLaunchExecution ViewModel { get; set; } = default!;
public LaunchScheme Scheme { get; private set; } = default!;
public LaunchScheme Scheme { get; set; } = default!;
public GameAccount? Account { get; private set; }
public bool ChannelOptionsChanged { get; set; }
public GameAccount? Account { get; set; }
public IProgress<LaunchStatus> Progress { get; set; } = default!;
public System.Diagnostics.Process Process { get; set; } = default!;
public bool TryGetGameFileSystem([NotNullWhen(true)] out GameFileSystem? gameFileSystem)
{
if (this.gameFileSystem is not null)
{
gameFileSystem = this.gameFileSystem;
return true;
}
if (!Options.TryGetGameFileSystem(out gameFileSystem))
{
Result.Kind = LaunchExecutionResultKind.NoActiveGamePath;
Result.ErrorMessage = SH.ServiceGameLaunchExecutionGamePathNotValid;
return false;
}
this.gameFileSystem = gameFileSystem;
return true;
}
}

View File

@@ -15,7 +15,7 @@ internal sealed class LaunchExecutionInvoker
{
handlers = [];
handlers.Enqueue(new LaunchExecutionEnsureGameNotRunningHandler());
handlers.Enqueue(new LaunchExecutionEnsureSchemeHandler());
handlers.Enqueue(new LaunchExecutionEnsureSchemeNotExistsHandler());
handlers.Enqueue(new LaunchExecutionSetChannelOptionsHandler());
handlers.Enqueue(new LaunchExecutionEnsureGameResourceHandler());
handlers.Enqueue(new LaunchExecutionSetGameAccountHandler());

View File

@@ -93,6 +93,7 @@ internal sealed partial class PackageConverter
ZipFile.ExtractToDirectory(sdkWebStream, gameFolder, true);
}
// TODO: verify sdk md5
if (File.Exists(sdkDllBackup) && File.Exists(sdkVersionBackup))
{
File.Delete(sdkDllBackup);

View File

@@ -9,7 +9,7 @@ internal sealed class GamePathEntry
public string Path { get; set; } = default!;
[JsonIgnore]
public GamePathEntryKind Kind { get => GetKind(Path); }
public GamePathKind Kind { get => GetKind(Path); }
public static GamePathEntry Create(string path)
{
@@ -19,8 +19,8 @@ internal sealed class GamePathEntry
};
}
private static GamePathEntryKind GetKind(string path)
private static GamePathKind GetKind(string path)
{
return GamePathEntryKind.None;
return GamePathKind.None;
}
}

View File

@@ -3,7 +3,7 @@
namespace Snap.Hutao.Service.Game.PathAbstraction;
internal enum GamePathEntryKind
internal enum GamePathKind
{
None,
ChineseClient,

View File

@@ -107,7 +107,6 @@
<None Remove="Control\Theme\Uri.xaml" />
<None Remove="Control\Theme\WindowOverride.xaml" />
<None Remove="GuideWindow.xaml" />
<None Remove="IdentifyMonitorWindow.xaml" />
<None Remove="IdentityStructs.json" />
<None Remove="LaunchGameWindow.xaml" />
<None Remove="Resource\BlurBackground.png" />
@@ -322,7 +321,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="StyleCop.Analyzers.Unstable" Version="1.2.0.556">
<PackageReference Include="StyleCop.Analyzers.Unstable" Version="1.2.0.507">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
@@ -545,13 +544,7 @@
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="IdentifyMonitorWindow.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="View\Control\HutaoStatisticsCard.xaml">
<Generator>MSBuild:Compile</Generator>

View File

@@ -228,7 +228,7 @@
<ToggleSwitch Width="120" IsOn="{Binding LaunchOptions.IsUseCloudThirdPartyMobile, Mode=TwoWay}"/>
</cwc:SettingsCard>
<cwc:SettingsCard Description="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceAspectRatioDescription}" Header="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceAspectRatioHeader}">
<shc:SizeRestrictedContentControl Margin="0,0,136,0" VerticalAlignment="Center">
<shc:SizeRestrictedContentControl Margin="0,0,136,0">
<ComboBox
ItemsSource="{Binding LaunchOptions.AspectRatios}"
PlaceholderText="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceAspectRatioPlaceHolder}"
@@ -259,11 +259,7 @@
</cwc:SettingsCard>
<cwc:SettingsCard Description="{shcm:ResourceString Name=ViewPageLaunchGameMonitorsDescription}" Header="-monitor">
<StackPanel Orientation="Horizontal" Spacing="16">
<Button
Width="120"
Command="{Binding IdentifyMonitorsCommand}"
Content="{shcm:ResourceString Name=ViewModelLaunchGameIdentifyMonitorsAction}"/>
<shc:SizeRestrictedContentControl VerticalAlignment="Center">
<shc:SizeRestrictedContentControl>
<ComboBox
DisplayMemberPath="Name"
IsEnabled="{Binding LaunchOptions.IsMonitorEnabled}"

View File

@@ -13,9 +13,17 @@ internal static class LaunchGameShared
{
public static LaunchScheme? GetCurrentLaunchSchemeFromConfigFile(IGameServiceFacade gameService, IInfoBarService infoBarService)
{
ChannelOptions options = gameService.GetChannelOptions();
ChannelOptions options;
try
{
options = gameService.GetChannelOptions();
}
catch (InvalidOperationException)
{
return default;
}
if (options.ErrorKind is ChannelOptionsErrorKind.None)
if (string.IsNullOrEmpty(options.ConfigFilePath))
{
try
{
@@ -32,7 +40,7 @@ internal static class LaunchGameShared
}
else
{
infoBarService.Warning($"{options.ErrorKind}", SH.FormatViewModelLaunchGameMultiChannelReadFail(options.FilePath));
infoBarService.Warning(SH.FormatViewModelLaunchGameMultiChannelReadFail(options.ConfigFilePath));
}
return default;

View File

@@ -3,7 +3,6 @@
using CommunityToolkit.WinUI.Collections;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.UI.Windowing;
using Snap.Hutao.Core;
using Snap.Hutao.Core.Diagnostics.CodeAnalysis;
using Snap.Hutao.Core.ExceptionService;
@@ -20,7 +19,6 @@ using Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.IO;
using Windows.Graphics;
namespace Snap.Hutao.ViewModel.Game;
@@ -340,28 +338,4 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel, IView
};
}
}
[Command("IdentifyMonitorsCommand")]
private async Task IdentifyMonitorsAsync()
{
List<IdentifyMonitorWindow> windows = [];
IReadOnlyList<DisplayArea> displayAreas = DisplayArea.FindAll();
for (int i = 0; i < displayAreas.Count; i++)
{
windows.Add(new IdentifyMonitorWindow(displayAreas[i], i + 1));
}
foreach (IdentifyMonitorWindow window in windows)
{
window.Activate();
}
await Delay.FromSeconds(3).ConfigureAwait(true);
foreach (IdentifyMonitorWindow window in windows)
{
window.Close();
}
}
}

View File

@@ -26,7 +26,18 @@ internal sealed partial class FolderViewModel : ObservableObject
public string? Size { get => size; set => SetProperty(ref size, value); }
public async ValueTask SetFolderSizeAsync()
public async ValueTask RefreshFolderSizeAsync()
{
await SetFolderSizeAsync().ConfigureAwait(false);
}
[Command("OpenFolderCommand")]
private async Task OpenDataFolderAsync()
{
await Launcher.LaunchFolderPathAsync(folder);
}
private async ValueTask SetFolderSizeAsync()
{
await taskContext.SwitchToBackgroundAsync();
long totalSize = 0;
@@ -39,10 +50,4 @@ internal sealed partial class FolderViewModel : ObservableObject
await taskContext.SwitchToMainThreadAsync();
Size = SH.FormatViewModelSettingFolderSizeDescription(Converters.ToFileSizeString(totalSize));
}
[Command("OpenFolderCommand")]
private async Task OpenDataFolderAsync()
{
await Launcher.LaunchFolderPathAsync(folder);
}
}

View File

@@ -284,11 +284,7 @@ internal sealed partial class SettingViewModel : Abstraction.ViewModel
Directory.Delete(cacheFolder, true);
}
if (DataFolderView is not null)
{
await DataFolderView.SetFolderSizeAsync().ConfigureAwait(false);
}
DataFolderView?.RefreshFolderSizeAsync().SafeForget();
infoBarService.Information(SH.ViewModelSettingActionComplete);
}
}