diff --git a/CollapseLauncher/Assets/Images/ImageCropperOverlay/normal.png b/CollapseLauncher/Assets/Images/ImageCropperOverlay/normal.png index def007b66..83b418446 100644 Binary files a/CollapseLauncher/Assets/Images/ImageCropperOverlay/normal.png and b/CollapseLauncher/Assets/Images/ImageCropperOverlay/normal.png differ diff --git a/CollapseLauncher/Assets/Images/ImageCropperOverlay/small.png b/CollapseLauncher/Assets/Images/ImageCropperOverlay/small.png index 123af6170..75af22741 100644 Binary files a/CollapseLauncher/Assets/Images/ImageCropperOverlay/small.png and b/CollapseLauncher/Assets/Images/ImageCropperOverlay/small.png differ diff --git a/CollapseLauncher/Classes/AnimatedVisuals/Lottie/NewLogoTitleIntro.cs b/CollapseLauncher/Classes/AnimatedVisuals/Lottie/NewLogoTitleIntro.cs index 8375f311b..bd524472c 100644 --- a/CollapseLauncher/Classes/AnimatedVisuals/Lottie/NewLogoTitleIntro.cs +++ b/CollapseLauncher/Classes/AnimatedVisuals/Lottie/NewLogoTitleIntro.cs @@ -156,7 +156,7 @@ void EnsureImageLoadingStarted() if (!_isImageLoadingStarted) { var eventHandler = new TypedEventHandler(HandleLoadCompleted); - _image_image_0 = LoadedImageSurface.StartLoadFromStream(File.OpenRead(Path.Combine(LauncherConfig.AppFolder, @"Assets\CollapseLauncherLogo.png")).AsRandomAccessStream()); + _image_image_0 = LoadedImageSurface.StartLoadFromStream(File.OpenRead(Path.Combine(LauncherConfig.AppExecutableDir, @"Assets\CollapseLauncherLogo.png")).AsRandomAccessStream()); _image_image_0.LoadCompleted += eventHandler; _isImageLoadingStarted = true; } diff --git a/CollapseLauncher/Classes/CachesManagement/Honkai/Fetch.cs b/CollapseLauncher/Classes/CachesManagement/Honkai/Fetch.cs index 0e71d106d..efa9ff8b3 100644 --- a/CollapseLauncher/Classes/CachesManagement/Honkai/Fetch.cs +++ b/CollapseLauncher/Classes/CachesManagement/Honkai/Fetch.cs @@ -7,6 +7,7 @@ using Hi3Helper.Http; using Hi3Helper.UABT; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; @@ -17,6 +18,7 @@ using static Hi3Helper.Data.ConverterTool; using static Hi3Helper.Locale; using static Hi3Helper.Logger; +// ReSharper disable SwitchStatementHandlesSomeKnownEnumValuesWithDefault namespace CollapseLauncher { @@ -41,34 +43,39 @@ private async Task> Fetch(CancellationToken token) await BuildGameRepoURL(downloadClient, token); // Iterate type and do fetch - foreach (CacheAssetType type in Enum.GetValues()) - { - // Skip for unused type - switch (type) + await Parallel.ForEachAsync( + Enum.GetValues(), + new ParallelOptions { - case CacheAssetType.Unused: - case CacheAssetType.Dispatcher: - case CacheAssetType.Gateway: - case CacheAssetType.General: - case CacheAssetType.IFix: - case CacheAssetType.DesignData: - case CacheAssetType.Lua: - continue; - } - - // uint = Count of the assets available - // long = Total size of the assets available - (int, long) count = await FetchByType(type, downloadClient, returnAsset, token); - - // Write a log about the metadata - LogWriteLine($"Cache Metadata [T: {type}]:", LogType.Default, true); - LogWriteLine($" Cache Count = {count.Item1}", LogType.NoTag, true); - LogWriteLine($" Cache Size = {SummarizeSizeSimple(count.Item2)}", LogType.NoTag, true); - - // Increment the Total Size and Count - _progressAllCountTotal += count.Item1; - _progressAllSizeTotal += count.Item2; - } + MaxDegreeOfParallelism = _threadCount, + CancellationToken = token + }, + async (type, ctx) => + { + switch (type) + { + case CacheAssetType.Data: + case CacheAssetType.Event: + case CacheAssetType.AI: + { + // uint = Count of the assets available + // long = Total size of the assets available + (int, long) count = await FetchByType(type, downloadClient, returnAsset, ctx); + + // Write a log about the metadata + LogWriteLine($"Cache Metadata [T: {type}]:", LogType.Default, true); + LogWriteLine($" Cache Count = {count.Item1}", LogType.NoTag, true); + LogWriteLine($" Cache Size = {SummarizeSizeSimple(count.Item2)}", LogType.NoTag, true); + + // Increment the Total Size and Count + Interlocked.Add(ref _progressAllCountTotal, count.Item1); + Interlocked.Add(ref _progressAllSizeTotal, count.Item2); + } + break; + default: + return; + } + }); // Return asset index return returnAsset; @@ -230,7 +237,7 @@ private IEnumerable EnumerateCacheTextAsset(CacheAssetType type, IEn } } - private async ValueTask<(int, long)> BuildAssetIndex(CacheAssetType type, string baseURL, Stream stream, + private async ValueTask> BuildAssetIndex(CacheAssetType type, string baseURL, Stream stream, List assetIndex, CancellationToken token) { int count = 0; @@ -258,6 +265,9 @@ private IEnumerable EnumerateCacheTextAsset(CacheAssetType type, IEn .SetAllowedDecompression(DecompressionMethods.None) .Create(); + // Use ConcurrentQueue for adding assets in parallel. + ConcurrentQueue assetQueue = []; + // Iterate lines of the TextAsset in parallel await Parallel.ForEachAsync(EnumerateCacheTextAsset(type, dataTextAsset.GetStringList(), baseURL), new ParallelOptions @@ -283,9 +293,18 @@ await Parallel.ForEachAsync(EnumerateCacheTextAsset(type, dataTextAsset.GetStrin if (!urlStatus.IsSuccessStatusCode) return; } - assetIndex.Add(content); + // Append the content to the queue + assetQueue.Enqueue(content); + + // Increment the count and size + Interlocked.Increment(ref count); + Interlocked.Add(ref size, content.CS); }); + // Take out from the ConcurrentQueue + assetIndex.AddRange(assetQueue); + assetQueue.Clear(); + // Return the count and the size return (count, size); } diff --git a/CollapseLauncher/Classes/FileDialog/FileDialogHelper.cs b/CollapseLauncher/Classes/FileDialog/FileDialogHelper.cs index 25271349f..5b9383084 100644 --- a/CollapseLauncher/Classes/FileDialog/FileDialogHelper.cs +++ b/CollapseLauncher/Classes/FileDialog/FileDialogHelper.cs @@ -150,7 +150,7 @@ private static bool IsProgramFilesPath(ReadOnlySpan path) private static bool IsCollapseProgramPath(ReadOnlySpan path) { - string collapseProgramPath = LauncherConfig.AppFolder; + string collapseProgramPath = LauncherConfig.AppExecutableDir; if (path.StartsWith(collapseProgramPath)) return true; diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Honkai/RegistryClass/Preset.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Honkai/RegistryClass/Preset.cs index c98017cf0..a2d5e4663 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Honkai/RegistryClass/Preset.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Honkai/RegistryClass/Preset.cs @@ -68,7 +68,7 @@ public Preset(string presetJSONPath, JsonTypeInfo?> jsonT /// The instance of preset public static Preset LoadPreset(GameNameType gameType, JsonTypeInfo?> jsonType) { - string presetPath = Path.Combine(AppFolder, $"Assets\\Presets\\{gameType}\\", $"{typeof(T1).Name}.json"); + string presetPath = Path.Combine(AppExecutableDir, $"Assets\\Presets\\{gameType}\\", $"{typeof(T1).Name}.json"); return new Preset(presetPath, jsonType); } diff --git a/CollapseLauncher/Classes/Helper/HttpClientBuilder.cs b/CollapseLauncher/Classes/Helper/HttpClientBuilder.cs index b7c89aa5b..0c231434c 100644 --- a/CollapseLauncher/Classes/Helper/HttpClientBuilder.cs +++ b/CollapseLauncher/Classes/Helper/HttpClientBuilder.cs @@ -2,7 +2,6 @@ using Hi3Helper.Shared.Region; using System; using System.Collections.Generic; -using System.Diagnostics; using System.Net; using System.Net.Http; using System.Net.Security; @@ -15,8 +14,8 @@ public class HttpClientBuilder : HttpClientBuilder; public class HttpClientBuilder where THandler : HttpMessageHandler, new() { - private const int _maxConnectionsDefault = 32; - private const double _httpTimeoutDefault = 90; // in Seconds + private const int MaxConnectionsDefault = 32; + private const double HttpTimeoutDefault = 90; // in Seconds private bool IsUseProxy { get; set; } = true; private bool IsUseSystemProxy { get; set; } = true; @@ -24,14 +23,14 @@ public class HttpClientBuilder : HttpClientBuilder; private bool IsAllowHttpCookies { get; set; } private bool IsAllowUntrustedCert { get; set; } - private int MaxConnections { get; set; } = _maxConnectionsDefault; + private int MaxConnections { get; set; } = MaxConnectionsDefault; private DecompressionMethods DecompressionMethod { get; set; } = DecompressionMethods.All; private WebProxy? ExternalProxy { get; set; } private Version HttpProtocolVersion { get; set; } = HttpVersion.Version30; private string? HttpUserAgent { get; set; } = GetDefaultUserAgent(); private string? HttpAuthHeader { get; set; } private HttpVersionPolicy HttpProtocolVersionPolicy { get; set; } = HttpVersionPolicy.RequestVersionOrLower; - private TimeSpan HttpTimeout { get; set; } = TimeSpan.FromSeconds(_httpTimeoutDefault); + private TimeSpan HttpTimeout { get; set; } = TimeSpan.FromSeconds(HttpTimeoutDefault); private Uri? HttpBaseUri { get; set; } private Dictionary HttpHeaders { get; set; } = new(StringComparer.OrdinalIgnoreCase); @@ -45,12 +44,11 @@ public HttpClientBuilder UseProxy(bool isUseSystemProxy = true) private static string GetDefaultUserAgent() { Version operatingSystemVer = Environment.OSVersion.Version; - FileVersionInfo winAppSDKVer = FileVersionInfo.GetVersionInfo("Microsoft.ui.xaml.dll"); return $"Mozilla/5.0 (Windows NT {operatingSystemVer}; Win64; x64) " + $"{RuntimeInformation.FrameworkDescription.Replace(' ', '/')} (KHTML, like Gecko) " + $"Collapse/{LauncherUpdateHelper.LauncherCurrentVersionString}-{(LauncherConfig.IsPreview ? "Preview" : "Stable")} " - + $"WinAppSDK/{winAppSDKVer.ProductVersion}"; + + $"WinAppSDK/{LauncherConfig.WindowsAppSdkVersion}"; } public HttpClientBuilder UseExternalProxy(string host, string? username = null, string? password = null) @@ -81,7 +79,7 @@ public HttpClientBuilder UseExternalProxy(Uri hostUri, string? usernam return this; } - public HttpClientBuilder UseLauncherConfig(int maxConnections = _maxConnectionsDefault) + public HttpClientBuilder UseLauncherConfig(int maxConnections = MaxConnectionsDefault) { bool lIsUseProxy = LauncherConfig.GetAppConfigValue("IsUseProxy").ToBool(); bool lIsAllowHttpRedirections = LauncherConfig.GetAppConfigValue("IsAllowHttpRedirections").ToBool(); @@ -112,7 +110,7 @@ public HttpClientBuilder UseLauncherConfig(int maxConnections = _maxCo return this; } - public HttpClientBuilder SetMaxConnection(int maxConnections = _maxConnectionsDefault) + public HttpClientBuilder SetMaxConnection(int maxConnections = MaxConnectionsDefault) { if (maxConnections < 2) maxConnections = 2; @@ -160,18 +158,18 @@ public HttpClientBuilder SetHttpVersion(Version? version = null, HttpV return this; } - public HttpClientBuilder SetTimeout(double fromSeconds = _httpTimeoutDefault) + public HttpClientBuilder SetTimeout(double fromSeconds = HttpTimeoutDefault) { if (double.IsNaN(fromSeconds) || double.IsInfinity(fromSeconds)) - fromSeconds = _httpTimeoutDefault; + fromSeconds = HttpTimeoutDefault; return SetTimeout(TimeSpan.FromSeconds(fromSeconds)); } public HttpClientBuilder SetTimeout(TimeSpan? timeout = null) { - timeout ??= TimeSpan.FromSeconds(_httpTimeoutDefault); - HttpTimeout = timeout.Value; + timeout ??= TimeSpan.FromSeconds(HttpTimeoutDefault); + HttpTimeout = timeout.Value; return this; } diff --git a/CollapseLauncher/Classes/Helper/Image/ImageLoaderHelper.cs b/CollapseLauncher/Classes/Helper/Image/ImageLoaderHelper.cs index 2a8b6933c..2a4f8f881 100644 --- a/CollapseLauncher/Classes/Helper/Image/ImageLoaderHelper.cs +++ b/CollapseLauncher/Classes/Helper/Image/ImageLoaderHelper.cs @@ -89,8 +89,8 @@ private static Waifu2X CreateWaifu2X() { waifu2X.SetParam(Param.Noise, -1); waifu2X.SetParam(Param.Scale, 2); - waifu2X.Load(Path.Combine(AppFolder!, @"Assets\Waifu2X_Models\scale2.0x_model.param.bin"), - Path.Combine(AppFolder!, @"Assets\Waifu2X_Models\scale2.0x_model.bin")); + waifu2X.Load(Path.Combine(AppExecutableDir, @"Assets\Waifu2X_Models\scale2.0x_model.param.bin"), + Path.Combine(AppExecutableDir, @"Assets\Waifu2X_Models\scale2.0x_model.bin")); _cachedStatus = waifu2X.Status; } return waifu2X; @@ -179,7 +179,7 @@ private static async Task SpawnImageCropperDialog(string filePath, s imageCropper.Opacity = 0; // Path of image - Uri overlayImageUri = new Uri(Path.Combine(AppFolder!, @"Assets\Images\ImageCropperOverlay", + Uri overlayImageUri = new Uri(Path.Combine(AppExecutableDir, @"Assets\Images\ImageCropperOverlay", GetAppConfigValue("WindowSizeProfile").ToString() == "Small" ? "small.png" : "normal.png")); // Why not use ImageBrush? diff --git a/CollapseLauncher/Classes/Helper/Image/Waifu2X.cs b/CollapseLauncher/Classes/Helper/Image/Waifu2X.cs index bef353f1c..3c07ae361 100644 --- a/CollapseLauncher/Classes/Helper/Image/Waifu2X.cs +++ b/CollapseLauncher/Classes/Helper/Image/Waifu2X.cs @@ -28,7 +28,7 @@ static Waifu2XPInvoke() private static IntPtr DllImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) { - appDirPath ??= LauncherConfig.AppFolder; + appDirPath ??= LauncherConfig.AppExecutableDir; if (DllImportSearchPath.AssemblyDirectory != searchPath && DllImportSearchPath.ApplicationDirectory != searchPath) diff --git a/CollapseLauncher/Classes/Helper/TaskSchedulerHelper.cs b/CollapseLauncher/Classes/Helper/TaskSchedulerHelper.cs index 08976a942..a42c1c5f9 100644 --- a/CollapseLauncher/Classes/Helper/TaskSchedulerHelper.cs +++ b/CollapseLauncher/Classes/Helper/TaskSchedulerHelper.cs @@ -233,7 +233,7 @@ private static async Task GetInvokeCommandReturnCode(string argument) const string retValMark = "RETURNVAL_"; // Get the applet path and check if the file exist - string appletPath = Path.Combine(LauncherConfig.AppFolder, "Lib", "win-x64", "Hi3Helper.TaskScheduler.exe"); + string appletPath = Path.Combine(LauncherConfig.AppExecutableDir, "Lib", "win-x64", "Hi3Helper.TaskScheduler.exe"); if (!File.Exists(appletPath)) { Logger.LogWriteLine($"Task Scheduler Applet does not exist in this path: {appletPath}", LogType.Error, true); diff --git a/CollapseLauncher/Classes/Helper/Update/LauncherUpdateHelper.cs b/CollapseLauncher/Classes/Helper/Update/LauncherUpdateHelper.cs index 84a77d15d..00d8f11c9 100644 --- a/CollapseLauncher/Classes/Helper/Update/LauncherUpdateHelper.cs +++ b/CollapseLauncher/Classes/Helper/Update/LauncherUpdateHelper.cs @@ -5,6 +5,8 @@ using Hi3Helper.Shared.Region; using System; using System.Threading.Tasks; +using System.Diagnostics.CodeAnalysis; + #if !USEVELOPACK using Squirrel; using Squirrel.Sources; @@ -20,26 +22,13 @@ namespace CollapseLauncher.Helper.Update { internal static class LauncherUpdateHelper { - static LauncherUpdateHelper() - { - string? versionString = LauncherConfig.AppCurrentVersionString; - if (string.IsNullOrEmpty(versionString)) - throw new NullReferenceException("App cannot retrieve the current version of the executable!"); - - _launcherCurrentVersion = new GameVersion(versionString); - _launcherCurrentVersionString = _launcherCurrentVersion.VersionString; - } - internal static AppUpdateVersionProp? AppUpdateVersionProp; internal static bool IsLauncherUpdateAvailable; - private static readonly GameVersion _launcherCurrentVersion; - internal static GameVersion? LauncherCurrentVersion - => _launcherCurrentVersion; + internal static GameVersion? LauncherCurrentVersion => field ??= new(LauncherConfig.AppCurrentVersionString); - private static readonly string _launcherCurrentVersionString; - internal static string LauncherCurrentVersionString - => _launcherCurrentVersionString; + [field: AllowNull, MaybeNull] + internal static string LauncherCurrentVersionString => field = LauncherConfig.AppCurrentVersionString; internal static async Task RunUpdateCheckDetached() { diff --git a/CollapseLauncher/Classes/Helper/WindowUtility.cs b/CollapseLauncher/Classes/Helper/WindowUtility.cs index 7952e3029..d7ae4be68 100644 --- a/CollapseLauncher/Classes/Helper/WindowUtility.cs +++ b/CollapseLauncher/Classes/Helper/WindowUtility.cs @@ -10,6 +10,8 @@ using Hi3Helper.Win32.Native.ManagedTools; using Hi3Helper.Win32.Native.Structs; using Hi3Helper.Win32.Screen; +using Hi3Helper.Win32.TaskbarListCOM; +using Hi3Helper.Win32.ToastCOM; using Hi3Helper.Win32.ToastCOM.Notification; using Microsoft.Graphics.Display; using Microsoft.UI; @@ -21,7 +23,6 @@ using Microsoft.UI.Xaml.Media; using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using Windows.Foundation; using Windows.Graphics; @@ -55,6 +56,8 @@ internal static class WindowUtility internal static WindowId? CurrentWindowId; internal static OverlappedPresenter? CurrentOverlappedPresenter; + private static readonly TaskbarList Taskbar = new(); + internal static DisplayArea? CurrentWindowDisplayArea { get @@ -285,44 +288,78 @@ internal static IconShowOptions CurrentWindowTitlebarIconShowOption } } - [field: AllowNull, MaybeNull] - internal static NotificationService CurrentToastNotificationService + internal static Guid? CurrentAumidInGuid + { + get => field ??= Extensions.GetGuidFromString(CurrentAumid ?? ""); + set; + } + + internal static string? CurrentAumid { get { - // If toast notification service field is null, then initialize if (field == null) { - // Get Icon location paths - (string iconLocationStartMenu, _) - = TaskSchedulerHelper.GetIconLocationPaths( - out string? appAumIdNameAlternative, - out _, - out string? executablePath, - out _); - - // Register notification service - field = new NotificationService(ILoggerHelper.GetILogger("ToastCOM")); + PInvoke.GetProcessAumid(out field); + } - // Get AUMID name from Win32 - PInvoke.GetProcessAumid(out string? appAumIdName); + return field; + } + set + { + if (value == null) + { + return; + } - // If it's empty due to an error, set the alternative AUMID - appAumIdName ??= appAumIdNameAlternative; + field = value; + PInvoke.SetProcessAumid(value); + CurrentAumidInGuid = Extensions.GetGuidFromString(value); + } + } - // Get string for AumId registration - if (!string.IsNullOrEmpty(appAumIdName)) + internal static NotificationService? CurrentToastNotificationService + { + get + { + // If toast notification service field is null, then initialize + if (field == null) + { + try + { + // Get Icon location paths + (string iconLocationStartMenu, _) + = TaskSchedulerHelper.GetIconLocationPaths( + out string? appAumIdNameAlternative, + out _, + out string? executablePath, + out _); + + // Register notification service + field = new NotificationService(ILoggerHelper.GetILogger("ToastCOM")); + + // Get AumId to use + string? currentAumId = CurrentAumid ??= appAumIdNameAlternative; + + // Get string for AumId registration + if (!string.IsNullOrEmpty(currentAumId)) + { + // Initialize Toast Notification service + CurrentAumidInGuid = field.Initialize( + currentAumId, + executablePath ?? "", + iconLocationStartMenu, + asElevatedUser: true + ); + + // Subscribe ToastCallback + field.ToastCallback += Service_ToastNotificationCallback; + } + } + catch (Exception ex) { - // Initialize Toast Notification service - field.Initialize( - appAumIdName, - executablePath ?? "", - iconLocationStartMenu, - asElevatedUser: true - ); - - // Subscribe ToastCallback - field.ToastCallback += Service_ToastNotificationCallback; + Logger.LogWriteLine($"Notification service initialization has failed, ignoring!\r\n{ex}", LogType.Error, true); + return null; } } @@ -493,15 +530,19 @@ private static IntPtr MainWndProc(IntPtr hwnd, uint msg, UIntPtr wParam, IntPtr const int HTCLIENT = 1; const int HTCAPTION = 2; const int HTMINBUTTON = 8; + const int HTMAXBUTTON = 9; const int HTRIGHT = 11; const int HTTOP = 12; const int HTTOPRIGHT = 14; + const int HTCLOSE = 20; var result = PInvoke.CallWindowProc(OldMainWndProcPtr, hwnd, msg, wParam, lParam); return result switch { - // Fix "Ghost Minimize Button" issue + // Hide all system buttons HTMINBUTTON => HTCLIENT, + HTMAXBUTTON => HTCLIENT, + HTCLOSE => HTCLIENT, // Fix "Caption Resize" issue HTRIGHT => HTCAPTION, HTTOP => HTCAPTION, @@ -833,5 +874,17 @@ private static void Service_ToastNotificationCallback(string app, string arg, Di // TODO: Make the callback actually usable on elevated app } #endregion + + #region Taskbar Methods + public static int SetTaskBarState(nint hwnd, TaskbarState state) + { + return Taskbar.SetProgressState(hwnd, state); + } + + public static int SetProgressValue(nint hwnd, ulong completed, ulong total) + { + return Taskbar.SetProgressValue(hwnd, completed, total); + } + #endregion } } diff --git a/CollapseLauncher/Classes/InstallManagement/BaseClass/InstallManagerBase.Sophon.cs b/CollapseLauncher/Classes/InstallManagement/BaseClass/InstallManagerBase.Sophon.cs index d96f54fd2..40c4ab65f 100644 --- a/CollapseLauncher/Classes/InstallManagement/BaseClass/InstallManagerBase.Sophon.cs +++ b/CollapseLauncher/Classes/InstallManagement/BaseClass/InstallManagerBase.Sophon.cs @@ -147,8 +147,11 @@ public virtual async Task StartPackageInstallSophon(GameInstallStateEnum gameSta // Clear the VO language list _sophonVOLanguageList.Clear(); - // Subscribe the logger event - SophonLogger.LogHandler += UpdateSophonLogHandler; + // Subscribe the logger event if fallback is not used + if (!fallbackFromUpdate) + { + SophonLogger.LogHandler += UpdateSophonLogHandler; + } // Get the requested URL and version based on current state. if (_gameVersionManager.GamePreset @@ -250,7 +253,7 @@ await _gameVersionManager.GamePreset voLang #if DEBUG , true - #endif +#endif ); if (string.IsNullOrEmpty(voLocaleId)) @@ -300,9 +303,15 @@ await SimpleDialogs.Dialog_ChooseAudioLanguageChoice( _gameVersionManager.GamePreset.SetVoiceLanguageID(setAsDefaultVo); // Get the remote total size and current total size - _progressAllCountTotal = sophonInfoPairList.Sum(x => x.ChunksInfo.FilesCount); - _progressAllSizeTotal = sophonInfoPairList.Sum(x => x.ChunksInfo.TotalSize); - _progressAllSizeCurrent = 0; + _progressAllCountTotal = sophonInfoPairList.Sum(x => x.ChunksInfo.FilesCount); + _progressAllSizeTotal = sophonInfoPairList.Sum(x => x.ChunksInfo.TotalSize); + _progressAllSizeCurrent = 0; + + // If the fallback is used from update, use the same display as All Size for Per File progress. + if (fallbackFromUpdate) + { + _progressPerFileSizeTotal = _progressAllSizeTotal; + } // Set the display to Install Mode UpdateStatus(); @@ -419,6 +428,7 @@ await Task.Run(() => // Get the original file path, ensure the existing file is not read only, // then move the temp file to the original file path var origFilePath = new FileInfo(assetFullPath) + .EnsureCreationOfDirectory() .EnsureNoReadOnly(); // Move the thing @@ -431,8 +441,11 @@ await Task.Run(() => } finally { - // Unsubscribe the logger event - SophonLogger.LogHandler -= UpdateSophonLogHandler; + // Unsubscribe the logger event if fallback is not used + if (!fallbackFromUpdate) + { + SophonLogger.LogHandler -= UpdateSophonLogHandler; + } httpClient.Dispose(); // Unsubscribe download limiter diff --git a/CollapseLauncher/Classes/Properties/InnerLauncherConfig.cs b/CollapseLauncher/Classes/Properties/InnerLauncherConfig.cs index d7aead8c2..81c0a6b2b 100644 --- a/CollapseLauncher/Classes/Properties/InnerLauncherConfig.cs +++ b/CollapseLauncher/Classes/Properties/InnerLauncherConfig.cs @@ -46,8 +46,6 @@ public enum AppMode public static AppMode m_appMode; public static Arguments m_arguments = new(); public static bool m_isWindows11; - public static IntPtr m_oldWndProc; - public static Delegate? m_newWndProcDelegate; public static ConsoleControlHandler? m_consoleCtrlHandler; public static MainPage? m_mainPage; public static HomePage? m_homePage; diff --git a/CollapseLauncher/Classes/RegionManagement/RegionManagement.cs b/CollapseLauncher/Classes/RegionManagement/RegionManagement.cs index e6b67f9f0..365e16d65 100644 --- a/CollapseLauncher/Classes/RegionManagement/RegionManagement.cs +++ b/CollapseLauncher/Classes/RegionManagement/RegionManagement.cs @@ -202,10 +202,10 @@ private async Task DownloadBackgroundImage(CancellationToken Token) var currentGameType = GamePropertyVault.GetCurrentGameProperty()._GameVersion.GameType; tempImage ??= currentGameType switch { - GameNameType.Honkai => Path.Combine(AppFolder, @"Assets\Images\GameBackground\honkai.webp"), - GameNameType.Genshin => Path.Combine(AppFolder, @"Assets\Images\GameBackground\genshin.webp"), - GameNameType.StarRail => Path.Combine(AppFolder, @"Assets\Images\GameBackground\starrail.webp"), - GameNameType.Zenless => Path.Combine(AppFolder, @"Assets\Images\GameBackground\zzz.webp"), + GameNameType.Honkai => Path.Combine(AppExecutableDir, @"Assets\Images\GameBackground\honkai.webp"), + GameNameType.Genshin => Path.Combine(AppExecutableDir, @"Assets\Images\GameBackground\genshin.webp"), + GameNameType.StarRail => Path.Combine(AppExecutableDir, @"Assets\Images\GameBackground\starrail.webp"), + GameNameType.Zenless => Path.Combine(AppExecutableDir, @"Assets\Images\GameBackground\zzz.webp"), _ => AppDefaultBG }; BackgroundImgChanger.ChangeBackground(tempImage, () => diff --git a/CollapseLauncher/Classes/RepairManagement/Genshin/Repair.cs b/CollapseLauncher/Classes/RepairManagement/Genshin/Repair.cs index a83dff831..a6c4b8025 100644 --- a/CollapseLauncher/Classes/RepairManagement/Genshin/Repair.cs +++ b/CollapseLauncher/Classes/RepairManagement/Genshin/Repair.cs @@ -96,9 +96,10 @@ private async Task RepairAssetTypeGeneric((PkgVersionProperties AssetIndex, IAss // Increment total count current _progressAllCountCurrent++; // Set repair activity status + string timeLeftString = string.Format(Lang!._Misc!.TimeRemainHMSFormat!, _progress.ProgressAllTimeLeft); UpdateRepairStatus( string.Format(Lang._GameRepairPage.Status8, asset.AssetIndex.remoteName), - string.Format(Lang._GameRepairPage.PerProgressSubtitle2, _progressAllCountCurrent, _progressAllCountTotal), + string.Format(Lang._GameRepairPage.PerProgressSubtitle2, ConverterTool.SummarizeSizeSimple(_progressAllSizeCurrent), ConverterTool.SummarizeSizeSimple(_progressAllSizeTotal)) + $" | {timeLeftString}", true); string assetPath = ConverterTool.NormalizePath(asset.AssetIndex.localName); diff --git a/CollapseLauncher/Classes/RepairManagement/Honkai/Fetch.cs b/CollapseLauncher/Classes/RepairManagement/Honkai/Fetch.cs index 02e9a0974..f3f232b07 100644 --- a/CollapseLauncher/Classes/RepairManagement/Honkai/Fetch.cs +++ b/CollapseLauncher/Classes/RepairManagement/Honkai/Fetch.cs @@ -14,6 +14,7 @@ using Microsoft.Win32; using System; using System.Buffers.Binary; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Data; using System.IO; @@ -319,9 +320,18 @@ private async Task BuildAndEnumerateVideoVersioningFile(CancellationToken token, // Get the base URL string baseURL = CombineURLFromString((GetAppConfigValue("EnableHTTPRepairOverride").ToBool() ? "http://" : "https://") + assetBundleURL, "/Video/"); + // Get FileInfo of the version.txt file + FileInfo fileInfo = new FileInfo(Path.Combine(_gamePath!, NormalizePath(_videoBaseLocalPath)!, "Version.txt")) + .EnsureCreationOfDirectory() + .EnsureNoReadOnly(); + // Build video versioning file - await using StreamWriter sw = new StreamWriter(Path.Combine(_gamePath!, NormalizePath(_videoBaseLocalPath)!, "Version.txt"), false); - // Iterate the metadata to be converted into asset index in parallel + await using TextWriter sw = fileInfo.CreateText(); + + // Initialize concurrent queue for video metadata + ConcurrentDictionary videoMetadataQueue = new ConcurrentDictionary(); + + // Iterate the metadata to be converted into asset index await Parallel.ForEachAsync(enumEntry!, new ParallelOptions { CancellationToken = token, @@ -333,14 +343,6 @@ private async Task BuildAndEnumerateVideoVersioningFile(CancellationToken token, // Starting from 7.1, the CGs that have included in ignoredAssetIDs (which is marked as deleted) will be ignored. bool isCGAvailable = await IsCGFileAvailable(metadata, baseURL, tokenInternal); bool isCGIgnored = ignoredAssetIDs.IgnoredVideoCGSubCategory.Contains(metadata!.CgSubCategory); - if (!metadata.InStreamingAssets) - { - lock (sw) - { - // Append the versioning list - sw.WriteLine("Video/" + (_audioLanguage == AudioLanguageType.Japanese ? metadata.CgPathHighBitrateJP : metadata.CgPathHighBitrateCN) + ".usm\t1"); - } - } #if DEBUG if (isCGIgnored) @@ -350,18 +352,26 @@ private async Task BuildAndEnumerateVideoVersioningFile(CancellationToken token, if (!metadata.InStreamingAssets && isCGAvailable && !isCGIgnored) { string name = (_audioLanguage == AudioLanguageType.Japanese ? metadata.CgPathHighBitrateJP : metadata.CgPathHighBitrateCN) + ".usm"; - lock (assetIndex) - { - assetIndex.Add(new FilePropertiesRemote - { - N = CombineURLFromString(_videoBaseLocalPath, name), - RN = CombineURLFromString(baseURL, name), - S = _audioLanguage == AudioLanguageType.Japanese ? metadata.FileSizeHighBitrateJP : metadata.FileSizeHighBitrateCN, - FT = FileType.Video - }); - } + _ = videoMetadataQueue.TryAdd(name, metadata); } }); + + foreach (KeyValuePair metadata in videoMetadataQueue) + { + assetIndex.Add(new FilePropertiesRemote + { + N = CombineURLFromString(_videoBaseLocalPath, metadata.Key), + RN = CombineURLFromString(baseURL, metadata.Key), + S = _audioLanguage == AudioLanguageType.Japanese ? metadata.Value.FileSizeHighBitrateJP : metadata.Value.FileSizeHighBitrateCN, + FT = FileType.Video + }); + + if (!metadata.Value.InStreamingAssets) + { + // Append the versioning list + await sw.WriteLineAsync("Video/" + (_audioLanguage == AudioLanguageType.Japanese ? metadata.Value.CgPathHighBitrateJP : metadata.Value.CgPathHighBitrateCN) + ".usm\t1"); + } + } } private async ValueTask IsCGFileAvailable(CGMetadata cgInfo, string baseURL, CancellationToken token) diff --git a/CollapseLauncher/Classes/RepairManagement/Honkai/Repair.cs b/CollapseLauncher/Classes/RepairManagement/Honkai/Repair.cs index 58016d1a6..278502822 100644 --- a/CollapseLauncher/Classes/RepairManagement/Honkai/Repair.cs +++ b/CollapseLauncher/Classes/RepairManagement/Honkai/Repair.cs @@ -141,9 +141,10 @@ private async Task RepairTypeAudioActionPatching((FilePropertiesRemote AssetInde string outputFilePath = inputFilePath + "_tmp"; // Set downloading patch status + string timeLeftString = string.Format(Lang!._Misc!.TimeRemainHMSFormat!, _progress.ProgressAllTimeLeft); UpdateRepairStatus( string.Format(Lang._GameRepairPage.Status12, asset.AssetIndex.N), - string.Format(Lang._GameRepairPage.PerProgressSubtitle4, ConverterTool.SummarizeSizeSimple(_progressAllSizeCurrent), ConverterTool.SummarizeSizeSimple(_progressAllSizeTotal)), + string.Format(Lang._GameRepairPage.PerProgressSubtitle4, ConverterTool.SummarizeSizeSimple(_progressAllSizeCurrent), ConverterTool.SummarizeSizeSimple(_progressAllSizeTotal)) + $" | {timeLeftString}", true); // Run patching task @@ -164,9 +165,10 @@ private async Task RepairAssetTypeGeneric((FilePropertiesRemote AssetIndex, IAss // Increment total count current _progressAllCountCurrent++; // Set repair activity status + string timeLeftString = string.Format(Lang!._Misc!.TimeRemainHMSFormat!, _progress.ProgressAllTimeLeft); UpdateRepairStatus( string.Format(asset.AssetIndex.FT == FileType.Block ? Lang._GameRepairPage.Status9 : Lang._GameRepairPage.Status8, asset.AssetIndex.FT == FileType.Block ? asset.AssetIndex.CRC : asset.AssetIndex.N), - string.Format(Lang._GameRepairPage.PerProgressSubtitle2, ConverterTool.SummarizeSizeSimple(_progressAllSizeCurrent), ConverterTool.SummarizeSizeSimple(_progressAllSizeTotal)), + string.Format(Lang._GameRepairPage.PerProgressSubtitle2, ConverterTool.SummarizeSizeSimple(_progressAllSizeCurrent), ConverterTool.SummarizeSizeSimple(_progressAllSizeTotal)) + $" | {timeLeftString}", true); // Set URL of the asset @@ -239,9 +241,10 @@ private async Task RepairTypeBlocksActionPatching((FilePropertiesRemote AssetInd string outputFilePath = Path.Combine(_gamePath, ConverterTool.NormalizePath(asset.AssetIndex.N)); // Set downloading patch status + string timeLeftString = string.Format(Lang!._Misc!.TimeRemainHMSFormat!, _progress.ProgressAllTimeLeft); UpdateRepairStatus( string.Format(Lang._GameRepairPage.Status13, asset.AssetIndex.CRC), - string.Format(Lang._GameRepairPage.PerProgressSubtitle4, ConverterTool.SummarizeSizeSimple(_progressAllSizeCurrent), ConverterTool.SummarizeSizeSimple(_progressAllSizeTotal)), + string.Format(Lang._GameRepairPage.PerProgressSubtitle4, ConverterTool.SummarizeSizeSimple(_progressAllSizeCurrent), ConverterTool.SummarizeSizeSimple(_progressAllSizeTotal)) + $" | {timeLeftString}", true); // Run patching task diff --git a/CollapseLauncher/Classes/RepairManagement/StarRail/Repair.cs b/CollapseLauncher/Classes/RepairManagement/StarRail/Repair.cs index 6d68613ea..fc88e99c0 100644 --- a/CollapseLauncher/Classes/RepairManagement/StarRail/Repair.cs +++ b/CollapseLauncher/Classes/RepairManagement/StarRail/Repair.cs @@ -113,9 +113,10 @@ private async Task RepairAssetTypeGeneric((FilePropertiesRemote AssetIndex, IAss // Increment total count current _progressAllCountCurrent++; // Set repair activity status + string timeLeftString = string.Format(Lang!._Misc!.TimeRemainHMSFormat!, _progress.ProgressAllTimeLeft); UpdateRepairStatus( string.Format(Lang._GameRepairPage.Status8, Path.GetFileName(asset.AssetIndex.N)), - string.Format(Lang._GameRepairPage.PerProgressSubtitle2, ConverterTool.SummarizeSizeSimple(_progressAllSizeCurrent), ConverterTool.SummarizeSizeSimple(_progressAllSizeTotal)), + string.Format(Lang._GameRepairPage.PerProgressSubtitle2, ConverterTool.SummarizeSizeSimple(_progressAllSizeCurrent), ConverterTool.SummarizeSizeSimple(_progressAllSizeTotal)) + $" | {timeLeftString}", true); FileInfo fileInfo = new FileInfo(asset.AssetIndex.N!).EnsureNoReadOnly(); diff --git a/CollapseLauncher/Classes/RepairManagement/Zenless/ZenlessRepair.Repair.cs b/CollapseLauncher/Classes/RepairManagement/Zenless/ZenlessRepair.Repair.cs index d37e0530f..28fa33b1c 100644 --- a/CollapseLauncher/Classes/RepairManagement/Zenless/ZenlessRepair.Repair.cs +++ b/CollapseLauncher/Classes/RepairManagement/Zenless/ZenlessRepair.Repair.cs @@ -105,9 +105,10 @@ private async Task RepairAssetTypeGeneric((FilePropertiesRemote AssetIndex, IAss // Increment total count current _progressAllCountCurrent++; // Set repair activity status + string timeLeftString = string.Format(Locale.Lang!._Misc!.TimeRemainHMSFormat!, _progress.ProgressAllTimeLeft); UpdateRepairStatus( string.Format(Locale.Lang._GameRepairPage.Status8, Path.GetFileName(asset.AssetIndex.N)), - string.Format(Locale.Lang._GameRepairPage.PerProgressSubtitle2, ConverterTool.SummarizeSizeSimple(_progressAllSizeCurrent), ConverterTool.SummarizeSizeSimple(_progressAllSizeTotal)), + string.Format(Locale.Lang._GameRepairPage.PerProgressSubtitle2, ConverterTool.SummarizeSizeSimple(_progressAllSizeCurrent), ConverterTool.SummarizeSizeSimple(_progressAllSizeTotal)) + $" | {timeLeftString}", true); FileInfo fileInfo = new FileInfo(asset.AssetIndex.N!).EnsureNoReadOnly(); diff --git a/CollapseLauncher/CollapseLauncher.csproj b/CollapseLauncher/CollapseLauncher.csproj index 28c3e7c5c..4cf96cd77 100644 --- a/CollapseLauncher/CollapseLauncher.csproj +++ b/CollapseLauncher/CollapseLauncher.csproj @@ -16,7 +16,7 @@ $(Company). neon-nyan, Cry0, bagusnl, shatyuka, gablm. Copyright 2022-2024 $(Company) - 1.82.12 + 1.82.13 preview x64 diff --git a/CollapseLauncher/Program.cs b/CollapseLauncher/Program.cs index 0e8901338..d91f39fc6 100644 --- a/CollapseLauncher/Program.cs +++ b/CollapseLauncher/Program.cs @@ -8,7 +8,6 @@ using Hi3Helper.Shared.ClassStruct; using Hi3Helper.Win32.Native.LibraryImport; using Hi3Helper.Win32.Native.ManagedTools; -using Hi3Helper.Win32.Screen; using Hi3Helper.Win32.ShellLinkCOM; using InnoSetupHelper; using Microsoft.UI.Dispatching; @@ -46,22 +45,19 @@ public static class MainEntryPoint #nullable enable public static int InstanceCount; public static App? CurrentAppInstance; - public static string[]? LastArgs; #nullable restore [STAThread] public static void Main(params string[] args) { - LastArgs = args; + AppCurrentArgument = args.ToList(); #if PREVIEW IsPreview = true; #endif try { - AppCurrentArgument = args; - // Extract icons from the executable file - string mainModulePath = Process.GetCurrentProcess().MainModule?.FileName ?? ""; + string mainModulePath = AppExecutablePath; var iconCount = PInvoke.ExtractIconEx(mainModulePath, -1, null, null, 0); if (iconCount > 0) { @@ -79,20 +75,20 @@ public static void Main(params string[] args) StartUpdaterHook(aumid); // Set AUMID - PInvoke.SetProcessAumid(aumid).ThrowOnFailure(); + WindowUtility.CurrentAumid = aumid; InitAppPreset(); var logPath = AppGameLogsFolder; - _log = IsConsoleEnabled + CurrentLogger = IsConsoleEnabled ? new LoggerConsole(logPath, Encoding.UTF8) : new LoggerNull(logPath, Encoding.UTF8); - if (Directory.GetCurrentDirectory() != AppFolder) + if (Directory.GetCurrentDirectory() != AppExecutableDir) { LogWriteLine( - $"Force changing the working directory from {Directory.GetCurrentDirectory()} to {AppFolder}!", + $"Force changing the working directory from {Directory.GetCurrentDirectory()} to {AppExecutableDir}!", LogType.Warning, true); - Directory.SetCurrentDirectory(AppFolder); + Directory.SetCurrentDirectory(AppExecutableDir); } InitializeAppSettings(); @@ -130,10 +126,9 @@ public static void Main(params string[] args) #endif IsPreview ? "Preview" : "Stable"), LogType.Scheme, true); - var winAppSDKVer = FileVersionInfo.GetVersionInfo("Microsoft.ui.xaml.dll"); #pragma warning disable CS0618 // Type or member is obsolete LogWriteLine( - $"Runtime: {RuntimeInformation.FrameworkDescription} - WindowsAppSDK {winAppSDKVer.ProductVersion}", + $"Runtime: {RuntimeInformation.FrameworkDescription} - WindowsAppSDK {WindowsAppSdkVersion}", LogType.Scheme, true); LogWriteLine($"Built from repo {ThisAssembly.Git.RepositoryUrl}\r\n\t" + $"Branch {ThisAssembly.Git.Branch} - Commit {ThisAssembly.Git.Commit} at {ThisAssembly.Git.CommitDate}", @@ -256,23 +251,24 @@ public static void SpawnFatalErrorConsole(Exception ex) "Press any key to exit or Press 'R' to restart the main thread app..."); #if !DEBUG - if (ConsoleKey.R == Console.ReadKey().Key) - { - ProcessStartInfo startInfo = new ProcessStartInfo - { - FileName = Process.GetCurrentProcess().MainModule?.FileName, - UseShellExecute = false - }; - foreach (string arg in LastArgs) + if (ConsoleKey.R == Console.ReadKey().Key) { - startInfo.ArgumentList.Add(arg); + ProcessStartInfo startInfo = new ProcessStartInfo + { + FileName = AppExecutablePath, + UseShellExecute = false + }; + + foreach (string arg in AppCurrentArgument) + { + startInfo.ArgumentList.Add(arg); + } + Process process = new Process() + { + StartInfo = startInfo + }; + process.Start(); } - Process process = new Process() - { - StartInfo = startInfo - }; - process.Start(); - } #endif } @@ -372,7 +368,7 @@ void DeleteVelopackLock() #if USEVELOPACK public static void TryCleanupFallbackUpdate(SemanticVersion newVersion) { - string currentExecutedAppFolder = AppFolder.TrimEnd('\\'); + string currentExecutedAppFolder = AppExecutableDir.TrimEnd('\\'); string currentExecutedPath = AppExecutablePath; // If the path is not actually running under "current" velopack folder, then return @@ -591,7 +587,7 @@ private static void GenerateVelopackMetadata(string aumid) "; string currentVersion = LauncherUpdateHelper.LauncherCurrentVersionString; - string xmlPath = Path.Combine(AppFolder, "sq.version"); + string xmlPath = Path.Combine(AppExecutableDir, "sq.version"); string xmlContent = string.Format(XmlTemplate, currentVersion, IsPreview ? "preview" : "stable", aumid); File.WriteAllText(xmlPath, xmlContent.ReplaceLineEndings("\n")); LogWriteLine($"Velopack metadata has been successfully written!\r\n{xmlContent}", LogType.Default, true); diff --git a/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs index 7ed00ae66..b3b3fa735 100644 --- a/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs @@ -418,7 +418,7 @@ public static async Task CheckForAdminAccess(UIElement root) UseShellExecute = true, Verb = "runas", FileName = AppExecutablePath, - WorkingDirectory = AppFolder, + WorkingDirectory = AppExecutableDir, Arguments = string.Join(' ', AppCurrentArgument) } }; @@ -549,13 +549,13 @@ internal async void ChangeBackgroundImageAsRegionAsync(bool ShowLoadingMsg = fal var posterBg = currentGameProperty?._GameVersion.GameType switch { - GameNameType.Honkai => Path.Combine(AppFolder, + GameNameType.Honkai => Path.Combine(AppExecutableDir, @"Assets\Images\GameBackground\honkai.webp"), - GameNameType.Genshin => Path.Combine(AppFolder, + GameNameType.Genshin => Path.Combine(AppExecutableDir, @"Assets\Images\GameBackground\genshin.webp"), - GameNameType.StarRail => Path.Combine(AppFolder, + GameNameType.StarRail => Path.Combine(AppExecutableDir, @"Assets\Images\GameBackground\starrail.webp"), - GameNameType.Zenless => Path.Combine(AppFolder, + GameNameType.Zenless => Path.Combine(AppExecutableDir, @"Assets\Images\GameBackground\zzz.webp"), _ => AppDefaultBG }; @@ -869,7 +869,7 @@ private void SpawnAppUpdatedNotification() { try { - string InnoLogPath = Path.Combine(Path.GetDirectoryName(AppFolder) ?? string.Empty, "unins000.dat"); + string InnoLogPath = Path.Combine(Path.GetDirectoryName(AppExecutableDir) ?? string.Empty, "unins000.dat"); if (File.Exists(InnoLogPath)) InnoSetupLogUpdate.UpdateInnoSetupLog(InnoLogPath); File.Delete(NeedInnoUpdateFile); } @@ -899,14 +899,14 @@ private void SpawnAppUpdatedNotification() ); string target; - string fold = Path.Combine(AppFolder, "_Temp"); + string fold = Path.Combine(AppExecutableDir, "_Temp"); if (Directory.Exists(fold)) { foreach (string file in Directory.EnumerateFiles(fold)) { if (Path.GetFileNameWithoutExtension(file).Contains("ApplyUpdate")) { - target = Path.Combine(AppFolder, Path.GetFileName(file)); + target = Path.Combine(AppExecutableDir, Path.GetFileName(file)); File.Move(file, target, true); } } @@ -942,12 +942,12 @@ private void SpawnAppUpdatedNotification() // Get notification service Windows.UI.Notifications.ToastNotification notificationService = - WindowUtility.CurrentToastNotificationService.CreateToastNotification(toastContent); + WindowUtility.CurrentToastNotificationService?.CreateToastNotification(toastContent); // Spawn notification service Windows.UI.Notifications.ToastNotifier notifier = - WindowUtility.CurrentToastNotificationService.CreateToastNotifier(); - notifier.Show(notificationService); + WindowUtility.CurrentToastNotificationService?.CreateToastNotifier(); + notifier?.Show(notificationService); } catch (Exception ex) { diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs index 89fe213d4..0de111daa 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/GameSettingsPages/ZenlessGameSettingsPage.xaml.cs @@ -164,17 +164,17 @@ private void InitializeSettings(object sender, RoutedEventArgs e) var resList = new List(); SizeProp = ScreenProp.CurrentResolution; - //SizeProp = new System.Drawing.Size(2560, 1600); // Get the native resolution first var nativeResSize = GetNativeDefaultResolution(); var nativeResString = string.Format(Lang._GameSettingsPage.Graphics_ResPrefixFullscreen, nativeResSize.Width, nativeResSize.Height) + $" [{Lang._Misc.Default}]"; // Then get the rest of the list - List resFullscreen = GetResPairs_Fullscreen(); - List resWindowed = GetResPairs_Windowed(); + List resFullscreen = GetResPairs_Fullscreen(nativeResSize); + List resWindowed = GetResPairs_Windowed(); - ScreenResolutionIsFullscreenIdx.Add(true); // Add first for the native resolution. + // Add the index of fullscreen and windowed resolution booleans + ScreenResolutionIsFullscreenIdx.Add(true); ScreenResolutionIsFullscreenIdx.AddRange(Enumerable.Range(0, resFullscreen.Count).Select(_ => true)); ScreenResolutionIsFullscreenIdx.AddRange(Enumerable.Range(0, resWindowed.Count).Select(_ => false)); @@ -188,12 +188,12 @@ private void InitializeSettings(object sender, RoutedEventArgs e) if (CurrentGameProperty.IsGameRunning) { - #if !GSPBYPASSGAMERUNNING + #if !GSPBYPASSGAMERUNNING Overlay.Visibility = Visibility.Visible; PageContent.Visibility = Visibility.Collapsed; OverlayTitle.Text = Lang._GameSettingsPage.OverlayGameRunningTitle; OverlaySubtitle.Text = Lang._GameSettingsPage.OverlayGameRunningSubtitle; - #endif + #endif } else if (GameInstallationState == GameInstallStateEnum.NotInstalled || GameInstallationState == GameInstallStateEnum.NeedsUpdate @@ -207,9 +207,9 @@ private void InitializeSettings(object sender, RoutedEventArgs e) } else { -#if !DISABLEDISCORD + #if !DISABLEDISCORD InnerLauncherConfig.AppDiscordPresence.SetActivity(ActivityType.GameSettings); -#endif + #endif } } catch (Exception ex) @@ -252,7 +252,7 @@ private System.Drawing.Size GetNativeDefaultResolution() return currentAcceptedRes.LastOrDefault(x => x.Width == maxAcceptedResW); } - private List GetResPairs_Fullscreen() + private List GetResPairs_Fullscreen(System.Drawing.Size defaultResolution) { var nativeAspRatio = (double)SizeProp.Width / SizeProp.Height; var acH = acceptableHeight; @@ -260,15 +260,31 @@ private List GetResPairs_Fullscreen() acH.RemoveAll(h => h > acceptedMaxHeight); //acH.RemoveAll(h => h > 1600); - + + // Get the resolution pairs and initialize default resolution index List resPairs = new List(); + int indexOfDefaultRes = -1; - foreach (var h in acH) + for (int i = 0; i < acH.Count; i++) { - int w = (int)(h * nativeAspRatio); + // Get height and calculate width + int h = acH[i]; + int w = (int)Math.Round(h * nativeAspRatio); + + // If the resolution is the same as default, set the index + if (h == defaultResolution.Height && w == defaultResolution.Width) + indexOfDefaultRes = i; + + // Add the resolution pair to the list resPairs.Add(string.Format(Lang._GameSettingsPage.Graphics_ResPrefixFullscreen, w, h)); } + // If the index of default resolution is found, remove it from the list + if (indexOfDefaultRes != -1) + { + resPairs.RemoveAt(indexOfDefaultRes); + } + return resPairs; } @@ -287,10 +303,14 @@ private List GetResPairs_Windowed() // If res is 21:9 then add proper native to the list if (Math.Abs(nativeAspRatio - ulWideRatio) < 0.01) resPairs.Add($"{SizeProp.Width}x{SizeProp.Height}"); - - foreach (var h in acH) + + for (int i = 0; i < acH.Count; i++) { - int w = (int)(h * wideRatio); + // Get height and calculate width + int h = acH[i]; + int w = (int)Math.Round(h * wideRatio); + + // Add the resolution pair to the list resPairs.Add(string.Format(Lang._GameSettingsPage.Graphics_ResPrefixWindowed, w, h)); } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs index 1c81a1fb6..ba22cbbaf 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs @@ -2143,7 +2143,7 @@ public async Task CheckMediaPackInstalled() LogWriteLine($"Media pack is not installed!\r\n\t" + $"If you encounter the 'cry_ware_unity' error, run this script as an administrator:\r\n\t" + - $"{Path.Combine(AppFolder, "Misc", "InstallMediaPack.cmd")}", LogType.Warning, true); + $"{Path.Combine(AppExecutableDir, "Misc", "InstallMediaPack.cmd")}", LogType.Warning, true); // Skip dialog if user asked before if (GetAppConfigValue("HI3IgnoreMediaPack").ToBool()) @@ -2165,11 +2165,11 @@ public async void TryInstallMediaPack() { try { - Process proc = new Process() + Process proc = new Process { StartInfo = new ProcessStartInfo { - FileName = Path.Combine(AppFolder, "Misc", "InstallMediaPack.cmd"), + FileName = Path.Combine(AppExecutableDir, "Misc", "InstallMediaPack.cmd"), UseShellExecute = true, Verb = "runas" } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/OOBE/OOBEAgreementMenu.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/OOBE/OOBEAgreementMenu.xaml.cs index 08f02d9c5..8c68224c7 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/OOBE/OOBEAgreementMenu.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/OOBE/OOBEAgreementMenu.xaml.cs @@ -10,7 +10,7 @@ internal class AgreementProperty { internal AgreementProperty(string title, string filePath) { - this.filePath = Path.Combine(LauncherConfig.AppFolder, filePath); + this.filePath = Path.Combine(LauncherConfig.AppExecutableDir, filePath); this.title = title; } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/OOBE/OOBESelectGameBG.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/OOBE/OOBESelectGameBG.xaml.cs index 9b098ef20..8819c80e2 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/OOBE/OOBESelectGameBG.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/OOBE/OOBESelectGameBG.xaml.cs @@ -94,10 +94,10 @@ internal static async Task TryLoadGameDetails(PresetConfig config = null) _gamePosterPath = config.GameType switch { - GameNameType.Honkai => Path.Combine(AppFolder, @"Assets\Images\GamePoster\poster_honkai.png"), - GameNameType.Genshin => Path.Combine(AppFolder, @"Assets\Images\GamePoster\poster_genshin.png"), - GameNameType.StarRail => Path.Combine(AppFolder, @"Assets\Images\GamePoster\poster_starrail.png"), - GameNameType.Zenless => Path.Combine(AppFolder, @"Assets\Images\GamePoster\poster_zzz.png"), + GameNameType.Honkai => Path.Combine(AppExecutableDir, @"Assets\Images\GamePoster\poster_honkai.png"), + GameNameType.Genshin => Path.Combine(AppExecutableDir, @"Assets\Images\GamePoster\poster_genshin.png"), + GameNameType.StarRail => Path.Combine(AppExecutableDir, @"Assets\Images\GamePoster\poster_starrail.png"), + GameNameType.Zenless => Path.Combine(AppExecutableDir, @"Assets\Images\GamePoster\poster_zzz.png"), _ => AppDefaultBG }; @@ -105,11 +105,15 @@ internal static async Task TryLoadGameDetails(PresetConfig config = null) //_gamePosterPath = await ImageLoaderHelper.GetCachedSpritesAsync(FallbackCDNUtil.TryGetAbsoluteToRelativeCDNURL(config.ZonePosterURL, "metadata/"), default); _gameLogoPath = await ImageLoaderHelper.GetCachedSpritesAsync(FallbackCDNUtil.TryGetAbsoluteToRelativeCDNURL(config.ZoneLogoURL, "metadata/"), default); - using (IRandomAccessStream fs2 = new FileStream(_gameLogoPath, FileMode.Open, FileAccess.Read, FileShare.Read).AsRandomAccessStream()) + if (_gameLogoPath == null) { - _gameLogoBitmapImage = await ImageLoaderHelper.Stream2BitmapImage(fs2); - (_gamePosterBitmap, _gamePosterBitmapImage) = await ImageLoaderHelper.GetResizedBitmapNew(_gamePosterPath); + LogWriteLine($"Failed while loading poster image as _gameLogoPath returns null!", LogType.Error, true); + return IsSuccess = false; } + + using IRandomAccessStream fs2 = new FileStream(_gameLogoPath, FileMode.Open, FileAccess.Read, FileShare.Read).AsRandomAccessStream(); + _gameLogoBitmapImage = await ImageLoaderHelper.Stream2BitmapImage(fs2); + (_gamePosterBitmap, _gamePosterBitmapImage) = await ImageLoaderHelper.GetResizedBitmapNew(_gamePosterPath); } catch (Exception ex) { diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/OOBE/OOBEStartUpMenu.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/OOBE/OOBEStartUpMenu.xaml.cs index b1bea794e..a793805ab 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/OOBE/OOBEStartUpMenu.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/OOBE/OOBEStartUpMenu.xaml.cs @@ -843,7 +843,7 @@ private async void ChooseFolder(object sender, RoutedEventArgs e) AppGameFolder = folder; SetAppConfigValue("GameFolder", AppGameFolder); selected = true; - _log?.SetFolderPathAndInitialize(AppGameLogsFolder, Encoding.UTF8); + CurrentLogger?.SetFolderPathAndInitialize(AppGameLogsFolder, Encoding.UTF8); } else { diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/SettingsPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/SettingsPage.xaml.cs index 7c8207d19..090c619fb 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/SettingsPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/SettingsPage.xaml.cs @@ -184,8 +184,8 @@ private async void ClearMetadataFolder(object sender, RoutedEventArgs e) case ContentDialogResult.Primary: try { - var collapsePath = Process.GetCurrentProcess().MainModule?.FileName; - if (collapsePath == null) return; + var collapsePath = AppExecutablePath; + if (string.IsNullOrEmpty(collapsePath)) return; Directory.Delete(LauncherMetadataHelper.LauncherMetadataFolder, true); Process.Start(collapsePath); (WindowUtility.CurrentWindow as MainWindow)?.CloseApp(); @@ -241,7 +241,7 @@ private async void ClearLogsFolder(object sender, RoutedEventArgs e) { try { - _log?.ResetLogFiles(AppGameLogsFolder, Encoding.UTF8); + CurrentLogger?.ResetLogFiles(AppGameLogsFolder, Encoding.UTF8); (sender as Button).IsEnabled = false; } catch (Exception ex) @@ -560,15 +560,15 @@ private bool IsConsoleEnabled } set { - _log.Dispose(); + CurrentLogger.Dispose(); if (value) { - _log = new LoggerConsole(AppGameLogsFolder, Encoding.UTF8); + CurrentLogger = new LoggerConsole(AppGameLogsFolder, Encoding.UTF8); ToggleIncludeGameLogs.Visibility = Visibility.Visible; } else { - _log = new LoggerNull(AppGameLogsFolder, Encoding.UTF8); + CurrentLogger = new LoggerNull(AppGameLogsFolder, Encoding.UTF8); ToggleIncludeGameLogs.Visibility = Visibility.Collapsed; } SetAndSaveConfigValue("EnableConsole", value); diff --git a/CollapseLauncher/XAMLs/Updater/Classes/Updater.cs b/CollapseLauncher/XAMLs/Updater/Classes/Updater.cs index e2eb1ca88..66866af4e 100644 --- a/CollapseLauncher/XAMLs/Updater/Classes/Updater.cs +++ b/CollapseLauncher/XAMLs/Updater/Classes/Updater.cs @@ -287,11 +287,11 @@ private void FallbackCDNUtil_DownloadProgress(object sender, DownloadEvent e) private bool IsCurrentHasLatestVersion(string latestVersionString) { // Check legacy version first - var filePath = Path.Combine(AppFolder, $@"..\app-{latestVersionString}\{Path.GetFileName(AppExecutablePath)}"); + var filePath = Path.Combine(AppExecutableDir, $@"..\app-{latestVersionString}\{Path.GetFileName(AppExecutablePath)}"); if (File.Exists(filePath)) return true; // If none does not exist, then check the latest version - filePath = Path.Combine(AppFolder, $@"..\current\{Path.GetFileName(AppExecutablePath)}"); + filePath = Path.Combine(AppExecutableDir, $@"..\current\{Path.GetFileName(AppExecutablePath)}"); if (!Version.TryParse(latestVersionString, out Version latestVersion)) { Logger.LogWriteLine($"[Updater::DoesLatestVersionExist] latestVersionString is not valid! {latestVersionString}", LogType.Error, true); @@ -356,7 +356,7 @@ private async Task Suicide() { try { - string currentAppPath = Path.Combine(Path.GetDirectoryName(AppFolder) ?? string.Empty, "current"); + string currentAppPath = Path.Combine(Path.GetDirectoryName(AppExecutableDir) ?? string.Empty, "current"); if (!Directory.Exists(currentAppPath)) Directory.CreateDirectory(currentAppPath); diff --git a/Hi3Helper.Core/Classes/Logger/Logger.cs b/Hi3Helper.Core/Classes/Logger/Logger.cs index a7a200de2..97e3fb66e 100644 --- a/Hi3Helper.Core/Classes/Logger/Logger.cs +++ b/Hi3Helper.Core/Classes/Logger/Logger.cs @@ -3,10 +3,10 @@ #nullable enable public static class Logger { - public static ILog? _log { get; set; } - public static void LogWriteLine() => _log?.LogWriteLine(); - public static void LogWriteLine(string line, LogType type = LogType.Default, bool writeToLog = false) => _log?.LogWriteLine(line, type, writeToLog); - public static void LogWrite(string line, LogType type = LogType.Default, bool writeToLog = false, bool resetLinePosition = false) => _log?.LogWrite(line, type, writeToLog, resetLinePosition); - public static void WriteLog(string line, LogType type = LogType.Default) => _log?.WriteLog(line, type); + public static ILog? CurrentLogger { get; set; } + public static void LogWriteLine() => CurrentLogger?.LogWriteLine(); + public static void LogWriteLine(string line, LogType type = LogType.Default, bool writeToLog = false) => CurrentLogger?.LogWriteLine(line, type, writeToLog); + public static void LogWrite(string line, LogType type = LogType.Default, bool writeToLog = false, bool resetLinePosition = false) => CurrentLogger?.LogWrite(line, type, writeToLog, resetLinePosition); + public static void WriteLog(string line, LogType type = LogType.Default) => CurrentLogger?.WriteLog(line, type); } } diff --git a/Hi3Helper.Core/Classes/Logger/LoggerBase.cs b/Hi3Helper.Core/Classes/Logger/LoggerBase.cs index de2e7d464..664ff73ae 100644 --- a/Hi3Helper.Core/Classes/Logger/LoggerBase.cs +++ b/Hi3Helper.Core/Classes/Logger/LoggerBase.cs @@ -1,6 +1,8 @@ using System; using System.IO; using System.Text; +using System.Threading; + #if !APPLYUPDATE using Hi3Helper.Shared.Region; using Hi3Helper.Win32.Native.ManagedTools; @@ -9,29 +11,28 @@ namespace Hi3Helper { - public class LoggerBase + public abstract class LoggerBase { #region Properties - private FileStream _logStream { get; set; } - private StreamWriter _logWriter { get; set; } - private bool _isWriterOnDispose { get; set; } - private object _lockObject = new object(); - private string _logFolder { get; set; } + private FileStream LogStream { get; set; } + private StreamWriter LogWriter { get; set; } + private bool IsWriterOnDispose { get; set; } + private Lock LockObject = new(); + private StringBuilder StringBuilder { get; } + private string LogFolder { get; set; } #if !APPLYUPDATE - private string _logPath { get; set; } public static string LogPath { get; set; } #endif - private StringBuilder _stringBuilder { get; set; } #endregion #region Statics public static string GetCurrentTime(string format) => DateTime.Now.ToLocalTime().ToString(format); #endregion - public LoggerBase(string logFolder, Encoding logEncoding) + protected LoggerBase(string logFolder, Encoding logEncoding) { // Initialize the writer and _stringBuilder - _stringBuilder = new StringBuilder(); + StringBuilder = new StringBuilder(); SetFolderPathAndInitialize(logFolder, logEncoding); } @@ -39,17 +40,17 @@ public LoggerBase(string logFolder, Encoding logEncoding) public void SetFolderPathAndInitialize(string folderPath, Encoding logEncoding) { // Set the folder path of the stored log - _logFolder = folderPath; + LogFolder = folderPath; #if !APPLYUPDATE // Check if the directory exist. If not, then create. - if (!string.IsNullOrEmpty(_logFolder) && !Directory.Exists(_logFolder)) + if (!string.IsNullOrEmpty(LogFolder) && !Directory.Exists(LogFolder)) { - Directory.CreateDirectory(_logFolder); + Directory.CreateDirectory(LogFolder); } #endif - lock (_lockObject) + lock (LockObject) { // Try dispose the _logWriter even though it's not initialized. // This will be used if the program need to change the log folder to another location. @@ -74,22 +75,22 @@ public void SetFolderPathAndInitialize(string folderPath, Encoding logEncoding) #nullable enable public void ResetLogFiles(string? reloadToPath, Encoding? encoding = null) { - lock (_lockObject) + lock (LockObject) { DisposeBase(); - if (!string.IsNullOrEmpty(_logFolder) && Directory.Exists(_logFolder)) - DeleteLogFilesInner(_logFolder); + if (!string.IsNullOrEmpty(LogFolder) && Directory.Exists(LogFolder)) + DeleteLogFilesInner(LogFolder); if (!string.IsNullOrEmpty(reloadToPath) && !Directory.Exists(reloadToPath)) Directory.CreateDirectory(reloadToPath); if (!string.IsNullOrEmpty(reloadToPath)) - _logFolder = reloadToPath; + LogFolder = reloadToPath; encoding ??= Encoding.UTF8; - SetFolderPathAndInitialize(_logFolder, encoding); + SetFolderPathAndInitialize(LogFolder, encoding); } } @@ -112,25 +113,24 @@ private void DeleteLogFilesInner(string folderPath) } #nullable restore -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously - public virtual async void LogWriteLine() { } + public abstract void LogWriteLine(); // ReSharper disable MethodOverloadWithOptionalParameter - public virtual async void LogWriteLine(string line = null) { } + public abstract void LogWriteLine(string line = null); // ReSharper restore MethodOverloadWithOptionalParameter - public virtual async void LogWriteLine(string line, LogType type) { } - public virtual async void LogWriteLine(string line, LogType type, bool writeToLog) { } - public virtual async void LogWrite(string line, LogType type, bool writeToLog, bool fromStart) { } + public abstract void LogWriteLine(string line, LogType type); + public abstract void LogWriteLine(string line, LogType type, bool writeToLog); + public abstract void LogWrite(string line, LogType type, bool writeToLog, bool fromStart); public void WriteLog(string line, LogType type) { // Always seek to the end of the file. - lock(_lockObject) + lock(LockObject) { try { - if (_isWriterOnDispose) return; + if (IsWriterOnDispose) return; - _logWriter?.BaseStream.Seek(0, SeekOrigin.End); - _logWriter?.WriteLine(GetLine(line, type, false, true)); + LogWriter?.BaseStream.Seek(0, SeekOrigin.End); + LogWriter?.WriteLine(GetLine(line, type, false, true)); } catch (IOException ex) when (ex.HResult == unchecked((int)0x80070070)) // Disk full? Delete all logs <: { @@ -139,10 +139,10 @@ public void WriteLog(string line, LogType type) // Rewrite log try { - Logger._log?.ResetLogFiles(LauncherConfig.AppGameLogsFolder, Encoding.UTF8); + Logger.CurrentLogger?.ResetLogFiles(LauncherConfig.AppGameLogsFolder, Encoding.UTF8); // Attempt to write the log again after resetting - _logWriter?.BaseStream.Seek(0, SeekOrigin.End); - _logWriter?.WriteLine(GetLine(line, type, false, true)); + LogWriter?.BaseStream.Seek(0, SeekOrigin.End); + LogWriter?.WriteLine(GetLine(line, type, false, true)); } catch (Exception retryEx) { @@ -158,7 +158,6 @@ public void WriteLog(string line, LogType type) } } } -#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously #endregion #region ProtectedMethods @@ -172,19 +171,19 @@ public void WriteLog(string line, LogType type) /// Decorated line with colored type or timestamp according to the parameters protected string GetLine(string line, LogType type, bool coloredType, bool withTimeStamp) { - lock (_stringBuilder) + lock (StringBuilder) { // Clear the _stringBuilder - _stringBuilder.Clear(); + StringBuilder.Clear(); // Colorize the log type if (coloredType) { - _stringBuilder.Append(GetColorizedString(type) + GetLabelString(type) + "\u001b[0m"); + StringBuilder.Append(GetColorizedString(type) + GetLabelString(type) + "\u001b[0m"); } else { - _stringBuilder.Append(GetLabelString(type)); + StringBuilder.Append(GetLabelString(type)); } // Append timestamp @@ -192,27 +191,27 @@ protected string GetLine(string line, LogType type, bool coloredType, bool withT { if (type != LogType.NoTag) { - _stringBuilder.Append($" [{GetCurrentTime("HH:mm:ss.fff")}]"); + StringBuilder.Append($" [{GetCurrentTime("HH:mm:ss.fff")}]"); } else { - _stringBuilder.Append(new string(' ', 15)); + StringBuilder.Append(new string(' ', 15)); } } // Append spaces between labels and text - _stringBuilder.Append(" "); - _stringBuilder.Append(line); + StringBuilder.Append(" "); + StringBuilder.Append(line); - return _stringBuilder.ToString(); + return StringBuilder.ToString(); } } protected void DisposeBase() { - _isWriterOnDispose = true; - _logWriter?.Dispose(); - _logStream?.Dispose(); + IsWriterOnDispose = true; + LogWriter?.Dispose(); + LogStream?.Dispose(); } #endregion @@ -229,20 +228,19 @@ private void InitializeWriter(bool isFallback, Encoding logEncoding) fallbackString += LauncherConfig.AppCurrentVersionString; // Append the current instance number fallbackString += $"-id{GetTotalInstance()}"; - _logPath = Path.Combine(_logFolder, $"log-{dateString + fallbackString}-{GetCurrentTime("HH-mm-ss")}.log"); - Console.WriteLine("\u001b[37;44m[LOGGER]\u001b[0m Log will be written to: " + _logPath); - LogPath = _logPath; + LogPath = Path.Combine(LogFolder, $"log-{dateString + fallbackString}-{GetCurrentTime("HH-mm-ss")}.log"); + Console.WriteLine("\u001b[37;44m[LOGGER]\u001b[0m Log will be written to: " + LogPath); // Initialize _logWriter to the given _logPath. // The FileShare.ReadWrite is still being used to avoid potential conflict if the launcher needs // to warm-restart itself in rare occasion (like update mechanism with Squirrel). - _logStream = new FileStream(_logPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite); + LogStream = new FileStream(LogPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite); // Seek the file to the EOF - _logStream.Seek(0, SeekOrigin.End); + LogStream.Seek(0, SeekOrigin.End); // Initialize the StreamWriter - _logWriter = new StreamWriter(_logStream, logEncoding) { AutoFlush = true }; - _isWriterOnDispose = false; + LogWriter = new StreamWriter(LogStream, logEncoding) { AutoFlush = true }; + IsWriterOnDispose = false; } private int GetTotalInstance() => ProcessChecker.EnumerateInstances(ILoggerHelper.GetILogger()); diff --git a/Hi3Helper.Core/Classes/Logger/Type/LoggerConsole.cs b/Hi3Helper.Core/Classes/Logger/Type/LoggerConsole.cs index 4626a82c2..3d0f10e62 100644 --- a/Hi3Helper.Core/Classes/Logger/Type/LoggerConsole.cs +++ b/Hi3Helper.Core/Classes/Logger/Type/LoggerConsole.cs @@ -2,7 +2,6 @@ using Hi3Helper.Win32.Native.ManagedTools; using System; using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; using System.Text; #if !APPLYUPDATE using static Hi3Helper.Shared.Region.LauncherConfig; diff --git a/Hi3Helper.Core/Classes/Logger/Type/LoggerNull.cs b/Hi3Helper.Core/Classes/Logger/Type/LoggerNull.cs index 1e15875fd..3b90eef4f 100644 --- a/Hi3Helper.Core/Classes/Logger/Type/LoggerNull.cs +++ b/Hi3Helper.Core/Classes/Logger/Type/LoggerNull.cs @@ -1,27 +1,26 @@ using System.Text; +// ReSharper disable MethodOverloadWithOptionalParameter namespace Hi3Helper { - public class LoggerNull : LoggerBase, ILog + public class LoggerNull(string folderPath, Encoding encoding) : LoggerBase(folderPath, encoding), ILog { - public LoggerNull(string folderPath, Encoding encoding) : base(folderPath, encoding) { } - ~LoggerNull() => DisposeBase(); #region Methods public void Dispose() => DisposeBase(); - -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously - public override async void LogWriteLine(string line, LogType type, bool writeToLog) + public override void LogWriteLine() { } + public override void LogWriteLine(string line = null) { } + public override void LogWriteLine(string line, LogType type) { } + public override void LogWriteLine(string line, LogType type, bool writeToLog) { if (writeToLog) WriteLog(line, type); } - public override async void LogWrite(string line, LogType type, bool writeToLog, bool resetLinePosition) + public override void LogWrite(string line, LogType type, bool writeToLog, bool resetLinePosition) { if (writeToLog) WriteLog(line, type); } -#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously #endregion } } diff --git a/Hi3Helper.Core/Classes/Shared/ClassStruct/StructsEnums.cs b/Hi3Helper.Core/Classes/Shared/ClassStruct/StructsEnums.cs index 6aa034b8e..7959c486a 100644 --- a/Hi3Helper.Core/Classes/Shared/ClassStruct/StructsEnums.cs +++ b/Hi3Helper.Core/Classes/Shared/ClassStruct/StructsEnums.cs @@ -1,13 +1,11 @@ using Hi3Helper.Data; -using System.IO; namespace Hi3Helper.Shared.ClassStruct { - public struct AppIniStruct + public class AppIniProperty { - public IniFile Profile; - public Stream ProfileStream; - public string ProfilePath; + public IniFile Profile { get; set; } = new(); + public string ProfilePath { get; set; } } public enum AppThemeMode diff --git a/Hi3Helper.Core/Classes/Shared/Region/LauncherConfig.cs b/Hi3Helper.Core/Classes/Shared/Region/LauncherConfig.cs index bfedc6798..9df95ad82 100644 --- a/Hi3Helper.Core/Classes/Shared/Region/LauncherConfig.cs +++ b/Hi3Helper.Core/Classes/Shared/Region/LauncherConfig.cs @@ -9,18 +9,28 @@ using System.Numerics; using System.Text; using static Hi3Helper.Locale; +// ReSharper disable IdentifierTypo +// ReSharper disable StringLiteralTypo +#pragma warning disable CA2211 +#nullable enable namespace Hi3Helper.Shared.Region { #region CDN Property - public struct CDNURLProperty : IEquatable + + public readonly struct CDNURLProperty : IEquatable { - public string URLPrefix { get; init; } - public string Name { get; init; } - public string Description { get; init; } - public bool PartialDownloadSupport { get; init; } - public bool Equals(CDNURLProperty other) => URLPrefix == other.URLPrefix && Name == other.Name && Description == other.Description; + public string URLPrefix { get; init; } + public string Name { get; init; } + public string Description { get; init; } + public bool PartialDownloadSupport { get; init; } + + public bool Equals(CDNURLProperty other) + { + return URLPrefix == other.URLPrefix && Name == other.Name && Description == other.Description; + } } + #endregion [SuppressMessage("ReSharper", "InconsistentNaming")] @@ -28,47 +38,51 @@ public struct CDNURLProperty : IEquatable public static class LauncherConfig { #region Main Launcher Config Methods + public static void InitAppPreset() { // Initialize resolution settings first and assign AppConfigFile to ProfilePath InitScreenResSettings(); - appIni.ProfilePath = AppConfigFile; + AppConfigProperty.ProfilePath = AppConfigFile; // Set user permission check to its default and check for the existence of config file. - bool IsConfigFileExist = File.Exists(appIni.ProfilePath); + bool IsConfigFileExist = File.Exists(AppConfigProperty.ProfilePath); // If the config file is exist, then continue to load the file - appIni.Profile = IniFile.LoadFrom(appIni.ProfilePath); + if (IsConfigFileExist) + { + LoadAppConfig(); + } // If the section doesn't exist, then add the section template - if (!appIni.Profile.ContainsKey(SectionName)) + if (!AppConfigProperty.Profile.ContainsKey(SectionName)) { - appIni.Profile.Add(SectionName, AppSettingsTemplate); + AppConfigProperty.Profile.Add(SectionName, AppSettingsTemplate); } // Check and assign default for the null and non-existence values. CheckAndSetDefaultConfigValue(); // Set the startup background path and GameFolder to check if user has permission. - startupBackgroundPath = GetAppConfigValue("CurrentBackground").ToString(); - string GameFolder = GetAppConfigValue("GameFolder").ToString(); + string? gameFolder = GetAppConfigValue("GameFolder").ToString(); - // Check if the drive is exist. If not, then reset the GameFolder variable and set IsFirstInstall to true; - if (!IsDriveExist(GameFolder)) + // Check if the drive exist. If not, then reset the GameFolder variable and set IsFirstInstall to true; + if (!string.IsNullOrEmpty(gameFolder) && !IsDriveExist(gameFolder)) { IsFirstInstall = true; // Reset GameFolder to default value - SetAppConfigValue("GameFolder", AppSettingsTemplate!["GameFolder"]); + SetAppConfigValue("GameFolder", AppSettingsTemplate["GameFolder"]); // Force enable Console Log and return - Logger._log = new LoggerConsole(AppGameLogsFolder, Encoding.UTF8); - Logger.LogWriteLine($"Game App Folder path: {GameFolder} doesn't exist! The launcher will be reinitialize the setup.", LogType.Error, true); + Logger.CurrentLogger = new LoggerConsole(AppGameLogsFolder, Encoding.UTF8); + Logger.LogWriteLine($"Game App Folder path: {gameFolder} doesn't exist! The launcher will be reinitialize the setup.", + LogType.Error, true); return; } // Check if user has permission - bool IsUserHasPermission = ConverterTool.IsUserHasPermission(GameFolder); + bool IsUserHasPermission = ConverterTool.IsUserHasPermission(gameFolder); // Assign boolean if IsConfigFileExist and IsUserHasPermission. IsFirstInstall = !(IsConfigFileExist && IsUserHasPermission); @@ -78,36 +92,61 @@ public static void InitAppPreset() _ = DownloadSpeedLimit; } - public static bool IsConfigKeyExist(string key) => appIni.Profile![SectionName!]!.ContainsKey(key!); - public static IniValue GetAppConfigValue(string key) => appIni.Profile![SectionName]![key!]; + public static bool IsConfigKeyExist(string key) + { + return AppConfigProperty.Profile[SectionName].ContainsKey(key); + } + + public static IniValue GetAppConfigValue(string key) + { + return AppConfigProperty.Profile[SectionName][key]; + } + public static void SetAndSaveConfigValue(string key, IniValue value, bool doNotLog = false) { SetAppConfigValue(key, value); SaveAppConfig(); - #if DEBUG + #if DEBUG if (!doNotLog) Logger.LogWriteLine($"SetAndSaveConfigValue::Key[{key}]::Value[{value}]", LogType.Debug); - #endif + #endif } - public static void SetAppConfigValue(string key, IniValue value) => appIni.Profile![SectionName]![key!] = value; - public static void LoadAppConfig() => appIni.Profile!.Load(appIni.ProfilePath, true); - public static void SaveAppConfig() => appIni.Profile!.Save(appIni.ProfilePath); + public static void SetAppConfigValue(string key, IniValue value) + { + AppConfigProperty.Profile[SectionName][key] = value; + } + + public static void LoadAppConfig() + { + AppConfigProperty.Profile.Load(AppConfigProperty.ProfilePath); + } + + public static void SaveAppConfig() + { + AppConfigProperty.Profile.Save(AppConfigProperty.ProfilePath); + } public static void CheckAndSetDefaultConfigValue() { - foreach (KeyValuePair Entry in AppSettingsTemplate!) + foreach (KeyValuePair Entry in AppSettingsTemplate) { - if (!appIni.Profile![SectionName]!.ContainsKey(Entry.Key!) || appIni.Profile[SectionName][Entry.Key].IsEmpty) + if (!AppConfigProperty.Profile[SectionName].ContainsKey(Entry.Key) || + AppConfigProperty.Profile[SectionName][Entry.Key].IsEmpty) { SetAppConfigValue(Entry.Key, Entry.Value); } } } + #endregion #region Misc Methods - public static void LoadGamePreset() => AppGameFolder = Path.Combine(GetAppConfigValue("GameFolder")!); + + public static void LoadGamePreset() + { + AppGameFolder = Path.Combine(GetAppConfigValue("GameFolder")!); + } private static bool IsDriveExist(string path) { @@ -117,138 +156,163 @@ private static bool IsDriveExist(string path) private static void InitScreenResSettings() { foreach (var res in ScreenProp.EnumerateScreenSizes()) + { ScreenResolutionsList.Add($"{res.Width}x{res.Height}"); + } } + #endregion #region CDN List - public static List CDNList => new() - { - new CDNURLProperty - { - Name = "GitHub", - URLPrefix = "https://github.com/CollapseLauncher/CollapseLauncher-ReleaseRepo/raw/main", - Description = Lang!._Misc!.CDNDescription_Github, - PartialDownloadSupport = true - }, - new CDNURLProperty - { - Name = "Cloudflare", - URLPrefix = "https://r2.bagelnl.my.id/cl-cdn", - Description = Lang!._Misc!.CDNDescription_Cloudflare, - PartialDownloadSupport = true - }, - new CDNURLProperty - { - Name = "GitLab", - URLPrefix = "https://gitlab.com/bagusnl/CollapseLauncher-ReleaseRepo/-/raw/main/", - Description = Lang!._Misc!.CDNDescription_GitLab - }, - new CDNURLProperty - { - Name = "Coding", - URLPrefix = "https://ohly-generic.pkg.coding.net/collapse/release/", - Description = Lang!._Misc!.CDNDescription_Coding - }, - }; - - public static CDNURLProperty GetCurrentCDN() => CDNList![GetAppConfigValue("CurrentCDN")]; + + public static List CDNList => + [ + new() + { + Name = "GitHub", + URLPrefix = "https://github.com/CollapseLauncher/CollapseLauncher-ReleaseRepo/raw/main", + Description = Lang._Misc!.CDNDescription_Github, + PartialDownloadSupport = true + }, + + new() + { + Name = "Cloudflare", + URLPrefix = "https://r2.bagelnl.my.id/cl-cdn", + Description = Lang._Misc!.CDNDescription_Cloudflare, + PartialDownloadSupport = true + }, + + new() + { + Name = "GitLab", + URLPrefix = "https://gitlab.com/bagusnl/CollapseLauncher-ReleaseRepo/-/raw/main/", + Description = Lang._Misc!.CDNDescription_GitLab + }, + + new() + { + Name = "Coding", + URLPrefix = "https://ohly-generic.pkg.coding.net/collapse/release/", + Description = Lang._Misc!.CDNDescription_Coding + } + ]; + #endregion #region Misc Fields + public static Vector3 Shadow16 = new(0, 0, 16); public static Vector3 Shadow32 = new(0, 0, 32); public static Vector3 Shadow48 = new(0, 0, 48); - // Format in milliseconds - public static int RefreshTime = 250; - const string SectionName = "app"; - public static string startupBackgroundPath; - public static List ScreenResolutionsList = new(); + + public const string AppNotifURLPrefix = "/notification_{0}.json"; + public const string AppGameRepairIndexURLPrefix = "/metadata/repair_indexes/{0}/{1}/index"; + public const string AppGameRepoIndexURLPrefix = "/metadata/repair_indexes/{0}/repo"; + private const string SectionName = "app"; + public static readonly List ScreenResolutionsList = []; public const long AppDiscordApplicationID = 1138126643592970251; public const long AppDiscordApplicationID_HI3 = 1124126288370737314; public const long AppDiscordApplicationID_GI = 1124137436650426509; public const long AppDiscordApplicationID_HSR = 1124153902959431780; public const long AppDiscordApplicationID_ZZZ = 1124154024879456276; - - public const string AppNotifURLPrefix = "/notification_{0}.json"; - public const string AppGameConfigV2URLPrefix = "/metadata/metadatav2_{0}.json"; - public const string AppGameRepairIndexURLPrefix = "/metadata/repair_indexes/{0}/{1}/index"; - public const string AppGameRepoIndexURLPrefix = "/metadata/repair_indexes/{0}/repo"; public static IntPtr AppIconLarge; public static IntPtr AppIconSmall; + #endregion #region App Config Definitions - public static AppIniStruct appIni; - - public static readonly string AppFolder = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule!.FileName); - public static readonly string AppDefaultBG = Path.Combine(AppFolder!, "Assets", "Images", "PageBackground", "default.png"); - - public static readonly string AppLangFolder = Path.Combine(AppFolder, "Lang"); - public static readonly string AppDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "AppData", "LocalLow", "CollapseLauncher"); - - public static string AppImagesFolder = Path.Combine(AppFolder, "Assets", "Images"); + public static AppIniProperty AppConfigProperty { get; set; } = new(); + public static List AppCurrentArgument { get; set; } = []; + + [field: AllowNull, MaybeNull] + public static Process AppCurrentProcess { get => field ??= Process.GetCurrentProcess(); } + public static int AppCurrentDownloadThread => GetAppConfigValue("DownloadThread"); + public static string AppGameConfigMetadataFolder => Path.Combine(AppGameFolder, "_metadatav3"); + + + [field: AllowNull, MaybeNull] + public static string AppExecutablePath + { + get + { + if (field != null) + { + return field; + } + + string execPath = AppCurrentProcess.MainModule?.FileName ?? ""; + return field = execPath; + } + } + public static string AppGameFolder { - get => GetAppConfigValue("GameFolder"); + get => GetAppConfigValue("GameFolder").Value ?? ""; set => SetAppConfigValue("GameFolder", value); } - public static string[] AppCurrentArgument; - private static string _appExecutablePath; - public static string AppExecutablePath + + [field: AllowNull, MaybeNull] + public static string AppExecutableName => field ??= Path.GetFileName(AppExecutablePath); + [field: AllowNull, MaybeNull] + public static string AppExecutableDir => field ??= Path.GetDirectoryName(AppExecutablePath) ?? ""; + public static string AppGameImgFolder => Path.Combine(AppGameFolder, "_img"); + public static string AppGameImgCachedFolder => Path.Combine(AppGameImgFolder, "cached"); + public static string AppGameLogsFolder => Path.Combine(AppGameFolder, "_logs"); + + [field: AllowNull, MaybeNull] + public static Version AppCurrentVersion { get { - if (string.IsNullOrEmpty(_appExecutablePath)) + if (field != null) { - using Process currentProcess = Process.GetCurrentProcess(); - string execName = Path.GetFileNameWithoutExtension(currentProcess.MainModule?.FileName); - string dirPath = AppFolder; - return _appExecutablePath = Path.Combine(dirPath!, execName + ".exe"); + return field; } - return _appExecutablePath; + + if (string.IsNullOrEmpty(AppExecutablePath)) + { + return field = new Version(); + } + + FileVersionInfo verInfo = FileVersionInfo.GetVersionInfo(AppExecutablePath); + return field = verInfo.FileVersion != null ? new Version(verInfo.FileMajorPart, verInfo.FileMinorPart, verInfo.FileBuildPart) : new Version(); } } - public static string AppExecutableName { get => Path.GetFileName(AppExecutablePath); } - public static string AppGameImgFolder { get => Path.Combine(AppGameFolder!, "_img"); } - public static string AppGameImgCachedFolder { get => Path.Combine(AppGameImgFolder!, "cached"); } - public static string AppGameLogsFolder { get => Path.Combine(AppGameFolder!, "_logs"); } - private static string _appCurrentVersionString; - public static string AppCurrentVersionString + [field: AllowNull, MaybeNull] + public static string AppCurrentVersionString => + field ??= $"{AppCurrentVersion.Major}.{AppCurrentVersion.Minor}.{AppCurrentVersion.Build}"; + + [field: AllowNull, MaybeNull] + public static Version WindowsAppSdkVersion { get { - if (string.IsNullOrEmpty(_appCurrentVersionString)) + if (field != null) { - string executablePath = AppExecutablePath; - if (string.IsNullOrEmpty(executablePath)) - return "Unknown"; - - try - { - FileVersionInfo verInfo = FileVersionInfo.GetVersionInfo(executablePath); - string version = $"{verInfo.FileMajorPart}.{verInfo.FileMinorPart}.{verInfo.FileBuildPart}"; - return _appCurrentVersionString = version; - } - catch (Exception ex) - { - SentryHelper.SentryHelper.ExceptionHandler(ex); - return "Unknown"; - } + return field; } - return _appCurrentVersionString; + + if (string.IsNullOrEmpty(AppExecutablePath)) + { + return field = new Version(); + } + + string dllPath = Path.Combine(AppExecutableDir, "Microsoft.ui.xaml.dll"); + if (!File.Exists(dllPath)) + { + return field = new Version(); + } + + FileVersionInfo verInfo = FileVersionInfo.GetVersionInfo(dllPath); + return field = verInfo.FileVersion != null ? new Version(verInfo.FileMajorPart, verInfo.FileMinorPart, verInfo.FileBuildPart) : new Version(); } } - public static readonly string AppConfigFile = Path.Combine(AppDataFolder!, "config.ini"); - public static readonly string AppNotifIgnoreFile = Path.Combine(AppDataFolder, "ignore_notif_ids.json"); - - public static string GamePathOnSteam; - public static long AppGameConfigLastUpdate; public static int AppCurrentThread { get @@ -257,16 +321,21 @@ public static int AppCurrentThread return val <= 0 ? Environment.ProcessorCount : val; } } - public static int AppCurrentDownloadThread => GetAppConfigValue("DownloadThread"); - public static string AppGameConfigMetadataFolder { get => Path.Combine(AppGameFolder!, "_metadatav3"); } - - public static readonly bool IsAppLangNeedRestart = false; public static bool IsPreview = false; - public static bool IsAppThemeNeedRestart = false; + public static bool IsFirstInstall = false; + public static bool IsAppLangNeedRestart = false; public static bool IsChangeRegionWarningNeedRestart = false; + public static bool IsAppThemeNeedRestart = false; public static bool IsInstantRegionNeedRestart = false; - public static bool IsFirstInstall = false; + + public static readonly string AppDefaultBG = Path.Combine(AppExecutableDir, "Assets", "Images", "PageBackground", "default.png"); + public static readonly string AppLangFolder = Path.Combine(AppExecutableDir, "Lang"); + public static readonly string AppDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "AppData", "LocalLow", "CollapseLauncher"); + public static readonly string AppImagesFolder = Path.Combine(AppExecutableDir, "Assets", "Images"); + public static readonly string AppConfigFile = Path.Combine(AppDataFolder, "config.ini"); + public static readonly string AppNotifIgnoreFile = Path.Combine(AppDataFolder, "ignore_notif_ids.json"); + public static bool IsConsoleEnabled { get => GetAppConfigValue("EnableConsole"); @@ -355,22 +424,20 @@ public static bool IsEnforceToUse7zipOnExtract set => SetAndSaveConfigValue("EnforceToUse7zipOnExtract", value); } - private static long _downloadSpeedLimitCached = 0; // Default: 0 == Unlimited public static long DownloadSpeedLimitCached { - get => _downloadSpeedLimitCached; + get; set { - _downloadSpeedLimitCached = IsUseDownloadSpeedLimiter ? value : 0; - DownloadSpeedLimitChanged?.Invoke(null, _downloadSpeedLimitCached); + field = IsUseDownloadSpeedLimiter ? value : 0; + DownloadSpeedLimitChanged?.Invoke(null, field); } } -#nullable enable public static event EventHandler? DownloadSpeedLimitChanged; -#nullable restore private static bool? _cachedIsInstantRegionChange = null; + public static bool IsInstantRegionChange { get @@ -393,23 +460,29 @@ public static Guid GetGuid(int sessionNum) SetAndSaveConfigValue($"sessionGuid{sessionNum}", g); return g; } + return guidString; } + #endregion #region App Settings Template - public static Dictionary AppSettingsTemplate = new Dictionary + + public static Dictionary AppSettingsTemplate = new() { { "CurrentBackground", "ms-appx:///Assets/Images/default.png" }, { "DownloadThread", 4 }, { "ExtractionThread", 0 }, { "GameFolder", Path.Combine(AppDataFolder, "GameFolder") }, - { "UserAgent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 Edg/118.0.0.0" }, - #if DEBUG + { + "UserAgent", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 Edg/118.0.0.0" + }, + #if DEBUG { "EnableConsole", true }, - #else + #else { "EnableConsole", false }, - #endif + #endif { "SendRemoteCrashData", true }, { "EnableMultipleInstance", false }, { "DontAskUpdate", false }, @@ -419,17 +492,17 @@ public static Guid GetGuid(int sessionNum) { "IsUseVideoBGDynamicColorUpdate", false }, { "ShowEventsPanel", true }, { "ShowSocialMediaPanel", true }, - { "ShowGamePlaytime", true}, + { "ShowGamePlaytime", true }, { "CustomBGPath", "" }, { "GameCategory", "Honkai Impact 3rd" }, { "WindowSizeProfile", "Normal" }, { "CurrentCDN", 0 }, { "ShowRegionChangeWarning", false }, - #if !DISABLEDISCORD + #if !DISABLEDISCORD { "EnableDiscordRPC", false }, { "EnableDiscordGameStatus", true }, - { "EnableDiscordIdleStatus", true}, - #endif + { "EnableDiscordIdleStatus", true }, + #endif { "EnableAcrylicEffect", true }, { "IncludeGameLogs", false }, { "UseDownloadChunksMerging", false }, @@ -445,9 +518,9 @@ public static Guid GetGuid(int sessionNum) { "BackgroundAudioIsMute", true }, { "UseInstantRegionChange", true }, { "IsIntroEnabled", true }, - { "IsEnableSophon", true}, - { "SophonCpuThread", 0}, - { "SophonHttpConnInt", 0}, + { "IsEnableSophon", true }, + { "SophonCpuThread", 0 }, + { "SophonHttpConnInt", 0 }, { "SophonPreloadApplyPerfMode", false }, { "EnforceToUse7zipOnExtract", false }, @@ -466,6 +539,7 @@ public static Guid GetGuid(int sessionNum) { "HttpProxyPassword", string.Empty }, { "HttpClientTimeout", 90 } }; + #endregion } -} +} \ No newline at end of file diff --git a/Hi3Helper.Core/Lang/es_419.json b/Hi3Helper.Core/Lang/es_419.json index 69299ca93..13a8eed11 100644 --- a/Hi3Helper.Core/Lang/es_419.json +++ b/Hi3Helper.Core/Lang/es_419.json @@ -995,7 +995,13 @@ "CloseOverlay": "Cerrar Superposición (Overlay)", "DbGenerateUid_Title": "¿Estas seguro que quieres cambiar tu ID de Usuario?", - "DbGenerateUid_Content": "Si cambias el ID de usuario actual, se perderán los datos asociados si lo pierdes." + "DbGenerateUid_Content": "Si cambias el ID de usuario actual, se perderán los datos asociados si lo pierdes.", + + "SophonIncrementUpdateUnavailTitle": "Actualización incremental no disponible: ¡La versión es demasiado obsoleta!", + "SophonIncrementUpdateUnavailSubtitle1": "Tu versión del juego: {0}", + "SophonIncrementUpdateUnavailSubtitle2": " es muy obsoleta", + "SophonIncrementUpdateUnavailSubtitle3": " y la actualización incremental no está disponible para tu versión. Sin embargo, aún podrías actualizar tu juego volviendo a descargarlo desde cero.", + "SophonIncrementUpdateUnavailSubtitle4": "Haz clic en \"{0}\" para continuar actualizando todo o en \"{1}\" para cancelar el proceso" }, "_FileMigrationProcess": { diff --git a/Hi3Helper.Core/Lang/fr_FR.json b/Hi3Helper.Core/Lang/fr_FR.json index 8497d55c0..9f7112d18 100644 --- a/Hi3Helper.Core/Lang/fr_FR.json +++ b/Hi3Helper.Core/Lang/fr_FR.json @@ -27,7 +27,7 @@ "ChooseFolderDialogTitle": "Localiser le dossier", "FolderInsufficientPermission": "Permissions refusées ! Merci de choisir un autre dossier.", "FolderNotSelected": "Aucun dossier choisi. Merci de sélectionner un dossier !", - "OverlayPrepareFolderSubtitle": "Application des permissions nécessaires…", + "OverlayPrepareFolderSubtitle": "Application de la/des permission(s) nécessaire(s)...", "OverlayPrepareFolderTitle": "Préparation du dossier d'application", "PageTitle": "Page de démarrage", "Pg1LoadingSubitle1": "Préparation d'une liste des jeux supportés pour vous…", @@ -81,6 +81,8 @@ "UnhandledSubtitle3": "Le jeu s'est arrêté de façon inattendue avec les détails d'erreur ci-dessous :", "UnhandledTitle4": "Avertissement", "UnhandledSubtitle4": "Ce n'est pas un problème majeur, mais nous pensons que nous devrions vous le faire savoir :\r\nEn raison de tests A/B de miHoYo, Collapse ne supporte pas la lecture de la clé suivante : App_Settings_h2319593470.\r\nNous nous excusons pour la gêne occasionnée et vous remercions de votre compréhension.", + "UnhandledTitleDiskCrc": "Corruption du disque détectée !", + "UnhandledSubDiskCrc": "Une corruption du disque a été détectée en tentant d'accéder à un fichier. Veuillez effectuer une vérification de votre disque en utilisant CHKDSK.", "CopyClipboardBtn1": "Tout copier dans le presse-papier", "CopyClipboardBtn2": "Copié dans le presse-papier !", "GoBackPageBtn1": "Retourner à la page précédente" @@ -146,12 +148,18 @@ "GameSettings_Panel2UninstallGame": "Désinstaller le jeu", "GameSettings_Panel2ConvertVersion": "Convertir la version du jeu", "GameSettings_Panel2MoveGameLocationGame": "Déplacer le jeu", + "GameSettings_Panel2MoveGameLocationGame_SamePath": "Impossible de déplacer le jeu à la racine de votre disque !\r\nVeuillez créer un nouveau dossier et réessayez.", "GameSettings_Panel2StopGame": "Forcer la fermeture du jeu", + "GameSettings_Panel3RegionalSettings": "Paramètres régionaux", "GameSettings_Panel3": "Arguments de démarrage personnalisés", - "GameSettings_Panel4": "Divers", + "GameSettings_Panel3RegionRpc": "Afficher le statut en jeu sur Discord", + "GameSettings_Panel3CustomBGRegion": "Modifier l'arrière-plan", + "GameSettings_Panel3CustomBGRegionSectionTitle": "Arrière-plan régional personnalisé", + "GameSettings_Panel4": "Global - Divers", "GameSettings_Panel4ShowEventsPanel": "Afficher le panneau des évènements", "GameSettings_Panel4ShowSocialMediaPanel": "Afficher le panneau des médias sociaux", "GameSettings_Panel4ShowPlaytimeButton": "Afficher le compteur de temps de jeu", + "GameSettings_Panel4SyncPlaytimeDatabase": "Synchroniser le temps de jeu avec la base de données", "GameSettings_Panel4CreateShortcutBtn": "Créer le raccourci", "GameSettings_Panel4AddToSteamBtn": "Ajouter à Steam", @@ -160,10 +168,20 @@ "GamePlaytime_Idle_Panel1Minutes": "Minutes", "GamePlaytime_Idle_ResetBtn": "Réinitialiser", "GamePlaytime_Idle_ChangeBtn": "Modifier", + "GamePlaytime_Idle_SyncDb": "Synchroniser", + "GamePlaytime_Idle_SyncDbSyncing": "Synchronisation…", "GamePlaytime_Running_Info1": "Une instance de ce jeu est actuellement en cours, la durée de jeu ne peut donc pas être modifiée.", - "GamePlaytime_Running_Info2": "Veuillez noter que la fermeture complète de Collapse arrêtera le suivi du temps de jeu (en sauvegardant ce qui a été joué jusqu'à maintenant) et que seules les sessions commencées à l'aide de Collapse seront suivies.", + "GamePlaytime_Running_Info2": "Sachez que la fermeture complète de Collapse interrompt le suivi de la durée de jeu (sauvegarde de ce qui a été joué jusqu'à ce point).", "GamePlaytime_Display": "{0} h {1:00} min", "GamePlaytime_DateDisplay": "{0:00}/{1:00}/{2:0000} {3:00}:{4:00}", + "GamePlaytime_Stats_Title": "Statistiques de temps de jeu", + "GamePlaytime_Stats_NeverPlayed": "Jamais joué", + "GamePlaytime_Stats_LastSession": "Dernière session", + "GamePlaytime_Stats_LastSession_StartTime": "Date", + "GamePlaytime_Stats_LastSession_Duration": "Durée", + "GamePlaytime_Stats_Daily": "Ajourd'hui", + "GamePlaytime_Stats_Weekly": "Semaine", + "GamePlaytime_Stats_Monthly": "Mois", "PostPanel_Events": "Évènements", "PostPanel_Notices": "Annonces", @@ -192,7 +210,10 @@ "GameStateInvalidFixed_Subtitle1": "Les propriétés des fichiers du jeu ont été corrigées pour la région :", "GameStateInvalidFixed_Subtitle2": "Veuillez effectuer une « ", "GameStateInvalidFixed_Subtitle3": " » dans le menu « ", - "GameStateInvalidFixed_Subtitle4": " » afin de s'assurer que les fichiers sont valides." + "GameStateInvalidFixed_Subtitle4": " » afin de s'assurer que les fichiers sont valides.", + + "InstallFolderRootTitle": "Racine du disque fournie !", + "InstallFolderRootSubtitle": "Impossible d'installer le jeu à la racine de votre disque ! Veuillez créer un nouveau dossier et réessayez." }, "_GameRepairPage": { @@ -404,6 +425,8 @@ "Debug": "Paramètres supplémentaires", "Debug_Console": "Afficher la console", "Debug_IncludeGameLogs": "Sauvegarder les journaux de jeu dans Collapse (peut contenir des données sensibles)", + "Debug_SendRemoteCrashData": "Envoyer des rapports de plantage anonymes aux développeurs", + "Debug_SendRemoteCrashData_EnvVarDisablement": "Ce paramètre est désactivé en raison de la variable d'environnement « DISABLE_SENTRY » définie sur « true ».", "Debug_MultipleInstance": "Permettre l'exécution de plusieurs instances de Collapse", "ChangeRegionWarning_Toggle": "Afficher un avertissement lors d'un changement de région", @@ -431,6 +454,7 @@ "AppBG": "Arrière-plan", "AppBG_Checkbox": "Utiliser une image d'arrière-plan personnalisée", "AppBG_Note": "Formats acceptés :\r\nImage : {0}\r\nVidéo : {1}", + "AppBG_Note_Regional": "Le paramètre d'arrière-plan spécifique à chaque région se trouve dans le bouton « Réglages rapides » sur la page principale.", "AppThreads": "Threads d'application", "AppThreads_Download": "Threads de téléchargement", @@ -455,6 +479,8 @@ "SophonHttpNumberBox": "Nombre de connexions HTTP maximum", "SophonHelp_Http": "Cette option contrôle le nombre maximium de connexions réseau que Collapse peut établir vers les serveurs de mise à jour de HoYoverse afin de télécharger les fragments.", "SophonToggle": "Activer le mode Sophon sur les régions supportées", + "SophonPredownPerfMode_Toggle": "[EXPÉRIMENTAL] Utiliser tous les cœurs CPU pour appliquer le paquet de préchargement", + "SophonPredownPerfMode_Tooltip": "L'activation de cette option définira les threads du processeur au maximum disponible pour votre système. Si vous rencontrez des problèmes, désactivez cette option.", "AppThreads_Attention": "Attention", "AppThreads_Attention1": "Avant de modifier la valeur «", @@ -463,14 +489,18 @@ "AppThreads_Attention4": "la valeur si vous avez un téléchargement existant car cela pourrait provoquer", "AppThreads_Attention5": "UN RETÉLÉCHARGEMENT AU COMPLET", "AppThreads_Attention6": "en raison du nombre de sessions nécessaires au téléchargement qui ne correspondrait pas.", + "AppThreads_AttentionTop1": "Les problèmes ci-dessous ne se produiront plus si le paramètre «", + "AppThreads_AttentionTop2": "» est activé.", "DiscordRPC": "Statut enrichi Discord", "DiscordRPC_Toggle": "Afficher le statut enrichi sur Discord", "DiscordRPC_GameStatusToggle": "Afficher le jeu en cours sur Discord", "DiscordRPC_IdleStatusToggle": "Afficher le statut enrichi lorsque vous êtes inactif", + "ImageBackground": "Paramètres de l'image d'arrière-plan", "VideoBackground": "Paramètres de l'arrière-plan vidéo", "VideoBackground_IsEnableAudio": "Activer l'audio", + "VideoBackground_IsEnableAcrylicBackground": "Activer les effets Acrylic lors de l'utilisation d'un arrière-plan vidéo", "VideoBackground_AudioVolume": "Volume de l'audio", "Update": "État des mises à jour", @@ -510,7 +540,7 @@ "Disclaimer1": "Cette application n'est pas affiliée à", "Disclaimer2": "d'aucune sorte", "Disclaimer3": "et est complètement Open Source. Toute contribution est la bienvenue.", - + "WebsiteBtn": "Visiter le site Web", "DiscordBtn1": "Discord de notre armada (HI3)", "DiscordBtn2": "Discord Honkai Impact 3rd", "DiscordBtn3": "Discord officiel de Collapse", @@ -519,6 +549,7 @@ "EnableAcrylicEffect": "Utiliser les effets de flou Acrylic", "EnableDownloadChunksMerging": "Fusionner les paquets téléchargés", + "Enforce7ZipExtract": "Toujours utiliser 7-Zip pour l'installation/mise à jour du jeu", "UseExternalBrowser": "Toujours utiliser un navigateur externe", "LowerCollapsePrioOnGameLaunch": "Réduire l'utilisation en ressources de Collapse lorsqu'un jeu est lancé", @@ -537,7 +568,7 @@ "AppBehavior_LaunchOnStartup": "Démarrer automatiquement Collapse au démarrage de l'ordinateur", "AppBehavior_StartupToTray": "Démarrer en mode réduit (dans la barre d'état système)", - "Waifu2X_Toggle": "Utiliser Waifu2X [Expérimental]", + "Waifu2X_Toggle": "Utiliser Waifu2X", "Waifu2X_Help": "Utiliser Waifu2X pour agrandir les images d'arrière-plan.\r\nLorsque cette option est activée, la qualité de l'image sera significativement améliorée, mais cela peut causer un temps de chargement accru de l'image lors du chargement initial.", "Waifu2X_Help2": "Uniquement disponible pour les images fixes !", "Waifu2X_Warning_CpuMode": "AVERTISSEMENT : Aucun GPU compatible Vulkan disponible n'a été trouvé et le mode CPU sera utilisé. Cela augmentera considérablement le temps de traitement de l’image.", @@ -578,7 +609,58 @@ "NetworkSettings_Http_Redirect": "Autoriser la redirection HTTP", "NetworkSettings_Http_SimulateCookies": "Autoriser la simulation de cookies HTTP", "NetworkSettings_Http_UntrustedHttps": "Autoriser l'utilisation de certificats HTTPS « non fiables »", - "NetworkSettings_Http_Timeout": "Délai d'attente du client HTTP (en secondes)" + "NetworkSettings_Http_Timeout": "Délai d'attente du client HTTP (en secondes)", + + "FileDownloadSettings_Title": "Paramètres de téléchargement des fichiers", + "FileDownloadSettings_SpeedLimit_Title": "Limiter la vitesse de téléchargement", + "FileDownloadSettings_SpeedLimit_NumBox": "Limite de vitesse (en Mio)", + "FileDownloadSettings_SpeedLimitHelp1": "Valeur par défaut :", + "FileDownloadSettings_SpeedLimitHelp2": "Plage de valeurs :", + "FileDownloadSettings_SpeedLimitHelp3": "1 - 1000 Mio/s", + "FileDownloadSettings_SpeedLimitHelp4": "Limite l'utilisation maximale de la bande passante pour le téléchargement. Le paramètre «", + "FileDownloadSettings_SpeedLimitHelp5": "» ne peut être utilisé si la limitation de bande passante est activée.", + + "FileDownloadSettings_NewPreallocChunk_Title": "Nouveau téléchargeur avec préallocation", + "FileDownloadSettings_NewPreallocChunk_Subtitle": "Uniquement pour l'installation, la réparation et la mise à jour des caches du jeu.", + "FileDownloadSettings_NewPreallocChunk_NumBox": "Taille des fragments (en Mio)", + "FileDownloadSettings_NewPreallocChunkHelp1": "Valeur par défaut :", + "FileDownloadSettings_NewPreallocChunkHelp2": "Plage de valeurs :", + "FileDownloadSettings_NewPreallocChunkHelp3": "1 - 32 Mio", + "FileDownloadSettings_NewPreallocChunkHelp4": "Lorsque ce paramètre est activé, le téléchargeur va préallouer dynamiquement la taille du fichier pendant que celui-ci le télécharge.", + "FileDownloadSettings_NewPreallocChunkHelp5": "Cela permet au téléchargeur de directement écrire les fragments de données du fichier sans le scinder en plusieurs fichiers distincts.", + "FileDownloadSettings_NewPreallocChunkHelp6": "Lorsque ce paramètre est désactivé, le launcher utilisera l'ancienne méthode où les fragments seront écrits dans des fichiers séparés. Les paramètres «", + "FileDownloadSettings_NewPreallocChunkHelp7": "» et «", + "FileDownloadSettings_NewPreallocChunkHelp8": "» seront également désactivés.", + "FileDownloadSettings_NewPreallocChunkHelp9": "Note :", + "FileDownloadSettings_NewPreallocChunkHelp10": "Cette fonctionnalité est uniquement disponible pour l'installation (incluant l'installation initiale, la mise à jour et le préchargement), la réparation et la mise à jour des caches du jeu.", + + "FileDownloadSettings_BurstDownload_Title": "Mode de téléchargement en parallèle", + "FileDownloadSettings_BurstDownload_Subtitle": "Uniquement pour la réparation et la mise à jour des caches du jeu.", + "FileDownloadSettings_BurstDownloadHelp1": "Valeur par défaut :", + "FileDownloadSettings_BurstDownloadHelp2": "Lorsque ce paramètre est activé, cette fonctionnalité permettra au processus de téléchargement des fonctions «", + "FileDownloadSettings_BurstDownloadHelp3": "» et «", + "FileDownloadSettings_BurstDownloadHelp4": "» de fonctionner en parallèle afin d'accélérer le processus de téléchargement.", + "FileDownloadSettings_BurstDownloadHelp5": "Lorsque ce paramètre est désactivé, le processus de téléchargement des fonctions «", + "FileDownloadSettings_BurstDownloadHelp6": "» et «", + "FileDownloadSettings_BurstDownloadHelp7": "» utilisera un processus de téléchargement séquentiel.", + + "Database_Title": "Base de données synchronisable utilisateur", + "Database_ConnectionOk": "Base de données connectée avec succès !", + "Database_ConnectFail": "Échec de la connexion à la base de données. Voir les erreurs ci-dessous :", + "Database_Toggle": "Activer la base de données en ligne", + "Database_Url": "URL de la base de données", + "Database_Url_Example": "Exemple : https://db-collapse.turso.io", + "Database_Token": "Jeton", + "Database_UserId": "ID utilisateur", + "Database_GenerateGuid": "Générer un nouvel UID", + "Database_Validate": "Valider et sauvegarder", + "Database_Error_EmptyUri": "L'URL de la base de données ne peut être vide !", + "Database_Error_EmptyToken": "Le jeton de la base de données ne peut être vide !", + "Database_Error_InvalidGuid": "L'ID utilisateur n'est pas un GUID valide !", + "Database_Warning_PropertyChanged": "Les paramètres de la base de données ont changé", + "Database_ValidationChecking": "Validation des paramètres…", + "Database_Placeholder_DbUserIdTextBox": "Exemple de GUID : ed6e8048-e3a0-4983-bd56-ad19956c701f", + "Database_Placeholder_DbTokenPasswordBox": "Entrez votre jeton d'authentification ici" }, "_Misc": { @@ -598,6 +680,7 @@ "PerFromTo": "{0} / {1}", "PerFromToPlaceholder": "- / -", + "EverythingIsOkay": "Tout est en ordre !", "Cancel": "Annuler", "Close": "Fermer", "UseCurrentDir": "Utiliser le dossier actuel", @@ -666,6 +749,7 @@ "Disabled": "Désactivé", "Enabled": "Activé", "UseAsDefault": "Utiliser par défaut", + "Default": "Par défaut", "BuildChannelPreview": "Preview", "BuildChannelStable": "Stable", @@ -706,7 +790,14 @@ "LauncherNameSteam": "Steam", "LauncherNameUnknown": "(Nom du launcher inconnu)", - "ImageCropperTitle": "Rogner l'image" + "ImageCropperTitle": "Rogner l'image", + + "IsBytesMoreThanBytes": "= {0} octets (~{1})", + "IsBytesUnlimited": "= Illimité", + "IsBytesNotANumber": "= NaN", + + "MissingVcRedist": "Redistribuable Visual C/C++ manquant", + "MissingVcRedistSubtitle": "Vous devez installer le redistribuable Visual C/C++ pour exécuter cette fonction. Voulez-vous le télécharger maintenant ?\r\nRemarque : Cela ouvrira une fenêtre de navigateur et téléchargera un fichier depuis le site de Microsoft. Veuillez exécuter le programme d'installation après l'avoir téléchargé, puis redémarrez Collapse et réessayez.\r\nSi l'erreur persiste malgré l'installation du redistribuable, veuillez soumettre un rapport de bug." }, "_BackgroundNotification": { @@ -753,7 +844,7 @@ "RepairCompletedSubtitle": "{0} fichier(s) ont été réparés.", "RepairCompletedSubtitleNoBroken": "Aucun fichier cassé n'a été trouvé.", "ExtremeGraphicsSettingsWarnTitle": "Préréglage « Très élevé » sélectionné !", - "ExtremeGraphicsSettingsWarnSubtitle": "Vous êtes sur le point de définir le préréglage « Très élevé ».\r\nLe préréglage « Très élevé » est essentiellement une échelle de rendu 2x avec MSAA activé, et est TRÈS MAL OPTIMISÉ !\r\n\r\nÊtes-vous sûr de vouloir utiliser ce réglage ?", + "ExtremeGraphicsSettingsWarnSubtitle": "Vous êtes sur le point de définir le préréglage « Très élevé ».\r\nLe préréglage « Très élevé » est essentiellement une échelle de rendu 1,6x avec MSAA activé, et est TRÈS MAL OPTIMISÉ !\r\n\r\nÊtes-vous sûr de vouloir utiliser ce réglage ?", "MigrateExistingMoveDirectoryTitle": "Déplacement de l'installation existante pour : {0}", "MigrateExistingInstallChoiceTitle": "Une installation existante de {0} a été détectée !", "MigrateExistingInstallChoiceSubtitle1": "Vous avez une installation existante du jeu utilisant {0} à cet emplacement :", @@ -818,7 +909,7 @@ "ChangePlaytimeTitle": "Modifier le temps de jeu ?", "ChangePlaytimeSubtitle": "Modifier votre temps de jeu signifie remplacer la valeur actuelle par celle que vous venez de saisir.\r\n\r\nVoulez-vous poursuivre ?\r\n\r\nNote : Ceci n'a aucun impact sur le fonctionnement de Collapse et vous pouvez modifier cette valeur à tout moment lorsque vous n'êtes pas en jeu.", "ResetPlaytimeTitle": "Réinitialisation du temps de jeu", - "ResetPlaytimeSubtitle": "Réinitialiser votre temps de jeu signifie remettre le compteur de temps de jeu à 0. Il s'agit d'une action ", + "ResetPlaytimeSubtitle": "Réinitialiser votre temps de jeu signifie remettre à 0 le compteur de temps de jeu et toutes les statistiques associées. ", "ResetPlaytimeSubtitle2": "destructrice", "ResetPlaytimeSubtitle3": ", ce qui signifie que vous ne pouvez pas revenir en arrière une fois confirmée.\r\n\r\nVoulez-vous poursuivre ?\r\n\r\nNote : Ceci n'a aucun impact sur le fonctionnement de Collapse et vous pouvez modifier cette valeur à tout moment lorsque vous n'êtes pas en jeu.", "InvalidPlaytimeTitle": "Un problème est survenu lors de l'enregistrement du temps de jeu de cette session", @@ -831,6 +922,22 @@ "CannotUseAppLocationForGameDirTitle": "Dossier invalide !", "CannotUseAppLocationForGameDirSubtitle": "Vous ne pouvez pas utiliser ce dossier car il est utilisé comme dossier système ou pour l'exécutable principal de l'application. Veuillez choisir un autre emplacement !", + "InvalidGameDirNewTitleFormat": "Chemin invalide : {0}", + "InvalidGameDirNewSubtitleSelectedPath": "Chemin sélectionné :", + "InvalidGameDirNewSubtitleSelectOther": "Veuillez sélectionner un autre dossier/emplacement !", + "InvalidGameDirNew1Title": "Dossier invalide !", + "InvalidGameDirNew1Subtitle": "Vous ne pouvez pas utiliser ce dossier car il est utilisé comme dossier système ou par l'exécutable principal de l'application. Veuillez choisir un autre emplacement !", + "InvalidGameDirNew2Title": "Impossible d'accéder au dossier sélectionné", + "InvalidGameDirNew2Subtitle": "Le launcher n'a pas la permission d'accéder à ce dossier !", + "InvalidGameDirNew3Title": "Impossible de sélectionner la racine du disque", + "InvalidGameDirNew3Subtitle": "Vous avez sélectionné un emplacement à la racine du disque, ce qui est interdit !", + "InvalidGameDirNew4Title": "Impossible de sélectionner le dossier « Windows »", + "InvalidGameDirNew4Subtitle": "Vous ne pouvez pas utiliser le dossier « Windows » comme dossier d'installation afin d'éviter tout problème qui pourrait en résulter.", + "InvalidGameDirNew5Title": "Impossible de sélectionner le dossier « ProgramData »", + "InvalidGameDirNew5Subtitle": "Vous ne pouvez pas utiliser le dossier « ProgramData » comme dossier d'installation afin d'éviter tout problème qui pourrait en résulter.", + "InvalidGameDirNew6Title": "Impossible de sélectionner le dossier « Programmes » ou « Programmes (x86) »", + "InvalidGameDirNew6Subtitle": "Vous ne pouvez pas utiliser le dossier « Programmes » ou « Programmes (x86) » comme dossier d'installation afin d'éviter tout problème qui pourrait en résulter.", + "FolderDialogTitle1": "Sélection du dossier d'installation du jeu", "StopGameTitle": "Forcer l'arrêt du jeu", "StopGameSubtitle": "Êtes-vous sûr de vouloir forcer l'arrêt du jeu en cours ?\r\nIl se peut que vous perdiez une partie de votre progression en jeu.", @@ -883,9 +990,18 @@ "DownloadSettingsTitle": "Paramètres de téléchargement", "DownloadSettingsOption1": "Démarrer le jeu après l'installation", - + "OpenInExternalBrowser": "Ouvrir dans un navigateur externe", - "CloseOverlay": "Fermer l'overlay" + "CloseOverlay": "Fermer l'overlay", + + "DbGenerateUid_Title": "Êtes-vous sûr de vouloir modifier votre ID utilisateur ?", + "DbGenerateUid_Content": "Modifier l'ID utilisateur actuel peut entraîner une perte des données associées si vous le perdez.", + + "SophonIncrementUpdateUnavailTitle": "Mise à jour incrémentielle indisponible : Version trop ancienne !", + "SophonIncrementUpdateUnavailSubtitle1": "La version de votre jeu: {0}", + "SophonIncrementUpdateUnavailSubtitle2": "est trop ancienne", + "SophonIncrementUpdateUnavailSubtitle3": " et la mise à jour incrémentielle à partir de celle-ci n'est pas disponible. Il est cependant possible de mettre à jour le jeu en effectuant un nouveau téléchargement au complet de celui-ci.", + "SophonIncrementUpdateUnavailSubtitle4": "Cliquez sur « {0} » pour procéder à la mise à jour complète ou sur « {1} » pour annuler le processus." }, "_FileMigrationProcess": { @@ -1041,6 +1157,10 @@ "ApplyUpdateErrCollapseRunTitle": "Veuillez fermer Collapse avant d'appliquer la mise à jour !", "ApplyUpdateErrCollapseRunSubtitle": "En l'attente de la fermeture de Collapse…", + "ApplyUpdateErrCollapseRunTitleWarnBox": "Une instance de Collapse Launcher est toujours en cours d'exécution !", + "ApplyUpdateErrCollapseRunSubtitleWarnBox": "Nous avons détecté qu'une instance de Collapse Launcher s'exécute en arrière-plan. Pour forcer la fermeture, cliquez sur « Oui ». Pour mettre en attente jusqu'à la fermeture manuelle, cliquez sur « Non ».", + "ApplyUpdateErrVelopackStateBrokenTitleWarnBox": "Installation existante défectueuse détectée !", + "ApplyUpdateErrVelopackStateBrokenSubtitleWarnBox": "Nous avons détecté que votre installation actuelle est défectueuse.\r\n\r\nCliquez sur « Oui » pour lancer une réparation de l'installation avant de la mettre à jour ou sur « Non » pour simplement effectuer la mise à jour.", "ApplyUpdateErrReleaseFileNotFoundTitle": "ERREUR :\r\nLe fichier « release » ne contient pas de chaîne « stable » ou « preview »", "ApplyUpdateErrReleaseFileNotFoundSubtitle": "Veuillez vérifier votre fichier « release » et réessayez.", @@ -1344,6 +1464,8 @@ "LoadingTitle": "Traitement", "LoadingSubtitle1": "Calcul des fichiers existants ({0} fichier(s) trouvé(s) - {1} au total)…", "LoadingSubtitle2": "Vérification de la disponibilité de « pkg_version »…", + "LoadingSubtitle3": "L'interface utilisateur peut ne plus répondre durant ce processus…", + "DeleteSubtitle": "Suppression des fichiers…", "BottomButtonDeleteAllFiles": "Tout supprimer", "BottomButtonDeleteSelectedFiles": "Supprimer {0} fichier(s) sélectionné(s)", "BottomCheckboxFilesSelected": "{0} fichier(s) sélectionné(s) ({1} / {2} au total)", @@ -1405,9 +1527,45 @@ "Graphics_EffectsQ": "Qualité des effets", "Graphics_ShadingQ": "Qualité des ombres", "Graphics_Distortion": "Distorsion", + "Graphics_HighPrecisionCharacterAnimation": "Animation des personnages de haute précision", "Audio_PlaybackDev": "Périphérique de sortie audio", "Audio_PlaybackDev_Headphones": "Casque", "Audio_PlaybackDev_Speakers": "Haut-parleurs", "Audio_PlaybackDev_TV": "Téléviseur" + }, + + "_NotificationToast": { + "WindowHiddenToTray_Title": "Collapse Launcher est actuellement minimisé", + "WindowHiddenToTray_Subtitle": "Le launcher s'exécute désormais en arrière-plan.\r\nCliquez sur cette notification ou l'icône de la zone de notification pour restaurer la fenêtre.", + + "GameInstallCompleted_Title": "{0} · Installation terminée", + "GameInstallCompleted_Subtitle": "{0} a été installé avec succès !", + + "GameUpdateCompleted_Title": "{0} · Mise à jour terminée", + "GameUpdateCompleted_Subtitle": "{0} a été mis à jour vers la v{1} !", + + "GamePreloadCompleted_Title": "{0} · Préchargement terminé", + + "GameRepairCheckCompleted_Title": "Vérification des ressources du jeu terminée", + "GameRepairCheckCompletedFound_Subtitle": "{0} fichier(s) a/ont besoin d'être réparé(s)/mis à jour. Cliquez sur cette notification pour revenir au launcher.", + "GameRepairCheckCompletedNotFound_Subtitle": "Aucun fichier ne nécessite de réparation/mise à jour.", + + "GameRepairDownloadCompleted_Title": "Réparation des ressources terminée", + "GameRepairDownloadCompleted_Subtitle": "{0} fichier(s) a/ont été réparé(s)/mis à jour avec succès.", + + "CacheUpdateCheckCompleted_Title": "Vérification des mises à jour des caches terminée", + "CacheUpdateCheckCompletedFound_Subtitle": "{0} fichier(s) de cache doit/doivent être mis à jour. Cliquez sur cette notification pour revenir au launcher.", + "CacheUpdateCheckCompletedNotFound_Subtitle": "Aucun fichier de cache n'a besoin d'être mis à jour.", + + "CacheUpdateDownloadCompleted_Title": "Téléchargement de la mise à jour des caches terminée", + "CacheUpdateDownloadCompleted_Subtitle": "{0} fichier(s) de cache a/ont été mis à jour avec succès.", + + "GenericClickNotifToGoBack_Subtitle": "Cliquez sur cette notification pour revenir au launcher.", + + "OOBE_WelcomeTitle": "Bienvenue sur Collapse Launcher !", + "OOBE_WelcomeSubtitle": "Vous sélectionnez actuellement {0} - {1} comme jeu. D'autres jeux vous attendent, découvrez-en plus !", + + "LauncherUpdated_NotifTitle": "Votre launcher est à jour !", + "LauncherUpdated_NotifSubtitle": "Votre launcher a été mis à jour vers la v{0}. Allez dans « {1} » et cliquez sur « {2} » pour afficher les derniers changements." } } diff --git a/Hi3Helper.Core/Lang/ja_JP.json b/Hi3Helper.Core/Lang/ja_JP.json index 806652ba3..a6386915d 100644 --- a/Hi3Helper.Core/Lang/ja_JP.json +++ b/Hi3Helper.Core/Lang/ja_JP.json @@ -995,7 +995,13 @@ "CloseOverlay": "閉じる", "DbGenerateUid_Title": "本当にユーザーIDを変更しますか?", - "DbGenerateUid_Content": "現在のユーザーIDを変更すると、ユーザーIDを紛失した場合にその関連データも失われます。" + "DbGenerateUid_Content": "現在のユーザーIDを変更すると、ユーザーIDを紛失した場合にその関連データも失われます。", + + "SophonIncrementUpdateUnavailTitle": "バージョンが古すぎるため、差分更新式アップデートができません!", + "SophonIncrementUpdateUnavailSubtitle1": "現在お使いのVer{0}は", + "SophonIncrementUpdateUnavailSubtitle2": "古すぎる", + "SophonIncrementUpdateUnavailSubtitle3": "ため、差分更新式アップデートができません。ゲームをアップデートするには、ゲーム全体の再ダウンロードが必要です。", + "SophonIncrementUpdateUnavailSubtitle4": "\"{0}\"を押すと全体の再ダウンロードを実行し、\"{1}\"を押すとアップデートをキャンセルします。" }, "_FileMigrationProcess": { diff --git a/Hi3Helper.Core/Lang/zh_CN.json b/Hi3Helper.Core/Lang/zh_CN.json index b88d2ca08..8ad042907 100644 --- a/Hi3Helper.Core/Lang/zh_CN.json +++ b/Hi3Helper.Core/Lang/zh_CN.json @@ -995,7 +995,13 @@ "CloseOverlay": "关闭", "DbGenerateUid_Title": "您确定要更改用户 ID 吗?", - "DbGenerateUid_Content": "更改用户 ID 后,如果遗失了用户 ID,与之相关的数据将会丢失。" + "DbGenerateUid_Content": "更改用户 ID 后,如果遗失了用户 ID,与之相关的数据将会丢失。", + + "SophonIncrementUpdateUnavailTitle": "增量更新不可用: 版本过旧!", + "SophonIncrementUpdateUnavailSubtitle1": "您的游戏版本:{0}", + "SophonIncrementUpdateUnavailSubtitle2": "过旧", + "SophonIncrementUpdateUnavailSubtitle3": "且该版本的增量更新不可用。不过,您仍然可以通过重新下载整个游戏来更新您的游戏。", + "SophonIncrementUpdateUnavailSubtitle4": "点击“{0}”继续更新整个游戏,或点击“{1}”取消更新过程" }, "_FileMigrationProcess": { diff --git a/Hi3Helper.EncTool b/Hi3Helper.EncTool index d7b49a16a..330a984bb 160000 --- a/Hi3Helper.EncTool +++ b/Hi3Helper.EncTool @@ -1 +1 @@ -Subproject commit d7b49a16a91620769d43da5868c30a8423ca594e +Subproject commit 330a984bb650dd219cdc575f59a39ff1a1881db5 diff --git a/Hi3Helper.Sophon b/Hi3Helper.Sophon index d7528e9a1..122c6193f 160000 --- a/Hi3Helper.Sophon +++ b/Hi3Helper.Sophon @@ -1 +1 @@ -Subproject commit d7528e9a19fe5062ccc75c78f03608579eaea383 +Subproject commit 122c6193f6e577fe21d28d7832c75e4251d0c5b3 diff --git a/Hi3Helper.Win32 b/Hi3Helper.Win32 index 7ea65c100..363717d24 160000 --- a/Hi3Helper.Win32 +++ b/Hi3Helper.Win32 @@ -1 +1 @@ -Subproject commit 7ea65c100f99e716cbf726364f46095835f4d3e8 +Subproject commit 363717d24cf3a426fff0418de73c1205ef8f07f4 diff --git a/README.md b/README.md index 6dde713c7..c4bfa7c5a 100644 --- a/README.md +++ b/README.md @@ -229,11 +229,11 @@ Not only that, this launcher also has some advanced features for **Genshin Impac > > Please keep in mind that the Game Conversion feature is currently only available for Honkai Impact: 3rd. Other miHoYo/Cognosphere Pte. Ltd. games are currently not planned for game conversion. # Download Ready-To-Use Builds -[](https://github.com/CollapseLauncher/Collapse/releases/download/CL-v1.82.11/CollapseLauncher-stable-Setup.exe) -> **Note**: The version for this build is `1.82.11` (Released on: January 1st, 2025). +[](https://github.com/CollapseLauncher/Collapse/releases/download/CL-v1.82.12/CollapseLauncher-stable-Setup.exe) +> **Note**: The version for this build is `1.82.12` (Released on: January 2nd, 2025). -[](https://github.com/CollapseLauncher/Collapse/releases/download/CL-v1.82.11-pre/CollapseLauncher-preview-Setup.exe) -> **Note**: The version for this build is `1.82.11` (Released on: January 1st, 2025). +[](https://github.com/CollapseLauncher/Collapse/releases/download/CL-v1.82.12-pre/CollapseLauncher-preview-Setup.exe) +> **Note**: The version for this build is `1.82.12` (Released on: January 2nd, 2025). To view all releases, [**click here**](https://github.com/neon-nyan/CollapseLauncher/releases).