mirror of
https://github.com/JuniorDark/RustyHearts-Launcher.git
synced 2026-05-07 05:21:44 -04:00
Version 1.2.0
This commit is contained in:
parent
9b3a0e00a2
commit
e74d93fab9
83 changed files with 110087 additions and 47507 deletions
223
RHLauncher.Http/ClientDownloader.cs
Normal file
223
RHLauncher.Http/ClientDownloader.cs
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
using Ionic.Zip;
|
||||
using RHLauncher.RHLauncher.Helper;
|
||||
using RHLauncher.RHLauncher.i8n;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
|
||||
namespace RHLauncher.RHLauncher.Http
|
||||
{
|
||||
public class ClientDownloader
|
||||
{
|
||||
public enum DownloadClientResultStatus
|
||||
{
|
||||
Success,
|
||||
Failed,
|
||||
Cancelled
|
||||
}
|
||||
|
||||
public class DownloadClientResult
|
||||
{
|
||||
public DownloadClientResultStatus Status { get; set; }
|
||||
public string? InstallDirectoryPath { get; set; }
|
||||
}
|
||||
|
||||
private const int BufferSize = 8192;
|
||||
private const int TimeoutSeconds = 30;
|
||||
|
||||
public static async Task<DownloadClientResult> DownloadClientAsync(string installDirectory, IProgress<ProgressReport> progress, CancellationToken cancellationToken)
|
||||
{
|
||||
using HttpClientHandler handler = new() { AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate };
|
||||
using HttpClient client = new(handler, true) { Timeout = TimeSpan.FromSeconds(TimeoutSeconds) };
|
||||
|
||||
try
|
||||
{
|
||||
string filelistUrl = Configuration.Default.ClientPartsListUrl;
|
||||
using HttpResponseMessage filelistResponse = await client.GetAsync(filelistUrl, cancellationToken);
|
||||
|
||||
if (!filelistResponse.IsSuccessStatusCode)
|
||||
{
|
||||
// Handle the case where the filelist URL is not found
|
||||
string errorMessage = $"{LocalizedStrings.ClientDownloadErrorMessage}:\n\n{LocalizedStrings.ClientDownloadFilelistError}: {filelistUrl}.\n\n {LocalizedStrings.Error}: {filelistResponse.ReasonPhrase}";
|
||||
MsgBoxForm.Show($" {LocalizedStrings.Error}: {errorMessage}", LocalizedStrings.Error);
|
||||
Logger.WriteLog(errorMessage);
|
||||
return new DownloadClientResult { Status = DownloadClientResultStatus.Failed };
|
||||
}
|
||||
string filelistText = await client.GetStringAsync(filelistUrl, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
string downloadDir = Path.Combine(installDirectory, "rhclient_download");
|
||||
if (!Directory.Exists(downloadDir))
|
||||
{
|
||||
Directory.CreateDirectory(downloadDir);
|
||||
}
|
||||
|
||||
List<string> filesToBeDownloaded = new();
|
||||
long totalBytesToDownload = 0L;
|
||||
|
||||
List<string> lines = filelistText.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||
int totalLines = lines.Count;
|
||||
int checkedFiles = 0;
|
||||
|
||||
foreach (string line in lines)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
string[] parts = line.Split(' ');
|
||||
if (parts.Length != 3)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string fileName = parts[0];
|
||||
long fileSize = long.Parse(parts[1]);
|
||||
ulong fileHashFromServer = ulong.Parse(parts[2], NumberStyles.HexNumber, CultureInfo.InvariantCulture);
|
||||
|
||||
string filePath = Path.Combine(downloadDir, fileName);
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
string existingFileHash = Crc32.GetFileHash(filePath);
|
||||
if (existingFileHash.Equals(fileHashFromServer.ToString("X8"), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
checkedFiles++;
|
||||
ProgressReporter.ReportFileCheckingProgress(progress, checkedFiles, totalLines, cancellationToken);
|
||||
continue; // Skip this file as it already exists and the hash matches
|
||||
}
|
||||
}
|
||||
|
||||
filesToBeDownloaded.Add(fileName);
|
||||
totalBytesToDownload += fileSize;
|
||||
checkedFiles++;
|
||||
ProgressReporter.ReportFileCheckingProgress(progress, checkedFiles, totalLines, cancellationToken);
|
||||
}
|
||||
|
||||
Stopwatch sw = Stopwatch.StartNew();
|
||||
long downloadedBytes = 0L;
|
||||
|
||||
foreach (string fileName in filesToBeDownloaded)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
string fileUrl = $"{Configuration.Default.DownloadClientPartUrl}/{fileName}";
|
||||
string filePath = Path.Combine(downloadDir, fileName);
|
||||
|
||||
using HttpResponseMessage response = await client.GetAsync(fileUrl, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
await using Stream contentStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||
await using FileStream fileStream = new(filePath, FileMode.Create, FileAccess.Write, FileShare.None, BufferSize, true);
|
||||
|
||||
byte[] buffer = new byte[BufferSize];
|
||||
int bytesRead;
|
||||
while ((bytesRead = await contentStream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) > 0)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
await fileStream.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken).ConfigureAwait(false);
|
||||
|
||||
downloadedBytes += bytesRead;
|
||||
ProgressReporter.ReportClientDownloadProgress(progress, downloadedBytes, totalBytesToDownload, sw, cancellationToken);
|
||||
}
|
||||
}
|
||||
ProgressReporter.ReportFinishedProgress(progress, cancellationToken);
|
||||
string rhExePath = await UnzipFilesAsync(downloadDir, installDirectory, progress, cancellationToken);
|
||||
ProgressReporter.ReportFinishedProgress(progress, cancellationToken);
|
||||
return new DownloadClientResult
|
||||
{
|
||||
Status = DownloadClientResultStatus.Success,
|
||||
InstallDirectoryPath = rhExePath
|
||||
};
|
||||
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
Logger.WriteLog($"Download cancelled: {ex.Message}");
|
||||
return new DownloadClientResult { Status = DownloadClientResultStatus.Cancelled };
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ProgressReporter.ReportErrorProgress(progress, ex.Message, cancellationToken);
|
||||
string errorMessage = LocalizedStrings.ClientDownloadError + "\n" + ex.Message;
|
||||
string errorLog = LocalizedStrings.ClientDownloadError + ex.Message + ex.StackTrace;
|
||||
Exception newEx = new(errorMessage, ex);
|
||||
Exception newLogEx = new(errorLog, ex);
|
||||
ExceptionHandler.HandleException(newEx, newLogEx);
|
||||
return new DownloadClientResult { Status = DownloadClientResultStatus.Failed };
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<string> UnzipFilesAsync(string sourceDirectory, string destinationDirectory, IProgress<ProgressReport> progress, CancellationToken cancellationToken)
|
||||
{
|
||||
string[] fileEntries = Directory.GetFiles(sourceDirectory, "*.zip.*").OrderBy(f => f).ToArray();
|
||||
string rhExeDirectoryPath = "";
|
||||
|
||||
// Calculate total uncompressed size of all files in all zip archives
|
||||
long totalUncompressedSize = fileEntries.Sum(file =>
|
||||
{
|
||||
using ZipFile zip = ZipFile.Read(file);
|
||||
return zip.Entries.Sum(entry => entry.UncompressedSize);
|
||||
});
|
||||
long totalExtracted = 0;
|
||||
|
||||
try
|
||||
{
|
||||
foreach (string file in fileEntries)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
using ZipFile zip = ZipFile.Read(file);
|
||||
foreach (var entry in zip)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
string destinationPath = Path.Combine(destinationDirectory, entry.FileName);
|
||||
string? directoryPath = Path.GetDirectoryName(destinationPath);
|
||||
if (!string.IsNullOrEmpty(directoryPath) && !Directory.Exists(directoryPath))
|
||||
{
|
||||
Directory.CreateDirectory(directoryPath);
|
||||
}
|
||||
|
||||
entry.Extract(destinationDirectory, ExtractExistingFileAction.OverwriteSilently);
|
||||
totalExtracted += entry.UncompressedSize;
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
ProgressReporter.ReportUnzipProgress(progress, totalExtracted, totalUncompressedSize, cancellationToken);
|
||||
|
||||
// Check if the current entry is "rustyhearts.exe" and store its directory path
|
||||
if (entry.FileName.EndsWith("rustyhearts.exe", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
rhExeDirectoryPath = Path.GetDirectoryName(destinationPath) ?? "";
|
||||
}
|
||||
}
|
||||
}, cancellationToken);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(sourceDirectory))
|
||||
{
|
||||
Directory.Delete(sourceDirectory, true);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidOperationException($"{LocalizedStrings.ClientFolderErrorMessage}", ex);
|
||||
}
|
||||
|
||||
// Check if 'rustyhearts.exe' directory exists
|
||||
if (!string.IsNullOrWhiteSpace(rhExeDirectoryPath) && Directory.Exists(rhExeDirectoryPath))
|
||||
{
|
||||
return rhExeDirectoryPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FileNotFoundException($"{LocalizedStrings.ClientFolderExeErrorMessage}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
69
RHLauncher.Http/DownloadHelper.cs
Normal file
69
RHLauncher.Http/DownloadHelper.cs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
using RHLauncher.RHLauncher.Helper;
|
||||
using System.Net;
|
||||
|
||||
namespace RHLauncher.RHLauncher.Http
|
||||
{
|
||||
public class DownloadHelper
|
||||
{
|
||||
public static async Task<long> GetFileSizeAsync(HttpClient client, string fileUrl, CancellationToken cancellationToken)
|
||||
{
|
||||
using HttpRequestMessage request = new(HttpMethod.Get, fileUrl);
|
||||
|
||||
try
|
||||
{
|
||||
using HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return response.Content.Headers.ContentLength ?? throw new Exception("Content-Length header not found in response headers.");
|
||||
}
|
||||
else if (response.StatusCode == HttpStatusCode.NotFound)
|
||||
{
|
||||
Logger.WriteLog($"File not found: {fileUrl}");
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.WriteLog($"Error getting file size for {fileUrl}: {response.StatusCode}");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
|
||||
{
|
||||
Logger.WriteLog($"File not found: {fileUrl}");
|
||||
return 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.WriteLog($"Error getting file size for {fileUrl}: {ex.Message}");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static string FormatDownloadSpeed(double totalDownloadSpeed)
|
||||
{
|
||||
string[] sizes = { "B/s", "KB/s", "MB/s", "GB/s" };
|
||||
int order = 0;
|
||||
while (totalDownloadSpeed >= 1024 && order < sizes.Length - 1)
|
||||
{
|
||||
order++;
|
||||
totalDownloadSpeed /= 1024;
|
||||
}
|
||||
|
||||
return $"{totalDownloadSpeed:0.#} {sizes[order]}";
|
||||
}
|
||||
|
||||
public static string FormatFileSize(long bytes)
|
||||
{
|
||||
string[] sizes = { "B", "KB", "MB", "GB" };
|
||||
int order = 0;
|
||||
while (bytes >= 1024 && order < sizes.Length - 1)
|
||||
{
|
||||
order++;
|
||||
bytes /= 1024;
|
||||
}
|
||||
|
||||
return $"{bytes:0.#} {sizes[order]}";
|
||||
}
|
||||
}
|
||||
}
|
||||
118
RHLauncher.Http/LauncherUpdater.cs
Normal file
118
RHLauncher.Http/LauncherUpdater.cs
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
using Newtonsoft.Json;
|
||||
using RHLauncher.RHLauncher.Helper;
|
||||
using RHLauncher.RHLauncher.i8n;
|
||||
using System.IO.Compression;
|
||||
using System.Text;
|
||||
|
||||
namespace RHLauncher.RHLauncher.Http
|
||||
{
|
||||
public class LauncherUpdater
|
||||
{
|
||||
private readonly string LauncherVersionUrl = Configuration.Default.GetLauncherVersion;
|
||||
private readonly string LauncherUpdateUrl = Configuration.Default.UpdateLauncherVersion;
|
||||
|
||||
public class LauncherVersion
|
||||
{
|
||||
public string? Version { get; set; }
|
||||
}
|
||||
|
||||
public async Task CheckForLauncherUpdateAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
using HttpClient client = new();
|
||||
using HttpResponseMessage response = await client.GetAsync(LauncherVersionUrl);
|
||||
response.EnsureSuccessStatusCode();
|
||||
string json = await response.Content.ReadAsStringAsync();
|
||||
|
||||
var result = JsonConvert.DeserializeObject<LauncherVersion>(json);
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
string? newVersion = result.Version;
|
||||
|
||||
if (!string.IsNullOrEmpty(newVersion))
|
||||
{
|
||||
string currentVersion = GetLauncherVersion.GetVersion();
|
||||
if (!string.IsNullOrEmpty(currentVersion) && !currentVersion.Equals(newVersion, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string updateMessage = $"{LocalizedStrings.LauncherUpdateText}\n\n" +
|
||||
$"{LocalizedStrings.Version}: {newVersion}";
|
||||
|
||||
MsgBoxForm.Show(updateMessage, LocalizedStrings.Info);
|
||||
await DownloadLauncherUpdateAsync(json);
|
||||
}
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HandleLauncherUpdateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DownloadLauncherUpdateAsync(string version)
|
||||
{
|
||||
try
|
||||
{
|
||||
using HttpClient client = new();
|
||||
using StringContent content = new(version, Encoding.UTF8, "application/json");
|
||||
|
||||
var result = JsonConvert.DeserializeObject<LauncherVersion>(version);
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
string? v = result.Version;
|
||||
|
||||
if (!string.IsNullOrEmpty(version))
|
||||
{
|
||||
|
||||
using HttpResponseMessage response = await client.PostAsync(LauncherUpdateUrl, content);
|
||||
response.EnsureSuccessStatusCode();
|
||||
byte[] updateBytes = await response.Content.ReadAsByteArrayAsync();
|
||||
string tempFilePath = Path.Combine(Path.GetTempPath(), $"launcher_update.zip");
|
||||
File.WriteAllBytes(tempFilePath, updateBytes);
|
||||
|
||||
string executablePath = AppDomain.CurrentDomain.BaseDirectory;
|
||||
string backupFilePath = Path.Combine(executablePath, $"Launcher_old.exe");
|
||||
string launcherExePath = Path.Combine(executablePath, "Launcher.exe");
|
||||
|
||||
if (File.Exists(backupFilePath))
|
||||
{
|
||||
File.Delete(backupFilePath);
|
||||
}
|
||||
|
||||
// Move existing launcher.exe to temporary location
|
||||
File.Move(launcherExePath, backupFilePath);
|
||||
|
||||
// Extract new update files
|
||||
ZipFile.ExtractToDirectory(tempFilePath, executablePath, true);
|
||||
|
||||
// Delete temporary zip file
|
||||
File.Delete(tempFilePath);
|
||||
|
||||
MsgBoxForm.Show(LocalizedStrings.LauncherUpdateSuccess + v, LocalizedStrings.Info);
|
||||
await Task.Delay(500).ContinueWith(_ =>
|
||||
{
|
||||
Application.Restart();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HandleLauncherUpdateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandleLauncherUpdateException(Exception ex)
|
||||
{
|
||||
string errorMessage = LocalizedStrings.LauncherUpdateFailed + ex.Message;
|
||||
string errorLog = LocalizedStrings.LauncherUpdateFailed + ex.Message + ex.StackTrace;
|
||||
Exception newEx = new(errorMessage, ex);
|
||||
Exception newLogEx = new(errorLog, ex);
|
||||
ExceptionHandler.HandleException(newEx, newLogEx);
|
||||
}
|
||||
}
|
||||
}
|
||||
212
RHLauncher.Http/UpdateDownloader.cs
Normal file
212
RHLauncher.Http/UpdateDownloader.cs
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
using RHLauncher.RHLauncher.Helper;
|
||||
using RHLauncher.RHLauncher.i8n;
|
||||
using RHLauncher.RHLauncher.PCK;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
|
||||
namespace RHLauncher.RHLauncher.Http
|
||||
{
|
||||
public class UpdateDownloader
|
||||
{
|
||||
public enum UpdateState
|
||||
{
|
||||
UpdateAvailable,
|
||||
NoUpdateAvailable,
|
||||
Error
|
||||
}
|
||||
|
||||
private const int BufferSize = 8192;
|
||||
private const int TimeoutSeconds = 30;
|
||||
|
||||
public static async Task<UpdateState> CheckForUpdatesAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
using var client = new HttpClient();
|
||||
|
||||
string fileListUrl = Configuration.Default.FileListUrl;
|
||||
|
||||
using HttpResponseMessage filelistResponse = await client.GetAsync(fileListUrl);
|
||||
|
||||
if (!filelistResponse.IsSuccessStatusCode)
|
||||
{
|
||||
// Handle the case where the filelist URL is not found
|
||||
return UpdateState.Error;
|
||||
}
|
||||
|
||||
using var response = await client.GetAsync(fileListUrl);
|
||||
using var content = await response.Content.ReadAsStreamAsync();
|
||||
using var reader = new StreamReader(content);
|
||||
string filelistText = await reader.ReadToEndAsync();
|
||||
|
||||
List<PCKFile> fileList = PCKReader.GetPCKFileList();
|
||||
Dictionary<string, uint> fileHashes = fileList.ToDictionary(f => f.Name, f => f.Hash);
|
||||
|
||||
List<string> filesToUpdate = new();
|
||||
|
||||
foreach (string line in filelistText.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
string[] parts = line.Split(' ');
|
||||
if (parts.Length != 3)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string fileName = parts[0];
|
||||
if (fileName.Contains(' '))
|
||||
{
|
||||
fileName = $"\"{fileName}\"";
|
||||
}
|
||||
|
||||
if (!ulong.TryParse(parts[2], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong fileHashFromServer))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!fileHashes.TryGetValue(fileName, out uint fileHash) || fileHash != fileHashFromServer)
|
||||
{
|
||||
filesToUpdate.Add(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
fileHashes.Clear();
|
||||
|
||||
return filesToUpdate.Count > 0 ? UpdateState.UpdateAvailable : UpdateState.NoUpdateAvailable;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string errorMessage = LocalizedStrings.UpdateCheckError + "\n" + ex.Message;
|
||||
string errorLog = LocalizedStrings.UpdateCheckError + ex.Message + ex.StackTrace;
|
||||
Exception newEx = new(errorMessage, ex);
|
||||
Exception newLogEx = new(errorLog, ex);
|
||||
ExceptionHandler.HandleException(newEx, newLogEx);
|
||||
|
||||
return UpdateState.Error;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task DownloadUpdatesAsync(string installDirectory, IProgress<ProgressReport> progress, CancellationToken cancellationToken)
|
||||
{
|
||||
using HttpClientHandler handler = new() { AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate };
|
||||
using HttpClient client = new(handler, true) { Timeout = TimeSpan.FromSeconds(TimeoutSeconds) };
|
||||
|
||||
try
|
||||
{
|
||||
string archiveDir = Path.Combine(installDirectory, "archive");
|
||||
if (!Directory.Exists(archiveDir))
|
||||
{
|
||||
Directory.CreateDirectory(archiveDir);
|
||||
}
|
||||
|
||||
string filelistUrl = Configuration.Default.FileListUrl;
|
||||
string filelistText = await client.GetStringAsync(filelistUrl, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
List<PCKFile> fileList = PCKReader.GetPCKFileList();
|
||||
Dictionary<string, uint> fileHashes = fileList.ToDictionary(f => f.Name, f => f.Hash);
|
||||
|
||||
HashSet<string> localFileNames = new(fileHashes.Keys);
|
||||
|
||||
List<string> filesToBeDownloaded = new();
|
||||
long totalBytesToDownload = 0L;
|
||||
int totalFilesToDownload = 0;
|
||||
|
||||
List<string> lines = filelistText.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||
int totalLines = lines.Count;
|
||||
|
||||
for (int i = 0; i < totalLines; i++)
|
||||
{
|
||||
string line = lines[i];
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
string[] parts = line.Split(' ');
|
||||
if (parts.Length != 3)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string fileName = parts[0];
|
||||
if (string.IsNullOrWhiteSpace(parts[0]))
|
||||
{
|
||||
fileName = $"\"{fileName}\"";
|
||||
}
|
||||
|
||||
if (!ulong.TryParse(parts[2], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong fileHashFromServer))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!fileHashes.TryGetValue(fileName, out uint fileHash) || fileHash != fileHashFromServer)
|
||||
{
|
||||
filesToBeDownloaded.Add(fileName);
|
||||
long fileSize = await DownloadHelper.GetFileSizeAsync(client, $"{Configuration.Default.DownloadUpdateFileUrl}/{fileName}.mip", cancellationToken).ConfigureAwait(false);
|
||||
totalBytesToDownload += fileSize;
|
||||
totalFilesToDownload++;
|
||||
}
|
||||
|
||||
ProgressReporter.ReportFileListCheckProgress(progress, i + 1, totalLines, cancellationToken);
|
||||
}
|
||||
|
||||
Stopwatch sw = Stopwatch.StartNew();
|
||||
|
||||
long downloadedBytes = 0L;
|
||||
int downloadedSize = 0;
|
||||
int downloadedFileCount = 0;
|
||||
foreach (string fileName in filesToBeDownloaded)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
string finalfileName = fileName + ".mip";
|
||||
string fileUrl = $"{Configuration.Default.DownloadUpdateFileUrl}/{finalfileName}";
|
||||
string filePath = Path.Combine(archiveDir, finalfileName);
|
||||
|
||||
using HttpResponseMessage response = await client.GetAsync(fileUrl, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
await using Stream contentStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
string? directoryPath = Path.GetDirectoryName(filePath);
|
||||
|
||||
if (!string.IsNullOrEmpty(directoryPath) && !Directory.Exists(directoryPath))
|
||||
{
|
||||
Directory.CreateDirectory(directoryPath);
|
||||
}
|
||||
|
||||
await using FileStream fileStream = new(filePath, FileMode.Create, FileAccess.Write, FileShare.None, BufferSize, true);
|
||||
byte[] buffer = new byte[BufferSize];
|
||||
int bytesRead;
|
||||
while ((bytesRead = await contentStream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) > 0)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
await fileStream.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken).ConfigureAwait(false);
|
||||
|
||||
downloadedBytes += bytesRead;
|
||||
downloadedSize += bytesRead;
|
||||
}
|
||||
|
||||
ProgressReporter.ReportDownloadProgress(progress, finalfileName, downloadedSize, totalBytesToDownload, downloadedFileCount, totalFilesToDownload, sw, cancellationToken);
|
||||
downloadedFileCount++;
|
||||
}
|
||||
fileHashes.Clear();
|
||||
|
||||
ProgressReporter.ReportFinishedProgress(progress, cancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
Logger.WriteLog($"Update download cancelled: {ex.Message}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ProgressReporter.ReportErrorProgress(progress, ex.Message, cancellationToken);
|
||||
|
||||
string errorMessage = LocalizedStrings.UpdateDownloadError + "\n" + ex.Message;
|
||||
string errorLog = LocalizedStrings.UpdateDownloadError + ex.Message + ex.StackTrace;
|
||||
Exception newEx = new(errorMessage, ex);
|
||||
Exception newLogEx = new(errorLog, ex);
|
||||
ExceptionHandler.HandleException(newEx, newLogEx);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue