diff --git a/src/Starward.Core/HoYoPlay/GameInfo.cs b/src/Starward.Core/HoYoPlay/GameInfo.cs index df42fc4a1..230c39edb 100644 --- a/src/Starward.Core/HoYoPlay/GameInfo.cs +++ b/src/Starward.Core/HoYoPlay/GameInfo.cs @@ -71,21 +71,21 @@ public class GameInfoDisplay /// 大背景图 /// [JsonPropertyName("background")] - public GameImage Background { get; set; } + public GameImage? Background { get; set; } /// /// 游戏Logo /// [JsonPropertyName("logo")] - public GameImage Logo { get; set; } + public GameImage? Logo { get; set; } /// /// 小缩略背景图 /// [JsonPropertyName("thumbnail")] - public GameImage Thumbnail { get; set; } + public GameImage? Thumbnail { get; set; } } diff --git a/src/Starward.Language/Lang.Designer.cs b/src/Starward.Language/Lang.Designer.cs index 7a0d8e522..007238d2c 100644 --- a/src/Starward.Language/Lang.Designer.cs +++ b/src/Starward.Language/Lang.Designer.cs @@ -1655,6 +1655,24 @@ public static string GameResourcePage_LatestVersion { } } + /// + /// 查找类似 Actual Size of Installed Games 的本地化字符串。 + /// + public static string GameSelector_ActualSizeOfInstalledGames { + get { + return ResourceManager.GetString("GameSelector_ActualSizeOfInstalledGames", resourceCulture); + } + } + + /// + /// 查找类似 Storage Space Saved by Hard Links 的本地化字符串。 + /// + public static string GameSelector_StorageSpaceSavedByHardLinks { + get { + return ResourceManager.GetString("GameSelector_StorageSpaceSavedByHardLinks", resourceCulture); + } + } + /// /// 查找类似 Anti-Aliasing 的本地化字符串。 /// diff --git a/src/Starward.Language/Lang.resx b/src/Starward.Language/Lang.resx index 1b011af94..688d2c149 100644 --- a/src/Starward.Language/Lang.resx +++ b/src/Starward.Language/Lang.resx @@ -1645,4 +1645,10 @@ Do you accept the risk and continue to use it? Use Version Poster + + Actual Size of Installed Games + + + Storage Space Saved by Hard Links + \ No newline at end of file diff --git a/src/Starward.Language/Lang.zh-CN.resx b/src/Starward.Language/Lang.zh-CN.resx index e1e18d040..5542304be 100644 --- a/src/Starward.Language/Lang.zh-CN.resx +++ b/src/Starward.Language/Lang.zh-CN.resx @@ -1645,4 +1645,10 @@ 使用版本海报 + + 已安装游戏实际占用空间 + + + 通过硬链接节省存储空间 + \ No newline at end of file diff --git a/src/Starward/Features/GameSelector/GameBizDisplay.cs b/src/Starward/Features/GameSelector/GameBizDisplay.cs index 5e07455b3..91abb6c19 100644 --- a/src/Starward/Features/GameSelector/GameBizDisplay.cs +++ b/src/Starward/Features/GameSelector/GameBizDisplay.cs @@ -1,5 +1,4 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using Starward.Core.HoYoPlay; +using Starward.Core.HoYoPlay; using System.Collections.Generic; @@ -11,18 +10,6 @@ public class GameBizDisplay public GameInfo GameInfo { get; set; } - public List Servers { get; set; } = new(); - -} - - -public class GameBizDisplayServer : ObservableObject -{ - - public GameBizIcon GameBizIcon { get; set; } - - - public bool IsPinned { get; set => SetProperty(ref field, value); } - + public List Servers { get; set; } = new(); } diff --git a/src/Starward/Features/GameSelector/GameBizIcon.cs b/src/Starward/Features/GameSelector/GameBizIcon.cs index f962469a0..4318f21a1 100644 --- a/src/Starward/Features/GameSelector/GameBizIcon.cs +++ b/src/Starward/Features/GameSelector/GameBizIcon.cs @@ -10,6 +10,9 @@ public partial class GameBizIcon : ObservableObject, IEquatable { + private const double GB = 1 << 30; + + public GameId GameId { get; set; } public GameBiz GameBiz { get; set; } @@ -25,6 +28,15 @@ public partial class GameBizIcon : ObservableObject, IEquatable public double MaskOpacity { get; set => SetProperty(ref field, value); } = 1.0; + public bool IsPinned { get; set => SetProperty(ref field, value); } + + public string? InstallPath { get; set => SetProperty(ref field, value); } + + public long TotalSize { get; set { field = value; OnPropertyChanged(nameof(TotalSizeText)); } } + + public string? TotalSizeText => TotalSize == 0 ? null : $"{TotalSize / GB:F2}GB"; + + public bool IsSelected { get; @@ -61,6 +73,25 @@ public GameBizIcon(GameInfo gameInfo) + public void UpdateInfo() + { + GameIcon = GameBizToIcon(GameBiz); + ServerIcon = GameBizToServerIcon(GameBiz); + GameName = GameBiz.ToGameName(); + ServerName = GameBiz.ToGameServerName(); + } + + + public void UpdateInfo(GameInfo gameInfo) + { + GameIcon = gameInfo.Display.Icon.Url; + ServerIcon = GameBizToServerIcon(gameInfo.GameBiz); + GameName = gameInfo.Display.Name; + ServerName = gameInfo.GameBiz.ToGameServerName(); + } + + + private static string GameBizToIcon(GameBiz gameBiz) { return gameBiz.Game switch diff --git a/src/Starward/Features/GameSelector/GameSelector.xaml b/src/Starward/Features/GameSelector/GameSelector.xaml index aca916c6e..fb1c3c185 100644 --- a/src/Starward/Features/GameSelector/GameSelector.xaml +++ b/src/Starward/Features/GameSelector/GameSelector.xaml @@ -65,7 +65,7 @@ - + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0,0,0,1 + 0 + + + - - + - - + + + + + Foreground="{ThemeResource TextFillColorSecondaryBrush}" + Visibility="{x:Bind InstalledGamesSavedSize, Converter={StaticResource ObjectToVisibilityConverter}}"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - diff --git a/src/Starward/Features/GameSelector/GameSelector.xaml.cs b/src/Starward/Features/GameSelector/GameSelector.xaml.cs index 112c30d83..5f13905a2 100644 --- a/src/Starward/Features/GameSelector/GameSelector.xaml.cs +++ b/src/Starward/Features/GameSelector/GameSelector.xaml.cs @@ -5,18 +5,24 @@ using Microsoft.UI.Xaml.Controls.Primitives; using Starward.Core; using Starward.Core.HoYoPlay; +using Starward.Features.GameLauncher; using Starward.Features.HoYoPlay; using Starward.Frameworks; using Starward.Helpers; using Starward.Messages; -using Starward.Services.Launcher; using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics; using System.Globalization; +using System.IO; using System.Linq; using System.Numerics; +using System.Runtime.InteropServices; using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Vanara.PInvoke; using Windows.Foundation; @@ -30,7 +36,10 @@ public sealed partial class GameSelector : UserControl public event EventHandler<(GameId, bool DoubleTapped)>? CurrentGameChanged; - private HoYoPlayService _hoyoplayService = AppService.GetService(); + private readonly HoYoPlayService _hoyoplayService = AppService.GetService(); + + + private readonly GameLauncherService _gameLauncherService = AppService.GetService(); @@ -38,11 +47,11 @@ public GameSelector() { this.InitializeComponent(); InitializeGameSelector(); + this.Loaded += GameSelector_Loaded; } - public GameBiz CurrentGameBiz { get; set; } @@ -64,38 +73,35 @@ public void InitializeGameSelector() { InitializeGameIconsArea(); InitializeGameServerArea(); + InitializeInstalledGamesCommand.Execute(null); } - [RelayCommand] - public void AutoSearchInstalledGames() + private async void GameSelector_Loaded(object sender, RoutedEventArgs e) { - try - { - // todo 从注册表自动搜索已安装的游戏 - var service = AppService.GetService(); - var sb = new StringBuilder(); - foreach (GameBiz biz in GameBiz.AllGameBizs) - { - if (service.IsGameExeExists(biz)) - { - sb.Append(biz.ToString()); - sb.Append(','); - } - } - AppSetting.SelectedGameBizs = sb.ToString().TrimEnd(','); - InitializeGameSelector(); - } - catch (Exception ex) - { + await Task.Delay(2000); + await UpdateGameInfoAsync(); + } + + + + + public async void OnLanguageChanged(object? sender, LanguageChangedMessage message) + { + // todo 语言切换 + if (message.Completed) + { + this.Bindings.Update(); + await UpdateGameInfoAsync(); } } + #region Game Icon @@ -415,7 +421,6 @@ private void GameBizIcons_CollectionChanged(object? sender, System.Collections.S - #region Full Background 黑色半透明背景 @@ -485,7 +490,7 @@ private void InitializeGameServerArea() try { var list = new List(); - var infos = _hoyoplayService.GetCachedGameInfos(); + var infos = _hoyoplayService.GetCachedGameInfoList(); if (LanguageUtil.FilterLanguage(CultureInfo.CurrentUICulture.Name) is "zh-cn") { @@ -510,6 +515,7 @@ private void InitializeGameServerArea() } } + // 分类每个游戏的服务器信息 foreach (var item in list) { string game = item.GameInfo.GameBiz.Game; @@ -518,18 +524,16 @@ private void InitializeGameServerArea() GameBiz biz = game + suffix; if (biz.IsKnown()) { - var server = new GameBizDisplayServer + var server = new GameBizIcon(biz) { - GameBizIcon = new GameBizIcon(biz), IsPinned = GameBizIcons.Any(x => x.GameBiz == biz), }; item.Servers.Add(server); } else if (_hoyoplayService.GetCachedGameInfo(biz) is GameInfo info) { - var server = new GameBizDisplayServer + var server = new GameBizIcon(info) { - GameBizIcon = new GameBizIcon(info), IsPinned = GameBizIcons.Any(x => x.GameBiz == biz), }; item.Servers.Add(server); @@ -543,6 +547,37 @@ private void InitializeGameServerArea() + /// + /// 更新所有游戏服务器信息,更新游戏图标 + /// + /// + private async Task UpdateGameInfoAsync() + { + try + { + _hoyoplayService.ClearCache(); + await _hoyoplayService.UpdateGameInfoListAsync(); + InitializeGameServerArea(); + foreach (GameBizIcon icon in GameBizIcons) + { + if (icon.GameBiz.IsKnown()) + { + icon.UpdateInfo(); + } + else + { + GameInfo info = await _hoyoplayService.GetGameInfoAsync(icon.GameId); + icon.UpdateInfo(info); + } + } + await InitializeInstalledGamesCommand.ExecuteAsync(null); + } + catch { } + } + + + + /// /// 游戏服务器选择区域是否可见 /// @@ -559,7 +594,7 @@ private void Grid_GameBizDisplay_Tapped(object sender, Microsoft.UI.Xaml.Input.T if (sender is FrameworkElement ele) { e.Handled = true; - _isGameBizDisplayPressed = true; + _isGameBizDisplayPressed = !_isGameBizDisplayPressed; FlyoutBase.GetAttachedFlyout(ele)?.ShowAt(ele, new FlyoutShowOptions { Placement = FlyoutPlacementMode.Bottom, @@ -587,6 +622,42 @@ private void Grid_GameBizDisplay_PointerEntered(object sender, Microsoft.UI.Xaml } + /// + /// 点击服务器图标 + /// + /// + /// + private void Button_GameServer_Click(object sender, RoutedEventArgs e) + { + try + { + if (sender is FrameworkElement fe && fe.DataContext is GameBizIcon server) + { + if (CurrentGameBizIcon is not null) + { + CurrentGameBizIcon.IsSelected = false; + } + + if (GameBizIcons.FirstOrDefault(x => x.GameId == server.GameId) is GameBizIcon icon) + { + CurrentGameBizIcon = icon; + CurrentGameBiz = icon.GameBiz; + CurrentGameId = icon.GameId; + icon.IsSelected = true; + } + else + { + CurrentGameBizIcon = server; + server.IsSelected = true; + } + + CurrentGameChanged?.Invoke(this, (server.GameId, false)); + AppSetting.CurrentGameBiz = server.GameBiz; + } + } + catch { } + } + /// /// 固定游戏服务器图标到待选区 @@ -595,9 +666,9 @@ private void Grid_GameBizDisplay_PointerEntered(object sender, Microsoft.UI.Xaml /// private void Button_PinGameBiz_Click(object sender, RoutedEventArgs e) { - if (sender is FrameworkElement fe && fe.DataContext is GameBizDisplayServer server) + if (sender is FrameworkElement fe && fe.DataContext is GameBizIcon server) { - var biz = server.GameBizIcon.GameBiz; + var biz = server.GameBiz; if (GameBizIcons.FirstOrDefault(x => x.GameBiz == biz) is GameBizIcon icon) { GameBizIcons.Remove(icon); @@ -605,7 +676,7 @@ private void Button_PinGameBiz_Click(object sender, RoutedEventArgs e) } else { - GameBizIcons.Add(server.GameBizIcon); + GameBizIcons.Add(server); server.IsPinned = true; } } @@ -620,17 +691,166 @@ private void Button_PinGameBiz_Click(object sender, RoutedEventArgs e) + #region Installed Games + + + /// + /// 已安装游戏的实际占用空间 + /// + public string? InstalledGamesActualSize { get; set => SetProperty(ref field, value); } + + + /// + /// 已安装游戏的总空间 + /// + public string? InstalledGamesSavedSize { get; set => SetProperty(ref field, value); } + + + + + public ObservableCollection InstalledGames { get; set; } = new(); + + - public void OnLanguageChanged(object? sender, LanguageChangedMessage message) + private CancellationTokenSource? _initializeInstalledGamesCancellationTokenSource; + + + /// + /// 初始化已安装游戏列表,计算已安装游戏的实际占用空间 + /// + /// + [RelayCommand] + private async Task InitializeInstalledGamesAsync() { - // todo 语言切换 - if (message.Completed) + const double GB = 1 << 30; + try { - this.Bindings.Update(); + _initializeInstalledGamesCancellationTokenSource?.Cancel(); + _initializeInstalledGamesCancellationTokenSource = new(); + CancellationToken token = _initializeInstalledGamesCancellationTokenSource.Token; + + InstalledGames.Clear(); + InstalledGamesActualSize = null; + InstalledGamesSavedSize = null; + List files = new(); + + foreach (GameBizDisplay display in GameBizDisplays) + { + List _duplicateFiles = new(); + int serverCount = 0; + foreach (GameBizIcon server in display.Servers) + { + string? installPath = _gameLauncherService.GetGameInstallPath(server.GameId); + if (Directory.Exists(installPath)) + { + server.InstallPath = installPath; + var _files = new DirectoryInfo(installPath).EnumerateFiles("*", SearchOption.AllDirectories).ToList(); + server.TotalSize = _files.Sum(x => x.Length); + InstalledGames.Add(server); + if (_files.Count() > 0) + { + serverCount++; + _duplicateFiles.AddRange(_files); + } + } + } + if (serverCount > 1) + { + files.AddRange(_duplicateFiles); + } + } + long totalSize = InstalledGames.Sum(x => x.TotalSize); + InstalledGamesActualSize = $"{totalSize / GB:F2}GB"; + + if (token.IsCancellationRequested) + { + return; + } + + if (files.Count > 0) + { + (long fileSize, long actualSize) = await Task.Run(() => + { + long size = 0; + Dictionary dic = new(); + foreach (var file in files) + { + size += file.Length; + using var handle = File.OpenHandle(file.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + var idInfo = Kernel32.GetFileInformationByHandleEx(handle, Kernel32.FILE_INFO_BY_HANDLE_CLASS.FileIdInfo); + var idInfoBytes = MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref idInfo, 1)); + dic[Convert.ToHexString(idInfoBytes)] = file.Length; + } + return (size, dic.Values.Sum()); + }, token); + + if (token.IsCancellationRequested) + { + return; + } + + InstalledGamesActualSize = $"{(totalSize - fileSize + actualSize) / GB:F2}GB"; + InstalledGamesSavedSize = $"{(fileSize - actualSize) / GB:F2}GB"; + } + } + catch (Exception ex) + { + Debug.WriteLine(ex); + } + } + + + + + + [RelayCommand] + public void AutoSearchInstalledGames() + { + try + { + // todo 从注册表自动搜索已安装的游戏 + //var service = AppService.GetService(); + //var sb = new StringBuilder(); + //foreach (GameBiz biz in GameBiz.AllGameBizs) + //{ + // if (service.IsGameExeExistsAsync(biz)) + // { + // sb.Append(biz.ToString()); + // sb.Append(','); + // } + //} + //AppSetting.SelectedGameBizs = sb.ToString().TrimEnd(','); + //InitializeGameSelector(); + } + catch (Exception ex) + { + Debug.WriteLine(ex); } } + + + /// + /// 防止点击已安装游戏列表时,触发 + /// + /// + /// + private void Expander_InstalledGamesActualSize_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e) + { + e.Handled = true; + } + + + + #endregion + + + + + + + } diff --git a/src/Starward/Features/HoYoPlay/HoYoPlayService.cs b/src/Starward/Features/HoYoPlay/HoYoPlayService.cs index c419948ac..6f5717ba7 100644 --- a/src/Starward/Features/HoYoPlay/HoYoPlayService.cs +++ b/src/Starward/Features/HoYoPlay/HoYoPlayService.cs @@ -10,6 +10,7 @@ using System.Linq; using System.Net.Http; using System.Text.Json; +using System.Threading; using System.Threading.Tasks; namespace Starward.Features.HoYoPlay; @@ -38,11 +39,12 @@ public HoYoPlayService(ILogger logger, HoYoPlayClient client, H Enabled = true, Interval = TimeSpan.FromMinutes(10).TotalMilliseconds, }; - _timer.Elapsed += async (_, _) => await PrepareDataAsync(); + _timer.Elapsed += (_, _) => ClearCache(); LoadCachedGameInfo(); } + private List _gameInfoList = new(); private ConcurrentDictionary _gameInfo = new(); @@ -60,6 +62,8 @@ public HoYoPlayService(ILogger logger, HoYoPlayClient client, H private ConcurrentDictionary _gameConfig = new(); + private ConcurrentDictionary _gameChannelSDK = new(); + private void LoadCachedGameInfo() { @@ -71,6 +75,7 @@ private void LoadCachedGameInfo() var infos = JsonSerializer.Deserialize>(json); if (infos is not null) { + _gameInfoList = infos; foreach (var item in infos) { _gameInfo[item] = item; @@ -82,229 +87,120 @@ private void LoadCachedGameInfo() } - private void CacheGameInfo() - { - try - { - var infos = _gameInfo.Values.ToList(); - string json = JsonSerializer.Serialize(infos); - AppSetting.CachedGameInfo = json; - } - catch { } - } - - public void ClearCache() { + _gameInfoList.Clear(); _gameInfo.Clear(); _gameBackground.Clear(); _gameContent.Clear(); _gamePackage.Clear(); + _gameChannelSDK.Clear(); } - - public async Task PrepareDataAsync() + public async Task GetGameInfoAsync(GameId gameId) { - try + if (!_gameInfo.TryGetValue(gameId, out GameInfo? info)) { - ClearCache(); string lang = CultureInfo.CurrentUICulture.Name; - List tasks = []; - tasks.Add(PrepareDataForServerAsync(LauncherId.ChinaOfficial, lang)); - tasks.Add(PrepareDataForServerAsync(LauncherId.GlobalOfficial, lang)); - tasks.Add(PrepareDataForBilibiliServerAsync(lang)); - await Task.WhenAll(tasks); - CacheGameInfo(); - await PrepareImagesAsync(); - } - catch (Exception ex) - { - _logger.LogError(ex, nameof(PrepareDataAsync)); + var list = await _client.GetGameInfoAsync(LauncherId.FromGameId(gameId)!, lang); + foreach (var item in list) + { + _gameInfo[item] = item; + } + info = list.First(x => x == gameId); } + return info; } - private async Task PrepareDataForServerAsync(string launcherId, string? language = null) + public GameInfo? GetCachedGameInfo(GameBiz biz) { - try - { - language ??= CultureInfo.CurrentUICulture.Name; - List infos = await _client.GetGameInfoAsync(launcherId, language); - foreach (GameInfo item in infos) - { - _gameInfo[item] = item; - } - List backgrounds = await _client.GetGameBackgroundAsync(launcherId, language); - foreach (GameBackgroundInfo item in backgrounds) - { - _gameBackground[item.GameId] = item; - } - foreach (var item in infos) - { - GameContent content = await _client.GetGameContentAsync(launcherId, language, item); - _gameContent[content.GameId] = content; - } - List packages = await _client.GetGamePackageAsync(launcherId, language); - foreach (GamePackage item in packages) - { - _gamePackage[item.GameId] = item; - } - List configs = await _client.GetGameConfigAsync(launcherId, language); - foreach (GameConfig item in configs) - { - _gameConfig[item.GameId] = item; - } - } - catch (Exception ex) - { - _logger.LogError(ex, nameof(PrepareDataForServerAsync)); - } + return _gameInfo.Values.FirstOrDefault(x => x.GameBiz == biz); } - private async Task PrepareDataForBilibiliServerAsync(string? language = null) + public List GetCachedGameInfoList() { - try + return _gameInfoList; + } + + + + public async Task> UpdateGameInfoListAsync(CancellationToken cancellationToken = default) + { + List infos = new List(); + string lang = CultureInfo.CurrentUICulture.Name; + if (LanguageUtil.FilterLanguage(lang) is "zh-cn") { - language ??= CultureInfo.CurrentUICulture.Name; - foreach ((GameBiz biz, string launcherId) in LauncherId.GetBilibiliLaunchers()) - { - List infos = await _client.GetGameInfoAsync(launcherId, language); - foreach (GameInfo item in infos) - { - _gameInfo[item] = item; - } - List backgrounds = await _client.GetGameBackgroundAsync(launcherId, language); - foreach (GameBackgroundInfo item in backgrounds) - { - _gameBackground[item.GameId] = item; - } - foreach (GameInfo item in infos) - { - GameContent content = await _client.GetGameContentAsync(launcherId, language, item); - _gameContent[item] = content; - } - List packages = await _client.GetGamePackageAsync(launcherId, language); - foreach (GamePackage item in packages) - { - _gamePackage[item.GameId] = item; - } - List configs = await _client.GetGameConfigAsync(launcherId, language); - foreach (GameConfig item in configs) - { - _gameConfig[item.GameId] = item; - } - } + infos.AddRange(await _client.GetGameInfoAsync(LauncherId.ChinaOfficial, lang, cancellationToken)); + infos.AddRange(await _client.GetGameInfoAsync(LauncherId.GlobalOfficial, lang, cancellationToken)); } - catch (Exception ex) + else + { + infos.AddRange(await _client.GetGameInfoAsync(LauncherId.GlobalOfficial, lang, cancellationToken)); + infos.AddRange(await _client.GetGameInfoAsync(LauncherId.ChinaOfficial, lang, cancellationToken)); + } + foreach ((GameBiz _, string launcherId) in LauncherId.GetBilibiliLaunchers()) + { + infos.AddRange(await _client.GetGameInfoAsync(launcherId, lang, cancellationToken)); + } + _gameInfoList = infos; + foreach (var item in infos) { - _logger.LogError(ex, nameof(PrepareDataForBilibiliServerAsync)); + _gameInfo[item] = item; } + string json = JsonSerializer.Serialize(infos); + AppSetting.CachedGameInfo = json; + _ = DownloadGameVersionPosterAsync(infos); + return infos; } - private async Task PrepareImagesAsync() + private async Task DownloadGameVersionPosterAsync(List infos) { try { - List bizs = GetSelectedGameBizs(); - List<(string Url, bool InBg)> urls = []; - foreach (GameBiz biz in bizs) + List urls = new(); + foreach (var info in infos) { - if (GameId.FromGameBiz(biz) is GameId gameId) + if (!string.IsNullOrWhiteSpace(info.Display.Background?.Url)) { - if (_gameInfo.TryGetValue(gameId, out GameInfo? info)) - { - urls.Add((info.Display.Background.Url, true)); - } - if (_gameBackground.TryGetValue(gameId, out GameBackgroundInfo? background)) - { - urls.AddRange(background.Backgrounds.Select(x => (x.Background.Url, false))); - } + urls.Add(info.Display.Background.Url); } } - string bg = Path.Combine(AppConfig.UserDataFolder, "bg"); - string cache = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Starward\cache"); - Directory.CreateDirectory(bg); - Directory.CreateDirectory(cache); - await Parallel.ForEachAsync(urls, async (item, _) => + if (AppSetting.UserDataFolder is not null) { - try + string bg = Path.Combine(AppSetting.UserDataFolder, "bg"); + Directory.CreateDirectory(bg); + await Parallel.ForEachAsync(urls, async (url, _) => { - if (string.IsNullOrWhiteSpace(item.Url)) + try { - return; + string name = Path.GetFileName(url); + string path = Path.Combine(bg, name); + if (!File.Exists(path)) + { + byte[] bytes = await _httpClient.GetByteArrayAsync(url); + await File.WriteAllBytesAsync(path, bytes); + } } - string name = Path.GetFileName(item.Url); - string path = item.InBg ? Path.Combine(bg, name) : Path.Combine(cache, name); - if (!File.Exists(path)) + catch (Exception ex) { - byte[] bytes = await _httpClient.GetByteArrayAsync(item.Url); - await File.WriteAllBytesAsync(path, bytes); + _logger.LogError(ex, "Download image: {url}", url); } - } - catch (Exception ex) - { - _logger.LogError(ex, "Download image: {url}", item); - } - }); - } - catch (Exception ex) - { - _logger.LogError(ex, nameof(PrepareImagesAsync)); - } - } - - - - private static List GetSelectedGameBizs() - { - List bizs = new(); - foreach (string str in AppConfig.SelectedGameBizs?.Split(',') ?? []) - { - if (GameBiz.TryParse(str, out GameBiz biz)) - { - bizs.Add(biz); + }); } } - return bizs; - } - - - - - public async Task GetGameInfoAsync(GameId gameId) - { - if (!_gameInfo.TryGetValue(gameId, out GameInfo? info)) + catch (Exception ex) { - string lang = CultureInfo.CurrentUICulture.Name; - var list = await _client.GetGameInfoAsync(LauncherId.FromGameId(gameId)!, lang); - foreach (var item in list) - { - _gameInfo[item] = item; - } - info = list.First(x => x == gameId); + _logger.LogError(ex, nameof(DownloadGameVersionPosterAsync)); } - return info; - } - - - - public GameInfo? GetCachedGameInfo(GameBiz gameBiz) - { - return _gameInfo.Values.FirstOrDefault(x => x.GameBiz == gameBiz); - } - - - public List GetCachedGameInfos() - { - return _gameInfo.Values.ToList(); } @@ -391,12 +287,17 @@ public async Task> GetGameDeprecatedFilesAsync(GameId g public async Task GetGameChannelSDKAsync(GameId gameId) { - var launcherId = LauncherId.FromGameId(gameId); - if (launcherId is not null) + if (!_gameChannelSDK.TryGetValue(gameId, out GameChannelSDK? sdk)) { - return await _client.GetGameChannelSDKAsync(launcherId, "en-us", gameId); + string lang = CultureInfo.CurrentUICulture.Name; + var list = await _client.GetGameChannelSDKAsync(LauncherId.FromGameId(gameId)!, lang); + foreach (var item in list) + { + _gameChannelSDK[item.GameId] = item; + } + sdk = list.FirstOrDefault(x => x.GameId == gameId); } - return null; + return sdk; }