Skip to content

Commit 3248fc6

Browse files
committed
[HI3 Game Repair] Fix Audio_Default_Manifest.m always get detected as broken asset
1 parent 61d3a15 commit 3248fc6

1 file changed

Lines changed: 59 additions & 9 deletions

File tree

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

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,18 @@
99
using Hi3Helper.EncTool.Parser.KianaDispatch;
1010
using Hi3Helper.EncTool.Parser.Senadina;
1111
using Hi3Helper.Shared.ClassStruct;
12+
using Hi3Helper.Sophon;
1213
using Microsoft.Win32;
1314
using System;
1415
using System.Buffers.Binary;
1516
using System.Collections.Generic;
1617
using System.IO;
18+
using System.IO.Hashing;
1719
using System.Linq;
20+
using System.Net.Http;
1821
using System.Runtime.CompilerServices;
1922
using System.Runtime.InteropServices;
23+
using System.Security.Cryptography;
2024
using System.Threading;
2125
using System.Threading.Tasks;
2226
// ReSharper disable StringLiteralTypo
@@ -113,7 +117,8 @@ private async Task FetchAssetFromGameAssetBundle(List<FilePropertiesRemote> asse
113117
.GetResultFromAction(async result =>
114118
{
115119
assetListFromAudio.AddRange(result);
116-
await FinalizeAudioAssetsPath(assetListFromAudio,
120+
await FinalizeAudioAssetsPath(assetIndex,
121+
assetListFromAudio,
117122
senadinaResult.Audio,
118123
token);
119124
});
@@ -296,7 +301,8 @@ private void FinalizeVideoAssetsPath(List<FilePropertiesRemote> assetList)
296301
}
297302
}
298303

299-
private async Task FinalizeAudioAssetsPath(List<FilePropertiesRemote> assetList,
304+
private async Task FinalizeAudioAssetsPath(List<FilePropertiesRemote> originAssetList,
305+
List<FilePropertiesRemote> assetList,
300306
SenadinaFileIdentifier? audioManifestIdentifier,
301307
CancellationToken token)
302308
{
@@ -310,14 +316,18 @@ private async Task FinalizeAudioAssetsPath(List<FilePropertiesRemote> assetList,
310316
.FirstOrDefault(x => x.Name.StartsWith("AUDIO_Default"));
311317

312318
string audioBasePath = Path.Combine(GamePath, AssetBundleExtension.RelativePathAudio);
313-
314-
if (audioDefaultAsset != null)
319+
FilePropertiesRemote? audioDefaultManifestAsset = originAssetList.FirstOrDefault(x => x.N.EndsWith("AUDIO_Default_manifest.m"));
320+
321+
// Edit: 2026-05-07
322+
// Starting from 8.8, AUDIO_Default_manifest.m must expect to be existed. So, if the file is missing or hash doesn't match,
323+
// perform download in the background. Then write AUDIO_Default_Version.txt after.
324+
string audioDefaultVersionPath = Path.Combine(audioBasePath, "AUDIO_Default_Version.txt");
325+
string audioDefaultManifestPath = Path.Combine(audioBasePath, "AUDIO_Default_manifest.m");
326+
if (await EnsureAudioDefaultManifestExisted(HttpClientGeneric, audioDefaultManifestAsset, audioDefaultManifestPath, token))
315327
{
316-
ulong audioDefaultAssetHash = GetLongFromHashStr(audioDefaultAsset.HashString);
317-
byte[] audioDefaultManifestBuffer = new byte[16];
318-
audioDefaultAsset.Hash.CopyTo(audioDefaultManifestBuffer);
319-
await File.WriteAllTextAsync(Path.Combine(audioBasePath, "AUDIO_Default_Version.txt"), $"{gameVersion}\t{audioDefaultAssetHash}", token);
320-
await File.WriteAllBytesAsync(Path.Combine(audioBasePath, "AUDIO_Default_manifest.m"), audioDefaultManifestBuffer, token);
328+
byte[] manifestContent = File.ReadAllBytes(audioDefaultManifestPath);
329+
ulong audioDefaultAssetHash = BinaryPrimitives.ReadUInt64BigEndian(manifestContent.AsSpan(0, 8));
330+
await File.WriteAllTextAsync(audioDefaultVersionPath, $"{gameVersion}\t{audioDefaultAssetHash}", token);
321331
}
322332

323333
// Create Versioning file.
@@ -380,6 +390,46 @@ await audioManifestIdentifier
380390
}
381391
}
382392

393+
private async Task<bool> EnsureAudioDefaultManifestExisted(
394+
HttpClient client,
395+
FilePropertiesRemote? audioDefaultManifestAsset,
396+
string audioDefaultManifestPath,
397+
CancellationToken token)
398+
{
399+
if (audioDefaultManifestAsset == null)
400+
{
401+
return false;
402+
}
403+
404+
// Check current hash first. If not match, delete and redownload.
405+
if (File.Exists(audioDefaultManifestPath))
406+
{
407+
byte[] hashRemote = audioDefaultManifestAsset.CRCArray;
408+
byte[] hashLocal = hashRemote.Length == 8 ?
409+
await GetHashAsync<XxHash64>(audioDefaultManifestPath, false, false, token) :
410+
await GetCryptoHashAsync<MD5>(audioDefaultManifestPath, null, false, false, token);
411+
412+
if (!IsBytesEqualReversible(hashRemote, hashLocal))
413+
{
414+
new FileInfo(audioDefaultManifestPath).TryDeleteFile();
415+
}
416+
}
417+
418+
if (!File.Exists(audioDefaultManifestPath))
419+
{
420+
if (audioDefaultManifestAsset.AssociatedObject is SophonAsset)
421+
{
422+
await RepairAssetGenericSophonType(audioDefaultManifestAsset, token);
423+
}
424+
else
425+
{
426+
await RepairAssetGenericType(client, audioDefaultManifestAsset, token);
427+
}
428+
}
429+
430+
return true;
431+
}
432+
383433
private async Task FinalizeBlockAssetsPath(
384434
List<FilePropertiesRemote> sourceAssetList,
385435
List<FilePropertiesRemote> targetAssetList,

0 commit comments

Comments
 (0)