Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,7 @@ internal static FileInfo EnsureCreationOfDirectory(this FileInfo filePath)

try
{
if (directoryInfo is { Exists: false })
directoryInfo.Create();

directoryInfo?.Create();
return filePath;
}
finally
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@ private async Task ConfirmAdditionalInstallDataPackageFiles(
.Where(x => matchingFieldsList.Contains(x.MatchingField, StringComparer.OrdinalIgnoreCase))
.Sum(x =>
{
var firstTag = x.ChunkInfo;
SophonManifestChunkInfo? firstTag = x.ChunkInfo;
return firstTag?.CompressedSize ?? 0;
});

Expand Down Expand Up @@ -968,7 +968,9 @@ private ValueTask RunSophonAssetDownloadThread(HttpClient client,
}

// Get the target and temp file info
FileInfo existingFileInfo = new FileInfo(filePath).EnsureNoReadOnly();
FileInfo existingFileInfo = new FileInfo(filePath)
.EnsureCreationOfDirectory()
.EnsureNoReadOnly();

return asset.WriteToStreamAsync(client,
assetSize => existingFileInfo.Open(new FileStreamOptions
Expand Down Expand Up @@ -1001,9 +1003,13 @@ private ValueTask RunSophonAssetUpdateThread(HttpClient client,

// Get the target and temp file info
FileInfo existingFileInfo =
new FileInfo(filePath).EnsureNoReadOnly(out bool isExistingFileInfoExist);
new FileInfo(filePath)
.EnsureCreationOfDirectory()
.EnsureNoReadOnly(out bool isExistingFileInfoExist);
FileInfo sophonFileInfo =
new FileInfo(filePath + "_tempSophon").EnsureNoReadOnly(out bool isSophonFileInfoExist);
new FileInfo(filePath + "_tempSophon")
.EnsureCreationOfDirectory()
.EnsureNoReadOnly(out bool isSophonFileInfoExist);

// Use "_tempSophon" if file is new or if "_tempSophon" file exist. Otherwise use original file if exist
if (!isExistingFileInfoExist || isSophonFileInfoExist
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
using CollapseLauncher.Extension;
using CollapseLauncher.Helper;
using CollapseLauncher.Helper.Metadata;
using CollapseLauncher.Interfaces;
using CollapseLauncher.Helper.StreamUtility;
using Hi3Helper;
using Hi3Helper.Data;
using Hi3Helper.Plugin.Core.Management;
Expand All @@ -25,8 +25,10 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Hashing;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;

Expand All @@ -51,15 +53,14 @@ protected virtual async Task<bool> AlterStartPatchUpdateSophon(HttpClient httpCl
nameof(GameVersionManager.GamePreset.LauncherBizName));

// Get GameVersionManager and GamePreset
IGameVersion gameVersion = GameVersionManager;
PresetConfig gamePreset = gameVersion.GamePreset;
PresetConfig gamePreset = GameVersionManager.GamePreset;

// Gt current and future version
GameVersion? requestedVersionFrom = gameVersion.GetGameExistingVersion() ??
GameVersion? requestedVersionFrom = GameVersionManager.GetGameExistingVersion() ??
throw new NullReferenceException("Cannot get previous/current version of the game");
GameVersion? requestedVersionTo = (isPreloadMode ?
gameVersion.GetGameVersionApiPreload() :
gameVersion.GetGameVersionApi()) ??
GameVersionManager.GetGameVersionApiPreload() :
GameVersionManager.GetGameVersionApi()) ??
throw new NullReferenceException("Cannot get next/future version of the game");

// Assign branch properties
Expand Down Expand Up @@ -168,13 +169,13 @@ protected virtual async Task ConfirmAdditionalPatchDataPackageFiles(SophonChunkM
.Where(x => matchingFieldsList.Contains(x.MatchingField, StringComparer.OrdinalIgnoreCase))
.Sum(x =>
{
var firstTag = x.DiffTaggedInfo.FirstOrDefault(y => y.Key == currentVersion).Value;
SophonManifestChunkInfo? firstTag = x.DiffTaggedInfo.FirstOrDefault(y => y.Key == currentVersion).Value;
return firstTag?.CompressedSize ?? 0;
});
long sizeAdditionalToDownload = otherManifestIdentity
.Sum(x =>
{
var firstTag = x.DiffTaggedInfo.FirstOrDefault(y => y.Key == currentVersion).Value;
SophonManifestChunkInfo? firstTag = x.DiffTaggedInfo.FirstOrDefault(y => y.Key == currentVersion).Value;
return firstTag?.CompressedSize ?? 0;
});

Expand All @@ -191,7 +192,7 @@ protected virtual async Task ConfirmAdditionalPatchDataPackageFiles(SophonChunkM
}
}

matchingFieldsList.AddRange(otherManifestIdentity.Select(identity => identity.MatchingField));
matchingFieldsList.AddRange(otherManifestIdentity.Select(identity => identity.MatchingField ?? ""));
return;

string GetFileDetails()
Expand All @@ -205,12 +206,12 @@ string GetFileDetails()
long chunkCount = 0;

// ReSharper disable once ConvertToUsingDeclaration
using (FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write))
using (FileStream fileStream = new(filePath, FileMode.Create, FileAccess.Write))
{
using StreamWriter writer = new StreamWriter(fileStream);
foreach (var field in otherManifestIdentity)
using StreamWriter writer = new(fileStream);
foreach (SophonManifestPatchIdentity field in otherManifestIdentity)
{
var fieldInfo = field.DiffTaggedInfo.FirstOrDefault(x => x.Key == currentVersion).Value;
SophonManifestChunkInfo? fieldInfo = field.DiffTaggedInfo.FirstOrDefault(x => x.Key == currentVersion).Value;
if (fieldInfo == null)
{
continue;
Expand Down Expand Up @@ -560,29 +561,67 @@ async ValueTask ImplDownload(Tuple<SophonPatchAsset, Dictionary<string, int>> ct
SophonPatchAsset patchAsset = ctx.Item1;
Dictionary<string, int> downloadedDict = ctx.Item2;

using (dictionaryLock.EnterScope())
try
{
_ = downloadedDict.TryAdd(patchAsset.PatchNameSource, 0);
downloadedDict[patchAsset.PatchNameSource]++;
UpdateCurrentDownloadStatus();
// Check if target file has already been patched so the launcher won't redownload everything.
if (!isPreloadMode && patchAsset.PatchMethod != SophonPatchMethod.Remove)
{
FileInfo fileInfo = new FileInfo(Path.Combine(GamePath, patchAsset.TargetFilePath))
.EnsureNoReadOnly()
.StripAlternateDataStream();

if (fileInfo.Exists &&
fileInfo.Length == patchAsset.TargetFileSize)
{
byte[] remoteHashBytes = HexTool.HexToBytesUnsafe(patchAsset.TargetFileHash);
byte[] localHashBytes = remoteHashBytes.Length > 8
? await GetCryptoHashAsync<MD5>(fileInfo, null, false, false, innerToken)
: await GetHashAsync<XxHash64>(fileInfo, false, false, innerToken);

// Try to reverse hash bytes in case the returned bytes are going to be Big-endian.
if (!localHashBytes.SequenceEqual(remoteHashBytes))
{
Array.Reverse(localHashBytes);
}

// Now compare. If the hash is already equal (means the target file is already downloaded),
// then skip from downloading the patch.
if (localHashBytes.SequenceEqual(remoteHashBytes))
{
long patchSize = patchAsset.PatchSize;
UpdateSophonFileTotalProgress(patchSize);
UpdateSophonFileDownloadProgress(patchSize, patchSize);
return;
}
}
}

await patchAsset.DownloadPatchAsync(httpClient,
GamePath,
patchOutputDir,
true,
read =>
{
UpdateSophonFileTotalProgress(read);
UpdateSophonFileDownloadProgress(read, read);
},
downloadLimiter,
innerToken);
}
finally
{
using (dictionaryLock.EnterScope())
{
_ = downloadedDict.TryAdd(patchAsset.PatchNameSource, 0);
downloadedDict[patchAsset.PatchNameSource]++;
}

UpdateCurrentDownloadStatus();
await patchAsset.DownloadPatchAsync(httpClient,
GamePath,
patchOutputDir,
true,
read =>
{
UpdateSophonFileTotalProgress(read);
UpdateSophonFileDownloadProgress(read, read);
},
downloadLimiter,
innerToken);

Logger.LogWriteLine($"Downloaded patch file for: {patchAsset.TargetFilePath}",
LogType.Debug,
true);
Interlocked.Increment(ref ProgressAllCountCurrent);
Logger.LogWriteLine($"Downloaded patch file for: {patchAsset.TargetFilePath}",
LogType.Debug,
true);
Interlocked.Increment(ref ProgressAllCountCurrent);
}
}

async ValueTask ImplPatchUpdate(Tuple<SophonPatchAsset, Dictionary<string, int>> ctx, CancellationToken innerToken)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@

protected List<GameInstallPackage> _gameDeltaPatchPreReqList { get; } = [];
protected bool _forceIgnoreDeltaPatch;
protected GameInstallFileInfo? _gameInstallFileInfo { get; set; }

Check warning on line 131 in CollapseLauncher/Classes/InstallManagement/Base/InstallManagerBase.cs

View workflow job for this annotation

GitHub Actions / build-nativeaot (Debug, x64, net9.0-windows10.0.26100.0)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 131 in CollapseLauncher/Classes/InstallManagement/Base/InstallManagerBase.cs

View workflow job for this annotation

GitHub Actions / build (Debug, x64, net9.0-windows10.0.26100.0)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 131 in CollapseLauncher/Classes/InstallManagement/Base/InstallManagerBase.cs

View workflow job for this annotation

GitHub Actions / build (Debug, x64, net9.0-windows10.0.26100.0)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 131 in CollapseLauncher/Classes/InstallManagement/Base/InstallManagerBase.cs

View workflow job for this annotation

GitHub Actions / build (Debug, x64, net9.0-windows10.0.26100.0)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
#endregion

#region Public Properties
Expand Down Expand Up @@ -1926,6 +1926,7 @@
) => langString switch
{
"Chinese" => "zh-cn",
"Chinese(PRC)" => "zh-cn",
"English" => "en-us",
"English(US)" => "en-us",
"Korean" => "ko-kr",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Hi3Helper.Sophon;
using Hi3Helper.Sophon.Infos;
using Hi3Helper.Sophon.Structs;
using System;
using System.Buffers;
using System.Collections.Generic;
Expand Down Expand Up @@ -29,47 +30,16 @@ public override async Task FilterAssetList<T>(
Func<T, string?> itemPathSelector,
CancellationToken token)
{
HashSet<int> exceptMatchFieldHashSet = await GetExceptMatchFieldHashSet(token);
HashSet<string> exceptMatchFieldHashSet = await GetExceptMatchFieldHashSet(token);
if (exceptMatchFieldHashSet.Count == 0)
{
return;
}

FilterSophonAsset(itemList, Selector, exceptMatchFieldHashSet);
return;

string? Selector(T item)
{
// For Game Update
if (item is SophonPatchAsset asset)
{
if (asset.MainAssetInfo is not { } assetSelected)
{
return null;
}

token.ThrowIfCancellationRequested();
ref SophonChunksInfo chunkInfo = ref Unsafe.IsNullRef(ref assetSelected)
? ref Unsafe.NullRef<SophonChunksInfo>()
: ref GetChunkAssetChunksInfo(assetSelected);

if (Unsafe.IsNullRef(ref chunkInfo))
{
chunkInfo = ref GetChunkAssetChunksInfoAlt(assetSelected);
}

return Unsafe.IsNullRef(ref chunkInfo)
? asset.MainAssetInfo.AssetName
: chunkInfo.ChunksBaseUrl;
}

// TODO: For Game Repair handle

return null;
}
FilterSophonAsset(itemList, exceptMatchFieldHashSet);
}

private async Task<HashSet<int>> GetExceptMatchFieldHashSet(CancellationToken token)
private async Task<HashSet<string>> GetExceptMatchFieldHashSet(CancellationToken token)
{
string gameExecDataName =
Path.GetFileNameWithoutExtension(GameVersionManager.GamePreset.GameExecutableName) ?? "ZenlessZoneZero";
Expand All @@ -82,38 +52,20 @@ private async Task<HashSet<int>> GetExceptMatchFieldHashSet(CancellationToken to
return [];
}

string exceptMatchFieldContent = await File.ReadAllTextAsync(gameExceptMatchFieldFile, token);
HashSet<int> exceptMatchFieldHashSet = CreateExceptMatchFieldHashSet<int>(exceptMatchFieldContent);
string exceptMatchFieldContent = await File.ReadAllTextAsync(gameExceptMatchFieldFile, token);
HashSet<string> exceptMatchFieldHashSet = CreateExceptMatchFieldHashSet(exceptMatchFieldContent);

return exceptMatchFieldHashSet;
}

// ReSharper disable once IdentifierTypo
private static void FilterSophonAsset<T>(List<T> itemList, Func<T, string?> assetSelector, HashSet<int> exceptMatchFieldHashSet)
private static void FilterSophonAsset<T>(List<T> itemList, HashSet<string> exceptMatchFieldHashSet)
{
const string separators = "/\\";
scoped Span<Range> urlPathRanges = stackalloc Range[32];

List<T> filteredList = [];
foreach (T asset in itemList)
{
string? assetPath = assetSelector(asset);
if (assetPath == null)
{
filteredList.Add(asset);
continue;
}

ReadOnlySpan<char> manifestUrl = assetPath;
int rangeLen = manifestUrl.SplitAny(urlPathRanges, separators, SplitOptions);
if (rangeLen <= 0)
{
continue;
}

ReadOnlySpan<char> manifestStr = manifestUrl[urlPathRanges[rangeLen - 1]];
if (int.TryParse(manifestStr, null, out int lookupNumber) &&
exceptMatchFieldHashSet.Contains(lookupNumber))
if (asset is SophonIdentifiableProperty { MatchingField: { } assetMatchingField } &&
exceptMatchFieldHashSet.Contains(assetMatchingField))
{
continue;
}
Expand All @@ -130,11 +82,10 @@ private static void FilterSophonAsset<T>(List<T> itemList, Func<T, string?> asse
itemList.AddRange(filteredList);
}

internal static HashSet<T> CreateExceptMatchFieldHashSet<T>(string exceptMatchFieldContent)
where T : ISpanParsable<T>
internal static HashSet<string> CreateExceptMatchFieldHashSet(string exceptMatchFieldContent)
{
const string lineFeedSeparators = "\r\n";
HashSet<T> hashSetReturn = [];
HashSet<string> hashSetReturn = new(StringComparer.OrdinalIgnoreCase);
scoped Span<Range> contentLineRange = stackalloc Range[2];

ReadOnlySpan<char> contentSpan = exceptMatchFieldContent.AsSpan();
Expand All @@ -157,10 +108,7 @@ internal static HashSet<T> CreateExceptMatchFieldHashSet<T>(string exceptMatchFi
}

ReadOnlySpan<char> contentMatch = contentSpan[contentMatchRange].Trim(separatorsChars);
if (T.TryParse(contentMatch, null, out T? result))
{
hashSetReturn.Add(result);
}
hashSetReturn.Add(contentMatch.ToString());
}

return hashSetReturn;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ private void FilterExcludedAssets(List<FilePropertiesRemote> assetList)
}

string exceptMatchFieldContent = File.ReadAllText(gameExceptMatchFieldFile);
HashSet<int> exceptMatchFieldHashSet = ZenlessInstall.CreateExceptMatchFieldHashSet<int>(exceptMatchFieldContent);
HashSet<string> exceptMatchFieldHashSet = ZenlessInstall.CreateExceptMatchFieldHashSet(exceptMatchFieldContent);

List<FilePropertiesRemote> filteredList = [];
foreach (FilePropertiesRemote asset in assetList)
Expand All @@ -120,7 +120,7 @@ private void FilterExcludedAssets(List<FilePropertiesRemote> assetList)
continue;
}

bool isExceptionFound = zenlessResAsset.PackageMatchingIds.Any(exceptMatchFieldHashSet.Contains);
bool isExceptionFound = zenlessResAsset.PackageMatchingIds.Any(x => exceptMatchFieldHashSet.Contains($"{x}"));
if (isExceptionFound)
{
continue;
Expand Down
Loading