Skip to content

Commit d4a49ee

Browse files
committed
fix(patch): Re-encode URI path fallback; add dir patch support
- Rewrite patch apply to use directory-level `krpdiff` per group - Add pre-flight resume support via `.preflight_state` file - Filter krpdiff downloads to only groups with mismatched files - Add CDN fallback download when source files are missing - Defer delete step until after patching (source files still needed) - Use `InstallProgressState.Install` instead of `Updating` (host recognizes it) - Track DownloadedBytes in verify/move loop for byte-level progress - Simplify URI fallback to single `ReEncodeUriPathSegments` helper - Add `ApplyDirPatch` wrapper in `HPatchZNative`
1 parent d8900c9 commit d4a49ee

3 files changed

Lines changed: 846 additions & 218 deletions

File tree

Hi3Helper.Plugin.Wuwa/Management/WuwaGameInstaller.Download.cs

Lines changed: 26 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -27,40 +27,18 @@ internal async Task TryDownloadWholeFileWithFallbacksAsync(Uri originalUri, stri
2727
SharedStatic.InstanceLogger.LogWarning("[WuwaGameInstaller::TryDownloadWholeFileWithFallbacksAsync] Primary download failed: {Uri}. Reason: {Msg}", originalUri, ex.Message);
2828
}
2929

30-
string encodedPath = EncodePathSegments(rawDest);
31-
32-
// Fallback 1: encoded concatenation using the Path portion of the original URI
30+
// Fallback 1: Re-encode each path segment of the original URI to handle special chars
3331
try
3432
{
35-
var basePath = originalUri.GetLeftPart(UriPartial.Path);
36-
string encodedConcatUrl = basePath.TrimEnd('/') + "/" + encodedPath;
37-
SharedStatic.InstanceLogger.LogDebug("[WuwaGameInstaller::TryDownloadWholeFileWithFallbacksAsync] Trying encoded concatenation fallback URI: {Uri}", encodedConcatUrl);
38-
Uri fallbackUri = new Uri(encodedConcatUrl, UriKind.Absolute);
33+
string reEncodedUrl = ReEncodeUriPathSegments(originalUri);
34+
SharedStatic.InstanceLogger.LogDebug("[WuwaGameInstaller::TryDownloadWholeFileWithFallbacksAsync] Trying re-encoded fallback URI: {Uri}", reEncodedUrl);
35+
Uri fallbackUri = new Uri(reEncodedUrl, UriKind.Absolute);
3936
await DownloadWholeFileAsync(fallbackUri, outputPath, token, progressCallback).ConfigureAwait(false);
4037
return;
4138
}
4239
catch (Exception ex) when (ex is HttpRequestException or IOException)
4340
{
44-
SharedStatic.InstanceLogger.LogWarning("[WuwaGameInstaller::TryDownloadWholeFileWithFallbacksAsync] Encoded concatenation fallback failed: {Msg}", ex.Message);
45-
}
46-
47-
// Fallback 2: try using a simple concatenation (encoded)
48-
try
49-
{
50-
var baseAuthority = originalUri.GetLeftPart(UriPartial.Authority);
51-
var baseDir = originalUri.AbsolutePath;
52-
int lastSlash = baseDir.LastIndexOf('/');
53-
if (lastSlash >= 0)
54-
baseDir = baseDir[..(lastSlash + 1)];
55-
string tryUrl = baseAuthority + baseDir + encodedPath;
56-
SharedStatic.InstanceLogger.LogDebug("[WuwaGameInstaller::TryDownloadWholeFileWithFallbacksAsync] Trying authority+dir fallback URI: {Uri}", tryUrl);
57-
Uri fallbackUri2 = new Uri(tryUrl, UriKind.Absolute);
58-
await DownloadWholeFileAsync(fallbackUri2, outputPath, token, progressCallback).ConfigureAwait(false);
59-
return;
60-
}
61-
catch (Exception ex) when (ex is HttpRequestException or IOException)
62-
{
63-
SharedStatic.InstanceLogger.LogWarning("[WuwaGameInstaller::TryDownloadWholeFileWithFallbacksAsync] Authority+dir fallback failed: {Msg}", ex.Message);
41+
SharedStatic.InstanceLogger.LogWarning("[WuwaGameInstaller::TryDownloadWholeFileWithFallbacksAsync] Re-encoded fallback failed: {Msg}", ex.Message);
6442
}
6543

6644
throw new HttpRequestException($"All download attempts failed for: {rawDest}");
@@ -79,40 +57,18 @@ internal async Task TryDownloadChunkedFileWithFallbacksAsync(Uri originalUri, st
7957
SharedStatic.InstanceLogger.LogWarning("[WuwaGameInstaller::TryDownloadChunkedFileWithFallbacksAsync] Primary chunked download failed: {Uri}. Reason: {Msg}", originalUri, ex.Message);
8058
}
8159

82-
string encodedPath = EncodePathSegments(rawDest);
83-
84-
// Fallback 1: encoded concatenation using the Path portion of the original URI
60+
// Fallback 1: Re-encode each path segment of the original URI to handle special chars
8561
try
8662
{
87-
var basePath = originalUri.GetLeftPart(UriPartial.Path);
88-
string encodedConcatUrl = basePath.TrimEnd('/') + "/" + encodedPath;
89-
SharedStatic.InstanceLogger.LogDebug("[WuwaGameInstaller::TryDownloadChunkedFileWithFallbacksAsync] Trying encoded concatenation fallback URI: {Uri}", encodedConcatUrl);
90-
Uri fallbackUri = new Uri(encodedConcatUrl, UriKind.Absolute);
63+
string reEncodedUrl = ReEncodeUriPathSegments(originalUri);
64+
SharedStatic.InstanceLogger.LogDebug("[WuwaGameInstaller::TryDownloadChunkedFileWithFallbacksAsync] Trying re-encoded fallback URI: {Uri}", reEncodedUrl);
65+
Uri fallbackUri = new Uri(reEncodedUrl, UriKind.Absolute);
9166
await DownloadChunkedFileAsync(fallbackUri, outputPath, chunkInfos, token, progressCallback).ConfigureAwait(false);
9267
return;
9368
}
9469
catch (Exception ex) when (ex is HttpRequestException or IOException)
9570
{
96-
SharedStatic.InstanceLogger.LogWarning("[WuwaGameInstaller::TryDownloadChunkedFileWithFallbacksAsync] Encoded concatenation fallback failed: {Msg}", ex.Message);
97-
}
98-
99-
// Fallback 2: authority+dir + encoded path
100-
try
101-
{
102-
var baseAuthority = originalUri.GetLeftPart(UriPartial.Authority);
103-
var baseDir = originalUri.AbsolutePath;
104-
int lastSlash = baseDir.LastIndexOf('/');
105-
if (lastSlash >= 0)
106-
baseDir = baseDir[..(lastSlash + 1)];
107-
string tryUrl = baseAuthority + baseDir + encodedPath;
108-
SharedStatic.InstanceLogger.LogDebug("[WuwaGameInstaller::TryDownloadChunkedFileWithFallbacksAsync] Trying authority+dir fallback URI: {Uri}", tryUrl);
109-
Uri fallbackUri2 = new Uri(tryUrl, UriKind.Absolute);
110-
await DownloadChunkedFileAsync(fallbackUri2, outputPath, chunkInfos, token, progressCallback).ConfigureAwait(false);
111-
return;
112-
}
113-
catch (Exception ex) when (ex is HttpRequestException or IOException)
114-
{
115-
SharedStatic.InstanceLogger.LogWarning("[WuwaGameInstaller::TryDownloadChunkedFileWithFallbacksAsync] Authority+dir fallback failed: {Msg}", ex.Message);
71+
SharedStatic.InstanceLogger.LogWarning("[WuwaGameInstaller::TryDownloadChunkedFileWithFallbacksAsync] Re-encoded fallback failed: {Msg}", ex.Message);
11672
}
11773

11874
throw new HttpRequestException($"All chunked download attempts failed for: {rawDest}");
@@ -126,6 +82,22 @@ internal static string EncodePathSegments(string path)
12682
return string.Join("/", parts.Select(Uri.EscapeDataString));
12783
}
12884

85+
/// <summary>
86+
/// Re-encodes each path segment of an existing URI. This handles cases where
87+
/// the original URI has un-encoded special characters (spaces, CJK, etc.)
88+
/// that cause the CDN to 404. Does NOT produce a doubled path — it only
89+
/// re-encodes what's already in the URI.
90+
/// </summary>
91+
internal static string ReEncodeUriPathSegments(Uri uri)
92+
{
93+
string authority = uri.GetLeftPart(UriPartial.Authority);
94+
// Use decoded AbsolutePath so we get the raw characters, then re-encode each segment
95+
string decodedPath = Uri.UnescapeDataString(uri.AbsolutePath);
96+
string[] segments = decodedPath.Split('/', StringSplitOptions.RemoveEmptyEntries);
97+
string encodedPath = string.Join("/", segments.Select(Uri.EscapeDataString));
98+
return $"{authority}/{encodedPath}";
99+
}
100+
129101
internal async Task DownloadWholeFileAsync(Uri uri, string outputPath, CancellationToken token, Action<long>? progressCallback)
130102
{
131103
string tempPath = outputPath + ".tmp";

0 commit comments

Comments
 (0)