Skip to content

Commit 3064ff9

Browse files
committed
Refactor Hi3 Repair (pt. 8) (should be final)
+ Use HashSet for checking ignored assets + Implement repair for Block/BlockUpdate and Audio/AudioUpdate kind
1 parent 8feb148 commit 3064ff9

13 files changed

+498
-46
lines changed

CollapseLauncher/Classes/Helper/FileUtility.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,5 +125,23 @@ public static int GetFileStreamBufferSize(this long fileSize)
125125
<= 100 << 20 => 512 << 10,
126126
_ => 1 << 20
127127
};
128+
129+
public static int GetFileStreamBufferSize(this int fileSize) => GetFileStreamBufferSize((long)fileSize);
130+
131+
public static int GetFileStreamBufferSize(this ulong fileSize)
132+
=> fileSize switch
133+
{
134+
// 128 KiB
135+
<= 128 << 10 => 4 << 10,
136+
// 1 MiB
137+
<= 1 << 20 => 64 << 10,
138+
// 32 MiB
139+
<= 32 << 20 => 128 << 10,
140+
// 100 MiB
141+
<= 100 << 20 => 512 << 10,
142+
_ => 1 << 20
143+
};
144+
145+
public static int GetFileStreamBufferSize(this uint fileSize) => GetFileStreamBufferSize((ulong)fileSize);
128146
}
129147
}

CollapseLauncher/Classes/Interfaces/Class/ProgressBase.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,8 @@ protected virtual void ResetStatusAndProgress()
730730
Token = new CancellationTokenSourceWrapper();
731731

732732
// Reset RepairAssetProperty list
733-
AssetEntry!.Clear();
733+
AssetEntry.Clear();
734+
AssetIndex.Clear();
734735

735736
// Reset status and progress properties
736737
ResetStatusAndProgressProperty();
@@ -952,11 +953,13 @@ protected virtual async Task<T> TryRunExamineThrow<T>(Task<T> action)
952953

953954
protected virtual async Task TryRunExamineThrow(Task task)
954955
{
956+
// Define if the status is still running
957+
Status.IsRunning = true;
958+
Status.IsCompleted = false;
959+
Status.IsCanceled = false;
960+
955961
try
956962
{
957-
// Define if the status is still running
958-
Status.IsRunning = true;
959-
960963
// Run the task
961964
await task;
962965

CollapseLauncher/Classes/RepairManagement/HonkaiV2/HonkaiRepairV2.AsbExt.Audio.cs

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using Hi3Helper.Shared.ClassStruct;
1212
using System;
1313
using System.Collections.Generic;
14+
using System.Diagnostics.CodeAnalysis;
1415
using System.IO;
1516
using System.Net.Http;
1617
using System.Threading;
@@ -38,12 +39,11 @@ internal static async Task<List<FilePropertiesRemote>>
3839
KianaDispatch gameServerInfo,
3940
SenadinaFileIdentifier? audioFileIdentifier,
4041
ProgressBase<T> progressibleInstance,
41-
AudioPCKType[]? ignoredCgIds = null,
42-
CancellationToken token = default)
42+
AudioPCKType[]? ignoredAudioIds = null,
43+
CancellationToken token = default)
4344
where T : IAssetIndexSummary
4445
{
45-
ignoredCgIds ??= [];
46-
int[] ignoredCgIdInts = Array.ConvertAll(ignoredCgIds, x => (int)x);
46+
HashSet<AudioPCKType> ignoredAudioHashset = new(ignoredAudioIds ?? []);
4747

4848
ArgumentNullException.ThrowIfNull(audioFileIdentifier);
4949
int parallelThread = progressibleInstance.ThreadForIONormalized;
@@ -90,7 +90,7 @@ async ValueTask ImplCheckAndAdd(ManifestAssetInfo audioAsset, CancellationToken
9090
// Eliminate removed audio assets or not matching language.
9191
if ((audioAsset.Language != gameLanguageType &&
9292
audioAsset.Language != AudioLanguageType.Common) ||
93-
ignoredCgIdInts.Contains((int)audioAsset.PckType))
93+
ignoredAudioHashset.Contains(audioAsset.PckType))
9494
{
9595
return;
9696
}
@@ -139,4 +139,41 @@ async ValueTask ImplCheckAndAdd(ManifestAssetInfo audioAsset, CancellationToken
139139

140140
throw lastException ?? new HttpRequestException("No Asset bundle URLs were reachable");
141141
}
142+
143+
internal static bool GetAudioPatchUrlProperty(
144+
this FilePropertiesRemote asset,
145+
[NotNullWhen(true)] out ManifestAudioPatchInfo? patchInfo,
146+
[NotNullWhen(true)] out string? patchUrl)
147+
{
148+
const string startTrim = "/";
149+
150+
patchInfo = null;
151+
patchUrl = null;
152+
153+
patchInfo = asset.AudioPatchInfo;
154+
if (patchInfo == null)
155+
{
156+
throw new InvalidOperationException("This method cannot be called while AudioPatchInfo is null");
157+
}
158+
159+
if (asset.AssociatedObject is not ManifestAssetInfo audioAssetInfo)
160+
{
161+
throw new InvalidOperationException("This method cannot be called while AssociatedObject is not a ManifestAssetInfo type");
162+
}
163+
164+
ReadOnlySpan<char> fullUrl = asset.RN;
165+
int trimStart = fullUrl.LastIndexOf(startTrim, StringComparison.OrdinalIgnoreCase);
166+
167+
if (trimStart < 0)
168+
{
169+
throw new InvalidOperationException($"Cannot find \"{startTrim}\" string inside of the URL!");
170+
}
171+
172+
fullUrl = fullUrl[..trimStart];
173+
patchUrl = ConverterTool.CombineURLFromString(fullUrl,
174+
"Patch",
175+
patchInfo.PatchFilename);
176+
177+
return true;
178+
}
142179
}

CollapseLauncher/Classes/RepairManagement/HonkaiV2/HonkaiRepairV2.AsbExt.Block.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
using Hi3Helper.Shared.ClassStruct;
1212
using System;
1313
using System.Collections.Generic;
14+
using System.Diagnostics.CodeAnalysis;
1415
using System.IO;
16+
using System.Linq;
1517
using System.Net.Http;
1618
using System.Runtime.CompilerServices;
1719
using System.Runtime.InteropServices;
@@ -133,4 +135,42 @@ string GetRandomBaseUrl(KianaDispatch kianaDispatch)
133135
return isUseHttpRepairOverride ? "http://" : "https://" + selectedUrl;
134136
}
135137
}
138+
139+
internal static bool GetBlockPatchUrlProperty(
140+
this FilePropertiesRemote asset,
141+
[NotNullWhen(true)] out BlockOldPatchInfo? patchInfo,
142+
[NotNullWhen(true)] out string? patchUrl)
143+
{
144+
const string startTrim = "pc/HD";
145+
patchInfo = null;
146+
patchUrl = null;
147+
148+
if (asset.BlockPatchInfo == null)
149+
{
150+
throw new InvalidOperationException("This method cannot be called while BlockPatchInfo is null");
151+
}
152+
153+
patchInfo = asset.BlockPatchInfo.PatchPairs.FirstOrDefault();
154+
if (patchInfo == null)
155+
{
156+
throw new InvalidOperationException("This method cannot be called while BlockPatchInfo.PatchPairs is empty or null");
157+
}
158+
159+
ReadOnlySpan<char> fullUrl = asset.RN;
160+
int trimStart = fullUrl.IndexOf(startTrim, StringComparison.OrdinalIgnoreCase);
161+
162+
if (trimStart < 0)
163+
{
164+
throw new InvalidOperationException($"Cannot find \"{startTrim}\" string inside of the URL!");
165+
}
166+
167+
fullUrl = fullUrl[..trimStart];
168+
patchUrl = ConverterTool.CombineURLFromString(fullUrl,
169+
startTrim,
170+
"patch",
171+
patchInfo.OldVersionDir,
172+
patchInfo.PatchName);
173+
174+
return true;
175+
}
136176
}

CollapseLauncher/Classes/RepairManagement/HonkaiV2/HonkaiRepairV2.AsbExt.Generic.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ internal static void AddBrokenAssetToList(
2424
new AssetProperty<RepairAssetType>(Path.GetFileName(asset.N),
2525
asset.GetRepairAssetType(),
2626
Path.GetDirectoryName(asset.N) ?? "\\",
27-
asset.S,
27+
useFoundSize ?? asset.S,
2828
finalHash,
2929
asset.CRCArray);
3030

CollapseLauncher/Classes/RepairManagement/HonkaiV2/HonkaiRepairV2.AsbExt.Video.cs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
using Hi3Helper.Plugin.Core.Management;
1010
using Hi3Helper.Preset;
1111
using Hi3Helper.Shared.ClassStruct;
12-
using Hi3Helper.Shared.Region;
1312
using System;
1413
using System.Collections.Generic;
1514
using System.IO;
@@ -18,6 +17,7 @@
1817
using System.Text;
1918
using System.Threading;
2019
using System.Threading.Tasks;
20+
2121
using CGMetadataHashId = Hi3Helper.EncTool.Parser.Cache.HashID;
2222

2323
// ReSharper disable CheckNamespace
@@ -46,13 +46,9 @@ internal static async Task<List<FilePropertiesRemote>>
4646
{
4747
bool isUseHttpRepairOverride = progressibleInstance.IsForceHttpOverride;
4848
AudioLanguageType gameLanguageType = GetCurrentGameAudioLanguage(presetConfig);
49-
int parallelThread = LauncherConfig.AppCurrentDownloadThread;
50-
if (parallelThread <= 0)
51-
{
52-
parallelThread = Environment.ProcessorCount;
53-
}
49+
int parallelThread = Math.Clamp(progressibleInstance.ThreadForIONormalized * 4, 16, 64);
5450

55-
ignoredCgIds ??= [];
51+
HashSet<int> ignoredCgHashset = new(ignoredCgIds ?? []);
5652
List<CacheAssetInfo> assetInfoList =
5753
await GetCacheAssetBundleListAsync(assetBundleHttpClient,
5854
presetConfig,
@@ -106,7 +102,7 @@ await Parallel
106102
async ValueTask ImplCheckAndAdd(CGMetadata entry, CancellationToken innerToken)
107103
{
108104
if (entry.InStreamingAssets ||
109-
ignoredCgIds.Contains(entry.CgSubCategory))
105+
ignoredCgHashset.Contains(entry.CgSubCategory))
110106
{
111107
return;
112108
}
@@ -126,10 +122,12 @@ async ValueTask ImplCheckAndAdd(CGMetadata entry, CancellationToken innerToken)
126122
assetUrl = assetUrl.CombineURLFromString("Video", assetName);
127123

128124
// If the file has no appoinment schedule (like non-birthday CG), then return true
125+
/*
129126
if (entry.AppointmentDownloadScheduleID == 0)
130127
{
131128
goto AddCgEntry; // I love goto. Dun ask me why :>
132129
}
130+
*/
133131

134132
// Update status
135133
if (progressibleInstance != null)
@@ -148,6 +146,11 @@ async ValueTask ImplCheckAndAdd(CGMetadata entry, CancellationToken innerToken)
148146
continue;
149147
}
150148

149+
if (urlStatus.FileSize > 0)
150+
{
151+
assetFilesize = urlStatus.FileSize;
152+
}
153+
151154
AddCgEntry:
152155
lock (cgEntries)
153156
{

CollapseLauncher/Classes/RepairManagement/HonkaiV2/HonkaiRepairV2.Check.Generic.cs

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
using Hi3Helper;
55
using Hi3Helper.Data;
66
using Hi3Helper.EncTool.Hashes;
7+
using Hi3Helper.EncTool.Parser.Senadina;
78
using Hi3Helper.Shared.ClassStruct;
89
using System;
910
using System.IO;
11+
using System.IO.Hashing;
1012
using System.Security.Cryptography;
1113
using System.Threading;
1214
using System.Threading.Tasks;
@@ -23,8 +25,8 @@ private async ValueTask CheckAssetGenericType(
2325
CancellationToken token)
2426
{
2527
_ = await IsHashMatchedAuto(asset,
26-
useFastCheck,
27-
token: token)
28+
useFastCheck,
29+
token: token)
2830
.ConfigureAwait(false);
2931
}
3032

@@ -71,7 +73,8 @@ private async Task<bool> IsHashMatchedAuto(
7173

7274
string assetFilePath = Path.Combine(GamePath, asset.N);
7375
FileInfo assetFileInfo = new FileInfo(assetFilePath)
74-
.EnsureNoReadOnly(out bool isAssetExist);
76+
.EnsureNoReadOnly(out bool isAssetExist)
77+
.StripAlternateDataStream();
7578

7679
if (!isAssetExist || (assetFileInfo.Length != asset.S && !isSkipSizeCheck))
7780
{
@@ -132,29 +135,52 @@ await hashUtil
132135
token);
133136
break;
134137
}
138+
case MD5.HashSizeInBytes when asset.AssociatedObject is SenadinaFileIdentifier:
139+
{
140+
XxHash128 hasher = new();
141+
HashUtility<XxHash128> hashUtil = HashUtility<XxHash128>.ThreadSafe;
142+
143+
(resultStatus, _) =
144+
await hashUtil
145+
.TryGetHashFromStreamAsync(hasher,
146+
assetFileStream,
147+
hashBufferSpan,
148+
ImplReadBytesAction,
149+
bufferSize,
150+
token);
151+
break;
152+
}
135153
case MD5.HashSizeInBytes:
136154
{
155+
using HashAlgorithm hasher = hmacKey != null ? new HMACMD5(hmacKey) : MD5.Create();
137156
CryptoHashUtility<MD5> hashUtil = CryptoHashUtility<MD5>.ThreadSafe;
157+
138158
(resultStatus, _) =
139159
await hashUtil
140-
.TryGetHashFromStreamAsync(assetFileStream,
160+
.TryGetHashFromStreamAsync(hasher,
161+
assetFileStream,
141162
hashBufferSpan,
142163
ImplReadBytesAction,
143164
hmacKey,
144165
bufferSize,
166+
false,
145167
token);
146168
break;
147169
}
148170
case SHA1.HashSizeInBytes:
149171
{
172+
using HashAlgorithm hasher = hmacKey != null ? new HMACSHA1(hmacKey) : SHA1.Create();
150173
CryptoHashUtility<SHA1> hashUtil = CryptoHashUtility<SHA1>.ThreadSafe;
174+
151175
(resultStatus, _) =
152176
await hashUtil
153-
.TryGetHashFromStreamAsync(assetFileStream,
177+
.TryGetHashFromStreamAsync(hasher,
178+
assetFileStream,
154179
hashBufferSpan,
155180
ImplReadBytesAction,
156181
hmacKey,
157182
bufferSize,
183+
false,
158184
token);
159185
break;
160186
}
@@ -179,7 +205,7 @@ await hashUtil
179205
// If we reach this point, it means the hash check failed
180206
if (addAssetIfBroken)
181207
{
182-
this.AddBrokenAssetToList(asset);
208+
this.AddBrokenAssetToList(asset, hashBufferSpan.Span.ToArray());
183209
}
184210

185211
// Reverse the hash back for logging

CollapseLauncher/Classes/RepairManagement/HonkaiV2/HonkaiRepairV2.Check.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ private async Task<bool> StartCheckRoutineCoreAsync(bool useFastCheck)
3737
await FetchAssetFromSophon(checkAssetIndex, Token.Token);
3838
if (!IsMainAssetOnlyMode)
3939
{
40+
RemoveBlockAssetFromList(checkAssetIndex);
4041
await FetchAssetFromGameAssetBundle(checkAssetIndex, Token.Token);
4142
}
4243
}

0 commit comments

Comments
 (0)