Skip to content

Commit a66d076

Browse files
authored
Preview 1.82.21 Update (Vuln CVE-2025-24070) (#708)
# What's changed? - 1.82.21 - **[Fix]** Update .NET to 9.0.3 due to CVE-2025-24070, by @bagusnl - We are not directly affected but due to the high score of the vuln, we have to update. - Read more about the vuln here GHSA-2865-hh9g-w894 - **[Fix]** Proxy with password always return invalid password, by @neon-nyan - **[Fix]** Duplicated entries in Zenless repair, by @neon-nyan - **[Fix]** Missing margin in HomePage's Additional Settings subpanel, by @shatyuka - **[Fix]** Taskbar progress bar did not reset after update is finished, by @shatyuka - **[New]** Anisotropic Filtering in Zenless game settings, by @Cryotechnic - **[Fix]** Playtime database did not sync if database stamp is null, by @bagusnl ### Templates <details> <summary>Changelog Prefixes</summary> ``` **[New]** **[Imp]** **[Fix]** **[Loc]** **[Doc]** ``` </details>
2 parents 00e0a96 + 2e40b9f commit a66d076

32 files changed

+340
-197
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ on:
2727

2828
env:
2929
DOTNET_INSTALL_DIR: '.\.dotnet'
30-
DOTNET_VERSION: '9.0.1xx'
30+
DOTNET_VERSION: '9.0.2xx'
3131
DOTNET_QUALITY: 'ga'
3232
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
3333

.github/workflows/qodana-scan-pr.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
Configuration: ${{ matrix.configuration }}
3030
Platform: ${{ matrix.platform }}
3131
DOTNET_INSTALL_DIR: '.\.dotnet'
32-
DOTNET_VERSION: '9.0.1xx'
32+
DOTNET_VERSION: '9.0.2xx'
3333
DOTNET_QUALITY: 'ga'
3434
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
3535

@@ -52,7 +52,7 @@ jobs:
5252

5353
- name: 'Qodana Scan'
5454
if: ${{ github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name }}
55-
uses: JetBrains/qodana-action@next
55+
uses: JetBrains/qodana-action@main
5656
with:
5757
args: --ide,QDNET
5858
pr-mode: true

.github/workflows/qodana-scan.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
Configuration: ${{ matrix.configuration }}
2121
Platform: ${{ matrix.platform }}
2222
DOTNET_INSTALL_DIR: '.\.dotnet'
23-
DOTNET_VERSION: '9.0.1xx'
23+
DOTNET_VERSION: '9.0.2xx'
2424
DOTNET_QUALITY: 'ga'
2525
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
2626
permissions:

CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ public void AddMinute()
296296
{
297297
// Fetch database last update stamp
298298
var stampDbStr = await DbHandler.QueryKey(KeyLastUpdated);
299-
_unixStampDb = !string.IsNullOrEmpty(stampDbStr) ? Convert.ToInt32(stampDbStr) : null;
299+
_unixStampDb = !string.IsNullOrWhiteSpace(stampDbStr) ? Convert.ToInt32(stampDbStr) : null;
300300

301301
// Compare unix stamp from config
302302
var unixStampLocal = Convert.ToInt32(DbConfig.GetConfig(KeyLastUpdated).ToString());
@@ -317,7 +317,7 @@ public void AddMinute()
317317
return (false, null); // Return if pull failed
318318
}
319319

320-
if (string.IsNullOrEmpty(_jsonDataDb))
320+
if (string.IsNullOrWhiteSpace(_jsonDataDb))
321321
{
322322
LogWriteLine("[CollapsePlaytime::DbSync] _jsonDataDb is empty, skipping sync~", default, true);
323323
return (false, null);
@@ -342,7 +342,7 @@ public void AddMinute()
342342
return (true, playtimeInner);
343343
}
344344

345-
if (!(_unixStampDb < unixStampLocal))
345+
if (_unixStampDb != null && !(_unixStampDb < unixStampLocal))
346346
{
347347
return (false, null);
348348
}
@@ -406,13 +406,18 @@ private async Task UpdatePlaytime_Database_Pull()
406406
_jsonDataDb = await DbHandler.QueryKey(KeyPlaytimeJson, true);
407407

408408
var totalTimeDbStr = await DbHandler.QueryKey(KeyTotalTime, true);
409-
_totalTimeDb = string.IsNullOrEmpty(totalTimeDbStr) ? null : Convert.ToDouble(totalTimeDbStr, CultureInfo.InvariantCulture);
409+
_totalTimeDb = string.IsNullOrWhiteSpace(totalTimeDbStr)
410+
? null
411+
: Convert.ToDouble(totalTimeDbStr, CultureInfo.InvariantCulture);
410412

411413
var lpDb = await DbHandler.QueryKey(KeyLastPlayed, true);
412-
_lastPlayedDb = !string.IsNullOrEmpty(lpDb) && !lpDb.Contains("null") ? Convert.ToDouble(lpDb, CultureInfo.InvariantCulture) : null; // if Db data is null, return null
414+
_lastPlayedDb = !string.IsNullOrWhiteSpace(lpDb) && !lpDb.Contains("null")
415+
? Convert.ToDouble(lpDb, CultureInfo.InvariantCulture)
416+
: null; // if Db data is null, return null
413417

414418
_isDbPullSuccess = true;
415-
LogWriteLine("[CollapsePlaytime::UpdatePlaytime_Database_Pull()] Successfully pulled data from database!", LogType.Scheme, true);
419+
LogWriteLine("[CollapsePlaytime::UpdatePlaytime_Database_Pull()] Successfully pulled data from database!",
420+
LogType.Scheme, true);
416421
}
417422
catch (Exception e)
418423
{

CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/Enums.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,20 @@ public enum AudioPlaybackDevice
151151
TV = 3
152152
}
153153

154+
/// <summary>
155+
/// Available options for graphics settings that have 5 options <br/>
156+
/// 1x, 2x, 4x, 8x, 16x
157+
/// Default : 8x [3]
158+
/// </summary>
159+
public enum AnisotropicSamplingOption
160+
{
161+
x1,
162+
x2,
163+
x4,
164+
x8,
165+
x16
166+
}
167+
154168
public static class ServerName
155169
{
156170
public const string Europe = "prod_gf_eu";

CollapseLauncher/Classes/GameManagement/GameSettings/Zenless/FileClass/GeneralData.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,23 @@ public QualityOption2 EnvironmentQuality
499499
.GetDataEnum<QualityOption2>();
500500
set => _envQualityData?.SetDataEnum(value);
501501
}
502-
502+
503+
// Key 16184 Anisotropic Sampling
504+
private SystemSettingLocalData<AnisotropicSamplingOption>? _anisotropicSamplingData;
505+
506+
/// <summary>
507+
/// Sets the in-game quality settings for Anisotropic Sampling
508+
/// </summary>
509+
/// <see cref="AnisotropicSamplingOption"/>
510+
[JsonIgnore]
511+
public AnisotropicSamplingOption AnisotropicSampling
512+
{
513+
get => (_anisotropicSamplingData ??= SystemSettingDataMap
514+
.AsSystemSettingLocalData("16184", AnisotropicSamplingOption.x8))
515+
.GetDataEnum<AnisotropicSamplingOption>();
516+
set => _anisotropicSamplingData?.SetDataEnum(value);
517+
}
518+
503519
// Key 12155 Global Illumination
504520
private SystemSettingLocalData<QualityOption3>? _envGlobalIllumination;
505521

CollapseLauncher/Classes/Helper/HttpClientBuilder.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Net.Http;
77
using System.Net.Security;
88
using System.Runtime.InteropServices;
9+
using System.Security;
910
// ReSharper disable StringLiteralTypo
1011
// ReSharper disable UnusedMember.Global
1112

@@ -53,7 +54,7 @@ private static string GetDefaultUserAgent()
5354
+ $"WinAppSDK/{LauncherConfig.WindowsAppSdkVersion}";
5455
}
5556

56-
public HttpClientBuilder<THandler> UseExternalProxy(string host, string? username = null, string? password = null)
57+
public HttpClientBuilder<THandler> UseExternalProxy(string host, string? username = null, SecureString? password = null)
5758
{
5859
// Try to create the Uri
5960
if (Uri.TryCreate(host, UriKind.Absolute, out Uri? hostUri))
@@ -65,17 +66,16 @@ public HttpClientBuilder<THandler> UseExternalProxy(string host, string? usernam
6566
IsUseSystemProxy = false;
6667
ExternalProxy = null;
6768
return this;
68-
6969
}
7070

71-
public HttpClientBuilder<THandler> UseExternalProxy(Uri hostUri, string? username = null, string? password = null)
71+
public HttpClientBuilder<THandler> UseExternalProxy(Uri hostUri, string? username = null, SecureString? password = null)
7272
{
7373
IsUseSystemProxy = false;
7474

7575
// Initialize the proxy host
7676
ExternalProxy =
7777
!string.IsNullOrEmpty(username)
78-
&& !string.IsNullOrEmpty(password) ?
78+
&& password != null ?
7979
new WebProxy(hostUri, true, null, new NetworkCredential(username, password))
8080
: new WebProxy(hostUri, true);
8181

@@ -100,7 +100,10 @@ public HttpClientBuilder<THandler> UseLauncherConfig(int maxConnections = MaxCon
100100
UseProxy();
101101

102102
if (lIsUseProxy && isHttpProxyUrlValid && lProxyUri != null)
103-
UseExternalProxy(lProxyUri, lHttpProxyUsername, lHttpProxyPassword);
103+
{
104+
using SecureString? proxyPassword = SimpleProtectData.UnprotectStringAsSecureString(lHttpProxyPassword);
105+
UseExternalProxy(lProxyUri, lHttpProxyUsername, proxyPassword);
106+
}
104107

105108
AllowUntrustedCert(lIsAllowUntrustedCert);
106109
AllowCookies(lIsAllowHttpCookies);

CollapseLauncher/Classes/Helper/SimpleProtectData.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Hi3Helper;
22
using System;
33
using System.Buffers.Text;
4+
using System.Security;
45
using System.Security.Cryptography;
56
using System.Text;
67
using Hi3Helper.SentryHelper;
@@ -106,5 +107,28 @@ internal static class SimpleProtectData
106107
Array.Clear(unprotectedData);
107108
}
108109
}
110+
111+
internal static unsafe SecureString? UnprotectStringAsSecureString(string? input)
112+
{
113+
string? rawString = UnprotectString(input);
114+
115+
if (string.IsNullOrEmpty(rawString))
116+
return null;
117+
118+
int len = rawString.Length;
119+
fixed (char* charB = rawString)
120+
{
121+
try
122+
{
123+
SecureString secureString = new SecureString(charB, rawString.Length);
124+
return secureString;
125+
}
126+
finally
127+
{
128+
for (int i = 0; i < len; i++)
129+
*(charB + i) = '\0';
130+
}
131+
}
132+
}
109133
}
110134
}

CollapseLauncher/Classes/InstallManagement/Base/InstallManagerBase.cs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3546,7 +3546,7 @@ public void UpdateCompletenessStatus(CompletenessStatus status)
35463546
switch (status)
35473547
{
35483548
case CompletenessStatus.Running:
3549-
IsRunning = true;
3549+
IsRunning = true;
35503550
Status.IsRunning = true;
35513551
Status.IsCompleted = false;
35523552
Status.IsCanceled = false;
@@ -3555,10 +3555,12 @@ public void UpdateCompletenessStatus(CompletenessStatus status)
35553555
#endif
35563556
break;
35573557
case CompletenessStatus.Completed:
3558-
IsRunning = false;
3558+
IsRunning = false;
35593559
Status.IsRunning = false;
35603560
Status.IsCompleted = true;
35613561
Status.IsCanceled = false;
3562+
Status.IsProgressAllIndetermined = false;
3563+
Status.IsProgressPerFileIndetermined = false;
35623564
#if !DISABLEDISCORD
35633565
InnerLauncherConfig.AppDiscordPresence?.SetActivity(ActivityType.Idle);
35643566
#endif
@@ -3570,19 +3572,23 @@ public void UpdateCompletenessStatus(CompletenessStatus status)
35703572
}
35713573
break;
35723574
case CompletenessStatus.Cancelled:
3573-
IsRunning = false;
3575+
IsRunning = false;
35743576
Status.IsRunning = false;
35753577
Status.IsCompleted = false;
35763578
Status.IsCanceled = true;
3579+
Status.IsProgressAllIndetermined = false;
3580+
Status.IsProgressPerFileIndetermined = false;
35773581
#if !DISABLEDISCORD
35783582
InnerLauncherConfig.AppDiscordPresence?.SetActivity(ActivityType.Idle);
35793583
#endif
35803584
break;
35813585
case CompletenessStatus.Idle:
3582-
IsRunning = false;
3586+
IsRunning = false;
35833587
Status.IsRunning = false;
35843588
Status.IsCompleted = false;
35853589
Status.IsCanceled = false;
3590+
Status.IsProgressAllIndetermined = false;
3591+
Status.IsProgressPerFileIndetermined = false;
35863592
#if !DISABLEDISCORD
35873593
InnerLauncherConfig.AppDiscordPresence?.SetActivity(ActivityType.Idle);
35883594
#endif
@@ -3876,4 +3882,4 @@ private void HttpClientDownloadProgressAdapter(object sender, DownloadEvent e)
38763882
}
38773883
#endregion
38783884
}
3879-
}
3885+
}

CollapseLauncher/Classes/RepairManagement/Zenless/ZenlessRepair.Extensions.cs

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -89,29 +89,52 @@ internal static async IAsyncEnumerable<PkgVersionProperties> RegisterSleepyFileI
8989
}
9090
}
9191

92-
internal static IEnumerable<FilePropertiesRemote?> RegisterMainCategorizedAssetsToHashSet(this IEnumerable<PkgVersionProperties> assetEnumerable, List<FilePropertiesRemote> assetIndex, Dictionary<string, FilePropertiesRemote> hashSet, string baseLocalPath, string baseUrl)
93-
=> assetEnumerable.Select(asset => ReturnCategorizedYieldValue(hashSet, assetIndex, asset, baseLocalPath, baseUrl));
92+
internal static IEnumerable<FilePropertiesRemote?> RegisterMainCategorizedAssetsToHashSet(
93+
this IEnumerable<PkgVersionProperties> assetEnumerable,
94+
List<FilePropertiesRemote> assetIndex,
95+
Dictionary<string, FilePropertiesRemote>.AlternateLookup<ReadOnlySpan<char>> alternativeHashSet,
96+
string baseLocalPath,
97+
string baseUrl)
98+
=> assetEnumerable.Select(asset => ReturnCategorizedYieldValue(alternativeHashSet, assetIndex, asset, baseLocalPath, baseUrl, null));
9499

95-
internal static async IAsyncEnumerable<FilePropertiesRemote?> RegisterMainCategorizedAssetsToHashSetAsync(this IAsyncEnumerable<PkgVersionProperties> assetEnumerable, List<FilePropertiesRemote> assetIndex, Dictionary<string, FilePropertiesRemote> hashSet, string baseLocalPath, string baseUrl, [EnumeratorCancellation] CancellationToken token = default)
100+
internal static async IAsyncEnumerable<FilePropertiesRemote?> RegisterMainCategorizedAssetsToHashSetAsync(
101+
this IAsyncEnumerable<PkgVersionProperties> assetEnumerable,
102+
List<FilePropertiesRemote> assetIndex,
103+
Dictionary<string, FilePropertiesRemote>.AlternateLookup<ReadOnlySpan<char>> alternativeHashSet,
104+
string baseLocalPath,
105+
string baseUrl,
106+
[EnumeratorCancellation] CancellationToken token = default)
96107

97108
{
98109
await foreach (PkgVersionProperties asset in assetEnumerable.WithCancellation(token))
99110
{
100-
yield return ReturnCategorizedYieldValue(hashSet, assetIndex, asset, baseLocalPath, baseUrl);
111+
yield return ReturnCategorizedYieldValue(alternativeHashSet, assetIndex, asset, baseLocalPath, baseUrl, null);
101112
}
102113
}
103114

104-
internal static async IAsyncEnumerable<FilePropertiesRemote?> RegisterResCategorizedAssetsToHashSetAsync(this IAsyncEnumerable<PkgVersionProperties> assetEnumerable, List<FilePropertiesRemote> assetIndex, Dictionary<string, FilePropertiesRemote> hashSet, string baseLocalPath, string basePatchUrl, string baseResUrl)
115+
internal static async IAsyncEnumerable<FilePropertiesRemote?> RegisterResCategorizedAssetsToHashSetAsync(
116+
this IAsyncEnumerable<PkgVersionProperties> assetEnumerable,
117+
List<FilePropertiesRemote> assetIndex,
118+
Dictionary<string, FilePropertiesRemote>.AlternateLookup<ReadOnlySpan<char>> alternativeHashSet,
119+
string baseLocalPath,
120+
string basePatchUrl,
121+
string baseResUrl)
105122
{
106123
await foreach (PkgVersionProperties asset in assetEnumerable)
107124
{
108125
string baseLocalPathMerged = Path.Combine(baseLocalPath, asset.isPatch ? PersistentAssetsPath : StreamingAssetsPath);
109126

110-
yield return ReturnCategorizedYieldValue(hashSet, assetIndex, asset, baseLocalPathMerged, basePatchUrl, baseResUrl);
127+
yield return ReturnCategorizedYieldValue(alternativeHashSet, assetIndex, asset, baseLocalPathMerged, basePatchUrl, baseResUrl);
111128
}
112129
}
113130

114-
private static FilePropertiesRemote? ReturnCategorizedYieldValue(Dictionary<string, FilePropertiesRemote> hashSet, List<FilePropertiesRemote> assetIndex, PkgVersionProperties asset, string baseLocalPath, string baseUrl, string? alternativeUrlIfNonPatch = null)
131+
private static FilePropertiesRemote? ReturnCategorizedYieldValue(
132+
Dictionary<string, FilePropertiesRemote>.AlternateLookup<ReadOnlySpan<char>> alternativeHashSet,
133+
List<FilePropertiesRemote> assetIndex,
134+
PkgVersionProperties asset,
135+
string baseLocalPath,
136+
string baseUrl,
137+
string? alternativeUrlIfNonPatch)
115138
{
116139
FilePropertiesRemote asRemoteProperty = GetNormalizedFilePropertyTypeBased(
117140
asset.isPatch || string.IsNullOrEmpty(alternativeUrlIfNonPatch) ? baseUrl : alternativeUrlIfNonPatch,
@@ -136,22 +159,28 @@ internal static async IAsyncEnumerable<PkgVersionProperties> RegisterSleepyFileI
136159
return asRemoteProperty;
137160
}
138161

139-
string relTypeRelativePathStr = relTypeRelativePath.ToString();
140-
if (hashSet.TryAdd(relTypeRelativePathStr, asRemoteProperty) || !asset.isPatch)
162+
bool isAdded = alternativeHashSet.TryAdd(relTypeRelativePath, asRemoteProperty);
163+
if (isAdded || asset.isPatch)
141164
{
142165
return asRemoteProperty;
143166
}
144167

145-
FilePropertiesRemote existingValue = hashSet[relTypeRelativePathStr];
146-
int indexOf = assetIndex.IndexOf(existingValue);
147-
if (indexOf < -1)
168+
int indexOf;
169+
if (!alternativeHashSet.TryGetValue(relTypeRelativePath, out _, out FilePropertiesRemote? existingValue) || (indexOf = assetIndex.IndexOf(existingValue)) < -1)
170+
{
148171
return asRemoteProperty;
172+
}
173+
174+
// Check whether the Hash is the same. If yes, then we don't need to update the assetIndex
175+
if (existingValue.CRCArray.SequenceEqual(asRemoteProperty.CRCArray))
176+
{
177+
return null;
178+
}
149179

150-
assetIndex[indexOf] = asRemoteProperty;
151-
hashSet[relTypeRelativePathStr] = asRemoteProperty;
180+
assetIndex[indexOf] = asRemoteProperty;
181+
alternativeHashSet[relTypeRelativePath] = asRemoteProperty;
152182

153183
return null;
154-
155184
}
156185

157186
private static FilePropertiesRemote GetNormalizedFilePropertyTypeBased(string remoteParentURL,

0 commit comments

Comments
 (0)