Skip to content

Commit 42fbd51

Browse files
committed
Make Sophon patch-update resumable
Previously, the launcher will try to redownload the entire patch files if say, the user tried to cancel the patch-update process, even though some target files have already been patched/updated. Now, the launcher will try to check for target file hash/existence before downloading the patch file. If the hash (of the target file) matches, then skip the patch process.
1 parent 65ca84b commit 42fbd51

File tree

1 file changed

+62
-21
lines changed

1 file changed

+62
-21
lines changed

CollapseLauncher/Classes/InstallManagement/Base/InstallManagerBase.SophonPatch.cs

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using CollapseLauncher.Extension;
1212
using CollapseLauncher.Helper;
1313
using CollapseLauncher.Helper.Metadata;
14+
using CollapseLauncher.Helper.StreamUtility;
1415
using Hi3Helper;
1516
using Hi3Helper.Data;
1617
using Hi3Helper.Plugin.Core.Management;
@@ -24,8 +25,10 @@
2425
using System.Collections.Generic;
2526
using System.Diagnostics;
2627
using System.IO;
28+
using System.IO.Hashing;
2729
using System.Linq;
2830
using System.Net.Http;
31+
using System.Security.Cryptography;
2932
using System.Threading;
3033
using System.Threading.Tasks;
3134

@@ -189,7 +192,7 @@ protected virtual async Task ConfirmAdditionalPatchDataPackageFiles(SophonChunkM
189192
}
190193
}
191194

192-
matchingFieldsList.AddRange(otherManifestIdentity.Select(identity => identity.MatchingField));
195+
matchingFieldsList.AddRange(otherManifestIdentity.Select(identity => identity.MatchingField ?? ""));
193196
return;
194197

195198
string GetFileDetails()
@@ -558,29 +561,67 @@ async ValueTask ImplDownload(Tuple<SophonPatchAsset, Dictionary<string, int>> ct
558561
SophonPatchAsset patchAsset = ctx.Item1;
559562
Dictionary<string, int> downloadedDict = ctx.Item2;
560563

561-
using (dictionaryLock.EnterScope())
564+
try
562565
{
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);
565611
}
612+
finally
613+
{
614+
using (dictionaryLock.EnterScope())
615+
{
616+
_ = downloadedDict.TryAdd(patchAsset.PatchNameSource, 0);
617+
downloadedDict[patchAsset.PatchNameSource]++;
618+
}
566619

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+
}
584625
}
585626

586627
async ValueTask ImplPatchUpdate(Tuple<SophonPatchAsset, Dictionary<string, int>> ctx, CancellationToken innerToken)

0 commit comments

Comments
 (0)