diff --git a/CollapseLauncher/App.xaml b/CollapseLauncher/App.xaml
index 29062f0d2..9e5269d99 100644
--- a/CollapseLauncher/App.xaml
+++ b/CollapseLauncher/App.xaml
@@ -385,13 +385,13 @@
+ Color="#60000000" />
+ Color="#00E00000" />
+ Color="#FFE00000" />
@@ -744,9 +744,9 @@
+ Color="#00FF6666" />
+ Color="#FFFF0000" />
diff --git a/CollapseLauncher/Classes/Extension/UIElementExtensions.cs b/CollapseLauncher/Classes/Extension/UIElementExtensions.cs
index 2ba8dca7a..2307e172b 100644
--- a/CollapseLauncher/Classes/Extension/UIElementExtensions.cs
+++ b/CollapseLauncher/Classes/Extension/UIElementExtensions.cs
@@ -1,6 +1,7 @@
using CommunityToolkit.WinUI;
using Hi3Helper;
using Hi3Helper.CommunityToolkit.WinUI.Controls;
+using Hi3Helper.SentryHelper;
using Microsoft.UI;
using Microsoft.UI.Input;
using Microsoft.UI.Text;
@@ -17,7 +18,6 @@
using System.Runtime.CompilerServices;
using Windows.UI;
using Windows.UI.Text;
-using Hi3Helper.SentryHelper;
namespace CollapseLauncher.Extension
{
diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Genshin/RegistryClass/ScreenManager.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Genshin/RegistryClass/ScreenManager.cs
index c21b587a1..dc1f77f86 100644
--- a/CollapseLauncher/Classes/GameManagement/GameSettings/Genshin/RegistryClass/ScreenManager.cs
+++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Genshin/RegistryClass/ScreenManager.cs
@@ -1,9 +1,9 @@
using CollapseLauncher.GameSettings.Base;
-using CollapseLauncher.Helper;
using CollapseLauncher.Interfaces;
using Hi3Helper;
using Hi3Helper.EncTool;
using Microsoft.Win32;
+using Hi3Helper.Win32.Screen;
using System;
using System.Drawing;
using static CollapseLauncher.GameSettings.Base.SettingsBase;
@@ -17,7 +17,7 @@ internal class ScreenManager : BaseScreenSettingData, IGameSettingsValue
{
#region Fields
- private const string _ValueName = "GENERAL_DATA_V2_ScreenSettingData_h1916288658";
+ private const string _ValueName = "GENERAL_DATA_V2_ScreenSettingData_h1916288658";
private const string _ValueNameScreenManagerFullscreen = "Screenmanager Is Fullscreen mode_h3981298716";
- private const string _ValueNameScreenManagerWidth = "Screenmanager Resolution Width_h182942802";
- private const string _ValueNameScreenManagerHeight = "Screenmanager Resolution Height_h2627697771";
- private static Size currentRes = WindowUtility.CurrentScreenProp.CurrentResolution;
+ private const string _ValueNameScreenManagerWidth = "Screenmanager Resolution Width_h182942802";
+ private const string _ValueNameScreenManagerHeight = "Screenmanager Resolution Height_h2627697771";
+ private static Size currentRes = ScreenProp.CurrentResolution;
#endregion
#region Properties
diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/StarRail/RegistryClass/PCResolution.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/StarRail/RegistryClass/PCResolution.cs
index 4cb156bfc..0c32d4644 100644
--- a/CollapseLauncher/Classes/GameManagement/GameSettings/StarRail/RegistryClass/PCResolution.cs
+++ b/CollapseLauncher/Classes/GameManagement/GameSettings/StarRail/RegistryClass/PCResolution.cs
@@ -1,6 +1,5 @@
using CollapseLauncher.GameSettings.Base;
using CollapseLauncher.GameSettings.StarRail.Context;
-using CollapseLauncher.Helper;
using CollapseLauncher.Interfaces;
using Hi3Helper;
using Hi3Helper.EncTool;
@@ -10,6 +9,7 @@
using System.Text;
using System.Text.Json.Serialization;
using Hi3Helper.SentryHelper;
+using Hi3Helper.Win32.Screen;
using static CollapseLauncher.GameSettings.Base.SettingsBase;
using static Hi3Helper.Logger;
@@ -18,11 +18,11 @@ namespace CollapseLauncher.GameSettings.StarRail
internal class PCResolution : BaseScreenSettingData, IGameSettingsValue
{
#region Fields
- private const string _ValueName = "GraphicsSettings_PCResolution_h431323223";
- private const string _ValueNameScreenManagerWidth = "Screenmanager Resolution Width_h182942802";
- private const string _ValueNameScreenManagerHeight = "Screenmanager Resolution Height_h2627697771";
+ private const string _ValueName = "GraphicsSettings_PCResolution_h431323223";
+ private const string _ValueNameScreenManagerWidth = "Screenmanager Resolution Width_h182942802";
+ private const string _ValueNameScreenManagerHeight = "Screenmanager Resolution Height_h2627697771";
private const string _ValueNameScreenManagerFullscreen = "Screenmanager Fullscreen mode_h3630240806";
- private static Size currentRes = WindowUtility.CurrentScreenProp.CurrentResolution;
+ private static Size currentRes = ScreenProp.CurrentResolution;
#endregion
#region Properties
diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/RegistryClass/ScreenManager.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/RegistryClass/ScreenManager.cs
index c49a02815..a2f795a86 100644
--- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/RegistryClass/ScreenManager.cs
+++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/RegistryClass/ScreenManager.cs
@@ -4,6 +4,7 @@
using Hi3Helper;
using Hi3Helper.EncTool;
using Microsoft.Win32;
+using Hi3Helper.Win32.Screen;
using System;
using System.Drawing;
using Hi3Helper.SentryHelper;
@@ -20,7 +21,7 @@ internal class ScreenManager : BaseScreenSettingData, IGameSettingsValue
+ /// Manages the thread pool settings to throttle the number of threads.
+ ///
+ internal partial class ThreadPoolThrottle : IDisposable
+ {
+ private readonly int PreviousThreadCount;
+ private readonly int PreviousCompletionPortThreadCount;
+ internal readonly int MultipliedThreadCount;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The previous maximum number of worker threads.
+ /// The previous maximum number of asynchronous I/O threads.
+ /// The multiplied thread count.
+ private ThreadPoolThrottle(int previousThreadCount, int previousCompletionPortThreadCount, int multipliedThreadCount)
+ {
+ PreviousThreadCount = previousThreadCount;
+ PreviousCompletionPortThreadCount = previousCompletionPortThreadCount;
+ MultipliedThreadCount = multipliedThreadCount;
+ }
+
+ ///
+ /// Starts the thread pool throttle by setting the maximum number of threads.
+ ///
+ /// The factor to multiply the processor count by to determine the maximum number of threads.
+ /// A instance that can be used to restore the previous thread pool settings.
+ public static ThreadPoolThrottle Start(int multiply = 4)
+ {
+ var threadCount = Environment.ProcessorCount * multiply;
+ ThreadPool.GetMinThreads(out var workerThreads, out var completionPortThreads);
+ ThreadPool.SetMinThreads(Math.Max(workerThreads, threadCount),
+ Math.Max(completionPortThreads, threadCount));
+ return new ThreadPoolThrottle(workerThreads, completionPortThreads, threadCount);
+ }
+
+ ///
+ /// Restores the previous thread pool settings.
+ ///
+ public void Dispose()
+ {
+ ThreadPool.SetMaxThreads(PreviousThreadCount, PreviousCompletionPortThreadCount);
+ }
+ }
+}
diff --git a/CollapseLauncher/Classes/Helper/WindowUtility.cs b/CollapseLauncher/Classes/Helper/WindowUtility.cs
index e0b68d2d6..7952e3029 100644
--- a/CollapseLauncher/Classes/Helper/WindowUtility.cs
+++ b/CollapseLauncher/Classes/Helper/WindowUtility.cs
@@ -54,7 +54,6 @@ internal static class WindowUtility
internal static AppWindow? CurrentAppWindow;
internal static WindowId? CurrentWindowId;
internal static OverlappedPresenter? CurrentOverlappedPresenter;
- internal static ScreenProp? CurrentScreenProp;
internal static DisplayArea? CurrentWindowDisplayArea
{
@@ -665,7 +664,7 @@ private static IntPtr DesktopSiteBridgeWndProc(IntPtr hwnd, uint msg, UIntPtr wP
internal static void SetWindowSize(int width, int height)
{
- if (CurrentScreenProp == null || CurrentWindowPtr == nint.Zero)
+ if (CurrentWindowPtr == nint.Zero)
return;
// Get the scale factor and calculate the size and offset
@@ -673,7 +672,7 @@ internal static void SetWindowSize(int width, int height)
int lastWindowWidth = (int)(width * scaleFactor);
int lastWindowHeight = (int)(height * scaleFactor);
- Size desktopSize = CurrentScreenProp.GetScreenSize();
+ Size desktopSize = ScreenProp.CurrentResolution;
int xOff = desktopSize.Width / 2 - lastWindowWidth / 2;
int yOff = desktopSize.Height / 2 - lastWindowHeight / 2;
diff --git a/CollapseLauncher/Classes/InstallManagement/BaseClass/InstallManagerBase.cs b/CollapseLauncher/Classes/InstallManagement/BaseClass/InstallManagerBase.cs
index 70e8a451a..77c76a0e6 100644
--- a/CollapseLauncher/Classes/InstallManagement/BaseClass/InstallManagerBase.cs
+++ b/CollapseLauncher/Classes/InstallManagement/BaseClass/InstallManagerBase.cs
@@ -123,6 +123,13 @@ protected struct UninstallGameProperty
// TODO: Override if the game was supposed to have voice packs (For example: Genshin)
protected virtual int _gameVoiceLanguageID => int.MinValue;
+ protected virtual string[] _gameVoiceLanguageLocaleIdOrdered => [
+ "zh-cn",
+ "en-us",
+ "ja-jp",
+ "ko-kr"
+ ];
+
protected virtual string _gameDataPath =>
Path.Combine(_gamePath,
$"{Path.GetFileNameWithoutExtension(_gameVersionManager.GamePreset.GameExecutableName)}_Data");
@@ -805,205 +812,218 @@ public virtual async Task StartPackageInstallSophon(GameInstallStateEnum gameSta
.UseLauncherConfig(maxHttpHandler)
.Create();
- try
+ using (ThreadPoolThrottle.Start())
{
- // Reset status and progress properties
- ResetStatusAndProgress();
+ try
+ {
+ // Reset status and progress properties
+ ResetStatusAndProgress();
- // Clear the VO language list
- _sophonVOLanguageList?.Clear();
+ // Clear the VO language list
+ _sophonVOLanguageList?.Clear();
- // Subscribe the logger event
- SophonLogger.LogHandler += UpdateSophonLogHandler;
+ // Subscribe the logger event
+ SophonLogger.LogHandler += UpdateSophonLogHandler;
- // Get the requested URL and version based on current state.
- if (_gameVersionManager.GamePreset
- .LauncherResourceChunksURL != null)
- {
- #nullable enable
- // Reassociate the URL if branch url exist
- string? branchUrl = _gameVersionManager.GamePreset
- .LauncherResourceChunksURL
- .BranchUrl;
- if (!string.IsNullOrEmpty(branchUrl)
- && !string.IsNullOrEmpty(_gameVersionManager.GamePreset.LauncherBizName))
+ // Get the requested URL and version based on current state.
+ if (_gameVersionManager.GamePreset
+ .LauncherResourceChunksURL != null)
{
- await _gameVersionManager.GamePreset
- .LauncherResourceChunksURL
- .EnsureReassociated(
- httpClient,
- branchUrl,
- _gameVersionManager.GamePreset.LauncherBizName,
- _token.Token);
- }
- #nullable restore
+#nullable enable
+ // Reassociate the URL if branch url exist
+ string? branchUrl = _gameVersionManager.GamePreset
+ .LauncherResourceChunksURL
+ .BranchUrl;
+ if (!string.IsNullOrEmpty(branchUrl)
+ && !string.IsNullOrEmpty(_gameVersionManager.GamePreset.LauncherBizName))
+ {
+ await _gameVersionManager.GamePreset
+ .LauncherResourceChunksURL
+ .EnsureReassociated(
+ httpClient,
+ branchUrl,
+ _gameVersionManager.GamePreset.LauncherBizName,
+ _token.Token);
+ }
+#nullable restore
- #if SIMULATEAPPLYPRELOAD
- string requestedUrl = gameState switch
- {
- GameInstallStateEnum.InstalledHavePreload => _gameVersionManager.GamePreset
- .LauncherResourceChunksURL.PreloadUrl,
- _ => _gameVersionManager.GamePreset.LauncherResourceChunksURL.MainUrl
- };
- GameVersion? requestedVersion = gameState switch
- {
- GameInstallStateEnum.InstalledHavePreload => _gameVersionManager!
- .GetGameVersionAPIPreload(),
- _ => _gameVersionManager!.GetGameVersionAPIPreload()
- } ?? _gameVersionManager!.GetGameVersionAPI();
- #else
- string requestedUrl = gameState switch
- {
- GameInstallStateEnum.InstalledHavePreload => _gameVersionManager
- .GamePreset
- .LauncherResourceChunksURL.PreloadUrl,
- _ => _gameVersionManager.GamePreset.LauncherResourceChunksURL.MainUrl
- };
- GameVersion? requestedVersion = gameState switch
- {
- GameInstallStateEnum.InstalledHavePreload =>
- _gameVersionManager!
- .GetGameVersionAPIPreload(),
- _ => _gameVersionManager!.GetGameVersionAPI()
- } ?? _gameVersionManager!.GetGameVersionAPI();
-
- // Add the tag query to the Url
- requestedUrl += $"&tag={requestedVersion.ToString()}";
- #endif
+#if SIMULATEAPPLYPRELOAD
+ string requestedUrl = gameState switch
+ {
+ GameInstallStateEnum.InstalledHavePreload => _gameVersionManager.GamePreset
+ .LauncherResourceChunksURL.PreloadUrl,
+ _ => _gameVersionManager.GamePreset.LauncherResourceChunksURL.MainUrl
+ };
+ GameVersion? requestedVersion = gameState switch
+ {
+ GameInstallStateEnum.InstalledHavePreload => _gameVersionManager!
+ .GetGameVersionAPIPreload(),
+ _ => _gameVersionManager!.GetGameVersionAPIPreload()
+ } ?? _gameVersionManager!.GetGameVersionAPI();
+#else
+ string requestedUrl = gameState switch
+ {
+ GameInstallStateEnum.InstalledHavePreload => _gameVersionManager
+ .GamePreset
+ .LauncherResourceChunksURL.PreloadUrl,
+ _ => _gameVersionManager.GamePreset.LauncherResourceChunksURL.MainUrl
+ };
+ GameVersion? requestedVersion = gameState switch
+ {
+ GameInstallStateEnum.InstalledHavePreload =>
+ _gameVersionManager!
+ .GetGameVersionAPIPreload(),
+ _ => _gameVersionManager!.GetGameVersionAPI()
+ } ?? _gameVersionManager!.GetGameVersionAPI();
+
+ // Add the tag query to the Url
+ requestedUrl += $"&tag={requestedVersion.ToString()}";
+#endif
- // Set the progress bar to indetermined
- _status.IsIncludePerFileIndicator = false;
- _status.IsProgressPerFileIndetermined = false;
- _status.IsProgressAllIndetermined = true;
- UpdateStatus();
+ // Set the progress bar to indetermined
+ _status.IsIncludePerFileIndicator = false;
+ _status.IsProgressPerFileIndetermined = false;
+ _status.IsProgressAllIndetermined = true;
+ UpdateStatus();
- // Initialize the info pair list
- var sophonInfoPairList = new List();
+ // Initialize the info pair list
+ var sophonInfoPairList = new List();
- // Get the info pair based on info provided above (for main game file)
- var sophonMainInfoPair = await
- SophonManifest.CreateSophonChunkManifestInfoPair(httpClient, requestedUrl, "game",
- _token.Token)
- .ConfigureAwait(false);
- sophonInfoPairList.Add(sophonMainInfoPair);
+ // Get the info pair based on info provided above (for main game file)
+ var sophonMainInfoPair = await
+ SophonManifest.CreateSophonChunkManifestInfoPair(httpClient, requestedUrl, "game", _token.Token);
- List voLanguageList =
- GetSophonLanguageDisplayDictFromVoicePackList(sophonMainInfoPair.OtherSophonData);
+ // Ensure that the manifest is ordered based on _gameVoiceLanguageLocaleIdOrdered
+ RearrangeSophonDataLocaleOrder(sophonMainInfoPair.OtherSophonData);
- Dispatch( async void () =>
- {
- try
- {
- (List addedVO, int setAsDefaultVO) =
- await Dialog_ChooseAudioLanguageChoice(_parentUI, voLanguageList);
- if (addedVO == null || setAsDefaultVO < 0)
- {
- throw new TaskCanceledException();
- }
-
- for (int i = 0; i < addedVO.Count; i++)
- {
- int voLangIndex = addedVO[i];
- string voLangLocaleCode = GetLanguageLocaleCodeByID(voLangIndex);
- _sophonVOLanguageList?.Add(voLangLocaleCode);
-
- // Get the info pair based on info provided above (for the selected VO audio file)
- SophonChunkManifestInfoPair sophonSelectedVoLang =
- sophonMainInfoPair.GetOtherManifestInfoPair(voLangLocaleCode);
- sophonInfoPairList.Add(sophonSelectedVoLang);
- }
-
- // Set the voice language ID to value given
- _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;
-
- // Set the display to Install Mode
- _isSophonInUpdateMode = false;
-
- // Set the progress bar to indetermined
- _status.IsIncludePerFileIndicator = false;
- _status.IsProgressPerFileIndetermined = false;
- _status.IsProgressAllIndetermined = false;
- UpdateStatus();
- }
- catch (Exception e)
- {
- ErrorSender.SendException(e);
- }
- });
-
- // Get the parallel options
- var parallelOptions = new ParallelOptions
- {
- MaxDegreeOfParallelism = maxThread,
- CancellationToken = _token.Token
- };
- var parallelChunksOptions = new ParallelOptions
- {
- MaxDegreeOfParallelism = maxChunksThread,
- CancellationToken = _token.Token
- };
-
- // Declare the download delegate
- async ValueTask DelegateAssetDownload(SophonAsset asset, CancellationToken _)
- {
- // ReSharper disable once AccessToDisposedClosure
- await RunSophonAssetDownloadThread(httpClient, asset, parallelChunksOptions);
- }
+ // Add the manifest to the pair list
+ sophonInfoPairList.Add(sophonMainInfoPair);
- // Declare the rename temp file delegate
- async ValueTask DelegateAssetRenameTempFile(SophonAsset asset, CancellationToken token)
- {
- await Task.Run(() =>
- {
- // If the asset is a dictionary, then return
- if (asset.IsDirectory)
- {
- return;
- }
-
- // Get the file path and start the write process
- var assetName = asset.AssetName;
- var filePath = new FileInfo(
- EnsureCreationOfDirectory(Path.Combine(_gamePath, assetName)) +
- "_tempSophon").EnsureNoReadOnly();
- var origFilePath = new FileInfo(Path.Combine(_gamePath, assetName)).EnsureNoReadOnly();
-
- if (filePath.Exists)
- {
- filePath.MoveTo(origFilePath.FullName, true);
- filePath.Refresh();
- origFilePath.Refresh();
- }
- }, token);
- }
+ List voLanguageList =
+ GetSophonLanguageDisplayDictFromVoicePackList(sophonMainInfoPair.OtherSophonData);
- // Enumerate the asset in parallel and start the download process
- await RunTaskAction(httpClient, sophonInfoPairList, parallelOptions, DelegateAssetDownload);
+ // Get Audio Choices first
+ (List addedVO, int setAsDefaultVO) =
+ await Dialog_ChooseAudioLanguageChoice(voLanguageList, GetSophonLocaleCodeIndex(sophonMainInfoPair.OtherSophonData, "ja-jp"));
- // Rename temporary files
- await RunTaskAction(httpClient, sophonInfoPairList, parallelOptions, DelegateAssetRenameTempFile);
+ try
+ {
+ if (addedVO == null || setAsDefaultVO < 0)
+ {
+ throw new TaskCanceledException();
+ }
- // Remove sophon verified files
- CleanupTempSophonVerifiedFiles();
- }
+ for (int i = 0; i < addedVO.Count; i++)
+ {
+ int voLangIndex = addedVO[i];
+ string voLangLocaleCode = GetLanguageLocaleCodeByID(voLangIndex);
+ _sophonVOLanguageList?.Add(voLangLocaleCode);
+
+ // Get the info pair based on info provided above (for the selected VO audio file)
+ SophonChunkManifestInfoPair sophonSelectedVoLang =
+ sophonMainInfoPair.GetOtherManifestInfoPair(voLangLocaleCode);
+ sophonInfoPairList.Add(sophonSelectedVoLang);
+ }
- _isSophonDownloadCompleted = true;
- }
- finally
- {
- // Unsubscribe the logger event
- SophonLogger.LogHandler -= UpdateSophonLogHandler;
- httpClient.Dispose();
+ // Set the voice language ID to value given
+ _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;
+
+ // Set the display to Install Mode
+ _isSophonInUpdateMode = false;
+
+ // Set the progress bar to indetermined
+ _status.IsIncludePerFileIndicator = false;
+ _status.IsProgressPerFileIndetermined = false;
+ _status.IsProgressAllIndetermined = false;
+ UpdateStatus();
+ }
+ catch (TaskCanceledException)
+ {
+ throw;
+ }
+ catch (OperationCanceledException)
+ {
+ throw;
+ }
+ catch (Exception e)
+ {
+ ErrorSender.SendException(e);
+ }
+
+ // Get the parallel options
+ var parallelOptions = new ParallelOptions
+ {
+ MaxDegreeOfParallelism = maxThread,
+ CancellationToken = _token.Token
+ };
+ var parallelChunksOptions = new ParallelOptions
+ {
+ MaxDegreeOfParallelism = maxChunksThread,
+ CancellationToken = _token.Token
+ };
+
+ // Declare the download delegate
+ async ValueTask DelegateAssetDownload(SophonAsset asset, CancellationToken _)
+ {
+ // ReSharper disable once AccessToDisposedClosure
+ await RunSophonAssetDownloadThread(httpClient, asset, parallelChunksOptions);
+ }
+
+ // Declare the rename temp file delegate
+ async ValueTask DelegateAssetRenameTempFile(SophonAsset asset, CancellationToken token)
+ {
+ await Task.Run(() =>
+ {
+ // If the asset is a dictionary, then return
+ if (asset.IsDirectory)
+ {
+ return;
+ }
+
+ // Get the file path and start the write process
+ var assetName = asset.AssetName;
+ var filePath = new FileInfo(
+ EnsureCreationOfDirectory(Path.Combine(_gamePath, assetName)) +
+ "_tempSophon").EnsureNoReadOnly();
+ var origFilePath = new FileInfo(Path.Combine(_gamePath, assetName)).EnsureNoReadOnly();
+
+ if (filePath.Exists)
+ {
+ filePath.MoveTo(origFilePath.FullName, true);
+ filePath.Refresh();
+ origFilePath.Refresh();
+ }
+ }, token);
+ }
+
+ // Enumerate the asset in parallel and start the download process
+ await RunTaskAction(httpClient, sophonInfoPairList, parallelOptions, DelegateAssetDownload);
+
+ // Rename temporary files
+ await RunTaskAction(httpClient, sophonInfoPairList, parallelOptions, DelegateAssetRenameTempFile);
+
+ // Remove sophon verified files
+ CleanupTempSophonVerifiedFiles();
+ }
+
+ _isSophonDownloadCompleted = true;
+ }
+ finally
+ {
+ // Unsubscribe the logger event
+ SophonLogger.LogHandler -= UpdateSophonLogHandler;
+ httpClient.Dispose();
+ }
}
return;
- async Task RunTaskAction(HttpClient client, List sophonInfoPairList,
+ async Task RunTaskAction(HttpClient client, List sophonInfoPairListLocal,
ParallelOptions parallelOptions,
Func actionDelegate)
{
@@ -1014,7 +1034,8 @@ async Task RunTaskAction(HttpClient client, List so
{
LauncherConfig.DownloadSpeedLimitChanged += downloadSpeedLimiter.GetListener();
var processingInfoPair = new ConcurrentDictionary();
- foreach (SophonChunkManifestInfoPair sophonDownloadInfoPair in sophonInfoPairList)
+ var infoPairListCopy = sophonInfoPairListLocal.ToList();
+ foreach (SophonChunkManifestInfoPair sophonDownloadInfoPair in infoPairListCopy)
{
if (!processingInfoPair.TryAdd(sophonDownloadInfoPair.ChunksInfo, 0))
{
@@ -1272,11 +1293,13 @@ private async Task AddSophonDiffAssetsToList(HttpClient httpClie
{
// Get the manifest pair for both previous (from) and next (to) version
SophonChunkManifestInfoPair requestPairFrom = await SophonManifest
- .CreateSophonChunkManifestInfoPair(httpClient, requestedUrlFrom, matchingField, _token.Token)
- .ConfigureAwait(false);
+ .CreateSophonChunkManifestInfoPair(httpClient, requestedUrlFrom, matchingField, _token.Token);
SophonChunkManifestInfoPair requestPairTo = await SophonManifest
- .CreateSophonChunkManifestInfoPair(httpClient, requestedUrlTo, matchingField, _token.Token)
- .ConfigureAwait(false);
+ .CreateSophonChunkManifestInfoPair(httpClient, requestedUrlTo, matchingField, _token.Token);
+
+ // Ensure that the manifest is ordered based on _gameVoiceLanguageLocaleIdOrdered
+ RearrangeSophonDataLocaleOrder(requestPairFrom.OtherSophonData);
+ RearrangeSophonDataLocaleOrder(requestPairTo.OtherSophonData);
// Add asset to the list
await foreach (SophonAsset sophonAsset in SophonUpdate
@@ -1737,10 +1760,10 @@ private async Task ExtractUsingNativeZipWorker(IEnumerable entriesIndex, L
continue;
}
- string outputPath = EnsureCreationOfDirectory(Path.Combine(_gamePath, zipEntry.Key));
+ string outputPath = Path.Combine(_gamePath, zipEntry.Key);
+ FileInfo outputFile = new FileInfo(outputPath).EnsureCreationOfDirectory().EnsureNoReadOnly();
- await using FileStream outputStream =
- new FileStream(outputPath, FileMode.Create, FileAccess.Write, FileShare.Write);
+ await using FileStream outputStream = outputFile.Open(FileMode.Create, FileAccess.Write, FileShare.Write);
await using Stream entryStream = zipEntry.OpenEntryStream();
Task runningTask = Task.Factory.StartNew(
@@ -1768,9 +1791,9 @@ void StartWriteInner(byte[] bufferInner, FileStream outputStream, Stream entrySt
// Increment total size
_progressAllSizeCurrent += read;
_progressPerFileSizeCurrent += read;
-
+
// Calculate the speed
- _progress.ProgressAllSpeed = CalculateSpeed(read);
+ lock (_progress) _progress.ProgressAllSpeed = CalculateSpeed(read);
if (!CheckIfNeedRefreshStopwatch())
{
@@ -1778,20 +1801,23 @@ void StartWriteInner(byte[] bufferInner, FileStream outputStream, Stream entrySt
}
// Assign local sizes to progress
- _progress.ProgressPerFileSizeCurrent = _progressPerFileSizeCurrent;
- _progress.ProgressPerFileSizeTotal = _progressPerFileSizeTotal;
- _progress.ProgressAllSizeCurrent = _progressAllSizeCurrent;
- _progress.ProgressAllSizeTotal = _progressAllSizeTotal;
-
- // 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)
+ {
+ _progress.ProgressPerFileSizeCurrent = _progressPerFileSizeCurrent;
+ _progress.ProgressPerFileSizeTotal = _progressPerFileSizeTotal;
+ _progress.ProgressAllSizeCurrent = _progressAllSizeCurrent;
+ _progress.ProgressAllSizeTotal = _progressAllSizeTotal;
+
+ // 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();
+ }
UpdateAll();
}
@@ -2094,7 +2120,9 @@ public async ValueTask UninstallGame()
string appDataPath = _gameVersionManager.GameDirAppDataPath;
try
{
- Directory.Delete(appDataPath, true);
+ if (Directory.Exists(appDataPath))
+ Directory.Delete(appDataPath, true);
+
LogWriteLine($"Deleted {appDataPath}", LogType.Default, true);
}
catch (Exception ex)
@@ -2293,11 +2321,14 @@ private async ValueTask FileHdiffPatcherInner(string patchPath, string sourceBas
public virtual async ValueTask ApplyHdiffListPatch()
{
List hdiffEntry = TryGetHDiffList();
-
- _progress.ProgressAllSizeTotal = hdiffEntry.Sum(x => x.fileSize);
- _progress.ProgressAllSizeCurrent = 0;
_status.IsIncludePerFileIndicator = false;
+ lock (_progress)
+ {
+ _progress.ProgressAllSizeTotal = hdiffEntry.Sum(x => x.fileSize);
+ _progress.ProgressAllSizeCurrent = 0;
+ }
+
_progressAllCountTotal = 1;
_progressAllCountFound = hdiffEntry.Count;
@@ -2346,14 +2377,15 @@ public virtual async ValueTask ApplyHdiffListPatch()
lock (_progress)
{
_progress.ProgressAllSizeCurrent += entry.fileSize;
+ _progress.ProgressAllPercentage =
+ Math.Round(_progress.ProgressAllSizeCurrent / _progress.ProgressAllSizeTotal * 100, 2);
+ _progress.ProgressAllSpeed = CalculateSpeed(entry.fileSize);
+
+ _progress.ProgressAllTimeLeft =
+ ((_progress.ProgressAllSizeTotal - _progress.ProgressAllSizeCurrent) /
+ _progress.ProgressAllSpeed.Unzeroed()).ToTimeSpanNormalized();
}
- _progress.ProgressAllPercentage =
- Math.Round(_progress.ProgressAllSizeCurrent / _progress.ProgressAllSizeTotal * 100, 2);
- _progress.ProgressAllSpeed = CalculateSpeed(entry.fileSize);
- _progress.ProgressAllTimeLeft =
- ((_progress.ProgressAllSizeTotal - _progress.ProgressAllSizeCurrent) /
- _progress.ProgressAllSpeed.Unzeroed()).ToTimeSpanNormalized();
UpdateProgress();
}
finally
@@ -2408,13 +2440,16 @@ private void EventListener_PatchEvent(object sender, PatchEvent e)
}
if (CheckIfNeedRefreshStopwatch())
{
- _progress.ProgressAllPercentage =
- Math.Round(_progress.ProgressAllSizeCurrent / _progress.ProgressAllSizeTotal * 100, 2);
- _progress.ProgressAllSpeed = CalculateSpeed(e.Read);
+ 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.ProgressAllTimeLeft =
+ ((_progress.ProgressAllSizeTotal - _progress.ProgressAllSizeCurrent) /
+ _progress.ProgressAllSpeed.Unzeroed()).ToTimeSpanNormalized();
+ }
UpdateProgress();
}
}
@@ -2581,22 +2616,15 @@ protected virtual string GetLanguageDisplayByLocaleCode(string localeCode, bool
};
}
- protected virtual List GetLanguageDisplayListFromVoicePackList(List voicePacks)
+ protected virtual int GetSophonLocaleCodeIndex(SophonData sophonData, string lookupName)
{
- List value = [];
- 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);
- if (string.IsNullOrEmpty(languageDisplay))
- {
- continue;
- }
-
- value.Add(languageDisplay);
- }
+ List localeList = sophonData.ManifestIdentityList
+ .Where(x => IsValidLocaleCode(x.MatchingField))
+ .Select(x => x.MatchingField.ToLower())
+ .ToList();
- return value;
+ int index = localeList.IndexOf(lookupName);
+ return Math.Max(0, index);
}
protected virtual Dictionary GetLanguageDisplayDictFromVoicePackList(
@@ -2643,6 +2671,54 @@ protected virtual List GetSophonLanguageDisplayDictFromVoicePackList(Sop
return value;
}
+ 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);
+ }
+
+ 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);
+ }
+
+ protected virtual void RearrangeDataListLocaleOrder(List assetDataList, Func matchingFieldPredicate)
+ {
+ // Get ordered locale string
+ string[] localeStringOrder = _gameVoiceLanguageLocaleIdOrdered;
+
+ // Separate non-locale and locale manifest list
+ List manifestListMain = assetDataList
+ .Where(x => !IsValidLocaleCode(matchingFieldPredicate(x)))
+ .ToList();
+ 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));
+ if (localeFound != null)
+ {
+ // Move from main to locale
+ manifestListMain.Add(localeFound);
+ manifestListLocale.Remove(localeFound);
+ }
+ }
+
+ // Add the rest of the unknown locale if exist
+ if (manifestListLocale.Count != 0)
+ {
+ manifestListMain.AddRange(manifestListLocale);
+ }
+
+ // Rearrange by cleaning the list and re-add the sorted list
+ assetDataList.Clear();
+ assetDataList.AddRange(manifestListMain);
+ }
+
protected virtual bool TryGetVoiceOverResourceByLocaleCode(List verResList,
string localeCode, out RegionResourceVersion outRes)
{
@@ -3267,6 +3343,7 @@ private async Task GetLatestPackageList(List packageList, Ga
continue;
}
+ RearrangeLegacyPackageLocaleOrder(asset);
await TryAddResourceVersionList(asset, packageList);
}
}
@@ -3985,8 +4062,11 @@ public void UpdateCompletenessStatus(CompletenessStatus status)
InnerLauncherConfig.AppDiscordPresence?.SetActivity(ActivityType.Idle);
#endif
// HACK: Fix the progress not achieving 100% while completed
- _progress.ProgressAllPercentage = 100f;
- _progress.ProgressPerFilePercentage = 100f;
+ lock (_progress)
+ {
+ _progress.ProgressAllPercentage = 100f;
+ _progress.ProgressPerFilePercentage = 100f;
+ }
break;
case CompletenessStatus.Cancelled:
IsRunning = false;
@@ -4061,13 +4141,16 @@ protected void UpdateProgressBase()
protected void DeltaPatchCheckProgress(object sender, PatchEvent e)
{
- _progress.ProgressAllPercentage = e.ProgressPercentage;
+ lock (_progress)
+ {
+ _progress.ProgressAllPercentage = e.ProgressPercentage;
- _progress.ProgressAllTimeLeft = e.TimeLeft;
- _progress.ProgressAllSpeed = e.Speed;
+ _progress.ProgressAllTimeLeft = e.TimeLeft;
+ _progress.ProgressAllSpeed = e.Speed;
- _progress.ProgressAllSizeTotal = e.TotalSizeToBePatched;
- _progress.ProgressAllSizeCurrent = e.CurrentSizePatched;
+ _progress.ProgressAllSizeTotal = e.TotalSizeToBePatched;
+ _progress.ProgressAllSizeCurrent = e.CurrentSizePatched;
+ }
if (CheckIfNeedRefreshStopwatch())
{
@@ -4105,14 +4188,17 @@ protected void DeltaPatchCheckLogEvent(object sender, LoggerEvent e)
protected void DeltaPatchCheckProgress(object sender, TotalPerFileProgress e)
{
- _progress.ProgressAllPercentage =
- e.ProgressAllPercentage == 0 ? e.ProgressPerFilePercentage : e.ProgressAllPercentage;
+ lock (_progress)
+ {
+ _progress.ProgressAllPercentage =
+ e.ProgressAllPercentage == 0 ? e.ProgressPerFilePercentage : e.ProgressAllPercentage;
- _progress.ProgressAllTimeLeft = e.ProgressAllTimeLeft;
- _progress.ProgressAllSpeed = e.ProgressAllSpeed;
+ _progress.ProgressAllTimeLeft = e.ProgressAllTimeLeft;
+ _progress.ProgressAllSpeed = e.ProgressAllSpeed;
- _progress.ProgressAllSizeTotal = e.ProgressAllSizeTotal;
- _progress.ProgressAllSizeCurrent = e.ProgressAllSizeCurrent;
+ _progress.ProgressAllSizeTotal = e.ProgressAllSizeTotal;
+ _progress.ProgressAllSizeCurrent = e.ProgressAllSizeCurrent;
+ }
if (CheckIfNeedRefreshStopwatch())
{
@@ -4134,24 +4220,27 @@ private void ZipProgressAdapter(object sender, ExtractProgressProp e)
_progressPerFileSizeCurrent = (long)e.TotalRead;
_progressPerFileSizeTotal = (long)e.TotalSize;
- // Assign local sizes to progress
- _progress.ProgressPerFileSizeCurrent = _progressPerFileSizeCurrent;
- _progress.ProgressPerFileSizeTotal = _progressPerFileSizeTotal;
- _progress.ProgressAllSizeCurrent = _progressAllSizeCurrent;
- _progress.ProgressAllSizeTotal = _progressAllSizeTotal;
+ lock (_progress)
+ {
+ // Assign local sizes to progress
+ _progress.ProgressPerFileSizeCurrent = _progressPerFileSizeCurrent;
+ _progress.ProgressPerFileSizeTotal = _progressPerFileSizeTotal;
+ _progress.ProgressAllSizeCurrent = _progressAllSizeCurrent;
+ _progress.ProgressAllSizeTotal = _progressAllSizeTotal;
- // Calculate the speed
- _progress.ProgressAllSpeed = CalculateSpeed(lastSize);
+ // 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();
+ // 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();
+ }
UpdateAll();
}
@@ -4183,31 +4272,35 @@ private void HttpClientDownloadProgressAdapter(int read, DownloadProgress downlo
if (CheckIfNeedRefreshStopwatch())
{
- // 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;
+ lock (_progress)
+ {
+ // Assign local sizes to progress
+ _progress.ProgressPerFileSizeCurrent = _progressPerFileSizeCurrent;
+ _progress.ProgressPerFileSizeTotal = _progressPerFileSizeTotal;
+ _progress.ProgressAllSizeCurrent = _progressAllSizeCurrent;
+ _progress.ProgressAllSizeTotal = _progressAllSizeTotal;
- // Calculate percentage
- _progress.ProgressPerFilePercentage =
- Math.Round(_progressPerFileSizeCurrent / (double)_progressPerFileSizeTotal * 100, 2);
- _progress.ProgressAllPercentage =
- Math.Round(_progressAllSizeCurrent / (double)_progressAllSizeTotal * 100, 2);
+ // Assign speed with clamped value
+ double speedClamped = speedAll.ClampLimitedSpeedNumber();
+ _progress.ProgressAllSpeed = speedClamped;
- // Calculate the timelapse
- double progressTimeAvg = (_progressAllSizeTotal - _progressAllSizeCurrent) / speedClamped;
- _progress.ProgressAllTimeLeft = progressTimeAvg.ToTimeSpanNormalized();
+ // Calculate percentage
+ _progress.ProgressPerFilePercentage =
+ Math.Round(_progressPerFileSizeCurrent / (double)_progressPerFileSizeTotal * 100, 2);
+ _progress.ProgressAllPercentage =
+ Math.Round(_progressAllSizeCurrent / (double)_progressAllSizeTotal * 100, 2);
- // 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);
+ // Calculate the timelapse
+ double progressTimeAvg = (_progressAllSizeTotal - _progressAllSizeCurrent) / speedClamped;
+ _progress.ProgressAllTimeLeft = progressTimeAvg.ToTimeSpanNormalized();
+ // 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);
+ }
// Update the status
UpdateAll();
}
@@ -4232,25 +4325,28 @@ private void HttpClientDownloadProgressAdapter(object sender, DownloadEvent e)
{
if (e.State != DownloadState.Merging)
{
- // Assign local sizes to progress
- _progress.ProgressPerFileSizeCurrent = _progressPerFileSizeCurrent;
- _progress.ProgressPerFileSizeTotal = _progressPerFileSizeTotal;
- _progress.ProgressAllSizeCurrent = _progressAllSizeCurrent;
- _progress.ProgressAllSizeTotal = _progressAllSizeTotal;
-
- // Calculate the speed
- _progress.ProgressAllSpeed = speedAll;
-
- // Calculate percentage
- _progress.ProgressPerFilePercentage =
- Math.Round(_progressPerFileSizeCurrent / (double)_progressPerFileSizeTotal * 100, 2);
- _progress.ProgressAllPercentage =
- Math.Round(_progressAllSizeCurrent / (double)_progressAllSizeTotal * 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;
+
+ // Calculate the speed
+ _progress.ProgressAllSpeed = speedAll;
+
+ // Calculate percentage
+ _progress.ProgressPerFilePercentage =
+ Math.Round(_progressPerFileSizeCurrent / (double)_progressPerFileSizeTotal * 100, 2);
+ _progress.ProgressAllPercentage =
+ Math.Round(_progressAllSizeCurrent / (double)_progressAllSizeTotal * 100, 2);
+
+ // Calculate the timelapse
+ _progress.ProgressAllTimeLeft =
+ ((_progressAllSizeTotal - _progressAllSizeCurrent) / _progress.ProgressAllSpeed.Unzeroed())
+ .ToTimeSpanNormalized();
+ }
}
else
{
@@ -4260,22 +4356,25 @@ private void HttpClientDownloadProgressAdapter(object sender, DownloadEvent e)
// If status is merging, then use progress for speed and timelapse from Http client
// and set the rest from the base class
- _progress.ProgressAllTimeLeft = e.TimeLeft;
+ lock (_progress)
+ {
+ _progress.ProgressAllTimeLeft = e.TimeLeft;
- _progress.ProgressAllSpeed = speedAll;
+ _progress.ProgressAllSpeed = speedAll;
- _progress.ProgressPerFileSizeCurrent = _progressPerFileSizeCurrent;
- _progress.ProgressPerFileSizeTotal = _progressPerFileSizeTotal;
- _progress.ProgressAllSizeCurrent = _progressAllSizeCurrent;
- _progress.ProgressAllSizeTotal = _progressAllSizeTotal;
- _progress.ProgressAllPercentage =
- Math.Round(_progressAllSizeCurrent / (double)_progressAllSizeTotal * 100, 2);
+ _progress.ProgressPerFileSizeCurrent = _progressPerFileSizeCurrent;
+ _progress.ProgressPerFileSizeTotal = _progressPerFileSizeTotal;
+ _progress.ProgressAllSizeCurrent = _progressAllSizeCurrent;
+ _progress.ProgressAllSizeTotal = _progressAllSizeTotal;
+ _progress.ProgressAllPercentage =
+ Math.Round(_progressAllSizeCurrent / (double)_progressAllSizeTotal * 100, 2);
+ }
}
// Update the status of per file size and current progress from Http client
_progressPerFileSizeCurrent = e.SizeDownloaded;
_progressPerFileSizeTotal = e.SizeToBeDownloaded;
- _progress.ProgressPerFilePercentage = e.ProgressPercentage;
+ lock (_progress) _progress.ProgressPerFilePercentage = e.ProgressPercentage;
// Update the status
UpdateAll();
diff --git a/CollapseLauncher/Classes/Interfaces/Class/ProgressBase.cs b/CollapseLauncher/Classes/Interfaces/Class/ProgressBase.cs
index e7aee12a8..1d340802d 100644
--- a/CollapseLauncher/Classes/Interfaces/Class/ProgressBase.cs
+++ b/CollapseLauncher/Classes/Interfaces/Class/ProgressBase.cs
@@ -2,6 +2,7 @@
using CollapseLauncher.Dialogs;
using CollapseLauncher.Extension;
using CollapseLauncher.Helper;
+using CommunityToolkit.WinUI;
using Hi3Helper;
using Hi3Helper.Data;
using Hi3Helper.Http;
@@ -21,12 +22,14 @@
using System.IO.Hashing;
using System.Linq;
using System.Net.Http;
+using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using Hi3Helper.SentryHelper;
using static Hi3Helper.Locale;
using static Hi3Helper.Logger;
+using CollapseUIExtension = CollapseLauncher.Extension.UIElementExtensions;
#nullable enable
namespace CollapseLauncher.Interfaces
@@ -675,16 +678,18 @@ protected void ResetStatusAndProgressProperty()
_status.IsCanceled = false;
// Reset all total activity progress
- _progress.ProgressPerFilePercentage = 0;
- _progress.ProgressAllPercentage = 0;
- _progress.ProgressPerFileSpeed = 0;
- _progress.ProgressAllSpeed = 0;
-
- _progress.ProgressAllEntryCountCurrent = 0;
- _progress.ProgressAllEntryCountTotal = 0;
- _progress.ProgressPerFileEntryCountCurrent = 0;
- _progress.ProgressPerFileEntryCountTotal = 0;
-
+ lock (_progress)
+ {
+ _progress.ProgressPerFilePercentage = 0;
+ _progress.ProgressAllPercentage = 0;
+ _progress.ProgressPerFileSpeed = 0;
+ _progress.ProgressAllSpeed = 0;
+
+ _progress.ProgressAllEntryCountCurrent = 0;
+ _progress.ProgressAllEntryCountTotal = 0;
+ _progress.ProgressPerFileEntryCountCurrent = 0;
+ _progress.ProgressPerFileEntryCountTotal = 0;
+ }
// Reset all inner counter
_progressAllCountCurrent = 0;
_progressAllCountTotal = 0;
@@ -748,7 +753,9 @@ protected async ValueTask FetchBilibiliSDK(CancellationToken token)
// Get the URL and get the remote stream of the zip file
// Also buffer the stream to memory
string? url = _gameVersionManager.GameAPIProp.data.sdk.path;
- using HttpResponseMessage httpResponse = await FallbackCDNUtil.GetURLHttpResponse(url, token);
+ if (url == null) throw new NullReferenceException();
+
+ HttpResponseMessage httpResponse = await FallbackCDNUtil.GetURLHttpResponse(url, token);
await using BridgedNetworkStream httpStream = await FallbackCDNUtil.GetHttpStreamFromResponse(httpResponse, token);
await using MemoryStream bufferedStream = await BufferSourceStreamToMemoryStream(httpStream, token);
using ZipArchive zip = new ZipArchive(bufferedStream, ZipArchiveMode.Read, true);
@@ -1168,7 +1175,7 @@ protected async Task SpawnRepairDialog(List assetIndex, Action? actionIfInte
{
ArgumentNullException.ThrowIfNull(assetIndex);
long totalSize = assetIndex.Sum(x => x.GetAssetSize());
- StackPanel content = UIElementExtensions.CreateStackPanel();
+ StackPanel content = CollapseUIExtension.CreateStackPanel();
content.AddElementToStackPanel(new TextBlock()
{
@@ -1177,7 +1184,7 @@ protected async Task SpawnRepairDialog(List assetIndex, Action? actionIfInte
TextWrapping = TextWrapping.Wrap
});
Button showBrokenFilesButton = content.AddElementToStackPanel(
- UIElementExtensions.CreateButtonWithIcon