|
11 | 11 | using CollapseLauncher.Extension; |
12 | 12 | using CollapseLauncher.Helper; |
13 | 13 | using CollapseLauncher.Helper.Metadata; |
| 14 | +using CollapseLauncher.Helper.StreamUtility; |
14 | 15 | using Hi3Helper; |
15 | 16 | using Hi3Helper.Data; |
16 | 17 | using Hi3Helper.Plugin.Core.Management; |
|
24 | 25 | using System.Collections.Generic; |
25 | 26 | using System.Diagnostics; |
26 | 27 | using System.IO; |
| 28 | +using System.IO.Hashing; |
27 | 29 | using System.Linq; |
28 | 30 | using System.Net.Http; |
| 31 | +using System.Security.Cryptography; |
29 | 32 | using System.Threading; |
30 | 33 | using System.Threading.Tasks; |
31 | 34 |
|
@@ -189,7 +192,7 @@ protected virtual async Task ConfirmAdditionalPatchDataPackageFiles(SophonChunkM |
189 | 192 | } |
190 | 193 | } |
191 | 194 |
|
192 | | - matchingFieldsList.AddRange(otherManifestIdentity.Select(identity => identity.MatchingField)); |
| 195 | + matchingFieldsList.AddRange(otherManifestIdentity.Select(identity => identity.MatchingField ?? "")); |
193 | 196 | return; |
194 | 197 |
|
195 | 198 | string GetFileDetails() |
@@ -558,29 +561,67 @@ async ValueTask ImplDownload(Tuple<SophonPatchAsset, Dictionary<string, int>> ct |
558 | 561 | SophonPatchAsset patchAsset = ctx.Item1; |
559 | 562 | Dictionary<string, int> downloadedDict = ctx.Item2; |
560 | 563 |
|
561 | | - using (dictionaryLock.EnterScope()) |
| 564 | + try |
562 | 565 | { |
563 | | - _ = downloadedDict.TryAdd(patchAsset.PatchNameSource, 0); |
564 | | - downloadedDict[patchAsset.PatchNameSource]++; |
| 566 | + UpdateCurrentDownloadStatus(); |
| 567 | + // Check if target file has already been patched so the launcher won't redownload everything. |
| 568 | + if (!isPreloadMode && patchAsset.PatchMethod != SophonPatchMethod.Remove) |
| 569 | + { |
| 570 | + FileInfo fileInfo = new FileInfo(Path.Combine(GamePath, patchAsset.TargetFilePath)) |
| 571 | + .EnsureNoReadOnly() |
| 572 | + .StripAlternateDataStream(); |
| 573 | + |
| 574 | + if (fileInfo.Exists && |
| 575 | + fileInfo.Length == patchAsset.TargetFileSize) |
| 576 | + { |
| 577 | + byte[] remoteHashBytes = HexTool.HexToBytesUnsafe(patchAsset.TargetFileHash); |
| 578 | + byte[] localHashBytes = remoteHashBytes.Length > 8 |
| 579 | + ? await GetCryptoHashAsync<MD5>(fileInfo, null, false, false, innerToken) |
| 580 | + : await GetHashAsync<XxHash64>(fileInfo, false, false, innerToken); |
| 581 | + |
| 582 | + // Try to reverse hash bytes in case the returned bytes are going to be Big-endian. |
| 583 | + if (!localHashBytes.SequenceEqual(remoteHashBytes)) |
| 584 | + { |
| 585 | + Array.Reverse(localHashBytes); |
| 586 | + } |
| 587 | + |
| 588 | + // Now compare. If the hash is already equal (means the target file is already downloaded), |
| 589 | + // then skip from downloading the patch. |
| 590 | + if (localHashBytes.SequenceEqual(remoteHashBytes)) |
| 591 | + { |
| 592 | + long patchSize = patchAsset.PatchSize; |
| 593 | + UpdateSophonFileTotalProgress(patchSize); |
| 594 | + UpdateSophonFileDownloadProgress(patchSize, patchSize); |
| 595 | + return; |
| 596 | + } |
| 597 | + } |
| 598 | + } |
| 599 | + |
| 600 | + await patchAsset.DownloadPatchAsync(httpClient, |
| 601 | + GamePath, |
| 602 | + patchOutputDir, |
| 603 | + true, |
| 604 | + read => |
| 605 | + { |
| 606 | + UpdateSophonFileTotalProgress(read); |
| 607 | + UpdateSophonFileDownloadProgress(read, read); |
| 608 | + }, |
| 609 | + downloadLimiter, |
| 610 | + innerToken); |
565 | 611 | } |
| 612 | + finally |
| 613 | + { |
| 614 | + using (dictionaryLock.EnterScope()) |
| 615 | + { |
| 616 | + _ = downloadedDict.TryAdd(patchAsset.PatchNameSource, 0); |
| 617 | + downloadedDict[patchAsset.PatchNameSource]++; |
| 618 | + } |
566 | 619 |
|
567 | | - UpdateCurrentDownloadStatus(); |
568 | | - await patchAsset.DownloadPatchAsync(httpClient, |
569 | | - GamePath, |
570 | | - patchOutputDir, |
571 | | - true, |
572 | | - read => |
573 | | - { |
574 | | - UpdateSophonFileTotalProgress(read); |
575 | | - UpdateSophonFileDownloadProgress(read, read); |
576 | | - }, |
577 | | - downloadLimiter, |
578 | | - innerToken); |
579 | | - |
580 | | - Logger.LogWriteLine($"Downloaded patch file for: {patchAsset.TargetFilePath}", |
581 | | - LogType.Debug, |
582 | | - true); |
583 | | - Interlocked.Increment(ref ProgressAllCountCurrent); |
| 620 | + Logger.LogWriteLine($"Downloaded patch file for: {patchAsset.TargetFilePath}", |
| 621 | + LogType.Debug, |
| 622 | + true); |
| 623 | + Interlocked.Increment(ref ProgressAllCountCurrent); |
| 624 | + } |
584 | 625 | } |
585 | 626 |
|
586 | 627 | async ValueTask ImplPatchUpdate(Tuple<SophonPatchAsset, Dictionary<string, int>> ctx, CancellationToken innerToken) |
|
0 commit comments