diff --git a/CollapseLauncher/Classes/CachesManagement/Zenless/ZenlessCache.cs b/CollapseLauncher/Classes/CachesManagement/Zenless/ZenlessCache.cs index d51a1e90b..51163446c 100644 --- a/CollapseLauncher/Classes/CachesManagement/Zenless/ZenlessCache.cs +++ b/CollapseLauncher/Classes/CachesManagement/Zenless/ZenlessCache.cs @@ -5,12 +5,9 @@ namespace CollapseLauncher { - internal class ZenlessCache : ZenlessRepair, ICache, ICacheBase + internal class ZenlessCache(UIElement parentUI, IGameVersionCheck gameVersionManager, ZenlessSettings gameSettings) + : ZenlessRepair(parentUI, gameVersionManager, gameSettings, false, null, true), ICache, ICacheBase { - public ZenlessCache(UIElement parentUI, IGameVersionCheck gameVersionManager, ZenlessSettings gameSettings) - : base(parentUI, gameVersionManager, gameSettings, false, null, true) - { } - public ZenlessCache AsBaseType() => this; public async Task StartUpdateRoutine(bool showInteractivePrompt = false) diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs index ad2b16858..1e8133ca5 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs @@ -6,7 +6,6 @@ using System.Text.Json.Nodes; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; -// ReSharper disable ReturnTypeCanBeNotNullable #nullable enable namespace CollapseLauncher.GameSettings.Zenless; @@ -20,7 +19,7 @@ internal class GeneralData : MagicNodeBaseValues [JsonPropertyName("SystemSettingDataMap")] [JsonIgnore] // We ignore this one from getting serialized to default JSON value - public JsonNode? SystemSettingDataMap + public JsonNode SystemSettingDataMap { // Cache the SystemSettingDataMap inside the parent SettingsJsonNode // and ensure that the node for SystemSettingDataMap exists. If not exist, @@ -30,7 +29,7 @@ public JsonNode? SystemSettingDataMap [JsonPropertyName("KeyboardBindingMap")] [JsonIgnore] // We ignore this one from getting serialized to default JSON value - public JsonNode? KeyboardBindingMap + public JsonNode KeyboardBindingMap { // Cache the KeyboardBindingMap inside the parent SettingsJsonNode // and ensure that the node for KeyboardBindingMap exists. If not exist, @@ -40,7 +39,7 @@ public JsonNode? KeyboardBindingMap [JsonPropertyName("MouseBindingMap")] [JsonIgnore] // We ignore this one from getting serialized to default JSON value - public JsonNode? MouseBindingMap + public JsonNode MouseBindingMap { // Cache the MouseBindingMap inside the parent SettingsJsonNode // and ensure that the node for MouseBindingMap exists. If not exist, @@ -50,7 +49,7 @@ public JsonNode? MouseBindingMap [JsonPropertyName("GamepadBindingMap")] [JsonIgnore] // We ignore this one from getting serialized to default JSON value - public JsonNode? GamepadBindingMap + public JsonNode GamepadBindingMap { // Cache the GamepadBindingMap inside the parent SettingsJsonNode // and ensure that the node for GamepadBindingMap exists. If not exist, @@ -99,14 +98,14 @@ public int SelectedServerIndex public LanguageText DeviceLanguageType { get => SettingsJsonNode.GetNodeValueEnum("DeviceLanguageType", LanguageText.Unset); - set => SettingsJsonNode.SetNodeValueEnum("DeviceLanguageType", value, JsonEnumStoreType.AsNumber); + set => SettingsJsonNode.SetNodeValueEnum("DeviceLanguageType", value); } [JsonPropertyName("DeviceLanguageVoiceType")] public LanguageVoice DeviceLanguageVoiceType { get => SettingsJsonNode.GetNodeValueEnum("DeviceLanguageVoiceType", LanguageVoice.Unset); - set => SettingsJsonNode.SetNodeValueEnum("DeviceLanguageVoiceType", value, JsonEnumStoreType.AsNumber); + set => SettingsJsonNode.SetNodeValueEnum("DeviceLanguageVoiceType", value); } [JsonPropertyName("PlayerPrefs_StringContainer")] @@ -207,12 +206,9 @@ public bool DisableBattleUIOptimization [JsonIgnore] public GraphicsPresetOption GraphicsPreset { - get => (_graphicsPresData.HasValue - ? _graphicsPresData - : _graphicsPresData = - SystemSettingDataMap! - .AsSystemSettingLocalData("3", GraphicsPresetOption.Medium)) - .Value.GetDataEnum(); + get => (_graphicsPresData ??= SystemSettingDataMap + .AsSystemSettingLocalData("3", GraphicsPresetOption.Medium)) + .GetDataEnum(); set => _graphicsPresData?.SetDataEnum(value); } @@ -225,12 +221,8 @@ public GraphicsPresetOption GraphicsPreset [JsonIgnore] public int ResolutionIndex { - get => (_resolutionIndexData.HasValue - ? _resolutionIndexData - : _resolutionIndexData = - SystemSettingDataMap! - .AsSystemSettingLocalData("5", -1)) - .Value.GetData(); + get => (_resolutionIndexData ??= SystemSettingDataMap + .AsSystemSettingLocalData("5", -1)).GetData(); set => _resolutionIndexData?.SetData(value); } @@ -245,10 +237,8 @@ public bool VSync { // Initialize the field under _vSyncData as SystemSettingLocalData get => - (_vSyncData.HasValue - ? _vSyncData - : _vSyncData = SystemSettingDataMap! - .AsSystemSettingLocalData("8", 1)).Value.GetData() == 1; + (_vSyncData ??= SystemSettingDataMap + .AsSystemSettingLocalData("8", 1)).GetData() == 1; set => _vSyncData?.SetData(value ? 1 : 0); } @@ -265,12 +255,9 @@ public RenderResOption RenderResolution { // Initialize the field under _renderResolutionData as SystemSettingLocalData get => - (_renderResolutionData.HasValue - ? _renderResolutionData - : _renderResolutionData = SystemSettingDataMap! - .AsSystemSettingLocalData("9", - RenderResOption.f10)).Value - .GetDataEnum(); + (_renderResolutionData ??= SystemSettingDataMap + .AsSystemSettingLocalData("9", + RenderResOption.f10)).GetDataEnum(); set => _renderResolutionData?.SetDataEnum(value); } @@ -287,11 +274,8 @@ public QualityOption3 ShadowQuality { // Initialize the field under _shadowQualityData as SystemSettingLocalData get => - (_shadowQualityData.HasValue - ? _shadowQualityData - : _shadowQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("10", - QualityOption3.Medium)).Value - .GetDataEnum(); + (_shadowQualityData ??= SystemSettingDataMap.AsSystemSettingLocalData("10", + QualityOption3.Medium)).GetDataEnum(); set => _shadowQualityData?.SetDataEnum(value); } @@ -304,12 +288,9 @@ public AntiAliasingOption AntiAliasing { // Initialize the field under _antiAliasingData as SystemSettingLocalData get => - (_antiAliasingData.HasValue - ? _antiAliasingData - : _antiAliasingData = SystemSettingDataMap! - .AsSystemSettingLocalData("12", - AntiAliasingOption.TAA)).Value - .GetDataEnum(); + (_antiAliasingData ??= SystemSettingDataMap + .AsSystemSettingLocalData("12", + AntiAliasingOption.TAA)).GetDataEnum(); set => _antiAliasingData?.SetDataEnum(value); } @@ -324,11 +305,8 @@ public AntiAliasingOption AntiAliasing [JsonIgnore] public QualityOption4 VolumetricFogQuality { - get => (_volFogQualityData.HasValue - ? _volFogQualityData - : _volFogQualityData = SystemSettingDataMap! - .AsSystemSettingLocalData("13", QualityOption4.Medium)).Value - .GetDataEnum(); + get => (_volFogQualityData ??= SystemSettingDataMap + .AsSystemSettingLocalData("13", QualityOption4.Medium)).GetDataEnum(); set => _volFogQualityData?.SetDataEnum(value); } @@ -338,10 +316,7 @@ public QualityOption4 VolumetricFogQuality [JsonIgnore] public bool Bloom { - get => (_bloomData.HasValue - ? _bloomData - : _bloomData = SystemSettingDataMap!.AsSystemSettingLocalData("14", 1)).Value - .GetData() == 1; + get => (_bloomData ??= SystemSettingDataMap.AsSystemSettingLocalData("14", 1)).GetData() == 1; set => _bloomData?.SetData(value ? 1 : 0); } @@ -356,11 +331,8 @@ public bool Bloom public QualityOption4 ReflectionQuality { get => - (_reflQualityData.HasValue - ? _reflQualityData - : _reflQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("15", - QualityOption4.Medium)).Value - .GetDataEnum(); + (_reflQualityData ??= SystemSettingDataMap.AsSystemSettingLocalData("15", + QualityOption4.Medium)).GetDataEnum(); set => _reflQualityData?.SetDataEnum(value); } @@ -376,11 +348,9 @@ public QualityOption4 ReflectionQuality public QualityOption3 FxQuality { get => - (_fxQualityData.HasValue - ? _fxQualityData - : _fxQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("16", - QualityOption3.Medium)).Value - .GetDataEnum(); + (_fxQualityData ??= SystemSettingDataMap.AsSystemSettingLocalData("16", + QualityOption3.Medium)) + .GetDataEnum(); set => _fxQualityData?.SetDataEnum(value); } @@ -390,10 +360,7 @@ public QualityOption3 FxQuality public int ColorFilter { - get => (_colorFilterData.HasValue - ? _colorFilterData - : _colorFilterData = SystemSettingDataMap!.AsSystemSettingLocalData("95", 10)) - .Value.GetData(); + get => (_colorFilterData ??= SystemSettingDataMap.AsSystemSettingLocalData("95", 10)).GetData(); set => _colorFilterData?.SetData(value); } @@ -408,11 +375,8 @@ public int ColorFilter public QualityOption2 CharacterQuality { get => - (_charQualityData.HasValue - ? _charQualityData - : _charQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("99", - QualityOption2.High)).Value - .GetDataEnum(); + (_charQualityData ??= SystemSettingDataMap.AsSystemSettingLocalData("99", + QualityOption2.High)).GetDataEnum(); set => _charQualityData?.SetDataEnum(value); } @@ -423,10 +387,8 @@ public QualityOption2 CharacterQuality [JsonIgnore] public bool Distortion { - get => (_distortionData.HasValue - ? _distortionData - : _distortionData = SystemSettingDataMap!.AsSystemSettingLocalData("107", 1)) - .Value.GetData() == 1; + get => (_distortionData ??= SystemSettingDataMap.AsSystemSettingLocalData("107", 1)) + .GetData() == 1; set => _distortionData?.SetData(value ? 1 : 0); } @@ -441,11 +403,8 @@ public bool Distortion public QualityOption3 ShadingQuality { get => - (_shadingQualityData.HasValue - ? _shadingQualityData - : _shadingQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("108", - QualityOption3.Medium)).Value - .GetDataEnum(); + (_shadingQualityData ??= SystemSettingDataMap.AsSystemSettingLocalData("108", + QualityOption3.Medium)).GetDataEnum(); set => _shadingQualityData?.SetDataEnum(value); } @@ -461,15 +420,46 @@ public QualityOption3 ShadingQuality public QualityOption2 EnvironmentQuality { get => - (_envQualityData.HasValue - ? _envQualityData - : _envQualityData = SystemSettingDataMap!.AsSystemSettingLocalData("109", - QualityOption2.High)).Value - .GetDataEnum(); + (_envQualityData ??= SystemSettingDataMap.AsSystemSettingLocalData("109", + QualityOption2.High)) + .GetDataEnum(); set => _envQualityData?.SetDataEnum(value); } - + + // Key 12155 Global Illumination + private SystemSettingLocalData? _envGlobalIllumination; + + /// + /// Sets the in-game global illumination settings for Environment + /// + /// + [JsonIgnore] + public QualityOption3 GlobalIllumination + { + get => + (_envGlobalIllumination ??= SystemSettingDataMap.AsSystemSettingLocalData("12155", + QualityOption3.High)).GetDataEnum(); + set => + _envGlobalIllumination?.SetDataEnum(value); + } + + // Key 8 VSync + private SystemSettingLocalData? _vMotionBlur; + + /// + /// Set Motion Blur mode + /// + [JsonIgnore] + public bool MotionBlur + { + get => + (_vMotionBlur ??= SystemSettingDataMap + .AsSystemSettingLocalData("106", 1)).GetData() == 1; + set => + _vMotionBlur?.SetData(value ? 1 : 0); + } + // Key 110 FPS private SystemSettingLocalData? _fpsData; @@ -481,8 +471,8 @@ public QualityOption2 EnvironmentQuality public FpsOption Fps { // Initialize the field under _fpsData as SystemSettingLocalData - get => (_fpsData.HasValue ? _fpsData : _fpsData = SystemSettingDataMap! - .AsSystemSettingLocalData("110", FpsOption.Hi60)).Value.GetDataEnum(); + get => (_fpsData ??= SystemSettingDataMap + .AsSystemSettingLocalData("110", FpsOption.Hi60)).GetDataEnum(); set => _fpsData?.SetDataEnum(value); } @@ -495,9 +485,7 @@ public FpsOption Fps [JsonIgnore] public int Audio_MainVolume { - get => (_mainVolData.HasValue - ? _mainVolData - : _mainVolData = SystemSettingDataMap!.AsSystemSettingLocalData("31", 10)).Value.GetData(); + get => (_mainVolData ??= SystemSettingDataMap.AsSystemSettingLocalData("31", 10)).GetData(); set => _mainVolData?.SetData(value); } @@ -507,9 +495,7 @@ public int Audio_MainVolume [JsonIgnore] public int Audio_MusicVolume { - get => (_musicVolData.HasValue - ? _musicVolData - : _musicVolData = SystemSettingDataMap!.AsSystemSettingLocalData("32", 10)).Value.GetData(); + get => (_musicVolData ??= SystemSettingDataMap.AsSystemSettingLocalData("32", 10)).GetData(); set => _musicVolData?.SetData(value); } @@ -519,9 +505,7 @@ public int Audio_MusicVolume [JsonIgnore] public int Audio_DialogVolume { - get => (_dialogVolData.HasValue - ? _dialogVolData - : _dialogVolData = SystemSettingDataMap!.AsSystemSettingLocalData("33", 10)).Value.GetData(); + get => (_dialogVolData ??= SystemSettingDataMap.AsSystemSettingLocalData("33", 10)).GetData(); set => _dialogVolData?.SetData(value); } @@ -531,9 +515,7 @@ public int Audio_DialogVolume [JsonIgnore] public int Audio_SfxVolume { - get => (_sfxVolData.HasValue - ? _sfxVolData - : _sfxVolData = SystemSettingDataMap!.AsSystemSettingLocalData("34", 10)).Value.GetData(); + get => (_sfxVolData ??= SystemSettingDataMap.AsSystemSettingLocalData("34", 10)).GetData(); set => _sfxVolData?.SetData(value); } @@ -543,11 +525,8 @@ public int Audio_SfxVolume [JsonIgnore] public AudioPlaybackDevice Audio_PlaybackDevice { - get => (_playDevData.HasValue - ? _playDevData - : _playDevData = - SystemSettingDataMap!.AsSystemSettingLocalData("10104", - AudioPlaybackDevice.Headphones)).Value + get => (_playDevData ??= SystemSettingDataMap.AsSystemSettingLocalData("10104", + AudioPlaybackDevice.Headphones)) .GetDataEnum(); set => _playDevData?.SetDataEnum(value); } @@ -558,10 +537,8 @@ public AudioPlaybackDevice Audio_PlaybackDevice [JsonIgnore] public bool Audio_MuteOnMinimize { - get => (_muteAudOnMinimizeData.HasValue - ? _muteAudOnMinimizeData - : _muteAudOnMinimizeData = SystemSettingDataMap!.AsSystemSettingLocalData("10113", 1)).Value.GetData() == 1; - set => _muteAudOnMinimizeData?.SetData((value ? 1 : 0)); + get => (_muteAudOnMinimizeData ??= SystemSettingDataMap.AsSystemSettingLocalData("10113", 1)).GetData() == 1; + set => _muteAudOnMinimizeData?.SetData(value ? 1 : 0); } #endregion diff --git a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs index 8326e9aea..55afd208a 100644 --- a/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs +++ b/CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/JsonProperties/Properties.cs @@ -9,7 +9,7 @@ #nullable enable namespace CollapseLauncher.GameSettings.Zenless.JsonProperties; -public struct SystemSettingLocalData +public readonly struct SystemSettingLocalData where TData : struct { private const string TypeKey = "MoleMole.SystemSettingLocalData"; @@ -44,7 +44,7 @@ public SystemSettingLocalData([NotNull] JsonNode node, TData defaultData = defau public static class SystemSettingLocalDataExt { public static SystemSettingLocalData AsSystemSettingLocalData( - [NotNull] this JsonNode node, string keyName, TData defaultData = default, int defaultVersion = 1) + [NotNull] this JsonNode? node, string keyName, TData defaultData = default, int defaultVersion = 1) where TData : struct { ArgumentNullException.ThrowIfNull(node, nameof(node)); diff --git a/CollapseLauncher/Classes/GameManagement/GameVersion/Genshin/VersionCheck.cs b/CollapseLauncher/Classes/GameManagement/GameVersion/Genshin/VersionCheck.cs index 5073b7cf1..51e94930a 100644 --- a/CollapseLauncher/Classes/GameManagement/GameVersion/Genshin/VersionCheck.cs +++ b/CollapseLauncher/Classes/GameManagement/GameVersion/Genshin/VersionCheck.cs @@ -38,7 +38,7 @@ public GameTypeGenshinVersion(UIElement parentUIElement, RegionResourceProp game return base.TryFindGamePathFromExecutableAndConfig(path, executableName); } - return null; + return basePath; } protected override bool IsExecutableFileExist(string? executableName) diff --git a/CollapseLauncher/Classes/InstallManagement/BaseClass/InstallManagerBase.cs b/CollapseLauncher/Classes/InstallManagement/BaseClass/InstallManagerBase.cs index 09397b7b3..3d22982e7 100644 --- a/CollapseLauncher/Classes/InstallManagement/BaseClass/InstallManagerBase.cs +++ b/CollapseLauncher/Classes/InstallManagement/BaseClass/InstallManagerBase.cs @@ -1574,6 +1574,10 @@ protected virtual async Task StartPackageInstallationInner(List= 0) { diff --git a/CollapseLauncher/Classes/Interfaces/Class/GamePropertyBase.cs b/CollapseLauncher/Classes/Interfaces/Class/GamePropertyBase.cs index 3247088ac..f27772f41 100644 --- a/CollapseLauncher/Classes/Interfaces/Class/GamePropertyBase.cs +++ b/CollapseLauncher/Classes/Interfaces/Class/GamePropertyBase.cs @@ -22,22 +22,23 @@ public GamePropertyBase(UIElement parentUI, IGameVersionCheck gameVersionManager public GamePropertyBase(UIElement parentUI, IGameVersionCheck gameVersionManager, string gamePath, string gameRepoURL, string versionOverride) { _gameVersionManager = gameVersionManager; - _parentUI = parentUI; - _gamePathField = gamePath; - _gameRepoURL = gameRepoURL; - _token = new CancellationTokenSourceWrapper(); - AssetEntry = new ObservableCollection(); + _parentUI = parentUI; + _gamePathField = gamePath; + _gameRepoURL = gameRepoURL; + _token = new CancellationTokenSourceWrapper(); + AssetEntry = new ObservableCollection(); + _isVersionOverride = versionOverride != null; // If the version override is not null, then assign the override value - if (_isVersionOverride = versionOverride != null) + if (_isVersionOverride) { _gameVersionOverride = new GameVersion(versionOverride); } } - protected const int _bufferLength = 4 << 10; - protected const int _bufferMediumLength = 512 << 10; - protected const int _bufferBigLength = 1 << 20; + protected const int _bufferLength = 4 << 10; // 4 KiB + protected const int _bufferMediumLength = 4 << 17; // 512 KiB + protected const int _bufferBigLength = 1 << 20; // 1 MiB protected const int _sizeForMultiDownload = 10 << 20; protected const int _downloadThreadCountReserved = 16; protected virtual string _userAgent { get; set; } = "UnityPlayer/2017.4.18f1 (UnityWebRequest/1.0, libcurl/7.51.0-DEV)"; @@ -51,7 +52,18 @@ public GamePropertyBase(UIElement parentUI, IGameVersionCheck gameVersionManager protected Stopwatch _stopwatch { get; set; } protected Stopwatch _refreshStopwatch { get; set; } protected Stopwatch _downloadSpeedRefreshStopwatch { get; set; } - protected GameVersion _gameVersion { get => _isVersionOverride ? _gameVersionOverride : _gameVersionManager.GetGameExistingVersion().Value; } + protected GameVersion _gameVersion + { + get + { + if (_gameVersionManager != null && _isVersionOverride) + { + return _gameVersionOverride; + } + return _gameVersionManager?.GetGameExistingVersion() ?? throw new NullReferenceException(); + } + } + protected IGameVersionCheck _gameVersionManager { get; set; } protected IGameSettings _gameSettings { get; set; } protected string _gamePath { get => string.IsNullOrEmpty(_gamePathField) ? _gameVersionManager.GameDirPath : _gamePathField; } diff --git a/CollapseLauncher/Classes/Interfaces/Class/ProgressBase.cs b/CollapseLauncher/Classes/Interfaces/Class/ProgressBase.cs index c37f074fa..0131f4a07 100644 --- a/CollapseLauncher/Classes/Interfaces/Class/ProgressBase.cs +++ b/CollapseLauncher/Classes/Interfaces/Class/ProgressBase.cs @@ -4,7 +4,6 @@ using Hi3Helper; using Hi3Helper.Data; using Hi3Helper.Http; -using Hi3Helper.Http.Legacy; using Hi3Helper.Preset; using Hi3Helper.Shared.Region; using Hi3Helper.Sophon; @@ -16,7 +15,6 @@ using System.Buffers; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.IO; using System.IO.Compression; using System.IO.Hashing; @@ -28,15 +26,16 @@ using static Hi3Helper.Locale; using static Hi3Helper.Logger; +#nullable enable namespace CollapseLauncher.Interfaces { internal class ProgressBase : GamePropertyBase where T1 : IAssetIndexSummary { - public ProgressBase(UIElement parentUI, IGameVersionCheck GameVersionManager, IGameSettings GameSettings, string gamePath, string gameRepoURL, string versionOverride) - : base(parentUI, GameVersionManager, GameSettings, gamePath, gameRepoURL, versionOverride) => Init(); + public ProgressBase(UIElement parentUI, IGameVersionCheck gameVersionManager, IGameSettings gameSettings, string? gamePath, string? gameRepoURL, string? versionOverride) + : base(parentUI, gameVersionManager, gameSettings, gamePath, gameRepoURL, versionOverride) => Init(); - public ProgressBase(UIElement parentUI, IGameVersionCheck GameVersionManager, string gamePath, string gameRepoURL, string versionOverride) - : base(parentUI, GameVersionManager, gamePath, gameRepoURL, versionOverride) => Init(); + public ProgressBase(UIElement parentUI, IGameVersionCheck gameVersionManager, string? gamePath, string? gameRepoURL, string? versionOverride) + : base(parentUI, gameVersionManager, gamePath, gameRepoURL, versionOverride) => Init(); private void Init() { @@ -50,15 +49,15 @@ private void Init() _assetIndex = new List(); } - private object _objLock = new object(); + private readonly Lock _objLock = new(); - public event EventHandler ProgressChanged; - public event EventHandler StatusChanged; + public event EventHandler? ProgressChanged; + public event EventHandler? StatusChanged; - protected TotalPerfileStatus _sophonStatus; - protected TotalPerfileProgress _sophonProgress; - protected TotalPerfileStatus _status; - protected TotalPerfileProgress _progress; + protected TotalPerfileStatus? _sophonStatus; + protected TotalPerfileProgress? _sophonProgress; + protected TotalPerfileStatus? _status; + protected TotalPerfileProgress? _progress; protected int _progressAllCountCurrent; protected int _progressAllCountFound; protected int _progressAllCountTotal; @@ -78,8 +77,8 @@ private void Init() protected bool _isSophonInUpdateMode { get; set; } #region ProgressEventHandlers - Fetch - protected void _innerObject_ProgressAdapter(object sender, TotalPerfileProgress e) => ProgressChanged?.Invoke(sender, e); - protected void _innerObject_StatusAdapter(object sender, TotalPerfileStatus e) => StatusChanged?.Invoke(sender, e); + protected void _innerObject_ProgressAdapter(object? sender, TotalPerfileProgress e) => ProgressChanged?.Invoke(sender, e); + protected void _innerObject_StatusAdapter(object? sender, TotalPerfileStatus e) => StatusChanged?.Invoke(sender, e); protected virtual async void _httpClient_FetchAssetProgress(int size, DownloadProgress downloadProgress) { @@ -87,7 +86,7 @@ protected virtual async void _httpClient_FetchAssetProgress(int size, DownloadPr { double speed = (downloadProgress.BytesDownloaded / _stopwatch.Elapsed.TotalSeconds).ClampLimitedSpeedNumber(); TimeSpan timeLeftSpan = ((downloadProgress.BytesTotal - downloadProgress.BytesDownloaded) / speed).ToTimeSpanNormalized(); - double percentage = ConverterTool.GetPercentageNumber(downloadProgress.BytesDownloaded, downloadProgress.BytesTotal, 2); + double percentage = ConverterTool.GetPercentageNumber(downloadProgress.BytesDownloaded, downloadProgress.BytesTotal); lock (_status!) { @@ -113,30 +112,6 @@ protected virtual async void _httpClient_FetchAssetProgress(int size, DownloadPr } } - protected virtual void _httpClient_FetchAssetProgress(object sender, DownloadEvent e) - { - lock (_status!) - { - // Update fetch status - _status.IsProgressPerFileIndetermined = false; - _status.IsProgressAllIndetermined = false; - _status.ActivityPerFile = string.Format(Lang!._GameRepairPage!.PerProgressSubtitle3!, ConverterTool.SummarizeSizeSimple(e!.Speed)); - } - - lock (_progress!) - { - // Update fetch progress - _progress.ProgressPerFilePercentage = e.ProgressPercentage; - _progress.ProgressAllSizeCurrent = e.SizeDownloaded; - _progress.ProgressAllSizeTotal = e.SizeToBeDownloaded; - _progress.ProgressAllSpeed = e.Speed; - _progress.ProgressAllTimeLeft = e.TimeLeft; - } - - // Push status and progress update - UpdateStatus(); - UpdateProgress(); - } #endregion #region ProgressEventHandlers - Repair @@ -149,7 +124,7 @@ protected virtual async void _httpClient_RepairAssetProgress(int size, DownloadP { double speed = (_progressAllIOReadCurrent / _downloadSpeedRefreshStopwatch.Elapsed.TotalSeconds).ClampLimitedSpeedNumber(); TimeSpan timeLeftSpan = ((_progressAllSizeCurrent - _progressAllSizeTotal) / speed).ToTimeSpanNormalized(); - double percentagePerFile = ConverterTool.GetPercentageNumber(downloadProgress.BytesDownloaded, downloadProgress.BytesTotal, 2); + double percentagePerFile = ConverterTool.GetPercentageNumber(downloadProgress.BytesDownloaded, downloadProgress.BytesTotal); lock (_progress!) { @@ -195,62 +170,13 @@ protected virtual async void _httpClient_RepairAssetProgress(int size, DownloadP } } - protected virtual async void _httpClient_RepairAssetProgress(object sender, DownloadEvent e) - { - lock (_progress!) - { - _progress.ProgressPerFilePercentage = e!.ProgressPercentage; - _progress.ProgressPerFileSizeCurrent = e!.SizeDownloaded; - _progress.ProgressPerFileSizeTotal = e!.SizeToBeDownloaded; - _progress.ProgressAllSizeCurrent = _progressAllSizeCurrent; - _progress.ProgressAllSizeTotal = _progressAllSizeTotal; - - // Calculate speed - long speed = (long)(_progressAllSizeCurrent / _stopwatch!.Elapsed.TotalSeconds); - _progress.ProgressAllSpeed = speed; - _progress.ProgressAllTimeLeft = ((_progressAllSizeCurrent - _progressAllSizeTotal) / ConverterTool.Unzeroed(speed)) - .ToTimeSpanNormalized(); - - // Update current progress percentages - _progress.ProgressAllPercentage = _progressAllSizeCurrent != 0 ? - ConverterTool.GetPercentageNumber(_progressAllSizeCurrent, _progressAllSizeTotal) : - 0; - - if (e.State != DownloadState.Merging) - { - _progressAllSizeCurrent += e.Read; - } - } - - if (await CheckIfNeedRefreshStopwatch()) - { - lock (_status!) - { - // Update current activity status - _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}"; - - // Trigger update - UpdateAll(); - } - } - } - - protected virtual void UpdateRepairStatus(string activityStatus, string ActivityAll, bool isPerFileIndetermined) + protected virtual void UpdateRepairStatus(string activityStatus, string activityAll, bool isPerFileIndetermined) { lock (_status!) { // Set repair activity status _status.ActivityStatus = activityStatus; - _status.ActivityAll = ActivityAll; + _status.ActivityAll = activityAll; _status.IsProgressPerFileIndetermined = isPerFileIndetermined; } @@ -269,17 +195,25 @@ protected virtual async void _httpClient_UpdateAssetProgress(int size, DownloadP { double speed = (_progressAllIOReadCurrent / _downloadSpeedRefreshStopwatch.Elapsed.TotalSeconds).ClampLimitedSpeedNumber(); TimeSpan timeLeftSpan = ((_progressAllSizeTotal - _progressAllSizeCurrent) / speed).ToTimeSpanNormalized(); - double percentage = ConverterTool.GetPercentageNumber(_progressAllSizeCurrent, _progressAllSizeTotal, 2); + double percentage = ConverterTool.GetPercentageNumber(_progressAllSizeCurrent, _progressAllSizeTotal); // Update current progress percentages and speed - _progress.ProgressAllPercentage = percentage; + if (_progress != null) + { + _progress.ProgressAllPercentage = percentage; + } // 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(speed)) - + $" | {timeLeftString}"; + if (_status != null) + { + _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(speed)) + + $" | {timeLeftString}"; + } if (_downloadSpeedRefreshInterval < _downloadSpeedRefreshStopwatch!.ElapsedMilliseconds) { @@ -291,42 +225,15 @@ protected virtual async void _httpClient_UpdateAssetProgress(int size, DownloadP UpdateAll(); } } - - protected virtual async void _httpClient_UpdateAssetProgress(object sender, DownloadEvent e) - { - // Update current progress percentages and speed - _progress.ProgressAllPercentage = _progressAllSizeCurrent != 0 ? - ConverterTool.GetPercentageNumber(_progressAllSizeCurrent, _progressAllSizeTotal) : - 0; - - if (e.State != DownloadState.Merging) - { - _progressAllSizeCurrent += e.Read; - } - long speed = (long)(_progressAllSizeCurrent / _stopwatch.Elapsed.TotalSeconds); - - if (await CheckIfNeedRefreshStopwatch()) - { - // Update current activity status - _status.IsProgressAllIndetermined = false; - string timeLeftString = string.Format(Lang._Misc.TimeRemainHMSFormat, ((_progressAllSizeCurrent - _progressAllSizeTotal) / ConverterTool.Unzeroed(speed)).ToTimeSpanNormalized()); - _status.ActivityAll = string.Format(Lang._Misc.Downloading + ": {0}/{1} ", _progressAllCountCurrent, _progressAllCountTotal) - + string.Format($"({Lang._Misc.SpeedPerSec})", ConverterTool.SummarizeSizeSimple(speed)) - + $" | {timeLeftString}"; - - // Trigger update - UpdateAll(); - } - } #endregion #region ProgressEventHandlers - Patch - protected virtual async void RepairTypeActionPatching_ProgressChanged(object sender, BinaryPatchProgress e) + protected virtual async void RepairTypeActionPatching_ProgressChanged(object? sender, BinaryPatchProgress e) { lock (_progress!) { - _progress.ProgressPerFilePercentage = e!.ProgressPercentage; - _progress.ProgressAllSpeed = e!.Speed; + _progress.ProgressPerFilePercentage = e.ProgressPercentage; + _progress.ProgressAllSpeed = e.Speed; // Update current progress percentages _progress.ProgressAllPercentage = _progressAllSizeCurrent != 0 ? @@ -354,46 +261,48 @@ protected virtual async void RepairTypeActionPatching_ProgressChanged(object sen #region ProgressEventHandlers - CRC/HashCheck protected virtual async void UpdateProgressCRC() { - if (await CheckIfNeedRefreshStopwatch()) + if (!await CheckIfNeedRefreshStopwatch()) { - lock (_progress!) - { - // Update current progress percentages - _progress.ProgressPerFilePercentage = _progressPerFileSizeCurrent != 0 ? - ConverterTool.GetPercentageNumber(_progressPerFileSizeCurrent, _progressPerFileSizeTotal) : - 0; - _progress.ProgressAllPercentage = _progressAllSizeCurrent != 0 ? - ConverterTool.GetPercentageNumber(_progressAllSizeCurrent, _progressAllSizeTotal) : - 0; + return; + } - // Update the progress of total size - _progress.ProgressPerFileSizeCurrent = _progressPerFileSizeCurrent; - _progress.ProgressPerFileSizeTotal = _progressPerFileSizeTotal; - _progress.ProgressAllSizeCurrent = _progressAllSizeCurrent; - _progress.ProgressAllSizeTotal = _progressAllSizeTotal; + lock (_progress!) + { + // Update current progress percentages + _progress.ProgressPerFilePercentage = _progressPerFileSizeCurrent != 0 ? + ConverterTool.GetPercentageNumber(_progressPerFileSizeCurrent, _progressPerFileSizeTotal) : + 0; + _progress.ProgressAllPercentage = _progressAllSizeCurrent != 0 ? + ConverterTool.GetPercentageNumber(_progressAllSizeCurrent, _progressAllSizeTotal) : + 0; - // Calculate current speed and update the status and progress speed - _progress.ProgressAllSpeed = _progressAllSizeCurrent / _stopwatch!.Elapsed.TotalSeconds; + // Update the progress of total size + _progress.ProgressPerFileSizeCurrent = _progressPerFileSizeCurrent; + _progress.ProgressPerFileSizeTotal = _progressPerFileSizeTotal; + _progress.ProgressAllSizeCurrent = _progressAllSizeCurrent; + _progress.ProgressAllSizeTotal = _progressAllSizeTotal; - // Calculate the timelapse - _progress.ProgressAllTimeLeft = ((_progressAllSizeTotal - _progressAllSizeCurrent) / ConverterTool.Unzeroed(_progress.ProgressAllSpeed)).ToTimeSpanNormalized(); - } + // Calculate current speed and update the status and progress speed + _progress.ProgressAllSpeed = _progressAllSizeCurrent / _stopwatch!.Elapsed.TotalSeconds; - lock (_status!) - { - // Set time estimation string - string timeLeftString = string.Format(Lang!._Misc!.TimeRemainHMSFormat!, _progress.ProgressAllTimeLeft); + // Calculate the timelapse + _progress.ProgressAllTimeLeft = ((_progressAllSizeTotal - _progressAllSizeCurrent) / ConverterTool.Unzeroed(_progress.ProgressAllSpeed)).ToTimeSpanNormalized(); + } - // Update current activity status - _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}"; - } + lock (_status!) + { + // Set time estimation string + string timeLeftString = string.Format(Lang!._Misc!.TimeRemainHMSFormat!, _progress.ProgressAllTimeLeft); - // Trigger update - UpdateAll(); + // Update current activity status + _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}"; } + + // Trigger update + UpdateAll(); } #endregion @@ -415,7 +324,7 @@ protected virtual async void UpdateProgressCopyStream(long currentPosition, int _progress.ProgressAllSpeed = currentPosition / _stopwatch!.Elapsed.TotalSeconds; // Calculate the timelapse - _progress.ProgressAllTimeLeft = ((totalReadSize - currentPosition) / ConverterTool.Unzeroed(_progress.ProgressAllSpeed)).ToTimeSpanNormalized(); + _progress.ProgressAllTimeLeft = ((totalReadSize - currentPosition) / _progress.ProgressAllSpeed.Unzeroed()).ToTimeSpanNormalized(); } lock (_status!) @@ -442,24 +351,31 @@ protected async void UpdateSophonFileTotalProgress(long read) Interlocked.Add(ref _progressAllSizeCurrent, read); _progressAllIOReadCurrent += read; - if (_refreshStopwatch!.ElapsedMilliseconds > _refreshInterval) + if (_refreshStopwatch!.ElapsedMilliseconds <= _refreshInterval) + { + return; + } + + // Assign local sizes to progress + if (_sophonProgress != null && _status != null) { - // Assign local sizes to progress - _sophonProgress.ProgressAllSizeCurrent = _progressAllSizeCurrent; - _sophonProgress.ProgressAllSizeTotal = _progressAllSizeTotal; - _sophonProgress.ProgressPerFileSizeCurrent = _progressPerFileSizeCurrent; - _sophonProgress.ProgressPerFileSizeTotal = _progressPerFileSizeTotal; + _sophonProgress.ProgressAllSizeCurrent = _progressAllSizeCurrent; + _sophonProgress.ProgressAllSizeTotal = _progressAllSizeTotal; + _sophonProgress.ProgressPerFileSizeCurrent = _progressPerFileSizeCurrent; + _sophonProgress.ProgressPerFileSizeTotal = _progressPerFileSizeTotal; // Calculate the speed - double speedAll = _progressAllIOReadCurrent / _downloadSpeedRefreshStopwatch.Elapsed.TotalSeconds; - double speedPerFile = (_progressPerFileIOReadCurrent / _downloadSpeedRefreshStopwatch.Elapsed.TotalSeconds).ClampLimitedSpeedNumber(); - double speedAllNoReset = _progressAllSizeCurrent / _stopwatch.Elapsed.TotalSeconds; - _sophonProgress.ProgressAllSpeed = speedAll; - _sophonProgress.ProgressPerFileSpeed = speedPerFile; + double speedAll = _progressAllIOReadCurrent / _downloadSpeedRefreshStopwatch.Elapsed.TotalSeconds; + double speedPerFile = + (_progressPerFileIOReadCurrent / _downloadSpeedRefreshStopwatch.Elapsed.TotalSeconds) + .ClampLimitedSpeedNumber(); + double speedAllNoReset = _progressAllSizeCurrent / _stopwatch.Elapsed.TotalSeconds; + _sophonProgress.ProgressAllSpeed = speedAll; + _sophonProgress.ProgressPerFileSpeed = speedPerFile; // Calculate Count - _sophonProgress.ProgressAllEntryCountCurrent = _progressAllCountCurrent; - _sophonProgress.ProgressAllEntryCountTotal = _progressAllCountTotal; + _sophonProgress.ProgressAllEntryCountCurrent = _progressAllCountCurrent; + _sophonProgress.ProgressAllEntryCountTotal = _progressAllCountTotal; // Always change the status progress to determined _status.IsProgressAllIndetermined = false; @@ -467,28 +383,31 @@ protected async void UpdateSophonFileTotalProgress(long read) StatusChanged?.Invoke(this, _status); // Calculate percentage - _sophonProgress.ProgressAllPercentage = + _sophonProgress.ProgressAllPercentage = Math.Round((double)_progressAllSizeCurrent / _progressAllSizeTotal * 100, 2); - _sophonProgress.ProgressPerFilePercentage = + _sophonProgress.ProgressPerFilePercentage = Math.Round((double)_progressPerFileSizeCurrent / _progressPerFileSizeTotal * 100, 2); // Calculate the timelapse - double progressTimeAvg = (_progressAllSizeTotal - _progressAllSizeCurrent) / speedAllNoReset; + double progressTimeAvg = speedPerFile > 0 + ? (_progressPerFileSizeTotal - _progressPerFileSizeCurrent) / speedPerFile + : (_progressAllSizeTotal - _progressAllSizeCurrent) / speedAllNoReset; + _sophonProgress.ProgressAllTimeLeft = progressTimeAvg.ToTimeSpanNormalized(); // Update progress ProgressChanged?.Invoke(this, _sophonProgress); + } - if (_downloadSpeedRefreshInterval < _downloadSpeedRefreshStopwatch!.ElapsedMilliseconds) - { - _progressAllIOReadCurrent = 0; - _progressPerFileIOReadCurrent = 0; - _downloadSpeedRefreshStopwatch.Restart(); - } - - _refreshStopwatch.Restart(); - await Task.Delay(_refreshInterval); + if (_downloadSpeedRefreshInterval < _downloadSpeedRefreshStopwatch!.ElapsedMilliseconds) + { + _progressAllIOReadCurrent = 0; + _progressPerFileIOReadCurrent = 0; + _downloadSpeedRefreshStopwatch.Restart(); } + + _refreshStopwatch.Restart(); + await Task.Delay(_refreshInterval); } protected void UpdateSophonFileDownloadProgress(long downloadedWrite, long currentWrite) @@ -500,9 +419,16 @@ protected void UpdateSophonFileDownloadProgress(long downloadedWrite, long curre protected void UpdateSophonDownloadStatus(SophonAsset asset) { Interlocked.Add(ref _progressAllCountCurrent, 1); - _status.ActivityStatus = string.Format("{0}: {1}", - _isSophonInUpdateMode ? Lang._Misc.Updating : Lang._Misc.Downloading, - string.Format(Lang._Misc.PerFromTo, _progressAllCountCurrent, _progressAllCountTotal)); + if (_status != null) + { + _status.ActivityStatus = string.Format("{0}: {1}", + _isSophonInUpdateMode + ? Lang._Misc.Updating + : Lang._Misc.Downloading, + string.Format(Lang._Misc.PerFromTo, _progressAllCountCurrent, + _progressAllCountTotal)); + } + UpdateStatus(); } @@ -527,7 +453,7 @@ protected async Task DoCopyStreamProgress(Stream source, Stream target, Cancella { int read; // ReSharper disable once ConstantNullCoalescingCondition - long inputSize = estimatedSize != null ? estimatedSize ?? 0 : source!.Length; + long inputSize = estimatedSize != null ? estimatedSize ?? 0 : source.Length; long currentPos = 0; RestartStopwatch(); @@ -540,14 +466,13 @@ protected async Task DoCopyStreamProgress(Stream source, Stream target, Cancella byte[] buffer = ArrayPool.Shared.Rent(16 << 10); try { - while ((read = await source!.ReadAsync(buffer, token)) > 0) + while ((read = await source.ReadAsync(buffer, token)) > 0) { - await target!.WriteAsync(buffer, 0, read, token); + await target.WriteAsync(buffer, 0, read, token); currentPos += read; UpdateProgressCopyStream(currentPos, read, inputSize); } } - catch { throw; } finally { _status!.IsProgressPerFileIndetermined = isLastPerfileStateIndetermined; @@ -556,22 +481,22 @@ protected async Task DoCopyStreamProgress(Stream source, Stream target, Cancella } } - protected string EnsureCreationOfDirectory([DisallowNull] string str) + protected string EnsureCreationOfDirectory(string str) { if (string.IsNullOrEmpty(str)) ArgumentException.ThrowIfNullOrEmpty(str); - string dir = Path.GetDirectoryName(str); - if (!Directory.Exists(dir)) - Directory.CreateDirectory(dir!); + string? dir = Path.GetDirectoryName(str); + if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) + Directory.CreateDirectory(dir); return str; } protected void TryUnassignReadOnlyFiles(string path) { - // Iterate every files and set the read-only flag to false - foreach (string file in Directory.EnumerateFiles(path!, "*", SearchOption.AllDirectories)) + // Iterate every file and set the read-only flag to false + foreach (string file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories)) { FileInfo fileInfo = new FileInfo(file); if (fileInfo.IsReadOnly) @@ -582,7 +507,7 @@ protected void TryUnassignReadOnlyFiles(string path) protected void TryUnassignReadOnlyFileSingle(string path) { FileInfo fileInfo = new FileInfo(path); - if (fileInfo.Exists && fileInfo.IsReadOnly) + if (fileInfo is { Exists: true, IsReadOnly: true }) fileInfo.IsReadOnly = false; } @@ -617,8 +542,10 @@ protected void TryDeleteReadOnlyFile(string path) if (!File.Exists(path)) return; try { - FileInfo file = new FileInfo(path!); - file.IsReadOnly = false; + FileInfo file = new FileInfo(path) + { + IsReadOnly = false + }; file.Delete(); } catch (Exception ex) @@ -627,23 +554,23 @@ protected void TryDeleteReadOnlyFile(string path) } } - protected void MoveFolderContent(string SourcePath, string DestPath) + protected void MoveFolderContent(string sourcePath, string destPath) { // Get the source folder path length + 1 - int DirLength = SourcePath!.Length + 1; + int dirLength = sourcePath.Length + 1; - // Initializw paths and error status - string destFilePath; - string destFolderPath; - bool ErrorOccured = false; + // Initialize paths and error status + string destFilePath; + string? destFolderPath; + bool errorOccured = false; // Enumerate files inside of source path - foreach (string filePath in Directory.EnumerateFiles(SourcePath, "*", SearchOption.AllDirectories)) + foreach (string filePath in Directory.EnumerateFiles(sourcePath, "*", SearchOption.AllDirectories)) { // Get the relative path of the file from source path - ReadOnlySpan relativePath = filePath.AsSpan().Slice(DirLength); + ReadOnlySpan relativePath = filePath.AsSpan().Slice(dirLength); // Get the absolute path for destination - destFilePath = Path.Combine(DestPath!, relativePath.ToString()); + destFilePath = Path.Combine(destPath, relativePath.ToString()); // Get folder path for destination destFolderPath = Path.GetDirectoryName(destFilePath); @@ -655,21 +582,23 @@ protected void MoveFolderContent(string SourcePath, string DestPath) { // Try moving the file LogWriteLine($"Moving \"{relativePath.ToString()}\" to \"{destFolderPath}\"", LogType.Default, true); - FileInfo filePathInfo = new FileInfo(filePath); - filePathInfo.IsReadOnly = false; + FileInfo filePathInfo = new FileInfo(filePath) + { + IsReadOnly = false + }; filePathInfo.MoveTo(destFilePath, true); } catch (Exception ex) { // If failed, flag ErrorOccured as true and skip to the next file LogWriteLine($"Error while moving \"{relativePath.ToString()}\" to \"{destFolderPath}\"\r\nException: {ex}", LogType.Error, true); - ErrorOccured = true; + errorOccured = true; } } // If no error occurred, then delete the source folder - if (!ErrorOccured) - Directory.Delete(SourcePath, true); + if (!errorOccured) + Directory.Delete(sourcePath, true); } protected virtual void ResetStatusAndProgress() @@ -742,7 +671,7 @@ protected async Task BufferSourceStreamToMemoryStream(Stream input MemoryStream stream = new MemoryStream(); // Initialize length and Stopwatch - double sizeToDownload = input!.Length; + double sizeToDownload = input.Length; double downloaded = 0; Stopwatch sw = Stopwatch.StartNew(); @@ -772,11 +701,11 @@ protected async Task BufferSourceStreamToMemoryStream(Stream input protected async ValueTask FetchBilibiliSDK(CancellationToken token) { // Check whether the sdk is not null, - if (_gameVersionManager!.GameAPIProp!.data!.sdk == null) return; + if (_gameVersionManager.GameAPIProp.data?.sdk == null) return; // Initialize SDK DLL path variables - string sdkDllPath; - string sdkDllDir; + string sdkDllPath; + string? sdkDllDir; FileInfo sdkDllFile; // Set total activity string as "Loading Indexes..." @@ -785,61 +714,55 @@ 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)) - 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)) + string? url = _gameVersionManager.GameAPIProp.data.sdk.path; + using 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); + // Iterate the Zip Entry + foreach (var entry in zip.Entries) { - // Iterate the Zip Entry - foreach (var entry in zip.Entries) - { - // Get the filename of the entry without ext. - string fileName = Path.GetFileNameWithoutExtension(entry.FullName); - - // If the entry is the "sdk_pkg_version", then override the info to sdk_pkg_version - switch (fileName) - { - case "PCGameSDK": - // Set the SDK DLL path - sdkDllPath = Path.Combine(_gamePath!, $"{Path.GetFileNameWithoutExtension(_gameVersionManager!.GamePreset!.GameExecutableName)}_Data", "Plugins", "PCGameSDK.dll"); - sdkDllDir = Path.GetDirectoryName(sdkDllPath); - sdkDllFile = new FileInfo(sdkDllPath); - - // Create the folder of the SDK DLL if doesn't exist - if (!Directory.Exists(sdkDllDir)) Directory.CreateDirectory(sdkDllDir!); - break; - case "sdk_pkg_version": - // Set the SDK DLL path to be used for sdk_pkg_version - sdkDllPath = Path.Combine(_gamePath!, "sdk_pkg_version"); - sdkDllFile = new FileInfo(sdkDllPath); - break; - default: - continue; - } - - // Do check if sdkDllFile is not null - // Try create the file if not exist or open an existing one - await using (Stream sdkDllStream = sdkDllFile.Open(!sdkDllFile.Exists || entry.Length < sdkDllFile.Length ? FileMode.Create : FileMode.OpenOrCreate)) - { - // Initiate the Crc32 hash - Crc32 hash = new Crc32(); - - // Append the SDK DLL stream to hash and get the result - await hash.AppendAsync(sdkDllStream, token); - byte[] hashByte = hash.GetHashAndReset(); - uint hashInt = BitConverter.ToUInt32(hashByte); + // Get the filename of the entry without ext. + string fileName = Path.GetFileNameWithoutExtension(entry.FullName); - // If the hash is the same, then skip - if (hashInt == entry.Crc32) continue; - await using (Stream entryStream = entry.Open()) - { - // Reset the SDK DLL stream pos and write the data - sdkDllStream.Position = 0; - await entryStream.CopyToAsync(sdkDllStream, token); - } - } + // If the entry is the "sdk_pkg_version", then override the info to sdk_pkg_version + switch (fileName) + { + case "PCGameSDK": + // Set the SDK DLL path + sdkDllPath = Path.Combine(_gamePath!, $"{Path.GetFileNameWithoutExtension(_gameVersionManager!.GamePreset!.GameExecutableName)}_Data", "Plugins", "PCGameSDK.dll"); + sdkDllDir = Path.GetDirectoryName(sdkDllPath); + sdkDllFile = new FileInfo(sdkDllPath); + + // Create the folder of the SDK DLL if it doesn't exist + if (!Directory.Exists(sdkDllDir)) Directory.CreateDirectory(sdkDllDir!); + break; + case "sdk_pkg_version": + // Set the SDK DLL path to be used for sdk_pkg_version + sdkDllPath = Path.Combine(_gamePath!, "sdk_pkg_version"); + sdkDllFile = new FileInfo(sdkDllPath); + break; + default: + continue; } + + // Do check if sdkDllFile is not null + // Try to create the file if not exist or open an existing one + await using Stream sdkDllStream = sdkDllFile.Open(!sdkDllFile.Exists || entry.Length < sdkDllFile.Length ? FileMode.Create : FileMode.OpenOrCreate); + // Initiate the Crc32 hash + Crc32 hash = new Crc32(); + + // Append the SDK DLL stream to hash and get the result + await hash.AppendAsync(sdkDllStream, token); + byte[] hashByte = hash.GetHashAndReset(); + uint hashInt = BitConverter.ToUInt32(hashByte); + + // If the hash is the same, then skip + if (hashInt == entry.Crc32) continue; + await using Stream entryStream = entry.Open(); + // Reset the SDK DLL stream pos and write the data + sdkDllStream.Position = 0; + await entryStream.CopyToAsync(sdkDllStream, token); } } @@ -862,16 +785,16 @@ protected IEnumerable EnforceHTTPSchemeToAssetIndex(IEnumerable assetInd const string HTTPSScheme = "https://"; const string HTTPScheme = "http://"; // Get the check if HTTP override is enabled - bool IsUseHTTPOverride = LauncherConfig.GetAppConfigValue("EnableHTTPRepairOverride").ToBool(); + bool isUseHttpOverride = LauncherConfig.GetAppConfigValue("EnableHTTPRepairOverride").ToBool(); // Iterate the IAssetIndexSummary asset - foreach (T1 asset in assetIndex!) + foreach (T1 asset in assetIndex) { // If the HTTP override is enabled, then start override the HTTPS scheme - if (IsUseHTTPOverride) + if (isUseHttpOverride) { // Get the remote url as span - ReadOnlySpan url = asset!.GetRemoteURL().AsSpan(); + ReadOnlySpan url = asset.GetRemoteURL().AsSpan(); // If the url starts with HTTPS scheme, then... if (url.StartsWith(HTTPSScheme)) { @@ -899,7 +822,7 @@ protected async Task TryRunExamineThrow(Task action) _status!.IsRunning = true; // Run the task - return await action!; + return await action; } catch (TaskCanceledException) { @@ -925,14 +848,17 @@ protected async Task TryRunExamineThrow(Task action) } finally { - // Clear the _assetIndex after that - if (!_status!.IsCompleted) + // Define that the status is not running + if (_status != null) { - _assetIndex!.Clear(); - } + // Clear the _assetIndex after that + if (_status is { IsCompleted: false }) + { + _assetIndex.Clear(); + } - // Define that the status is not running - _status.IsRunning = false; + _status.IsRunning = false; + } } } @@ -956,24 +882,23 @@ protected bool SummarizeStatusAndProgress(List assetIndex, string msgIfFound SetFoundToTotalValue(); // Set check if broken asset is found or not - bool IsBrokenFound = assetIndex!.Count > 0; + bool isBrokenFound = assetIndex.Count > 0; // Set status - _status!.IsAssetEntryPanelShow = IsBrokenFound; + _status!.IsAssetEntryPanelShow = isBrokenFound; _status.IsCompleted = true; _status.IsCanceled = false; - _status.ActivityStatus = IsBrokenFound ? msgIfFound : msgIfClear; + _status.ActivityStatus = isBrokenFound ? msgIfFound : msgIfClear; // Update status and progress UpdateAll(); // Return broken asset check - return IsBrokenFound; + return isBrokenFound; } protected virtual bool IsArrayMatch(ReadOnlySpan source, ReadOnlySpan target) => source.SequenceEqual(target); -#nullable enable protected virtual async Task RunDownloadTask(long assetSize, string assetPath, string assetURL, DownloadClient downloadClient, DownloadProgressDelegate downloadProgress, CancellationToken token, bool isOverwrite = true) { @@ -1010,26 +935,6 @@ await downloadClient.DownloadAsync( } } } -#nullable restore - - protected virtual async Task RunDownloadTask(long assetSize, string assetPath, string assetURL, Http _httpClient, CancellationToken token) - { - // Check for directory availability - string dirPath = Path.GetDirectoryName(assetPath); - if (!Directory.Exists(dirPath)) - Directory.CreateDirectory(dirPath); - - // Start downloading asset - if (assetSize >= _sizeForMultiDownload && !_isBurstDownloadEnabled) - { - await _httpClient!.Download(assetURL, assetPath, _downloadThreadCount, true, token); - await _httpClient.Merge(token); - } - else - { - await _httpClient!.Download(assetURL, assetPath, true, null, null, token); - } - } /// /// IDK what Microsoft is smoking but for some reason, the file were throwing IO_SharingViolation_File error, @@ -1037,7 +942,7 @@ protected virtual async Task RunDownloadTask(long assetSize, string assetPath, s /// internal static async ValueTask NaivelyOpenFileStreamAsync(FileInfo info, FileMode fileMode, FileAccess fileAccess, FileShare fileShare) { - const int MaxTry = 10; + const int maxTry = 10; int currentTry = 1; while (true) { @@ -1047,88 +952,104 @@ internal static async ValueTask NaivelyOpenFileStreamAsync(FileInfo } catch { - if (currentTry <= MaxTry) + if (currentTry > maxTry) { - LogWriteLine($"Failed while trying to open: {info.FullName}. Retry attempt: {++currentTry} / {MaxTry}", LogType.Warning, true); - await Task.Delay(50); // Adding 50ms delay - continue; + throw; // Throw this MFs } - throw; // Throw this MFs + + LogWriteLine($"Failed while trying to open: {info.FullName}. Retry attempt: {++currentTry} / {maxTry}", LogType.Warning, true); + await Task.Delay(50); // Adding 50ms delay } } } #endregion #region HashTools - protected virtual async ValueTask CheckHashAsync(Stream stream, HashAlgorithm hashProvider, CancellationToken token, bool updateTotalProgress = true) + protected virtual async Task CheckHashAsync(Stream stream, T hashProvider, CancellationToken token, bool updateTotalProgress = true) + where T : HashAlgorithm { // Get length based on stream length or at least if bigger, use the default one - int bufferLen = stream is FileStream && _bufferBigLength < stream.Length ? (int)stream.Length : _bufferBigLength; + int bufferLen = _bufferBigLength > stream.Length ? (int)stream.Length : _bufferBigLength; - // Initialize Xxh64 instance and assign buffer - byte[] buffer = GC.AllocateUninitializedArray(bufferLen); + // Initialize buffer + byte[] buffer = ArrayPool.Shared.Rent(bufferLen); - // Do read activity - int read; - while ((read = await stream!.ReadAsync(buffer, token)) > 0) + try { - // Throw Cancellation exception if detected - token.ThrowIfCancellationRequested(); + // Do read activity + int read; + while ((read = await stream.ReadAsync(buffer, token)) > 0) + { + // Throw Cancellation exception if detected + token.ThrowIfCancellationRequested(); - // Append buffer into hash block - hashProvider!.TransformBlock(buffer, 0, read, buffer, 0); + // Append buffer into hash block + hashProvider.TransformBlock(buffer, 0, read, buffer, 0); - lock (this) - { - // Increment total size counter - if (updateTotalProgress) _progressAllSizeCurrent += read; - // Increment per file size counter - _progressPerFileSizeCurrent += read; - } + lock (this) + { + // Increment total size counter + if (updateTotalProgress) _progressAllSizeCurrent += read; + // Increment per file size counter + _progressPerFileSizeCurrent += read; + } - // Update status and progress for MD5 calculation - UpdateProgressCRC(); - } + // Update status and progress for MD5 calculation + UpdateProgressCRC(); + } - // Finalize the hash calculation - hashProvider!.TransformFinalBlock(buffer, 0, read); + // Finalize the hash calculation + hashProvider.TransformFinalBlock(buffer, 0, read); - // Return computed hash byte - return hashProvider.Hash; + // Return computed hash byte + return hashProvider.Hash ?? []; + } + finally + { + ArrayPool.Shared.Return(buffer); + } } - protected virtual async ValueTask CheckHashAsync(Stream stream, NonCryptographicHashAlgorithm hashProvider, CancellationToken token, bool updateTotalProgress = true) + protected virtual async Task CheckNonCryptoHashAsync(Stream stream, T hashProvider, CancellationToken token, bool updateTotalProgress = true) + where T : NonCryptographicHashAlgorithm { // Get length based on stream length or at least if bigger, use the default one - int bufferLen = stream is FileStream && _bufferBigLength < stream.Length ? (int)stream.Length : _bufferBigLength; + int bufferLen = _bufferBigLength > stream.Length ? (int)stream.Length : _bufferBigLength; - // Initialize Xxh64 instance and assign buffer - byte[] buffer = GC.AllocateUninitializedArray(bufferLen); + // Initialize buffer + byte[] buffer = ArrayPool.Shared.Rent(bufferLen); - // Do read activity - int read; - while ((read = await stream!.ReadAsync(buffer, token)) > 0) + try { - // Throw Cancellation exception if detected - token.ThrowIfCancellationRequested(); + // Do read activity + int read; + while ((read = await stream.ReadAsync(buffer, token)) > 0) + { + // Throw Cancellation exception if detected + token.ThrowIfCancellationRequested(); - // Append buffer into hash block - hashProvider.Append(buffer.AsSpan(0, read)); + // Append buffer into hash block + hashProvider.Append(buffer.AsSpan(0, read)); - lock (this) - { - // Increment total size counter - if (updateTotalProgress) _progressAllSizeCurrent += read; - // Increment per file size counter - _progressPerFileSizeCurrent += read; + lock (this) + { + // Increment total size counter + if (updateTotalProgress) _progressAllSizeCurrent += read; + // Increment per file size counter + _progressPerFileSizeCurrent += read; + } + + // Update status and progress for Xxh64 calculation + UpdateProgressCRC(); } - // Update status and progress for Xxh64 calculation - UpdateProgressCRC(); + // Return computed hash byte + return hashProvider.GetHashAndReset(); + } + finally + { + ArrayPool.Shared.Return(buffer); } - - // Return computed hash byte - return hashProvider.GetHashAndReset(); } #endregion @@ -1147,25 +1068,23 @@ protected virtual async ValueTask RunPatchTask(DownloadClient downloadClient, Do if (!patchInfo.Exists || patchInfo.Length != patchSize) { // Download patch File first - await RunDownloadTask(patchSize, patchOutputFile, patchURL, downloadClient, downloadProgress, token)!; + await RunDownloadTask(patchSize, patchOutputFile, patchURL, downloadClient, downloadProgress, token); } // Always do loop if patch doesn't get downloaded properly while (true) { - using (FileStream patchfs = new FileStream(patchOutputFile, FileMode.Open, FileAccess.Read, FileShare.None, _bufferBigLength)) + await using FileStream patchfs = new FileStream(patchOutputFile, FileMode.Open, FileAccess.Read, FileShare.None, _bufferBigLength); + // Verify the patch file and if it doesn't match, then redownload it + byte[] patchCrc = await CheckHashAsync(patchfs, MD5.Create(), token, false); + if (!IsArrayMatch(patchCrc, patchHash.Span)) { - // Verify the patch file and if it doesn't match, then redownload it - byte[] patchCRC = await CheckHashAsync(patchfs, MD5.Create(), token, false); - if (!IsArrayMatch(patchCRC, patchHash.Span)) - { - // Revert back the total size - _progressAllSizeCurrent -= patchSize; + // Revert back the total size + _progressAllSizeCurrent -= patchSize; - // Redownload the patch file - await RunDownloadTask(patchSize, patchOutputFile, patchURL, downloadClient, downloadProgress, token)!; - continue; - } + // Redownload the patch file + await RunDownloadTask(patchSize, patchOutputFile, patchURL, downloadClient, downloadProgress, token); + continue; } // else, break and quit from loop @@ -1179,7 +1098,7 @@ protected virtual async ValueTask RunPatchTask(DownloadClient downloadClient, Do // Subscribe patching progress and start applying patch patchUtil.ProgressChanged += RepairTypeActionPatching_ProgressChanged; patchUtil.Initialize(inputFile, patchOutputFile, outputFile); - await Task.Run(() => patchUtil.Apply(token)); + await Task.Run(() => patchUtil.Apply(token), token); // Delete old block File.Delete(inputFile); @@ -1189,7 +1108,6 @@ protected virtual async ValueTask RunPatchTask(DownloadClient downloadClient, Do File.Move(outputFile, inputFile, true); } } - catch { throw; } finally { // Delete the patch file and unsubscribe the patching progress @@ -1208,16 +1126,16 @@ protected virtual async ValueTask RunPatchTask(DownloadClient downloadClient, Do protected async Task SpawnRepairDialog(List assetIndex, Action actionIfInteractiveCancel) { ArgumentNullException.ThrowIfNull(assetIndex); - long totalSize = assetIndex.Sum(x => x!.GetAssetSize()); - StackPanel Content = UIElementExtensions.CreateStackPanel(); + long totalSize = assetIndex.Sum(x => x.GetAssetSize()); + StackPanel content = UIElementExtensions.CreateStackPanel(); - Content.AddElementToStackPanel(new TextBlock() + content.AddElementToStackPanel(new TextBlock() { Text = string.Format(Lang._InstallMgmt.RepairFilesRequiredSubtitle!, assetIndex.Count, ConverterTool.SummarizeSizeSimple(totalSize)), Margin = new Thickness(0, 0, 0, 16), TextWrapping = TextWrapping.Wrap }); - Button ShowBrokenFilesButton = Content.AddElementToStackPanel( + Button showBrokenFilesButton = content.AddElementToStackPanel( UIElementExtensions.CreateButtonWithIcon