diff --git a/CollapseLauncher/Classes/Helper/StreamUtility.cs b/CollapseLauncher/Classes/Helper/StreamUtility.cs index 7b932743a..071601a08 100644 --- a/CollapseLauncher/Classes/Helper/StreamUtility.cs +++ b/CollapseLauncher/Classes/Helper/StreamUtility.cs @@ -54,12 +54,19 @@ internal static FileInfo EnsureNoReadOnly(this FileInfo fileInfo) internal static FileInfo EnsureNoReadOnly(this FileInfo fileInfo, out bool isFileExist) { - if (!(isFileExist = fileInfo.Exists)) - return fileInfo; + try + { + if (!(isFileExist = fileInfo.Exists)) + return fileInfo; - fileInfo.IsReadOnly = false; + fileInfo.IsReadOnly = false; - return fileInfo; + return fileInfo; + } + finally + { + fileInfo.Refresh(); + } } internal static IEnumerable EnumerateNoReadOnly(this IEnumerable enumeratedFileInfo) @@ -111,10 +118,17 @@ internal static FileInfo EnsureCreationOfDirectory(this FileInfo filePath) ArgumentNullException.ThrowIfNull(filePath, nameof(filePath)); DirectoryInfo? directoryInfo = filePath.Directory; - if (directoryInfo is { Exists: false }) - directoryInfo.Create(); + try + { + if (directoryInfo is { Exists: false }) + directoryInfo.Create(); - return filePath; + return filePath; + } + finally + { + filePath.Refresh(); + } } } } diff --git a/CollapseLauncher/Classes/InstallManagement/BaseClass/InstallManagerBase.cs b/CollapseLauncher/Classes/InstallManagement/BaseClass/InstallManagerBase.cs index 77c76a0e6..729bc0dc5 100644 --- a/CollapseLauncher/Classes/InstallManagement/BaseClass/InstallManagerBase.cs +++ b/CollapseLauncher/Classes/InstallManagement/BaseClass/InstallManagerBase.cs @@ -44,6 +44,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Net; @@ -1793,7 +1794,7 @@ void StartWriteInner(byte[] bufferInner, FileStream outputStream, Stream entrySt _progressPerFileSizeCurrent += read; // Calculate the speed - lock (_progress) _progress.ProgressAllSpeed = CalculateSpeed(read); + double speed = CalculateSpeed(read); if (!CheckIfNeedRefreshStopwatch()) { @@ -1807,16 +1808,13 @@ void StartWriteInner(byte[] bufferInner, FileStream outputStream, Stream entrySt _progress.ProgressPerFileSizeTotal = _progressPerFileSizeTotal; _progress.ProgressAllSizeCurrent = _progressAllSizeCurrent; _progress.ProgressAllSizeTotal = _progressAllSizeTotal; + _progress.ProgressAllSpeed = speed; // Calculate percentage - _progress.ProgressAllPercentage = - Math.Round((double)_progressAllSizeCurrent / _progressAllSizeTotal * 100, 2); - _progress.ProgressPerFilePercentage = - Math.Round((double)_progressPerFileSizeCurrent / _progressPerFileSizeTotal * 100, 2); + _progress.ProgressAllPercentage = ConverterTool.ToPercentage(_progressAllSizeTotal, _progressAllSizeCurrent); + _progress.ProgressPerFilePercentage = ConverterTool.ToPercentage(_progressPerFileSizeTotal, _progressPerFileSizeCurrent); // Calculate the timelapse - _progress.ProgressAllTimeLeft = - ((_progressAllSizeTotal - _progressAllSizeCurrent) / _progress.ProgressAllSpeed.Unzeroed()) - .ToTimeSpanNormalized(); + _progress.ProgressAllTimeLeft = ConverterTool.ToTimeSpanRemain(_progressAllSizeTotal, _progressAllSizeCurrent, speed); } UpdateAll(); @@ -2377,13 +2375,9 @@ public virtual async ValueTask ApplyHdiffListPatch() lock (_progress) { _progress.ProgressAllSizeCurrent += entry.fileSize; - _progress.ProgressAllPercentage = - Math.Round(_progress.ProgressAllSizeCurrent / _progress.ProgressAllSizeTotal * 100, 2); + _progress.ProgressAllPercentage = ConverterTool.ToPercentage(_progress.ProgressAllSizeTotal, _progress.ProgressAllSizeCurrent); _progress.ProgressAllSpeed = CalculateSpeed(entry.fileSize); - - _progress.ProgressAllTimeLeft = - ((_progress.ProgressAllSizeTotal - _progress.ProgressAllSizeCurrent) / - _progress.ProgressAllSpeed.Unzeroed()).ToTimeSpanNormalized(); + _progress.ProgressAllTimeLeft = ConverterTool.ToTimeSpanRemain(_progress.ProgressAllSizeTotal, _progress.ProgressAllSizeCurrent, _progress.ProgressAllSpeed); } UpdateProgress(); @@ -2434,21 +2428,16 @@ public virtual async ValueTask ApplyHdiffListPatch() private void EventListener_PatchEvent(object sender, PatchEvent e) { - lock (_progress) - { - _progress.ProgressAllSizeCurrent += e.Read; - } + Interlocked.Add(ref _progress.ProgressAllSizeCurrent, e.Read); + double speed = CalculateSpeed(e.Read); + if (CheckIfNeedRefreshStopwatch()) { lock (_progress) { - _progress.ProgressAllPercentage = - Math.Round(_progress.ProgressAllSizeCurrent / _progress.ProgressAllSizeTotal * 100, 2); - _progress.ProgressAllSpeed = CalculateSpeed(e.Read); - - _progress.ProgressAllTimeLeft = - ((_progress.ProgressAllSizeTotal - _progress.ProgressAllSizeCurrent) / - _progress.ProgressAllSpeed.Unzeroed()).ToTimeSpanNormalized(); + _progress.ProgressAllSpeed = speed; + _progress.ProgressAllPercentage = ConverterTool.ToPercentage(_progress.ProgressAllSizeTotal, _progress.ProgressAllSizeCurrent); + _progress.ProgressAllTimeLeft = ConverterTool.ToTimeSpanRemain(_progress.ProgressAllSizeTotal, _progress.ProgressAllSizeCurrent, _progress.ProgressAllSpeed); } UpdateProgress(); } @@ -2541,6 +2530,7 @@ public virtual List TryGetHDiffList() return _out; } +#nullable enable protected virtual string GetLanguageLocaleCodeByID(int id) { return id switch @@ -2553,7 +2543,7 @@ protected virtual string GetLanguageLocaleCodeByID(int id) }; } - protected virtual int GetIDByLanguageLocaleCode(string localeCode) + protected virtual int GetIDByLanguageLocaleCode([NotNull] string? localeCode) { return localeCode switch { @@ -2565,7 +2555,7 @@ protected virtual int GetIDByLanguageLocaleCode(string localeCode) }; } - protected virtual string GetLanguageStringByLocaleCode(string localeCode) + protected virtual string GetLanguageStringByLocaleCode([NotNull] string? localeCode) { return localeCode switch { @@ -2589,7 +2579,7 @@ protected virtual string GetLanguageStringByID(int id) }; } - protected virtual string GetLanguageLocaleCodeByLanguageString(string langString) + protected virtual string GetLanguageLocaleCodeByLanguageString([NotNull] string? langString) { return langString switch { @@ -2602,7 +2592,7 @@ protected virtual string GetLanguageLocaleCodeByLanguageString(string langString }; } - protected virtual string GetLanguageDisplayByLocaleCode(string localeCode, bool throwIfInvalid = true) + protected virtual string? GetLanguageDisplayByLocaleCode([NotNullIfNotNull(nameof(localeCode))] string? localeCode, bool throwIfInvalid = true) { return localeCode switch { @@ -2634,7 +2624,7 @@ protected virtual Dictionary GetLanguageDisplayDictFromVoicePack foreach (RegionResourceVersion Entry in voicePacks) { // Check the lang ID and add the translation of the language to the list - string languageDisplay = GetLanguageDisplayByLocaleCode(Entry.language, false); + string? languageDisplay = GetLanguageDisplayByLocaleCode(Entry.language, false); if (string.IsNullOrEmpty(languageDisplay)) { continue; @@ -2658,7 +2648,7 @@ protected virtual List GetSophonLanguageDisplayDictFromVoicePackList(Sop string localeCode = identity.MatchingField.ToLower(); if (IsValidLocaleCode(localeCode)) { - string languageDisplay = GetLanguageDisplayByLocaleCode(localeCode, false); + string? languageDisplay = GetLanguageDisplayByLocaleCode(localeCode, false); if (string.IsNullOrEmpty(languageDisplay)) { continue; @@ -2671,20 +2661,26 @@ protected virtual List GetSophonLanguageDisplayDictFromVoicePackList(Sop return value; } - protected virtual void RearrangeLegacyPackageLocaleOrder(RegionResourceVersion regionResource) + protected virtual void RearrangeLegacyPackageLocaleOrder(RegionResourceVersion? regionResource) { // Rearrange the region resource list order based on matching field for the locale - RearrangeDataListLocaleOrder(regionResource.voice_packs, x => x.language); + RearrangeDataListLocaleOrder(regionResource?.voice_packs, x => x.language); } - protected virtual void RearrangeSophonDataLocaleOrder(SophonData sophonData) + protected virtual void RearrangeSophonDataLocaleOrder(SophonData? sophonData) { // Rearrange the sophon data list order based on matching field for the locale - RearrangeDataListLocaleOrder(sophonData.ManifestIdentityList, x => x.MatchingField); + RearrangeDataListLocaleOrder(sophonData?.ManifestIdentityList, x => x.MatchingField); } - protected virtual void RearrangeDataListLocaleOrder(List assetDataList, Func matchingFieldPredicate) + protected virtual void RearrangeDataListLocaleOrder(List? assetDataList, Func matchingFieldPredicate) { + // If the asset list is null or empty, return + if (assetDataList == null || assetDataList.Count == 0) + { + return; + } + // Get ordered locale string string[] localeStringOrder = _gameVoiceLanguageLocaleIdOrdered; @@ -2692,14 +2688,14 @@ protected virtual void RearrangeDataListLocaleOrder(List assetDataList, Fu List manifestListMain = assetDataList .Where(x => !IsValidLocaleCode(matchingFieldPredicate(x))) .ToList(); - List manifestListLocale = assetDataList. - Where(x => IsValidLocaleCode(matchingFieldPredicate(x))) + List manifestListLocale = assetDataList + .Where(x => IsValidLocaleCode(matchingFieldPredicate(x))) .ToList(); // SLOW: Order the locale manifest list by the localeStringOrder for (int i = 0; i < localeStringOrder.Length; i++) { - var localeFound = manifestListLocale.FirstOrDefault(x => matchingFieldPredicate(x).Equals(localeStringOrder[i], StringComparison.OrdinalIgnoreCase)); + var localeFound = manifestListLocale.FirstOrDefault(x => matchingFieldPredicate(x)?.Equals(localeStringOrder[i], StringComparison.OrdinalIgnoreCase) ?? false); if (localeFound != null) { // Move from main to locale @@ -2720,7 +2716,7 @@ protected virtual void RearrangeDataListLocaleOrder(List assetDataList, Fu } protected virtual bool TryGetVoiceOverResourceByLocaleCode(List verResList, - string localeCode, out RegionResourceVersion outRes) + string localeCode, [NotNullWhen(true)] out RegionResourceVersion? outRes) { outRes = null; // Sanitation check: Check if the localeId argument is null or have no content @@ -2736,8 +2732,7 @@ protected virtual bool TryGetVoiceOverResourceByLocaleCode(List x.language != null && - x.language.Equals(localeCode, StringComparison.OrdinalIgnoreCase)); + outRes = verResList.FirstOrDefault(x => x.language?.Equals(localeCode, StringComparison.OrdinalIgnoreCase) ?? false); if (outRes == null) { return false; @@ -2746,6 +2741,7 @@ protected virtual bool TryGetVoiceOverResourceByLocaleCode(List localeCode) { @@ -4210,39 +4206,39 @@ protected void DeltaPatchCheckProgress(object sender, TotalPerFileProgress e) private void ZipProgressAdapter(object sender, ExtractProgressProp e) { - if (CheckIfNeedRefreshStopwatch()) + // Increment current total size + lock (_progress) { - // Incrment current total size long lastSize = GetLastSize((long)e.TotalRead); + double speed = CalculateSpeed(lastSize); _progressAllSizeCurrent += lastSize; - // Assign per file size - _progressPerFileSizeCurrent = (long)e.TotalRead; - _progressPerFileSizeTotal = (long)e.TotalSize; - - lock (_progress) + if (CheckIfNeedRefreshStopwatch()) { - // Assign local sizes to progress - _progress.ProgressPerFileSizeCurrent = _progressPerFileSizeCurrent; - _progress.ProgressPerFileSizeTotal = _progressPerFileSizeTotal; - _progress.ProgressAllSizeCurrent = _progressAllSizeCurrent; - _progress.ProgressAllSizeTotal = _progressAllSizeTotal; + // Assign per file size + _progressPerFileSizeCurrent = (long)e.TotalRead; + _progressPerFileSizeTotal = (long)e.TotalSize; - // Calculate the speed - _progress.ProgressAllSpeed = CalculateSpeed(lastSize); - - // Calculate percentage - _progress.ProgressAllPercentage = - Math.Round((double)_progressAllSizeCurrent / _progressAllSizeTotal * 100, 2); - _progress.ProgressPerFilePercentage = - Math.Round((double)_progressPerFileSizeCurrent / _progressPerFileSizeTotal * 100, 2); - // Calculate the timelapse - _progress.ProgressAllTimeLeft = - ((_progressAllSizeTotal - _progressAllSizeCurrent) / _progress.ProgressAllSpeed.Unzeroed()) - .ToTimeSpanNormalized(); - } + lock (_progress) + { + // Assign local sizes to progress + _progress.ProgressPerFileSizeCurrent = _progressPerFileSizeCurrent; + _progress.ProgressPerFileSizeTotal = _progressPerFileSizeTotal; + _progress.ProgressAllSizeCurrent = _progressAllSizeCurrent; + _progress.ProgressAllSizeTotal = _progressAllSizeTotal; - UpdateAll(); + // Calculate the speed + _progress.ProgressAllSpeed = CalculateSpeed(lastSize); + + // Calculate percentage + _progress.ProgressAllPercentage = ConverterTool.ToPercentage(_progressAllSizeTotal, _progressAllSizeCurrent); + _progress.ProgressPerFilePercentage = ConverterTool.ToPercentage(_progressPerFileSizeTotal, _progressPerFileSizeCurrent); + // Calculate the timelapse + _progress.ProgressAllTimeLeft = ConverterTool.ToTimeSpanRemain(_progressAllSizeTotal, _progressAllSizeCurrent, speed); + } + + UpdateAll(); + } } } @@ -4274,32 +4270,21 @@ private void HttpClientDownloadProgressAdapter(int read, DownloadProgress downlo { lock (_progress) { - // Assign local sizes to progress - _progress.ProgressPerFileSizeCurrent = _progressPerFileSizeCurrent; - _progress.ProgressPerFileSizeTotal = _progressPerFileSizeTotal; - _progress.ProgressAllSizeCurrent = _progressAllSizeCurrent; - _progress.ProgressAllSizeTotal = _progressAllSizeTotal; - // Assign speed with clamped value double speedClamped = speedAll.ClampLimitedSpeedNumber(); - _progress.ProgressAllSpeed = speedClamped; - // Calculate percentage - _progress.ProgressPerFilePercentage = - Math.Round(_progressPerFileSizeCurrent / (double)_progressPerFileSizeTotal * 100, 2); - _progress.ProgressAllPercentage = - Math.Round(_progressAllSizeCurrent / (double)_progressAllSizeTotal * 100, 2); - - // Calculate the timelapse - double progressTimeAvg = (_progressAllSizeTotal - _progressAllSizeCurrent) / speedClamped; - _progress.ProgressAllTimeLeft = progressTimeAvg.ToTimeSpanNormalized(); + // Assign local sizes to progress + _progress.ProgressAllSizeCurrent = _progressAllSizeCurrent; + _progress.ProgressAllSizeTotal = _progressAllSizeTotal; + _progress.ProgressAllSpeed = speedClamped; + _progress.ProgressAllPercentage = ConverterTool.ToPercentage(_progressAllSizeTotal, _progressAllSizeCurrent); + _progress.ProgressAllTimeLeft = ConverterTool.ToTimeSpanRemain(_progressAllSizeTotal, _progressAllSizeCurrent, speedClamped); // Update the status of per file size and current progress from Http client - _progressPerFileSizeCurrent = downloadProgress.BytesDownloaded; - _progressPerFileSizeTotal = downloadProgress.BytesTotal; - _progress.ProgressPerFilePercentage = - ConverterTool.GetPercentageNumber(downloadProgress.BytesDownloaded, - downloadProgress.BytesTotal); + _progress.ProgressPerFileSizeCurrent = downloadProgress.BytesDownloaded; + _progress.ProgressPerFileSizeTotal = downloadProgress.BytesTotal; + _progress.ProgressPerFileSpeed = speedClamped; + _progress.ProgressPerFilePercentage = ConverterTool.ToPercentage(downloadProgress.BytesTotal, downloadProgress.BytesDownloaded); } // Update the status UpdateAll(); @@ -4337,15 +4322,10 @@ private void HttpClientDownloadProgressAdapter(object sender, DownloadEvent e) _progress.ProgressAllSpeed = speedAll; // Calculate percentage - _progress.ProgressPerFilePercentage = - Math.Round(_progressPerFileSizeCurrent / (double)_progressPerFileSizeTotal * 100, 2); - _progress.ProgressAllPercentage = - Math.Round(_progressAllSizeCurrent / (double)_progressAllSizeTotal * 100, 2); - + _progress.ProgressPerFilePercentage = ConverterTool.ToPercentage(_progressPerFileSizeTotal, _progressPerFileSizeCurrent); + _progress.ProgressAllPercentage = ConverterTool.ToPercentage(_progressAllSizeTotal, _progressAllSizeCurrent); // Calculate the timelapse - _progress.ProgressAllTimeLeft = - ((_progressAllSizeTotal - _progressAllSizeCurrent) / _progress.ProgressAllSpeed.Unzeroed()) - .ToTimeSpanNormalized(); + _progress.ProgressAllTimeLeft = ConverterTool.ToTimeSpanRemain(_progressAllSizeTotal, _progressAllSizeCurrent, speedAll); } } else @@ -4366,8 +4346,7 @@ private void HttpClientDownloadProgressAdapter(object sender, DownloadEvent e) _progress.ProgressPerFileSizeTotal = _progressPerFileSizeTotal; _progress.ProgressAllSizeCurrent = _progressAllSizeCurrent; _progress.ProgressAllSizeTotal = _progressAllSizeTotal; - _progress.ProgressAllPercentage = - Math.Round(_progressAllSizeCurrent / (double)_progressAllSizeTotal * 100, 2); + _progress.ProgressAllPercentage = ConverterTool.ToPercentage(_progressAllSizeTotal, _progressAllSizeCurrent); } } diff --git a/CollapseLauncher/Classes/InstallManagement/GameConversionManagement.cs b/CollapseLauncher/Classes/InstallManagement/GameConversionManagement.cs index 1c9ceae2d..028474b7a 100644 --- a/CollapseLauncher/Classes/InstallManagement/GameConversionManagement.cs +++ b/CollapseLauncher/Classes/InstallManagement/GameConversionManagement.cs @@ -461,12 +461,12 @@ public ConvertProgress(long StartSize, long EndSize, int StartCount, int EndCoun public long EndSize { get; private set; } public int StartCount { get; private set; } public int EndCount { get; private set; } - public double Percentage => UseCountUnit ? Math.Round((StartCount / (double)EndCount) * 100, 2) : - Math.Round((StartSize / (double)EndSize) * 100, 2); - public long ProgressSpeed => (long)(StartSize / _TimeSecond); - public TimeSpan RemainingTime => UseCountUnit ? TimeSpan.FromSeconds(0f) : - ((EndSize - StartSize) / Unzeroed(ProgressSpeed)).ToTimeSpanNormalized(); - private double Unzeroed(double i) => i == 0 ? 1 : i; + public double Percentage => UseCountUnit ? ToPercentage(EndCount, StartCount) : + ToPercentage(EndSize, StartSize); + public double ProgressSpeed => StartSize / _TimeSecond; + public TimeSpan RemainingTime => UseCountUnit ? TimeSpan.Zero : + ToTimeSpanRemain(EndSize, StartSize, ProgressSpeed); + public string ProgressStatus => _StatusMsg; public string ProgressDetail => string.Format( "[{0}] ({1})\r\n{2}...", diff --git a/CollapseLauncher/Classes/Interfaces/Class/ProgressBase.cs b/CollapseLauncher/Classes/Interfaces/Class/ProgressBase.cs index 1d340802d..50c08cc10 100644 --- a/CollapseLauncher/Classes/Interfaces/Class/ProgressBase.cs +++ b/CollapseLauncher/Classes/Interfaces/Class/ProgressBase.cs @@ -85,27 +85,26 @@ protected virtual void _httpClient_FetchAssetProgress(int size, DownloadProgress { // Calculate the clamped speed and timelapse double speedClamped = speedAll.ClampLimitedSpeedNumber(); - double progressTimeAvg = (downloadProgress.BytesTotal - downloadProgress.BytesDownloaded) / speedClamped; - TimeSpan timeLeftSpan = progressTimeAvg.ToTimeSpanNormalized(); - double percentage = ConverterTool.GetPercentageNumber(downloadProgress.BytesDownloaded, downloadProgress.BytesTotal); + TimeSpan timeLeftSpan = ConverterTool.ToTimeSpanRemain(downloadProgress.BytesTotal, downloadProgress.BytesDownloaded, speedClamped); + double percentage = ConverterTool.ToPercentage(downloadProgress.BytesTotal, downloadProgress.BytesDownloaded); lock (_status) { // Update fetch status _status.IsProgressPerFileIndetermined = false; - _status.IsProgressAllIndetermined = false; - _status.ActivityPerFile = string.Format(Lang!._GameRepairPage!.PerProgressSubtitle3!, ConverterTool.SummarizeSizeSimple(speedClamped)); + _status.IsProgressAllIndetermined = false; + _status.ActivityPerFile = string.Format(Lang!._GameRepairPage!.PerProgressSubtitle3!, ConverterTool.SummarizeSizeSimple(speedClamped)); } lock (_progress) { // Update fetch progress _progress.ProgressPerFilePercentage = percentage; - _progress.ProgressAllSizeCurrent = downloadProgress.BytesDownloaded; - _progress.ProgressAllSizeTotal = downloadProgress.BytesTotal; - _progress.ProgressAllSpeed = speedClamped; - _progress.ProgressAllTimeLeft = timeLeftSpan; + _progress.ProgressAllSizeCurrent = downloadProgress.BytesDownloaded; + _progress.ProgressAllSizeTotal = downloadProgress.BytesTotal; + _progress.ProgressAllSpeed = speedClamped; + _progress.ProgressAllTimeLeft = timeLeftSpan; } // Push status and progress update @@ -128,42 +127,41 @@ protected virtual void _httpClient_RepairAssetProgress(int size, DownloadProgres { // Calculate the clamped speed and timelapse double speedClamped = speedAll.ClampLimitedSpeedNumber(); - double progressTimeAvg = (_progressAllSizeTotal - _progressAllSizeCurrent) / speedClamped; - TimeSpan timeLeftSpan = progressTimeAvg.ToTimeSpanNormalized(); - double percentagePerFile = ConverterTool.GetPercentageNumber(downloadProgress.BytesDownloaded, downloadProgress.BytesTotal); + TimeSpan timeLeftSpan = ConverterTool.ToTimeSpanRemain(_progressAllSizeTotal, _progressAllSizeCurrent, speedClamped); + double percentagePerFile = ConverterTool.ToPercentage(downloadProgress.BytesTotal, downloadProgress.BytesDownloaded); lock (_progress) { - _progress.ProgressPerFilePercentage = percentagePerFile; + _progress.ProgressPerFilePercentage = percentagePerFile; _progress.ProgressPerFileSizeCurrent = downloadProgress.BytesDownloaded; - _progress.ProgressPerFileSizeTotal = downloadProgress.BytesTotal; - _progress.ProgressAllSizeCurrent = _progressAllSizeCurrent; - _progress.ProgressAllSizeTotal = _progressAllSizeTotal; + _progress.ProgressPerFileSizeTotal = downloadProgress.BytesTotal; + _progress.ProgressAllSizeCurrent = _progressAllSizeCurrent; + _progress.ProgressAllSizeTotal = _progressAllSizeTotal; // Calculate speed - _progress.ProgressAllSpeed = speedClamped; + _progress.ProgressAllSpeed = speedClamped; _progress.ProgressAllTimeLeft = timeLeftSpan; // Update current progress percentages _progress.ProgressAllPercentage = _progressAllSizeCurrent != 0 ? - ConverterTool.GetPercentageNumber(_progressAllSizeCurrent, _progressAllSizeTotal) : + ConverterTool.ToPercentage(_progressAllSizeTotal, _progressAllSizeCurrent) : 0; } lock (_status) { // Update current activity status - _status.IsProgressAllIndetermined = false; + _status.IsProgressAllIndetermined = false; _status.IsProgressPerFileIndetermined = false; // Set time estimation string string timeLeftString = string.Format(Lang!._Misc!.TimeRemainHMSFormat!, _progress.ProgressAllTimeLeft); _status.ActivityPerFile = string.Format(Lang._Misc.Speed!, ConverterTool.SummarizeSizeSimple(_progress.ProgressAllSpeed)); - _status.ActivityAll = string.Format(Lang._GameRepairPage!.PerProgressSubtitle2!, - ConverterTool.SummarizeSizeSimple(_progressAllSizeCurrent), - ConverterTool.SummarizeSizeSimple(_progressAllSizeTotal)) + $" | {timeLeftString}"; + _status.ActivityAll = string.Format(Lang._GameRepairPage!.PerProgressSubtitle2!, + ConverterTool.SummarizeSizeSimple(_progressAllSizeCurrent), + ConverterTool.SummarizeSizeSimple(_progressAllSizeTotal)) + $" | {timeLeftString}"; // Trigger update UpdateAll(); @@ -197,11 +195,9 @@ protected virtual void _httpClient_UpdateAssetProgress(int size, DownloadProgres if (CheckIfNeedRefreshStopwatch()) { // Calculate the clamped speed and timelapse - double speedClamped = speedAll.ClampLimitedSpeedNumber(); - double progressTimeAvg = (_progressAllSizeTotal - _progressAllSizeCurrent) / speedClamped; - - TimeSpan timeLeftSpan = progressTimeAvg.ToTimeSpanNormalized(); - double percentage = ConverterTool.GetPercentageNumber(_progressAllSizeCurrent, _progressAllSizeTotal); + double speedClamped = speedAll.ClampLimitedSpeedNumber(); + TimeSpan timeLeftSpan = ConverterTool.ToTimeSpanRemain(_progressAllSizeTotal, _progressAllSizeCurrent, speedClamped); + double percentage = ConverterTool.ToPercentage(_progressAllSizeTotal, _progressAllSizeCurrent); // Update current progress percentages and speed lock (_progress) @@ -211,12 +207,12 @@ protected virtual void _httpClient_UpdateAssetProgress(int size, DownloadProgres // Update current activity status _status.IsProgressAllIndetermined = false; - string timeLeftString = string.Format(Lang._Misc.TimeRemainHMSFormat, timeLeftSpan); - _status.ActivityAll = string.Format(Lang._Misc.Downloading + ": {0}/{1} ", _progressAllCountCurrent, - _progressAllCountTotal) - + string.Format($"({Lang._Misc.SpeedPerSec})", - ConverterTool.SummarizeSizeSimple(speedClamped)) - + $" | {timeLeftString}"; + string timeLeftString = string.Format(Lang._Misc.TimeRemainHMSFormat, timeLeftSpan); + _status.ActivityAll = string.Format(Lang._Misc.Downloading + ": {0}/{1} ", _progressAllCountCurrent, + _progressAllCountTotal) + + string.Format($"({Lang._Misc.SpeedPerSec})", + ConverterTool.SummarizeSizeSimple(speedClamped)) + + $" | {timeLeftString}"; // Trigger update UpdateAll(); @@ -234,7 +230,7 @@ protected virtual void RepairTypeActionPatching_ProgressChanged(object? sender, // Update current progress percentages _progress.ProgressAllPercentage = _progressAllSizeCurrent != 0 ? - ConverterTool.GetPercentageNumber(_progressAllSizeCurrent, _progressAllSizeTotal) : + ConverterTool.ToPercentage(_progressAllSizeTotal, _progressAllSizeCurrent) : 0; } @@ -243,10 +239,13 @@ protected virtual void RepairTypeActionPatching_ProgressChanged(object? sender, lock (_status) { // Update current activity status - _status.IsProgressAllIndetermined = false; + _status.IsProgressAllIndetermined = false; _status.IsProgressPerFileIndetermined = false; - _status.ActivityPerFile = string.Format(Lang!._GameRepairPage!.PerProgressSubtitle5!, ConverterTool.SummarizeSizeSimple(_progress.ProgressAllSpeed)); - _status.ActivityAll = string.Format(Lang._GameRepairPage.PerProgressSubtitle2!, ConverterTool.SummarizeSizeSimple(_progressAllSizeCurrent), ConverterTool.SummarizeSizeSimple(_progressAllSizeTotal)); + _status.ActivityPerFile = string.Format(Lang!._GameRepairPage!.PerProgressSubtitle5!, + ConverterTool.SummarizeSizeSimple(_progress.ProgressAllSpeed)); + _status.ActivityAll = string.Format(Lang._GameRepairPage.PerProgressSubtitle2!, + ConverterTool.SummarizeSizeSimple(_progressAllSizeCurrent), + ConverterTool.SummarizeSizeSimple(_progressAllSizeTotal)); } // Trigger update @@ -270,10 +269,10 @@ protected virtual void UpdateProgressCRC(long read) { // Update current progress percentages _progress.ProgressPerFilePercentage = _progressPerFileSizeCurrent != 0 ? - ConverterTool.GetPercentageNumber(_progressPerFileSizeCurrent, _progressPerFileSizeTotal) : + ConverterTool.ToPercentage(_progressPerFileSizeTotal, _progressPerFileSizeCurrent) : 0; _progress.ProgressAllPercentage = _progressAllSizeCurrent != 0 ? - ConverterTool.GetPercentageNumber(_progressAllSizeCurrent, _progressAllSizeTotal) : + ConverterTool.ToPercentage(_progressAllSizeTotal, _progressAllSizeCurrent) : 0; // Update the progress of total size @@ -286,7 +285,7 @@ protected virtual void UpdateProgressCRC(long read) _progress.ProgressAllSpeed = speedAll; // Calculate the timelapse - _progress.ProgressAllTimeLeft = ((_progressAllSizeTotal - _progressAllSizeCurrent) / ConverterTool.Unzeroed(_progress.ProgressAllSpeed)).ToTimeSpanNormalized(); + _progress.ProgressAllTimeLeft = ConverterTool.ToTimeSpanRemain(_progressAllSizeTotal, _progressAllSizeCurrent, speedAll); } lock (_status) @@ -317,17 +316,17 @@ protected virtual void UpdateProgressCopyStream(long currentPosition, int read, lock (_progress) { // Update current progress percentages - _progress.ProgressPerFilePercentage = ConverterTool.GetPercentageNumber(currentPosition, totalReadSize); + _progress.ProgressPerFilePercentage = ConverterTool.ToPercentage(totalReadSize, currentPosition); // Update the progress of total size _progress.ProgressPerFileSizeCurrent = currentPosition; - _progress.ProgressPerFileSizeTotal = totalReadSize; + _progress.ProgressPerFileSizeTotal = totalReadSize; // Calculate current speed and update the status and progress speed _progress.ProgressAllSpeed = speedAll; // Calculate the timelapse - _progress.ProgressAllTimeLeft = ((totalReadSize - currentPosition) / _progress.ProgressAllSpeed.Unzeroed()).ToTimeSpanNormalized(); + _progress.ProgressAllTimeLeft = ConverterTool.ToTimeSpanRemain(totalReadSize, currentPosition, speedAll); } lock (_status) @@ -337,7 +336,7 @@ protected virtual void UpdateProgressCopyStream(long currentPosition, int read, // Update current activity status _status.ActivityPerFile = string.Format(Lang._Misc.Speed!, ConverterTool.SummarizeSizeSimple(_progress.ProgressAllSpeed)); - _status.ActivityAll = string.Format(Lang._GameRepairPage!.PerProgressSubtitle2!, + _status.ActivityAll = string.Format(Lang._GameRepairPage!.PerProgressSubtitle2!, ConverterTool.SummarizeSizeSimple(currentPosition), ConverterTool.SummarizeSizeSimple(totalReadSize)) + $" | {timeLeftString}"; } @@ -427,8 +426,6 @@ protected void UpdateSophonFileTotalProgress(long read) _sophonProgress.ProgressPerFileSizeCurrent = _progressPerFileSizeCurrent; _sophonProgress.ProgressPerFileSizeTotal = _progressPerFileSizeTotal; - double progressTimeAvg = (_progressAllSizeTotal - _progressAllSizeCurrent) / speedAll; - _sophonProgress.ProgressAllSpeed = speedAll; _sophonProgress.ProgressPerFileSpeed = speedDownloadClamped; @@ -442,12 +439,9 @@ protected void UpdateSophonFileTotalProgress(long read) StatusChanged?.Invoke(this, _status); // Calculate percentage - _sophonProgress.ProgressAllPercentage = - Math.Round((double)_progressAllSizeCurrent / _progressAllSizeTotal * 100, 2); - _sophonProgress.ProgressPerFilePercentage = - Math.Round((double)_progressPerFileSizeCurrent / _progressPerFileSizeTotal * 100, 2); - - _sophonProgress.ProgressAllTimeLeft = progressTimeAvg.ToTimeSpanNormalized(); + _sophonProgress.ProgressAllPercentage = ConverterTool.ToPercentage(_progressAllSizeTotal, _progressAllSizeCurrent); + _sophonProgress.ProgressPerFilePercentage = ConverterTool.ToPercentage(_progressPerFileSizeTotal, _progressPerFileSizeCurrent); + _sophonProgress.ProgressAllTimeLeft = ConverterTool.ToTimeSpanRemain(_progressAllSizeTotal, _progressAllSizeCurrent, speedAll); // Update progress ProgressChanged?.Invoke(this, _sophonProgress); @@ -526,33 +520,40 @@ protected string EnsureCreationOfDirectory(string str) protected string EnsureCreationOfDirectory(FileInfo str) => str.EnsureCreationOfDirectory().FullName; - protected void TryUnassignReadOnlyFiles(string path) + protected void TryUnassignReadOnlyFiles(string dirPath) { + DirectoryInfo directoryInfo = new DirectoryInfo(dirPath); + if (!directoryInfo.Exists) + { + return; + } + // Iterate every file and set the read-only flag to false - foreach (string file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories)) + foreach (FileInfo file in directoryInfo.EnumerateFiles("*", SearchOption.AllDirectories)) { - FileInfo fileInfo = new FileInfo(file); - if (fileInfo is { Exists: true, IsReadOnly: true }) - fileInfo.IsReadOnly = false; + _ = file.EnsureNoReadOnly(); } } - protected void TryUnassignReadOnlyFileSingle(string path) + protected void TryUnassignReadOnlyFileSingle(string filePath) { - FileInfo fileInfo = new FileInfo(path); - if (fileInfo is { Exists: true, IsReadOnly: true }) - fileInfo.IsReadOnly = false; + FileInfo fileInfo = new FileInfo(filePath); + _ = fileInfo.EnsureNoReadOnly(); } protected void TryDeleteReadOnlyDir(string dirPath) { - if (!Directory.Exists(dirPath)) return; DirectoryInfo dirInfo = new DirectoryInfo(dirPath); - foreach (FileInfo files in dirInfo.EnumerateFiles("*", SearchOption.AllDirectories)) + if (!dirInfo.Exists) + { + return; + } + + foreach (FileInfo files in dirInfo.EnumerateFiles("*", SearchOption.AllDirectories) + .EnumerateNoReadOnly()) { try { - files.IsReadOnly = false; files.Delete(); } catch (Exception ex) @@ -561,8 +562,10 @@ protected void TryDeleteReadOnlyDir(string dirPath) LogWriteLine($"Failed while deleting file: {files.FullName}\r\n{ex}", LogType.Warning, true); } // Suppress errors } + try { + dirInfo.Refresh(); dirInfo.Delete(true); } catch (Exception ex) @@ -572,21 +575,20 @@ protected void TryDeleteReadOnlyDir(string dirPath) } // Suppress errors } - protected void TryDeleteReadOnlyFile(string path) + protected void TryDeleteReadOnlyFile(string filePath) { - if (!File.Exists(path)) return; + FileInfo fileInfo = new FileInfo(filePath) + .EnsureNoReadOnly(out bool isFileExist); + if (!isFileExist) return; + try { - FileInfo file = new FileInfo(path) - { - IsReadOnly = false - }; - file.Delete(); + fileInfo.Delete(); } catch (Exception ex) { SentryHelper.ExceptionHandler(ex, SentryHelper.ExceptionType.UnhandledOther); - LogWriteLine($"Failed to delete file: {path}\r\n{ex}", LogType.Error, true); + LogWriteLine($"Failed to delete file: {fileInfo.FullName}\r\n{ex}", LogType.Error, true); } } @@ -601,28 +603,30 @@ protected void MoveFolderContent(string sourcePath, string destPath) bool errorOccured = false; // Enumerate files inside of source path - foreach (string filePath in Directory.EnumerateFiles(sourcePath, "*", SearchOption.AllDirectories)) + DirectoryInfo directoryInfoSource = new DirectoryInfo(sourcePath); + if (!directoryInfoSource.Exists) + { + throw new DirectoryNotFoundException($"Cannot find source directory on this path: {sourcePath}"); + } + foreach (FileInfo fileInfo in directoryInfoSource.EnumerateFiles("*", SearchOption.AllDirectories) + .EnumerateNoReadOnly()) { // Get the relative path of the file from source path - ReadOnlySpan relativePath = filePath.AsSpan().Slice(dirLength); + ReadOnlySpan relativePath = fileInfo.FullName.AsSpan().Slice(dirLength); // Get the absolute path for destination destFilePath = Path.Combine(destPath, relativePath.ToString()); // Get folder path for destination destFolderPath = Path.GetDirectoryName(destFilePath); // Create the destination folder if not exist - if (!Directory.Exists(destFolderPath)) - Directory.CreateDirectory(destFolderPath!); + if (!string.IsNullOrEmpty(destFolderPath)) + _ = Directory.CreateDirectory(destFolderPath); try { // Try moving the file LogWriteLine($"Moving \"{relativePath.ToString()}\" to \"{destFolderPath}\"", LogType.Default, true); - FileInfo filePathInfo = new FileInfo(filePath) - { - IsReadOnly = false - }; - filePathInfo.MoveTo(destFilePath, true); + fileInfo.MoveTo(destFilePath, true); } catch (Exception ex) { @@ -635,7 +639,20 @@ protected void MoveFolderContent(string sourcePath, string destPath) // If no error occurred, then delete the source folder if (!errorOccured) - Directory.Delete(sourcePath, true); + { + try + { + directoryInfoSource.Refresh(); + directoryInfoSource.Delete(true); + } + catch (Exception ex) + { + SentryHelper.ExceptionHandler(ex, SentryHelper.ExceptionType.UnhandledOther); + // If failed, flag ErrorOccured as true and skip the source directory deletion + LogWriteLine($"Error while deleting source directory \"{directoryInfoSource.FullName}\"\r\nException: {ex}", LogType.Error, true); + errorOccured = true; + } + } } protected virtual void ResetStatusAndProgress() diff --git a/CollapseLauncher/Classes/Interfaces/Class/Structs.cs b/CollapseLauncher/Classes/Interfaces/Class/Structs.cs index b1c998435..1e016f409 100644 --- a/CollapseLauncher/Classes/Interfaces/Class/Structs.cs +++ b/CollapseLauncher/Classes/Interfaces/Class/Structs.cs @@ -9,31 +9,22 @@ namespace CollapseLauncher { internal class TotalPerFileProgress { - private double _progressPerFilePercentage; - private double _progressPerFileSpeed; - private double _progressAllPercentage; - private double _progressAllSpeed; - private long _progressPerFileEntryCountCurrent; - private long _progressPerFileEntryCountTotal; - private long _progressAllEntryCountCurrent; - private long _progressAllEntryCountTotal; - - public double ProgressPerFilePercentage { get => _progressPerFilePercentage; set => _progressPerFilePercentage = value.UnNaNInfinity(); } - public double ProgressPerFileSpeed { get => _progressPerFileSpeed; set => _progressPerFileSpeed = value.UnNaNInfinity(); } - public double ProgressAllPercentage { get => _progressAllPercentage; set => _progressAllPercentage = value.UnNaNInfinity(); } - public double ProgressAllSpeed { get => _progressAllSpeed; set => _progressAllSpeed = value.UnNaNInfinity(); } - - public long ProgressPerFileEntryCountCurrent { get => _progressPerFileEntryCountCurrent; set => _progressPerFileEntryCountCurrent = value; } - public long ProgressPerFileEntryCountTotal { get => _progressPerFileEntryCountTotal; set => _progressPerFileEntryCountTotal = value; } - public long ProgressAllEntryCountCurrent { get => _progressAllEntryCountCurrent; set => _progressAllEntryCountCurrent = value; } - public long ProgressAllEntryCountTotal { get => _progressAllEntryCountTotal; set => _progressAllEntryCountTotal = value; } + public double ProgressPerFilePercentage; + public double ProgressPerFileSpeed; + public double ProgressAllPercentage; + public double ProgressAllSpeed; + + public long ProgressPerFileEntryCountCurrent; + public long ProgressPerFileEntryCountTotal; + public long ProgressAllEntryCountCurrent; + public long ProgressAllEntryCountTotal; // Extension for IGameInstallManager - public double ProgressPerFileSizeCurrent { get; set; } - public double ProgressPerFileSizeTotal { get; set; } - public double ProgressAllSizeCurrent { get; set; } - public double ProgressAllSizeTotal { get; set; } - public TimeSpan ProgressAllTimeLeft { get; set; } + public long ProgressPerFileSizeCurrent; + public long ProgressPerFileSizeTotal; + public long ProgressAllSizeCurrent; + public long ProgressAllSizeTotal; + public TimeSpan ProgressAllTimeLeft; } internal class TotalPerFileStatus diff --git a/CollapseLauncher/Classes/RegionManagement/FallbackCDNUtil.cs b/CollapseLauncher/Classes/RegionManagement/FallbackCDNUtil.cs index 6ded5f80d..67bea5a5b 100644 --- a/CollapseLauncher/Classes/RegionManagement/FallbackCDNUtil.cs +++ b/CollapseLauncher/Classes/RegionManagement/FallbackCDNUtil.cs @@ -54,10 +54,11 @@ public async Task DownloadFile(string url, try { FallbackCDNUtil.DownloadProgress += progressEvent; + string relativePath = GetRelativePathOnly(url); await FallbackCDNUtil.DownloadCDNFallbackContent(downloadClient, targetFile, AppCurrentDownloadThread, - GetRelativePathOnly(url), + relativePath, #if !USEVELOPACK - default + default #else cancelToken #endif @@ -80,7 +81,8 @@ public async Task DownloadBytes(string url, string authorization = null, public async Task DownloadBytes(string url, string? authorization = null, string? accept = null, double timeout = 30.0) #endif { - await using BridgedNetworkStream stream = await FallbackCDNUtil.TryGetCDNFallbackStream(GetRelativePathOnly(url), default, true); + string relativePath = GetRelativePathOnly(url); + await using BridgedNetworkStream stream = await FallbackCDNUtil.TryGetCDNFallbackStream(relativePath, default, true); byte[] buffer = new byte[stream.Length]; await stream.ReadExactlyAsync(buffer); return buffer; @@ -92,16 +94,33 @@ public async Task DownloadString(string url, string authorization = null public async Task DownloadString(string url, string? authorization = null, string? accept = null, double timeout = 30.0) #endif { - await using BridgedNetworkStream stream = await FallbackCDNUtil.TryGetCDNFallbackStream(GetRelativePathOnly(url), default, true); + string relativePath = GetRelativePathOnly(url); + await using BridgedNetworkStream stream = await FallbackCDNUtil.TryGetCDNFallbackStream(relativePath, default, true); byte[] buffer = new byte[stream.Length]; await stream.ReadExactlyAsync(buffer); return Encoding.UTF8.GetString(buffer); } + private string[]? CdnBaseUrls; private string GetRelativePathOnly(string url) { - string toCompare = FallbackCDNUtil.GetPreferredCDN().URLPrefix; - return url.AsSpan(toCompare.Length).ToString(); + // Populate the CDN Base URLs if the field is null + CdnBaseUrls ??= CDNList.Select(x => x.URLPrefix).ToArray(); + + // Get URL span and iterate through the CDN Base URLs + ReadOnlySpan urlSpan = url.AsSpan(); + for (int i = 0; i < CdnBaseUrls.Length; i++) + { + // Get the index of the base URL. If not found (-1), then continue + int indexOf = urlSpan.IndexOf(CdnBaseUrls[i], StringComparison.OrdinalIgnoreCase); + if (indexOf < 0) continue; + + // Otherwise, slice the urlSpan based on the index and return the sliced relative URL. + return urlSpan.Slice(indexOf + CdnBaseUrls[i].Length).ToString(); + } + + // If it's not a CDN-based url, return it anyway. + return url; } } diff --git a/CollapseLauncher/Classes/RepairManagement/BSDiff.cs b/CollapseLauncher/Classes/RepairManagement/BSDiff.cs index a05334edd..5ac9260e5 100644 --- a/CollapseLauncher/Classes/RepairManagement/BSDiff.cs +++ b/CollapseLauncher/Classes/RepairManagement/BSDiff.cs @@ -414,7 +414,7 @@ public BinaryPatchProgress() public void UpdatePatchEvent(long sizePatched, long sizeToBePatched, long read, double totalSecond) { - this.Speed = (sizePatched / totalSecond); + this.Speed = sizePatched / totalSecond; this.SizePatched = sizePatched; this.SizeToBePatched = sizeToBePatched; this.Read = read; @@ -422,9 +422,9 @@ public void UpdatePatchEvent(long sizePatched, long sizeToBePatched, long read, public long SizePatched { get; private set; } public long SizeToBePatched { get; private set; } - public double ProgressPercentage => Math.Round((SizePatched / (double)SizeToBePatched) * 100, 2); + public double ProgressPercentage => ConverterTool.ToPercentage(SizeToBePatched, SizePatched); public long Read { get; private set; } public double Speed { get; private set; } - public TimeSpan TimeLeft => checked(((SizeToBePatched - SizePatched) / Speed.Unzeroed()).ToTimeSpanNormalized()); + public TimeSpan TimeLeft => ConverterTool.ToTimeSpanRemain(SizeToBePatched, SizePatched, Speed); } } \ No newline at end of file diff --git a/CollapseLauncher/Classes/RepairManagement/Genshin/Fetch.cs b/CollapseLauncher/Classes/RepairManagement/Genshin/Fetch.cs index ca5edf561..abb2bec1e 100644 --- a/CollapseLauncher/Classes/RepairManagement/Genshin/Fetch.cs +++ b/CollapseLauncher/Classes/RepairManagement/Genshin/Fetch.cs @@ -588,7 +588,7 @@ private void _httpClient_FetchManifestAssetProgress(int read, DownloadProgress d lock (_progress) { _progress.ProgressPerFilePercentage = - GetPercentageNumber(downloadProgress.BytesDownloaded, downloadProgress.BytesTotal); + ToPercentage(downloadProgress.BytesTotal, downloadProgress.BytesDownloaded); } // Push status and progress update diff --git a/CollapseLauncher/Classes/RepairManagement/StarRail/Check.cs b/CollapseLauncher/Classes/RepairManagement/StarRail/Check.cs index 46fc093ac..7276d2fce 100644 --- a/CollapseLauncher/Classes/RepairManagement/StarRail/Check.cs +++ b/CollapseLauncher/Classes/RepairManagement/StarRail/Check.cs @@ -1,4 +1,5 @@ -using Hi3Helper; +using CollapseLauncher.Helper; +using Hi3Helper; using Hi3Helper.Data; using Hi3Helper.SentryHelper; using Hi3Helper.Shared.ClassStruct; @@ -389,10 +390,18 @@ private static void RemoveHashMarkFile(string filePath, out string basePath, out basePath = Path.GetDirectoryName(filePath); baseName = Path.GetFileNameWithoutExtension(filePath); + // Get directory base info. If it doesn't exist, return + DirectoryInfo basePathDirInfo = new DirectoryInfo(basePath); + if (!basePathDirInfo.Exists) + { + return; + } + // Enumerate any possible existing hash path and delete it - foreach (string existingPath in Directory.EnumerateFiles(basePath!, $"{baseName}_*.hash")) + foreach (FileInfo existingPath in basePathDirInfo.EnumerateFiles($"{baseName}_*.hash") + .EnumerateNoReadOnly()) { - File.Delete(existingPath); + existingPath.Delete(); } } #endregion diff --git a/CollapseLauncher/CollapseLauncher.csproj b/CollapseLauncher/CollapseLauncher.csproj index b7c26a042..2ad805ec6 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.9 + 1.82.10 preview x64 diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/Dialogs/SimpleDialogs.cs b/CollapseLauncher/XAMLs/MainApp/Pages/Dialogs/SimpleDialogs.cs index f9e936607..7148b0d3b 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/Dialogs/SimpleDialogs.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/Dialogs/SimpleDialogs.cs @@ -472,6 +472,16 @@ public static async Task, int>> Dialog_ChooseAudioLanguageChoice Grid thisIconText = (Grid)thisCheckBox?.FindDescendant("IconText"); if (thisIconText != null) thisIconText.Opacity = 1; + + RadioButton thisRadioButton = thisCheckBox.Parent as RadioButton; + TextBlock textBlockLocal = thisRadioButton?.FindDescendant("UseAsDefaultLabel") as TextBlock; + if (thisRadioButton != null && textBlockLocal != null) + { + if (thisIndex != defaultChoiceRadioButton.SelectedIndex) + { + textBlockLocal.Opacity = 0.5; + } + } }; checkBox.Unchecked += (sender, _) => { @@ -663,24 +673,24 @@ await SpawnDialog( Lang._Misc.NoKeepInstallIt ); - public static async Task Dialog_ExistingInstallationBetterLauncher(UIElement Content, string gamePath) => + public static async Task Dialog_ExistingInstallationBetterLauncher(UIElement Content, string gamePath, bool isHasOnlyMigrateOption) => await SpawnDialog( Lang._Dialogs.ExistingInstallBHI3LTitle, string.Format(Lang._Dialogs.ExistingInstallBHI3LSubtitle, gamePath), Content, Lang._Misc.Cancel, Lang._Misc.YesMigrateIt, - Lang._Misc.NoKeepInstallIt + isHasOnlyMigrateOption ? null : Lang._Misc.NoKeepInstallIt ); - public static async Task Dialog_ExistingInstallationSteam(UIElement Content, string gamePath) => + public static async Task Dialog_ExistingInstallationSteam(UIElement Content, string gamePath, bool isHasOnlyMigrateOption) => await SpawnDialog( Lang._Dialogs.ExistingInstallSteamTitle, string.Format(Lang._Dialogs.ExistingInstallSteamSubtitle, gamePath), Content, Lang._Misc.Cancel, Lang._Misc.YesMigrateIt, - Lang._Misc.NoKeepInstallIt + isHasOnlyMigrateOption ? null : Lang._Misc.NoKeepInstallIt ); @@ -692,9 +702,9 @@ public static async Task Dialog_MigrationChoiceDialog(UIEle switch (migrateFromLauncherType) { case MigrateFromLauncherType.BetterHi3Launcher: - return await Dialog_ExistingInstallationBetterLauncher(Content, existingGamePath); + return await Dialog_ExistingInstallationBetterLauncher(Content, existingGamePath, isHasOnlyMigrateOption); case MigrateFromLauncherType.Steam: - return await Dialog_ExistingInstallationSteam(Content, existingGamePath); + return await Dialog_ExistingInstallationSteam(Content, existingGamePath, isHasOnlyMigrateOption); default: throw new InvalidOperationException($"Dialog is not supported for unknown migration!"); } diff --git a/CollapseLauncher/XAMLs/Updater/Classes/Updater.cs b/CollapseLauncher/XAMLs/Updater/Classes/Updater.cs index 07c9d24ef..e2eb1ca88 100644 --- a/CollapseLauncher/XAMLs/Updater/Classes/Updater.cs +++ b/CollapseLauncher/XAMLs/Updater/Classes/Updater.cs @@ -415,7 +415,9 @@ public UpdaterProgress(Stopwatch currentStopwatch, int counter, int counterGoal) var elapsedMin = (float)currentStopwatch.ElapsedMilliseconds / 1000 / 60; var minLeft = elapsedMin / counter * (counterGoal - counter); - TimeLeft = TimeSpan.FromMinutes(minLeft.UnNaNInfinity()); + TimeLeft = double.IsNaN(minLeft) || double.IsInfinity(minLeft) ? + TimeSpan.Zero : + TimeSpan.FromMinutes(minLeft); ProgressPercentage = counter; } diff --git a/CollapseLauncher/XAMLs/Updater/UpdaterWindow.xaml.cs b/CollapseLauncher/XAMLs/Updater/UpdaterWindow.xaml.cs index 15f658070..44a87a89b 100644 --- a/CollapseLauncher/XAMLs/Updater/UpdaterWindow.xaml.cs +++ b/CollapseLauncher/XAMLs/Updater/UpdaterWindow.xaml.cs @@ -125,8 +125,8 @@ await File.WriteAllTextAsync(Path.Combine(workingDir, "..\\", "release"), private void FallbackCDNUtil_DownloadProgress(object sender, DownloadEvent e) { - double speed = e.SizeDownloaded / CurrentStopwatch.Elapsed.TotalSeconds; - TimeSpan timeLeft = ((e.SizeToBeDownloaded - e.SizeDownloaded) / speed).ToTimeSpanNormalized(); + double speed = e.SizeDownloaded / CurrentStopwatch.Elapsed.TotalSeconds; + TimeSpan timeLeft = ToTimeSpanRemain(e.SizeToBeDownloaded, e.SizeDownloaded, speed); DispatcherQueue?.TryEnqueue(() => { diff --git a/Hi3Helper.Core/Lang/Locale/LangUpdatePage.cs b/Hi3Helper.Core/Lang/Locale/LangUpdatePage.cs index 2dbb3100e..a558f8962 100644 --- a/Hi3Helper.Core/Lang/Locale/LangUpdatePage.cs +++ b/Hi3Helper.Core/Lang/Locale/LangUpdatePage.cs @@ -61,6 +61,10 @@ public sealed partial class LangUpdatePage public string ApplyUpdateErrCollapseRunTitle { get; set; } = LangFallback?._UpdatePage.ApplyUpdateErrCollapseRunTitle; public string ApplyUpdateErrCollapseRunSubtitle { get; set; } = LangFallback?._UpdatePage.ApplyUpdateErrCollapseRunSubtitle; + public string ApplyUpdateErrCollapseRunTitleWarnBox { get; set; } = LangFallback?._UpdatePage.ApplyUpdateErrCollapseRunTitleWarnBox; + public string ApplyUpdateErrCollapseRunSubtitleWarnBox { get; set; } = LangFallback?._UpdatePage.ApplyUpdateErrCollapseRunSubtitleWarnBox; + public string ApplyUpdateErrVelopackStateBrokenTitleWarnBox { get; set; } = LangFallback?._UpdatePage.ApplyUpdateErrVelopackStateBrokenTitleWarnBox; + public string ApplyUpdateErrVelopackStateBrokenSubtitleWarnBox { get; set; } = LangFallback?._UpdatePage.ApplyUpdateErrVelopackStateBrokenSubtitleWarnBox; public string ApplyUpdateErrReleaseFileNotFoundTitle { get; set; } = LangFallback?._UpdatePage.ApplyUpdateErrReleaseFileNotFoundTitle; public string ApplyUpdateErrReleaseFileNotFoundSubtitle { get; set; } = LangFallback?._UpdatePage.ApplyUpdateErrReleaseFileNotFoundSubtitle; @@ -69,7 +73,7 @@ public sealed partial class LangUpdatePage public string ApplyUpdateDownloadSpeedPlaceholder { get; set; } = LangFallback?._UpdatePage.ApplyUpdateDownloadSpeedPlaceholder; public string ApplyUpdateDownloadTimeEst { get; set; } = LangFallback?._UpdatePage.ApplyUpdateDownloadTimeEst; public string ApplyUpdateDownloadTimeEstPlaceholder { get; set; } = LangFallback?._UpdatePage.ApplyUpdateDownloadTimeEstPlaceholder; - + public string ApplyUpdateTaskLegacyVerFoundTitle { get; set; } = LangFallback?._UpdatePage.ApplyUpdateTaskLegacyVerFoundTitle; public string ApplyUpdateTaskLegacyVerFoundSubtitle1 { get; set; } = LangFallback?._UpdatePage.ApplyUpdateTaskLegacyVerFoundSubtitle1; public string ApplyUpdateTaskLegacyVerFoundSubtitle2 { get; set; } = LangFallback?._UpdatePage.ApplyUpdateTaskLegacyVerFoundSubtitle2; diff --git a/Hi3Helper.Core/Lang/en_US.json b/Hi3Helper.Core/Lang/en_US.json index 04e27808f..8243d9936 100644 --- a/Hi3Helper.Core/Lang/en_US.json +++ b/Hi3Helper.Core/Lang/en_US.json @@ -1149,6 +1149,10 @@ "ApplyUpdateErrCollapseRunTitle": "Please close Collapse before applying the update!", "ApplyUpdateErrCollapseRunSubtitle": "Waiting for Collapse to close...", + "ApplyUpdateErrCollapseRunTitleWarnBox": "An Instance of Collapse Launcher is Still Running!", + "ApplyUpdateErrCollapseRunSubtitleWarnBox": "We detected an instance of Collapse Launcher is running in the background. To forcely close the launcher, click \"Yes\". To wait until you manually close it, click \"No\".", + "ApplyUpdateErrVelopackStateBrokenTitleWarnBox": "Broken Existing Installation Detected!", + "ApplyUpdateErrVelopackStateBrokenSubtitleWarnBox": "We detected that you have a broken existing installation.\r\n\r\nClick on \"Yes\" to repair the installation before installing updates or Click \"No\" to just run the update installation.", "ApplyUpdateErrReleaseFileNotFoundTitle": "ERROR:\r\n\"release\" file doesn't have \"stable\" or \"preview\" string in it", "ApplyUpdateErrReleaseFileNotFoundSubtitle": "Please check your \"release\" file and try again.", diff --git a/Hi3Helper.EncTool b/Hi3Helper.EncTool index 6f527aac4..e07511e7e 160000 --- a/Hi3Helper.EncTool +++ b/Hi3Helper.EncTool @@ -1 +1 @@ -Subproject commit 6f527aac4f040ce06620699632d124ed5e4c14b2 +Subproject commit e07511e7ed24bd2e0222e226bf4963440cd02045 diff --git a/Hi3Helper.Http b/Hi3Helper.Http index 5757ee282..67063c030 160000 --- a/Hi3Helper.Http +++ b/Hi3Helper.Http @@ -1 +1 @@ -Subproject commit 5757ee28249d5bdaac91ae15ddd8f12e21cda2b3 +Subproject commit 67063c0301e05733b34025040c9b681736d7c2d3 diff --git a/Hi3Helper.Win32 b/Hi3Helper.Win32 index 1fccce1f6..b0cbcfc9f 160000 --- a/Hi3Helper.Win32 +++ b/Hi3Helper.Win32 @@ -1 +1 @@ -Subproject commit 1fccce1f6e60de5d1ad3d942c8a691e17a350a69 +Subproject commit b0cbcfc9f22871cc8f090b9c30ac1fd5825c7520 diff --git a/README.md b/README.md index c858d1614..ba62e1eee 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.8/CollapseLauncher-stable-Setup.exe) -> **Note**: The version for this build is `1.82.8` (Released on: December 22nd, 2024). +[](https://github.com/CollapseLauncher/Collapse/releases/download/CL-v1.82.9/CollapseLauncher-stable-Setup.exe) +> **Note**: The version for this build is `1.82.9` (Released on: December 25th, 2024). -[](https://github.com/CollapseLauncher/Collapse/releases/download/CL-v1.82.8-pre/CollapseLauncher-preview-Setup.exe) -> **Note**: The version for this build is `1.82.8` (Released on: December 22nd, 2024). +[](https://github.com/CollapseLauncher/Collapse/releases/download/CL-v1.82.9-pre/CollapseLauncher-preview-Setup.exe) +> **Note**: The version for this build is `1.82.9` (Released on: December 25th, 2024). To view all releases, [**click here**](https://github.com/neon-nyan/CollapseLauncher/releases).