Skip to content

Commit 2c8f7d7

Browse files
authored
[EMERGENCY HOTFIX] Stable - 1.82.23 Sophon Incident (#739)
# Main Goal As per May 5th 2025 (on Genshin Impact 5.6 update), HoYo just removed the entire Zip packages from the launcher APIs. causing a major issue throughout Third-party Launcher Community (Collapse is no exception). This PR also fixed some essential features which has stopped working due its dependency on Zip's ScatteredFiles references. Now, all those features are moving its dependency entirely to Sophon as its game files references. # What's changed? - **[Fix - Genshin Impact]** Infinite Loop/Getting Stuck while Updating Game to 5.6.0 - **[Fix - Genshin Impact]** Crash on File Cleanup feature due to missing Zip's pkg_version ScatteredFiles reference. - **[Fix - Genshin Impact]** Crash on Game Repair feature due to missing Zip's pkg_version ScatteredFiles reference. - **[Fix]** Possible crash when user defines ``version`` field in ``config.ini`` with 2-numbers or less format. - **[Fix]** [#736](#736) GameIniVersion ignores mismatched value # Note These changes were backported from the ``main`` branch manually.
2 parents 4106bc9 + 9f6c493 commit 2c8f7d7

File tree

17 files changed

+825
-375
lines changed

17 files changed

+825
-375
lines changed

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

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,42 @@ static void ExitFromOverlay(object? sender, RoutedEventArgs args)
160160
}
161161
}
162162

163+
protected virtual async Task<string> DownloadPkgVersion(DownloadClient downloadClient, RegionResourceVersion? packageLatestBase)
164+
{
165+
string? packageExtractBasePath = packageLatestBase?.decompressed_path;
166+
167+
// Check Fail-safe: Download pkg_version files if not exist
168+
string pkgVersionPath = Path.Combine(GamePath, "pkg_version");
169+
if (string.IsNullOrEmpty(packageExtractBasePath))
170+
{
171+
return pkgVersionPath;
172+
}
173+
174+
// Check Fail-safe: Download main pkg_version file
175+
string mainPkgVersionUrl = packageExtractBasePath.CombineURLFromString("pkg_version");
176+
await downloadClient.DownloadAsync(mainPkgVersionUrl, pkgVersionPath, true);
177+
178+
// Check Fail-safe: Download audio pkg_version files
179+
if (string.IsNullOrEmpty(_gameAudioLangListPathStatic) ||
180+
string.IsNullOrEmpty(packageExtractBasePath))
181+
{
182+
return pkgVersionPath;
183+
}
184+
185+
if (!File.Exists(_gameAudioLangListPathStatic))
186+
{
187+
throw new
188+
FileNotFoundException("Game does have audio lang index file but does not exist!"
189+
+ $" Expecting location: {_gameAudioLangListPathStatic}");
190+
}
191+
192+
await DownloadOtherAudioPkgVersion(_gameAudioLangListPathStatic,
193+
packageExtractBasePath,
194+
downloadClient);
195+
196+
return pkgVersionPath;
197+
}
198+
163199
protected virtual async Task<(List<LocalFileInfo>, long)> GetUnusedFileInfoList(bool includeZipCheck)
164200
{
165201
LoadingMessageHelper.ShowLoadingFrame();
@@ -195,33 +231,9 @@ static void ExitFromOverlay(object? sender, RoutedEventArgs args)
195231
DownloadClient downloadClient = DownloadClient.CreateInstance(httpClient);
196232
RegionResourceVersion? packageLatestBase = GameVersionManager
197233
.GetGameLatestZip(gameStateEnum).FirstOrDefault();
198-
string? packageExtractBasePath = packageLatestBase?.decompressed_path;
199234

200-
// Check Fail-safe: Download pkg_version files if not exist
201-
string pkgVersionPath = Path.Combine(GamePath, "pkg_version");
202-
if (!string.IsNullOrEmpty(packageExtractBasePath))
203-
{
204-
// Check Fail-safe: Download main pkg_version file
205-
string mainPkgVersionUrl = ConverterTool.CombineURLFromString(packageExtractBasePath,
206-
"pkg_version");
207-
await downloadClient.DownloadAsync(mainPkgVersionUrl, pkgVersionPath, true);
208-
209-
// Check Fail-safe: Download audio pkg_version files
210-
if (!string.IsNullOrEmpty(_gameAudioLangListPathStatic) &&
211-
!string.IsNullOrEmpty(packageExtractBasePath))
212-
{
213-
if (!File.Exists(_gameAudioLangListPathStatic))
214-
{
215-
throw new
216-
FileNotFoundException("Game does have audio lang index file but does not exist!"
217-
+ $" Expecting location: {_gameAudioLangListPathStatic}");
218-
}
219-
220-
await DownloadOtherAudioPkgVersion(_gameAudioLangListPathStatic,
221-
packageExtractBasePath,
222-
downloadClient);
223-
}
224-
}
235+
// Download pkg_version file (with additional audio ones)
236+
string pkgVersionPath = await DownloadPkgVersion(downloadClient, packageLatestBase);
225237

226238
// Check Fail-safe: If the main pkg_version still not exist, throw!
227239
bool isMainPkgVersionExist = File.Exists(pkgVersionPath);
@@ -342,7 +354,7 @@ protected virtual async ValueTask DownloadOtherAudioPkgVersion(string audioListF
342354
string pkgUrl = ConverterTool.CombineURLFromString(baseExtractUrl, pkgFileName);
343355

344356
// Skip if URL is not found
345-
if ((await FallbackCDNUtil.GetURLStatusCode(pkgUrl, default)).StatusCode == HttpStatusCode.NotFound)
357+
if ((await FallbackCDNUtil.GetURLStatusCode(pkgUrl, CancellationToken.None)).StatusCode == HttpStatusCode.NotFound)
346358
{
347359
continue;
348360
}

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

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ await branchResources.EnsureReassociated(httpClient,
7777
await SophonPatch.CreateSophonChunkManifestInfoPair(httpClient,
7878
url: branchResources.PatchUrl,
7979
versionUpdateFrom: requestedVersionFrom.Value.VersionString,
80-
matchingField: GameVersionManager.GamePreset.LauncherResourceChunksURL.MainBranchMatchingField,
80+
matchingField: branchResources.MainBranchMatchingField,
8181
token: Token.Token);
8282

8383
// If the patch metadata is not found, then return false and back to old compare method
@@ -101,7 +101,7 @@ await SophonPatch.CreateSophonChunkManifestInfoPair(httpClient,
101101
(List<SophonPatchAsset>, List<SophonChunkManifestInfoPair>) patchAssets =
102102
await GetAlterSophonPatchAssets(httpClient,
103103
branchResources.PatchUrl,
104-
GameRepoURL,
104+
(isPreloadMode ? branchResources.PreloadUrl : branchResources.MainUrl) ?? "",
105105
matchingFields,
106106
requestedVersionFrom.Value.VersionString,
107107
downloadSpeedLimiter,
@@ -128,23 +128,35 @@ await StartAlterSophonPatch(httpClient,
128128
SophonDownloadSpeedLimiter downloadLimiter,
129129
CancellationToken token)
130130
{
131-
SophonChunkManifestInfoPair? rootPatchManifest = null;
132-
List<SophonChunkManifestInfoPair> patchManifestList = [];
131+
SophonChunkManifestInfoPair? rootPatchManifest = null;
132+
SophonChunkManifestInfoPair? rootMainManifest = null;
133+
List<(SophonChunkManifestInfoPair Patch, SophonChunkManifestInfoPair Main)> patchManifestList = [];
133134

134135
// Iterate matching fields and get the patch metadata
135136
foreach (string matchingField in matchingFields)
136137
{
137138
// Initialize root manifest if it's null
138-
rootPatchManifest ??= await SophonPatch.CreateSophonChunkManifestInfoPair(httpClient,
139-
url: manifestUrl,
140-
versionUpdateFrom: updateVersionfrom,
141-
matchingField: matchingField,
142-
token: token);
139+
rootPatchManifest ??= await SophonPatch
140+
.CreateSophonChunkManifestInfoPair(httpClient,
141+
url: manifestUrl,
142+
versionUpdateFrom: updateVersionfrom,
143+
matchingField: matchingField,
144+
token: token);
145+
146+
rootMainManifest ??= await SophonManifest
147+
.CreateSophonChunkManifestInfoPair(httpClient,
148+
url: downloadOverUrl,
149+
matchingField: matchingField,
150+
token: token);
143151

144152
// Get the manifest pair based on the matching field
145153
SophonChunkManifestInfoPair patchManifest = rootPatchManifest
146154
.GetOtherPatchInfoPair(matchingField, updateVersionfrom);
147155

156+
// Get the main manifest pair based on the matching field
157+
SophonChunkManifestInfoPair mainManifest = rootMainManifest
158+
.GetOtherManifestInfoPair(matchingField);
159+
148160
// If the patch metadata is not found, continue to other manifest pair
149161
if (!patchManifest.IsFound)
150162
{
@@ -155,27 +167,27 @@ await StartAlterSophonPatch(httpClient,
155167
}
156168

157169
// Otherwise, add the manifest to the list
158-
patchManifestList.Add(patchManifest);
170+
patchManifestList.Add((patchManifest, mainManifest));
159171
}
160172

161173
// Initialize the return list and iterate the manifests
162174
List<SophonPatchAsset> patchAssets = [];
163-
foreach (SophonChunkManifestInfoPair manifestPair in patchManifestList)
175+
foreach (var manifestPair in patchManifestList)
164176
{
165177
// Get the asset and add it to the list
166178
await foreach (SophonPatchAsset patchAsset in SophonPatch
167179
.EnumerateUpdateAsync(httpClient,
168-
manifestPair,
180+
manifestPair.Patch,
181+
manifestPair.Main,
169182
updateVersionfrom,
170-
downloadOverUrl,
171183
downloadLimiter,
172184
token))
173185
{
174186
patchAssets.Add(patchAsset);
175187
}
176188
}
177189

178-
return (patchAssets, patchManifestList);
190+
return (patchAssets, patchManifestList.Select(x => x.Patch).ToList());
179191
}
180192

181193
protected virtual async Task<List<string>> GetAlterSophonPatchVOMatchingFields(CancellationToken token)
@@ -235,7 +247,7 @@ protected virtual async Task StartAlterSophonPatch(HttpClient httpClient,
235247
long downloadSizePatchOnlyRemote = patchManifestInfoPairs.Sum(x => x.ChunksInfo.TotalSize);
236248

237249
// Get download counts
238-
int downloadCountTotalAssetRemote = patchAssets.Count(x => x.PatchMethod != SophonPatchMethod.Remove);
250+
int downloadCountTotalAssetRemote = patchAssets.Count;
239251
int downloadCountPatchOnlyRemote = patchManifestInfoPairs.Sum(x => x.ChunksInfo.ChunksCount);
240252

241253
// Ensure disk space sufficiency

0 commit comments

Comments
 (0)