diff --git a/BenchmarkLab/DownloadTool.cs b/BenchmarkLab/DownloadTool.cs new file mode 100644 index 000000000..37a6b7f13 --- /dev/null +++ b/BenchmarkLab/DownloadTool.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; + +namespace BenchmarkLab +{ + public class DownloadTool + { + long bufflength = 262144; + HttpClient httpClient = new HttpClient(new HttpClientHandler { AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip }); + Stream localStream; + Stream remoteStream; + + public async Task Dunlud(string inp, string outp) + { + long ExistingLength = 0; + int byteSize; + localStream = new FileStream(outp, FileMode.Create, FileAccess.Write); + + var request = new HttpRequestMessage { RequestUri = new Uri(inp) }; + request.Headers.Range = new RangeHeaderValue(ExistingLength, null); + + byte[] buffer = new byte[bufflength]; + + using (var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead)) + { + Console.WriteLine($"{response.Content.Headers.ContentLength ?? 0}"); + using (remoteStream = await response.Content.ReadAsStreamAsync()) + { + while ((byteSize = await remoteStream.ReadAsync(buffer, 0, buffer.Length)) > 0) + { + await localStream.WriteAsync(buffer, 0, byteSize); + } + } + } + + localStream.Dispose(); + remoteStream.Dispose(); + + return true; + } + } +} diff --git a/BenchmarkLab/Program.cs b/BenchmarkLab/Program.cs index 7653c7f77..cad28f5f9 100644 --- a/BenchmarkLab/Program.cs +++ b/BenchmarkLab/Program.cs @@ -6,6 +6,8 @@ using BenchmarkDotNet.Order; using System.IO; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Force.Crc32; namespace BenchmarkLab @@ -24,6 +26,13 @@ static void Main(string[] args) Console.WriteLine(new Tool().Digits_FindCountWithPrec(12345678.87654)); */ + DownloadTool tool = new(); + Task a = tool.Dunlud( + @"https://hk-bigfile-os-mihayo.akamaized.net/com.miHoYo.bh3oversea/StreamingAsb/5_1_0/android/HD/asb/Blocks_5_1.xmf", + @"C:\Users\neon-nyan\AppData\LocalLow\miHoYo\Honkai Impact 3\test.xmf"); + + Console.ReadLine(); + /* CRCTest a = new CRCTest(); byte[] data = File.ReadAllBytes(@"C:\Users\neon-nyan\Downloads\yoimiya_ayaka.png"); FileStream stream = new FileStream(@"C:\Users\neon-nyan\Downloads\yoimiya_ayaka.png", FileMode.Open, FileAccess.Read); @@ -36,6 +45,7 @@ static void Main(string[] args) Console.WriteLine(hash); BenchmarkRunner.Run(); + */ } } diff --git a/Hi3HelperGUI/Classes/Data/Tools/ConverterTool.cs b/Hi3HelperGUI/Classes/Data/Tools/ConverterTool.cs index 8bf7ab553..1690a721c 100644 --- a/Hi3HelperGUI/Classes/Data/Tools/ConverterTool.cs +++ b/Hi3HelperGUI/Classes/Data/Tools/ConverterTool.cs @@ -30,6 +30,8 @@ public class ConverterTool (value & 0x000000FFU) << 24 | (value & 0x0000FF00U) << 8 | (value & 0x00FF0000U) >> 8 | (value & 0xFF000000U) >> 24; + public static double GetPercentageNumber(double cur, double max, int round = 2) => Math.Round((100 * cur) / max, round); + public static ushort ToUInt16Big(ushort value) => (ushort)((value & 0xFFU) << 8 | (value & 0xFF00U) >> 8); public static string BytesToMD5(byte[] stream) => BytesToHex(MD5.Create().ComputeHash(stream)); diff --git a/Hi3HelperGUI/Classes/Data/Tools/DownloadTool.cs b/Hi3HelperGUI/Classes/Data/Tools/DownloadTool.cs index 15cea6035..624156ed4 100644 --- a/Hi3HelperGUI/Classes/Data/Tools/DownloadTool.cs +++ b/Hi3HelperGUI/Classes/Data/Tools/DownloadTool.cs @@ -7,6 +7,9 @@ using System.Net.Http.Headers; #endif using System.Diagnostics; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Threading; using System.Threading; using System.Threading.Tasks; using Hi3HelperGUI; @@ -14,6 +17,8 @@ using static Hi3HelperGUI.Logger; using static Hi3HelperGUI.Data.ConverterTool; +using static Hi3HelperGUI.MainWindow; + namespace Hi3HelperGUI.Data { public class DownloadStatusChangedEventArgs : EventArgs @@ -39,39 +44,54 @@ public class DownloadProgressChangedEventArgs : EventArgs get; set; } } + + public class DownloadProgressCompletedEventArgs : EventArgs + { + public bool DownloadCompleted { get; set; } + } + public class DownloadUtils { public event EventHandler DownloadStatusChanged; public event EventHandler DownloadProgressChanged; - public event EventHandler DownloadCompleted; + public event EventHandler DownloadCompleted; public string userAgent = "Dalvik/2.1.0 (Linux; U; Android 11; M2012K11AG Build/RKQ1.200826.002)"; // By default, stop is true public bool stop = true; /* Declare download buffer - * by default: 2 KiB (262144 bytes) + * by default: 256 KiB (262144 bytes) */ long bufflength = 262144; + HttpWebRequest request; -#if (!NETFRAMEWORK) - HttpClient httpClient = new HttpClient(); +#if (NETCOREAPP) + HttpClient httpClient; #endif - - HttpWebRequest request; DownloadStatusChangedEventArgs downloadStatusArgs; Stream localStream; Stream remoteStream; - /* TODO: - * Change from HttpWebRequest to HttpClient - */ + public DownloadUtils() + { +#if (NETCOREAPP) + httpClient = new HttpClient( + new HttpClientHandler + { + AutomaticDecompression = DecompressionMethods.All, + UseCookies = true, + MaxConnectionsPerServer = 256, + AllowAutoRedirect = true, + }); +#endif + } -#if (!NETFRAMEWORK) - public async Task DownloadFileAsyncNew(string DownloadLink, string path, CancellationToken token) + public async Task DownloadFileAsyncHTTP(string DownloadLink, string path, long startOffset = -1, long endOffset = -1, CancellationToken token = new CancellationToken()) { - var downloadStatusArgs = new DownloadStatusChangedEventArgs(); + bool ret = true; + downloadStatusArgs = new DownloadStatusChangedEventArgs(); long ExistingLength = 0; bool downloadResumable; @@ -81,14 +101,18 @@ public async Task DownloadFileAsyncNew(string DownloadLink, string path, C ExistingLength = fileInfo.Length; } - Console.WriteLine("Getting Request..."); - try { - using (HttpResponseMessage message = await httpClient.GetAsync(DownloadLink, HttpCompletionOption.ResponseHeadersRead, token)) + var request = new HttpRequestMessage { RequestUri = new Uri(DownloadLink) }; + + if (startOffset != -1 && endOffset != -1) + request.Headers.Range = new RangeHeaderValue(startOffset, endOffset); + else + request.Headers.Range = new RangeHeaderValue(ExistingLength, null); + + using (HttpResponseMessage message = await httpClient.SendAsync(new HttpRequestMessage { RequestUri = new Uri(DownloadLink) }, HttpCompletionOption.ResponseHeadersRead, token)) { - message.Content.Headers.ContentRange = new ContentRangeHeaderValue(ExistingLength); - long FileSize = ExistingLength + message.Content.Headers.ContentLength ?? 0; + long ContentLength = ExistingLength + message.Content.Headers.ContentLength ?? 0; if ((int)message.StatusCode == 206) { @@ -107,70 +131,72 @@ public async Task DownloadFileAsyncNew(string DownloadLink, string path, C localStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite); } - using (remoteStream = await message.Content.ReadAsStreamAsync(token).ConfigureAwait(false)) - { - int byteSize = 0; - long totalReceived = byteSize + ExistingLength; - var sw = new Stopwatch(); - sw.Start(); - - byte[] downBuffer = new byte[bufflength]; - while ((byteSize = await remoteStream.ReadAsync(downBuffer, 0, downBuffer.Length, token).ConfigureAwait(false)) > 0) - { - Console.WriteLine(byteSize); - await localStream.WriteAsync(downBuffer, 0, byteSize, token).ConfigureAwait(false); - totalReceived += byteSize; - - var args = new DownloadProgressChangedEventArgs(); - args.CurrentReceivedBytes = byteSize; - args.BytesReceived = totalReceived; - args.TotalBytesToReceive = FileSize; - float currentSpeed = totalReceived / (float)sw.Elapsed.TotalSeconds; - args.CurrentSpeed = currentSpeed; - if (downloadResumable == true) - { - args.ProgressPercentage = ((float)totalReceived / (float)FileSize) * 100; - long bytesRemainingtoBeReceived = FileSize - totalReceived; - args.TimeLeft = (long)(bytesRemainingtoBeReceived / currentSpeed); - } - else - { - args.ProgressPercentage = 0; - args.TimeLeft = 0; - } - - OnDownloadProgressChanged(args); - } - sw.Stop(); - } - var completedArgs = new EventArgs(); - OnDownloadCompleted(completedArgs); + await ReadRemoteResponseAsyncHTTP(message.Content, localStream, ExistingLength, ContentLength, downloadResumable, token); + message.Dispose(); } + OnDownloadCompleted(new DownloadProgressCompletedEventArgs()); } - catch + catch (WebException e) { - + ret = ThrowWebExceptionAsBool(e); + } + catch (TaskCanceledException e) + { + ret = true; + throw new TaskCanceledException(e.ToString()); + } + catch (Exception e) + { + LogWriteLine($"An error occured while downloading {Path.GetFileName(path)}\r\nTraceback: {e}", LogType.Error, true); + ret = false; } finally { + localStream?.Dispose(); + remoteStream?.Dispose(); } - return true; + return ret; } -#endif - public async Task DownloadFileAsync(string DownloadLink, string path, CancellationToken token, long startOffset = -1, long endOffset = -1) + public async Task ReadRemoteResponseAsyncHTTP(HttpContent response, Stream localStream, long ExistingLength, long ContentLength, bool downloadResumable, CancellationToken token) + { + using (remoteStream = await response.ReadAsStreamAsync(token).ConfigureAwait(false)) + { + int byteSize = 0; + long totalReceived = byteSize + ExistingLength; + var sw = new Stopwatch(); + sw.Start(); + float currentSpeed; + long bytesRemainingtoBeReceived; + byte[] downBuffer = new byte[bufflength]; + while ((byteSize = await remoteStream.ReadAsync(downBuffer, 0, downBuffer.Length, token).ConfigureAwait(false)) > 0) + { + await localStream.WriteAsync(downBuffer, 0, byteSize, token).ConfigureAwait(false); + totalReceived += byteSize; + currentSpeed = totalReceived / (float)sw.Elapsed.TotalSeconds; + bytesRemainingtoBeReceived = ContentLength - totalReceived; + + OnDownloadProgressChanged(new DownloadProgressChangedEventArgs() + { + CurrentReceivedBytes = byteSize, + BytesReceived = totalReceived, + TotalBytesToReceive = ContentLength, + CurrentSpeed = currentSpeed, + ProgressPercentage = downloadResumable ? (((float)totalReceived / (float)ContentLength) * 100) : 0, + TimeLeft = downloadResumable ? ((long)(bytesRemainingtoBeReceived / currentSpeed)) : 0 + }); + } + sw.Stop(); + } + } + + public async Task DownloadFileAsync(string DownloadLink, string path, long startOffset = -1, long endOffset = -1, CancellationToken token = new CancellationToken()) { bool ret = true; downloadStatusArgs = new DownloadStatusChangedEventArgs(); - long ExistingLength = 0; - - if (File.Exists(path)) - { - FileInfo fileInfo = new FileInfo(path); - ExistingLength = fileInfo.Length; - } + long ExistingLength = File.Exists(path) ? new FileInfo(path).Length : 0; request = (HttpWebRequest)WebRequest.Create(DownloadLink); @@ -183,41 +209,30 @@ public async Task DownloadFileAsync(string DownloadLink, string path, Canc try { - using (HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync().ConfigureAwait(false)) + using (HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync()) { long ContentLength = ExistingLength + response.ContentLength; - bool downloadResumable; if ((int)response.StatusCode == 206) { - downloadResumable = true; - localStream = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite); + downloadStatusArgs.ResumeSupported = true; } else { ExistingLength = 0; - downloadResumable = false; - localStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite); + downloadStatusArgs.ResumeSupported = false; } - downloadStatusArgs.ResumeSupported = downloadResumable; + + localStream = new FileStream(path, downloadStatusArgs.ResumeSupported ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.ReadWrite); OnDownloadStatusChanged(downloadStatusArgs); - await ReadRemoteResponse(response, localStream, ExistingLength, ContentLength, downloadResumable, token).ConfigureAwait(false); + await ReadRemoteResponse(response, localStream, ExistingLength, ContentLength, downloadStatusArgs.ResumeSupported, token).ConfigureAwait(false); } - OnDownloadCompleted(new EventArgs()); + OnDownloadCompleted(new DownloadProgressCompletedEventArgs()); } catch (WebException e) { - switch (GetStatusCodeResponse(e.Response)) - { - case 416: - break; - case -1: - default: - LogWriteLine(e.Message, LogType.Error, true); - ret = false; - break; - } + ret = ThrowWebExceptionAsBool(e); } catch (TaskCanceledException e) { @@ -232,6 +247,7 @@ public async Task DownloadFileAsync(string DownloadLink, string path, Canc finally { localStream.Dispose(); + remoteStream.Dispose(); } return ret; @@ -247,28 +263,22 @@ public async Task ReadRemoteResponse(HttpWebResponse response, Stream localStrea sw = new Stopwatch(); sw.Start(); byte[] downBuffer = new byte[bufflength]; + float currentSpeed; while ((byteSize = await remoteStream.ReadAsync(downBuffer, 0, downBuffer.Length, token).ConfigureAwait(false)) > 0) { await localStream.WriteAsync(downBuffer, 0, byteSize, token).ConfigureAwait(false); totalReceived += byteSize; + currentSpeed = totalReceived / (float)sw.Elapsed.TotalSeconds; - DownloadProgressChangedEventArgs args = new DownloadProgressChangedEventArgs(); - args.CurrentReceivedBytes = byteSize; - args.BytesReceived = totalReceived; - args.TotalBytesToReceive = ContentLength; - args.CurrentSpeed = totalReceived / (float)sw.Elapsed.TotalSeconds; - if (downloadResumable == true) - { - args.ProgressPercentage = ((float)totalReceived / (float)ContentLength) * 100; - args.TimeLeft = (long)(ContentLength - totalReceived / args.CurrentSpeed); - } - else + OnDownloadProgressChanged(new DownloadProgressChangedEventArgs() { - args.ProgressPercentage = 0; - args.TimeLeft = 0; - } - - OnDownloadProgressChanged(args); + CurrentReceivedBytes = byteSize, + BytesReceived = totalReceived, + TotalBytesToReceive = ContentLength, + CurrentSpeed = currentSpeed, + ProgressPercentage = downloadResumable ? ((float)totalReceived / (float)ContentLength) * 100 : 0, + TimeLeft = downloadResumable ? (long)(ContentLength - totalReceived / currentSpeed) : 0 + }); } sw.Stop(); } @@ -280,9 +290,8 @@ public bool DownloadFileToBuffer(string DownloadLink, in Stream output) stop = false; long ExistingLength = output.Length; - long ContentLength = 0; - HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(DownloadLink); + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(DownloadLink); request.UserAgent = userAgent; request.AddRange(ExistingLength); @@ -293,7 +302,7 @@ public bool DownloadFileToBuffer(string DownloadLink, in Stream output) long FileSize = ExistingLength + response.ContentLength; bool downloadResumable; - ContentLength = response.ContentLength; + long ContentLength = response.ContentLength; if ((int)response.StatusCode == 206) { @@ -310,26 +319,16 @@ public bool DownloadFileToBuffer(string DownloadLink, in Stream output) downloadResumable = false; downloadStatusArgs.ResumeSupported = downloadResumable; OnDownloadStatusChanged(downloadStatusArgs); - output.Dispose(); + output.Flush(); } ReadRemoteResponse(response, output, ExistingLength, ContentLength, downloadResumable); } - var completedArgs = new EventArgs(); - OnDownloadCompleted(completedArgs); + OnDownloadCompleted(new DownloadProgressCompletedEventArgs()); } catch (WebException e) { - switch (GetStatusCodeResponse(e.Response)) - { - case 416: - break; - case -1: - default: - LogWriteLine(e.Message, LogType.Error, true); - ret = false; - break; - } + ret = ThrowWebExceptionAsBool(e); } catch (Exception e) { @@ -340,6 +339,20 @@ public bool DownloadFileToBuffer(string DownloadLink, in Stream output) return ret; } + bool ThrowWebExceptionAsBool(WebException e) + { + switch (GetStatusCodeResponse(e.Response)) + { + // Always ignore 416 code + case 416: + return true; + case -1: + default: + LogWriteLine(e.Message, LogType.Error, true); + return false; + } + } + int GetStatusCodeResponse(WebResponse e) => e == null ? -1 : (int)((HttpWebResponse)e).StatusCode; public void ReadRemoteResponse(HttpWebResponse response, in Stream localStream, long ExistingLength, long ContentLength, bool downloadResumable) @@ -349,6 +362,7 @@ public void ReadRemoteResponse(HttpWebResponse response, in Stream localStream, { int byteSize = 0; long totalReceived = byteSize + ExistingLength; + float currentSpeed; sw = new Stopwatch(); sw.Start(); byte[] downBuffer = new byte[bufflength]; @@ -356,24 +370,17 @@ public void ReadRemoteResponse(HttpWebResponse response, in Stream localStream, { localStream.Write(downBuffer, 0, byteSize); totalReceived += byteSize; + currentSpeed = totalReceived / (float)sw.Elapsed.TotalSeconds; - DownloadProgressChangedEventArgs args = new DownloadProgressChangedEventArgs(); - args.CurrentReceivedBytes = byteSize; - args.BytesReceived = totalReceived; - args.TotalBytesToReceive = ContentLength; - args.CurrentSpeed = totalReceived / (float)sw.Elapsed.TotalSeconds; - if (downloadResumable == true) - { - args.ProgressPercentage = ((float)totalReceived / (float)ContentLength) * 100; - args.TimeLeft = (long)(ContentLength - totalReceived / args.CurrentSpeed); - } - else + OnDownloadProgressChanged(new DownloadProgressChangedEventArgs() { - args.ProgressPercentage = 0; - args.TimeLeft = 0; - } - - OnDownloadProgressChanged(args); + CurrentReceivedBytes = byteSize, + BytesReceived = totalReceived, + TotalBytesToReceive = ContentLength, + CurrentSpeed = currentSpeed, + ProgressPercentage = downloadResumable ? ((float)totalReceived / (float)ContentLength) * 100 : 0, + TimeLeft = downloadResumable ? (long)(ContentLength - totalReceived / currentSpeed) : 0 + }); } sw.Stop(); } @@ -397,9 +404,9 @@ protected virtual void OnDownloadProgressChanged(DownloadProgressChangedEventArg } } - protected virtual void OnDownloadCompleted(EventArgs e) + protected virtual void OnDownloadCompleted(DownloadProgressCompletedEventArgs e) { - EventHandler handler = DownloadCompleted; + EventHandler handler = DownloadCompleted; if (handler != null) { handler(this, e); @@ -408,11 +415,11 @@ protected virtual void OnDownloadCompleted(EventArgs e) } public class DownloadTool { - public bool DownloadToBuffer(string input, in MemoryStream output, string CustomMessage = "") + public bool DownloadToBuffer(string input, in MemoryStream output, string CustomMessage = "", bool updateProgress = false) { DownloadUtils client = new DownloadUtils(); - client.DownloadProgressChanged += Client_DownloadProgressChanged(CustomMessage != "" ? CustomMessage : $"Downloading to buffer"); + client.DownloadProgressChanged += Client_DownloadProgressChanged(CustomMessage != "" ? CustomMessage : $"Downloading to buffer", updateProgress); client.DownloadCompleted += Client_DownloadFileCompleted(); while (!client.DownloadFileToBuffer(input, output)) @@ -424,24 +431,28 @@ public bool DownloadToBuffer(string input, in MemoryStream output, string Custom return false; } - private EventHandler Client_DownloadProgressChanged(string customMessage = "") + private EventHandler Client_DownloadProgressChanged(string customMessage = "", bool updateProgress = false) { Action action = (sender, e) => { +#if DEBUG LogWrite($"{customMessage} \u001b[33;1m{(byte)e.ProgressPercentage}%" + $"\u001b[0m ({SummarizeSizeSimple(e.BytesReceived)}) (\u001b[32;1m{SummarizeSizeSimple(e.CurrentSpeed)}/s\u001b[0m)", LogType.NoTag, false, true); +#endif }; return new EventHandler(action); } - private static EventHandler Client_DownloadFileCompleted() + private static EventHandler Client_DownloadFileCompleted() { - Action action = (sender, e) => + Action action = (sender, e) => { +#if DEBUG LogWrite($" Done!", LogType.Empty); - Console.WriteLine(); + LogWriteLine(); +#endif }; - return new EventHandler(action); + return new EventHandler(action); } } } diff --git a/Hi3HelperGUI/Classes/Data/Tools/HttpClientTool.cs b/Hi3HelperGUI/Classes/Data/Tools/HttpClientTool.cs new file mode 100644 index 000000000..9ed99d801 --- /dev/null +++ b/Hi3HelperGUI/Classes/Data/Tools/HttpClientTool.cs @@ -0,0 +1,399 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Threading; +using System.Threading; +using System.Threading.Tasks; +using System.Net; +using System.IO; +using System.Diagnostics; +#if (!NETFRAMEWORK) +using System.Net.Http; +using System.Net.Http.Headers; +#endif + +using static Hi3HelperGUI.Logger; +using static Hi3HelperGUI.Data.ConverterTool; + +namespace Hi3HelperGUI.Data +{ + public class HttpClientTool + { + +#if (NETCOREAPP) + HttpClient httpClient; +#endif + + protected Stream localStream; + protected Stream remoteStream; + public event EventHandler ResumablityChanged; + public event EventHandler ProgressChanged; + public event EventHandler Completed; + + public bool stop = true; // by default stop is true + /* Declare download buffer + * by default: 256 KiB (262144 bytes) + */ + long bufflength = 262144; + + public HttpClientTool() + { +#if (NETCOREAPP) + httpClient = new HttpClient( + new HttpClientHandler + { + AutomaticDecompression = DecompressionMethods.All, + UseCookies = true, + MaxConnectionsPerServer = 32, + AllowAutoRedirect = true + }); +#endif + } + + DownloadStatusChanged resumabilityStatus; + + public async Task DownloadFile(string input, string output, CancellationToken token, long startOffset = -1, long endOffset = -1, string customMessage = "") + { + if (string.IsNullOrEmpty(customMessage)) + customMessage = $"Downloading {Path.GetFileName(output)}"; + + return await GetRemoteStreamResponse(input, output, token, startOffset, endOffset, customMessage, false); + } + + public async Task DownloadFileToStream(string input, CancellationToken token, long startOffset = -1, long endOffset = -1, string customMessage = "") + { + if (string.IsNullOrEmpty(customMessage)) + customMessage = $"Downloading to buffer"; + + localStream = new MemoryStream(); + + await GetRemoteStreamResponse(input, "", token, startOffset, endOffset, customMessage, true); + + return localStream; + } + + public bool DownloadFile(string input, string output, long startOffset = -1, long endOffset = -1, string customMessage = "") + { + stop = false; + if (string.IsNullOrEmpty(customMessage)) + customMessage = $"Downloading {Path.GetFileName(output)}"; + + FileStream file = new FileStream(output, File.Exists(output) ? FileMode.Append : FileMode.Create, FileAccess.Write); + + return GetRemoteStreamResponse(input, output, startOffset, endOffset, customMessage, false, file); + } + + public bool DownloadFileToStream(string input, in Stream output, long startOffset = -1, long endOffset = -1, string customMessage = "") + { + stop = false; + if (string.IsNullOrEmpty(customMessage)) + customMessage = $"Downloading to buffer"; + + return GetRemoteStreamResponse(input, "", startOffset, endOffset, customMessage, true, output); + } + + async Task GetRemoteStreamResponse(string input, string output, CancellationToken token, long startOffset, long endOffset, string customMessage, bool isStreamOutput) + { + long existingLength = 0; + bool returnValue = true; + OnCompleted(new DownloadProgressCompleted() { DownloadCompleted = false }); + + try + { + FileInfo fileinfo = new FileInfo(isStreamOutput ? "" : output); + if (fileinfo.Exists) existingLength = fileinfo.Length; + + HttpRequestMessage request = new HttpRequestMessage { RequestUri = new Uri(input) }; + + if (startOffset != -1 && endOffset != -1) + request.Headers.Range = new RangeHeaderValue(startOffset, endOffset); + else + request.Headers.Range = new RangeHeaderValue(existingLength, null); + + using (HttpResponseMessage response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false)) + { + long contentLength = existingLength + (response.Content.Headers.ContentRange.Length - response.Content.Headers.ContentRange.From) ?? 0; + resumabilityStatus = new DownloadStatusChanged((int)response.StatusCode == 206); + + if (!isStreamOutput) + { + localStream = fileinfo.Open(resumabilityStatus.ResumeSupported ? FileMode.Append : FileMode.Create, FileAccess.Write); + } + + OnResumabilityChanged(resumabilityStatus); + + await ReadRemoteStream( + response, + localStream, + existingLength, + contentLength, + customMessage, + token + ); + response.Dispose(); + } + } + catch (WebException e) + { + returnValue = ThrowWebExceptionAsBool(e); + } + catch (TaskCanceledException e) + { + returnValue = true; + throw new TaskCanceledException(e.ToString()); + } + catch (NullReferenceException e) + { + LogWriteLine($"This file {input} has 0 byte in size.\r\nTraceback: {e}", LogType.Error, true); + returnValue = false; + } + catch (Exception e) + { + LogWriteLine($"An error occured while downloading {(isStreamOutput ? "to buffer" : Path.GetFileName(output))}\r\nTraceback: {e}", LogType.Error, true); + returnValue = false; + } + finally + { + if (!isStreamOutput) localStream?.Dispose(); + remoteStream?.Dispose(); + } + + OnCompleted(new DownloadProgressCompleted() { DownloadCompleted = true }); + + return returnValue; + } + + bool GetRemoteStreamResponse(string input, string output, long startOffset, long endOffset, string customMessage, bool isStreamOutput, in Stream outputStream) + { + long existingLength = isStreamOutput ? outputStream.Length : new FileInfo(output).Length; + bool returnValue = true; + OnCompleted(new DownloadProgressCompleted() { DownloadCompleted = false }); + + try + { + HttpRequestMessage request = new HttpRequestMessage { RequestUri = new Uri(input) }; + + if (startOffset != -1 && endOffset != -1) + request.Headers.Range = new RangeHeaderValue(startOffset, endOffset); + else + request.Headers.Range = new RangeHeaderValue(existingLength, null); + + using (HttpResponseMessage response = httpClient.Send(request, HttpCompletionOption.ResponseHeadersRead)) + { + long contentLength = existingLength + (response.Content.Headers.ContentRange.Length - response.Content.Headers.ContentRange.From) ?? 0; + resumabilityStatus = new DownloadStatusChanged((int)response.StatusCode == 206); + + OnResumabilityChanged(resumabilityStatus); + + ReadRemoteStream( + response, + outputStream, + existingLength, + contentLength, + customMessage + ); + response.Dispose(); + } + } + catch (WebException e) + { + returnValue = ThrowWebExceptionAsBool(e); + } + catch (TaskCanceledException e) + { + returnValue = true; + throw new TaskCanceledException(e.ToString()); + } + catch (NullReferenceException e) + { + LogWriteLine($"This file {input} has 0 byte in size.\r\nTraceback: {e}", LogType.Error, true); + returnValue = false; + } + catch (Exception e) + { + LogWriteLine($"An error occured while downloading {Path.GetFileName(output)}\r\nTraceback: {e}", LogType.Error, true); + returnValue = false; + } + finally + { + if (!isStreamOutput) outputStream?.Dispose(); + remoteStream?.Dispose(); + } + + OnCompleted(new DownloadProgressCompleted() { DownloadCompleted = true }); + + stop = true; + + return returnValue; + } + + async Task ReadRemoteStream( + HttpResponseMessage response, + Stream localStream, + long existingLength, + long contentLength, + string customMessage, + CancellationToken token) + { + int byteSize = 0; + long totalReceived = byteSize + existingLength; + byte[] buffer = new byte[bufflength]; + using (remoteStream = await response.Content.ReadAsStreamAsync(token).ConfigureAwait(false)) + { + var sw = Stopwatch.StartNew(); + do + { + await localStream.WriteAsync(buffer, 0, byteSize, token).ConfigureAwait(false); + totalReceived += byteSize; + + OnProgressChanged(new DownloadProgressChanged(totalReceived, contentLength, sw.Elapsed.TotalSeconds) { Message = customMessage, CurrentReceived = byteSize }); + } + while ((byteSize = await remoteStream.ReadAsync(buffer, 0, buffer.Length, token).ConfigureAwait(false)) > 0); + + sw.Stop(); + } + } + + void ReadRemoteStream( + HttpResponseMessage response, + in Stream stream, + long existingLength, + long contentLength, + string customMessage) + { + int byteSize = 0; + long totalReceived = byteSize + existingLength; + byte[] buffer = new byte[bufflength]; + using (remoteStream = response.Content.ReadAsStream()) + { + var sw = Stopwatch.StartNew(); + do + { + if (stop) return; + stream.Write(buffer, 0, byteSize); + totalReceived += byteSize; + + OnProgressChanged(new DownloadProgressChanged(totalReceived, contentLength, sw.Elapsed.TotalSeconds) { Message = customMessage, CurrentReceived = byteSize }); + } + while ((byteSize = remoteStream.Read(buffer, 0, buffer.Length)) > 0); + + sw.Stop(); + } + } + + bool ThrowWebExceptionAsBool(WebException e) + { + switch (GetStatusCodeResponse(e)) + { + // Always ignore 416 code + case 416: + return true; + case -1: + default: + LogWriteLine(e.Message, LogType.Error, true); + return false; + } + } + + int GetStatusCodeResponse(WebException e) => e.Response == null ? -1 : (int)((HttpWebResponse)e.Response).StatusCode; + + public void StopDownload() + { + stop = true; + } + + protected virtual void OnResumabilityChanged(DownloadStatusChanged e) + { + var handler = ResumablityChanged; + if (handler != null) + { + handler(this, e); + } + } + + protected virtual void OnProgressChanged(DownloadProgressChanged e) + { + var handler = ProgressChanged; + if (handler != null) + { + handler(this, e); + } + } + + protected virtual void OnCompleted(DownloadProgressCompleted e) + { + var handler = Completed; + if (handler != null) + { + handler(this, e); + } + } + + public bool DownloadToStream(string input, in Stream output, string CustomMessage = "") + { + HttpClientTool client = this; + + client.ProgressChanged += DownloadProgressChanged; + client.Completed += DownloadProgressCompleted; + + return client.DownloadFileToStream(input, output, -1, -1, CustomMessage); + } + + void DownloadProgressCompleted(object sender, DownloadProgressCompleted e) + { +#if DEBUG + if (e.DownloadCompleted) + { + LogWrite($" Done!", LogType.Empty); + LogWriteLine(); + } +#endif + } + + void DownloadProgressChanged(object sender, DownloadProgressChanged e) + { +#if DEBUG + LogWrite($"{e.Message} \u001b[33;1m{(byte)e.ProgressPercentage}%" + + $"\u001b[0m ({SummarizeSizeSimple(e.BytesReceived)}) (\u001b[32;1m{SummarizeSizeSimple(e.CurrentSpeed)}/s\u001b[0m)", LogType.NoTag, false, true); +#endif + } + } + public class DownloadStatusChanged : EventArgs + { + public DownloadStatusChanged(bool canResume) + { + ResumeSupported = canResume; + } + public bool ResumeSupported { get; private set; } + } + + public class DownloadProgressCompleted : EventArgs + { + public bool DownloadCompleted { get; set; } + } + + public class DownloadProgressChanged : EventArgs + { + public DownloadProgressChanged(long totalReceived, long fileSize, double totalSecond) + { + BytesReceived = totalReceived; + TotalBytesToReceive = fileSize; + CurrentSpeed = (long)(totalReceived / totalSecond); + } + public string Message { get; set; } + public long CurrentReceived { get; set; } + public long BytesReceived { get; private set; } + public long TotalBytesToReceive { get; private set; } + public float ProgressPercentage { get { return ((float)BytesReceived / (float)TotalBytesToReceive) * 100; } } + public long CurrentSpeed { get; private set; } + public TimeSpan TimeLeft + { + get + { + var bytesRemainingtoBeReceived = TotalBytesToReceive - BytesReceived; + return TimeSpan.FromSeconds(bytesRemainingtoBeReceived / CurrentSpeed); + } + } + } +} diff --git a/Hi3HelperGUI/Classes/Data/UpdateData.cs b/Hi3HelperGUI/Classes/Data/UpdateData.cs index bdf9f77b4..4afc0be0c 100644 --- a/Hi3HelperGUI/Classes/Data/UpdateData.cs +++ b/Hi3HelperGUI/Classes/Data/UpdateData.cs @@ -8,6 +8,7 @@ using System.Net.Http; #endif using System.IO; +using System.Windows.Controls; using Newtonsoft.Json; using Hi3HelperGUI.Preset; @@ -21,7 +22,6 @@ public class UpdateData protected internal string LocalPath; protected internal _RemoteURL RemoteURL; - protected internal WebClient webClient; protected internal class _RemoteURL { internal string Data { get; set; } @@ -81,17 +81,23 @@ public UpdateData(PresetConfigClasses i) */ public void GetDataDict(PresetConfigClasses i, byte dataType) { + HttpClientTool downloader = new HttpClientTool(); string LocalDirPath = Path.Combine(Environment.GetEnvironmentVariable("userprofile"), $"AppData\\LocalLow\\miHoYo\\{Path.GetFileName(i.ConfigRegistryLocation)}\\{(dataType > 0 ? "Resources" : "Data")}"); string RemotePath = dataType == 1 ? RemoteURL.Event : dataType == 2 ? RemoteURL.Ai : RemoteURL.Data; string LocalPath; - // Span DictData = webClient.DownloadString(dataType == 1 ? RemoteURL.EventDictionary : dataType == 2 ? RemoteURL.AiDictionary : RemoteURL.DataDictionary).Split("\n"); MemoryStream memoryData = new MemoryStream(); - DownloadTool Downloader = new DownloadTool(); - Downloader.DownloadToBuffer(dataType == 1 ? RemoteURL.EventDictionary : dataType == 2 ? RemoteURL.AiDictionary : RemoteURL.DataDictionary, memoryData, $"Fetch to buffer: {Enum.GetName(typeof(ConfigStore.DataType), dataType)} list"); + // Span DictData = webClient.DownloadString(dataType == 1 ? RemoteURL.EventDictionary : dataType == 2 ? RemoteURL.AiDictionary : RemoteURL.DataDictionary).Split("\n"); + downloader.DownloadToStream( + dataType == 1 ? RemoteURL.EventDictionary : dataType == 2 ? RemoteURL.AiDictionary : RemoteURL.DataDictionary, + memoryData, + $"Fetch to buffer: {Enum.GetName(typeof(ConfigStore.DataType), dataType)} list" + ); - // For NET5 - // Span DictData = Encoding.UTF8.GetString(memoryData.ToArray()).Split('\n'); +#if NETCOREAPP + Span DictData = Encoding.UTF8.GetString(memoryData.ToArray()).Split('\n'); +#else string[] DictData = Encoding.UTF8.GetString(memoryData.ToArray()).Split('\n'); +#endif for (ushort a = (ushort)(dataType > 0 ? 0 : 1); a < DictData.Length - 1; a++) { @@ -127,7 +133,7 @@ public void GetDataDict(PresetConfigClasses i, byte dataType) ActualPath = LocalPath, ZoneName = i.ZoneName, DataType = Enum.GetName(typeof(ConfigStore.DataType), dataType), - DownloadStatus = $"Uncompleted {((100 * new FileInfo(LocalPath).Length) / ConfigStore.DataProp.CS)}% ({SummarizeSizeSimple(new FileInfo(LocalPath).Length)})" + DownloadStatus = $"Uncompleted {100 * new FileInfo(LocalPath).Length / ConfigStore.DataProp.CS}% ({SummarizeSizeSimple(new FileInfo(LocalPath).Length)})" }); } } diff --git a/Hi3HelperGUI/Classes/GUI/BlockSection.cs b/Hi3HelperGUI/Classes/GUI/BlockSection.cs index b71367368..f9c76a121 100644 --- a/Hi3HelperGUI/Classes/GUI/BlockSection.cs +++ b/Hi3HelperGUI/Classes/GUI/BlockSection.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Windows.Threading; using System.Threading; using System.Threading.Tasks; using System.IO; @@ -16,14 +17,31 @@ namespace Hi3HelperGUI { public partial class MainWindow { - public void BlockCheckStart(object sender, RoutedEventArgs e) => CheckBlockData(); + public void BlockCheckStart(object sender, RoutedEventArgs e) => FetchDictionaryData(); - public async void CheckBlockData() + public async void FetchDictionaryData() { await Task.Run(() => { LogWriteLine($"Bruh"); - }); + RefreshBlockCheckProgressBar(); + }).ConfigureAwait(false); } + + private void RefreshBlockCheckProgressBar(double cur = 0, double max = 100) => Dispatcher.Invoke(() => BlockProgressBar.Value = Math.Round((100 * cur) / max, 2), DispatcherPriority.Background); + + private void ChangeBlockRepair(bool a) => + Dispatcher.Invoke(() => + { + UpdateDownloadBtn.IsEnabled = a; + UpdateListView.IsEnabled = a; + }); + + private void ChangeBlockRepairStatus(string s, bool b) => + Dispatcher.Invoke(() => + { + UpdateCheckStatus.Content = s; + UpdateCheckBtn.IsEnabled = b; + }); } } diff --git a/Hi3HelperGUI/Classes/GUI/CheckAvailableVersion.cs b/Hi3HelperGUI/Classes/GUI/CheckAvailableVersion.cs index 6f523b723..d23093e0a 100644 --- a/Hi3HelperGUI/Classes/GUI/CheckAvailableVersion.cs +++ b/Hi3HelperGUI/Classes/GUI/CheckAvailableVersion.cs @@ -111,11 +111,11 @@ internal static bool isConfigAvailable(PresetConfigClasses i) } catch (NullReferenceException e) { - LogWriteLine(e.ToString(), Logger.LogType.Warning, true); + LogWriteLine(e.ToString(), LogType.Warning, true); } catch (Exception e) { - LogWriteLine(e.ToString(), Logger.LogType.Error, true); + LogWriteLine(e.ToString(), LogType.Error, true); } return ret; diff --git a/Hi3HelperGUI/Classes/GUI/DownloadSection.cs b/Hi3HelperGUI/Classes/GUI/DownloadSection.cs index a77d9a05e..2c253b39e 100644 --- a/Hi3HelperGUI/Classes/GUI/DownloadSection.cs +++ b/Hi3HelperGUI/Classes/GUI/DownloadSection.cs @@ -2,6 +2,9 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.ComponentModel; +using System.Windows; +using System.Windows.Threading; using System.Threading; using System.Threading.Tasks; using System.IO; @@ -13,27 +16,30 @@ namespace Hi3HelperGUI { - public partial class MainWindow + public partial class MainWindow : Window { + readonly HttpClientTool client = new HttpClientTool(); public async Task DownloadUpdateFilesAsync(CancellationTokenSource tokenSource, CancellationToken token) { - for (ushort p = 0; p < ConfigStore.UpdateFiles.Count; p++) + int updateFilesCount = ConfigStore.UpdateFiles.Count; + string message; + for (ushort p = 0; p < updateFilesCount; p++) { - DownloadUtils client = new DownloadUtils(); + RemoveDownloadHandler(); if (!Directory.Exists(Path.GetDirectoryName(ConfigStore.UpdateFiles[p].ActualPath))) Directory.CreateDirectory(Path.GetDirectoryName(ConfigStore.UpdateFiles[p].ActualPath)); - client.DownloadProgressChanged += Client_UpdateFilesDownloadProgressChanged($"Down: [{ConfigStore.UpdateFiles[p].ZoneName} > {ConfigStore.UpdateFiles[p].DataType}] ({p+1}/{ConfigStore.UpdateFiles.Count}) {Path.GetFileName(ConfigStore.UpdateFiles[p].N)}"); - client.DownloadCompleted += Client_UpdateFilesDownloadFileCompleted(); + client.ProgressChanged += new EventHandler(DownloadProgressChanged); + client.Completed += new EventHandler(DownloadProgressCompleted); - ChangeUpdateStatus($"Down: [{ConfigStore.UpdateFiles[p].ZoneName} > {ConfigStore.UpdateFiles[p].DataType}] ({p+1}/{ConfigStore.UpdateFiles.Count}) {Path.GetFileName(ConfigStore.UpdateFiles[p].N)}", false); + message = $"Down: [{ConfigStore.UpdateFiles[p].ZoneName} > {ConfigStore.UpdateFiles[p].DataType}] ({p + 1}/{ConfigStore.UpdateFiles.Count}) {Path.GetFileName(ConfigStore.UpdateFiles[p].N)}"; + //client.ProgressChanged += DownloadProgressChanges($"Down: [{ConfigStore.UpdateFiles[p].ZoneName} > {ConfigStore.UpdateFiles[p].DataType}] ({p + 1}/{ConfigStore.UpdateFiles.Count}) {Path.GetFileName(ConfigStore.UpdateFiles[p].N)}"); + //client.Completed += DownloadProgressCompleted(); + + ChangeUpdateStatus(message, false); await Task.Run(async () => { - token.ThrowIfCancellationRequested(); - while (!await client.DownloadFileAsync(ConfigStore.UpdateFiles[p].RemotePath, ConfigStore.UpdateFiles[p].ActualPath, token)) + while (!await client.DownloadFile(ConfigStore.UpdateFiles[p].RemotePath, ConfigStore.UpdateFiles[p].ActualPath, token, -1, -1, message)) { - if (token.IsCancellationRequested) - token.ThrowIfCancellationRequested(); - LogWriteLine($"Retrying...", LogType.Warning); await Task.Delay(3000); } @@ -43,29 +49,38 @@ public async Task DownloadUpdateFilesAsync(CancellationTokenSource tokenSo return false; } - private EventHandler Client_UpdateFilesDownloadProgressChanged(string customMessage = "") + void RemoveDownloadHandler() { - Action action = (sender, e) => - { - ConfigStore.UpdateFilesTotalDownloaded += e.CurrentReceivedBytes; - - RefreshUpdateProgressLabel($"{(byte)e.ProgressPercentage}% ({SummarizeSizeSimple(e.BytesReceived)}) ({SummarizeSizeSimple(e.CurrentSpeed)}/s)"); - - RefreshTotalProgressBar(ConfigStore.UpdateFilesTotalDownloaded, ConfigStore.UpdateFilesTotalSize); - LogWrite($"{customMessage} \u001b[33;1m{(e.NoProgress ? "Unknown" : $"{(byte)e.ProgressPercentage}%")}" - + $"\u001b[0m ({SummarizeSizeSimple(e.BytesReceived)}) (\u001b[32;1m{SummarizeSizeSimple(e.CurrentSpeed)}/s\u001b[0m)", LogType.NoTag, false, true); - }; - return new EventHandler(action); + client.ProgressChanged -= new EventHandler(DownloadProgressChanged); + client.Completed -= new EventHandler(DownloadProgressCompleted); } - private static EventHandler Client_UpdateFilesDownloadFileCompleted() + void DownloadProgressCompleted(object sender, DownloadProgressCompleted e) { - Action action = (sender, e) => +#if DEBUG + if (e.DownloadCompleted) { LogWrite($" Done!", LogType.Empty); - Console.WriteLine(); - }; - return new EventHandler(action); + LogWriteLine(); + } +#endif + } + + void DownloadProgressChanged(object sender, DownloadProgressChanged e) + { + string BytesReceived = SummarizeSizeSimple(e.BytesReceived); + string CurrentSpeed = SummarizeSizeSimple(e.CurrentSpeed); + Dispatcher.Invoke(() => + { + ConfigStore.UpdateFilesTotalDownloaded += e.CurrentReceived; + + UpdateProgressLabel.Content = $"{(byte)e.ProgressPercentage}% ({BytesReceived}) ({CurrentSpeed}/s)"; + UpdateProgressBar.Value = GetPercentageNumber(ConfigStore.UpdateFilesTotalDownloaded, ConfigStore.UpdateFilesTotalSize); + }, DispatcherPriority.Background); +#if DEBUG + LogWrite($"{e.Message} \u001b[33;1m{(byte)e.ProgressPercentage}%" + + $"\u001b[0m ({BytesReceived}) (\u001b[32;1m{CurrentSpeed}/s\u001b[0m)", LogType.NoTag, false, true); +#endif } } } diff --git a/Hi3HelperGUI/Classes/GUI/UpdateSection.cs b/Hi3HelperGUI/Classes/GUI/UpdateSection.cs index 04e5a7069..a0604aabe 100644 --- a/Hi3HelperGUI/Classes/GUI/UpdateSection.cs +++ b/Hi3HelperGUI/Classes/GUI/UpdateSection.cs @@ -5,6 +5,7 @@ using System.Threading; using System.Threading.Tasks; using System.Windows; +using System.Windows.Threading; using System.Windows.Controls; //using System.Runtime.InteropServices; using System.IO; @@ -24,7 +25,6 @@ public partial class MainWindow private void UpdateDownloadStart(object sender, RoutedEventArgs e) => DoUpdateDownload(); private void UpdateDownloadCancel(object sender, RoutedEventArgs e) { - Console.WriteLine(); LogWriteLine($"Cancelling update...", LogType.Warning); DownloadTokenSource.Cancel(); } @@ -36,17 +36,19 @@ private async void FetchUpdateData() await Task.Run(() => { (ConfigStore.UpdateFilesTotalSize, ConfigStore.UpdateFilesTotalDownloaded) = (0, 0); - RefreshTotalProgressBar(); + RefreshUpdateProgressBar(); ConfigStore.UpdateFiles = new List(); RefreshUpdateProgressLabel(); ChangeUpdateDownload(false); - ChangeUpdateStatus($"Fetching update data...", false); foreach (PresetConfigClasses i in ConfigStore.Config) { LogWriteLine($"Fetching update data for \u001b[34;1m{i.ZoneName}\u001b[0m (\u001b[32;1m{Path.GetFileName(i.InstallRegistryLocation)}\u001b[0m) version... "); UpdateDataUtil = new UpdateData(i); for (byte j = 0; j < 3; j++) + { + ChangeUpdateStatus($"Fetching update data for {i.ZoneName} {Enum.GetName(typeof(ConfigStore.DataType), j)} zone...", false); UpdateDataUtil.GetDataDict(i, j); + } } ConfigStore.UpdateFilesTotalSize = ConfigStore.UpdateFiles.Sum(item => item.ECS); StoreToListView(ConfigStore.UpdateFiles); @@ -57,12 +59,11 @@ private async void FetchUpdateData() } ChangeUpdateStatus($"{ConfigStore.UpdateFiles.Count} files ({SummarizeSizeSimple(ConfigStore.UpdateFilesTotalSize)}) are ready to be updated. Click Download to start the update!", true); ChangeUpdateDownload(true); - LogWriteLine($"{ConfigStore.UpdateFiles.Count} files ({SummarizeSizeSimple(ConfigStore.UpdateFilesTotalSize)}) will be updated"); + LogWriteLine($"{ConfigStore.UpdateFiles.Count} files ({SummarizeSizeSimple(ConfigStore.UpdateFilesTotalSize)}) will be updated", LogType.Default, true); return; }); } - private void StoreToListView(List i) => Dispatcher.Invoke(() => UpdateListView.ItemsSource = i); private async void DoUpdateDownload() @@ -93,13 +94,14 @@ private async void DoUpdateDownload() ToggleUpdateCancelBtnState(false); // await DownloadTask; ChangeUpdateStatus($"{ConfigStore.UpdateFiles.Count} files have been downloaded!", true); + LogWriteLine($"{ConfigStore.UpdateFiles.Count} files have been downloaded!", LogType.Default, true); } private void RefreshUpdateProgressLabel(string i = "none") => Dispatcher.Invoke(() => UpdateProgressLabel.Content = i); private void RefreshUpdateListView() => Dispatcher.Invoke(() => UpdateListView.Items.Refresh()); - private void RefreshTotalProgressBar(double cur = 0, double max = 100) => Dispatcher.Invoke(() => TotalProgressBar.Value = Math.Round((100 * cur) / max, 2)); + private void RefreshUpdateProgressBar(double cur = 0, double max = 100) => Dispatcher.Invoke(() => UpdateProgressBar.Value = Math.Round((100 * cur) / max, 2), DispatcherPriority.Background); private void ToggleUpdateCancelBtnState(bool i) => Dispatcher.Invoke(() => { diff --git a/Hi3HelperGUI/Classes/ILogger.cs b/Hi3HelperGUI/Classes/ILogger.cs new file mode 100644 index 000000000..2a881920b --- /dev/null +++ b/Hi3HelperGUI/Classes/ILogger.cs @@ -0,0 +1,13 @@ +using System.Windows.Media; +using System.Windows.Controls; + +namespace Hi3HelperGUI +{ + interface ILogger + { + //void SetLabelAttrib(out Label i, string s, SolidColorBrush a); + void LogWriteLine(string i = "", LogType a = LogType.Default, bool writeToLog = false); + void LogWrite(string i = "", LogType a = LogType.Default, bool writeToLog = false, bool overwriteCurLine = false); + void WriteLog(string i = "", LogType a = LogType.Default); + } +} diff --git a/Hi3HelperGUI/Classes/Logger.cs b/Hi3HelperGUI/Classes/Logger.cs index 5866d3ab7..a71d17d54 100644 --- a/Hi3HelperGUI/Classes/Logger.cs +++ b/Hi3HelperGUI/Classes/Logger.cs @@ -1,5 +1,5 @@ using System; -//using System.Collections.Generic; +using System.Collections.Generic; //using System.Linq; //using System.Text; //using System.Threading.Tasks; @@ -12,26 +12,93 @@ namespace Hi3HelperGUI { - public partial class Logger : Window + public enum LogType { Error, Warning, Default, Scheme, Empty, NoTag } + public static class Logger { - protected internal static StreamWriter logstream; - protected internal static string logdir, - filename; - public static bool DisableConsole = false; - public enum LogType { Error, Warning, Default, Scheme, Empty, NoTag } + internal static StreamWriter logstream; + internal static string logdir, + filename; + public static bool DisableConsole = false; + private static ILogger logger; + public static string GetCurrentTime(string format) => DateTime.Now.ToLocalTime().ToString(format); public static Version GetRunningVersion() => Assembly.GetExecutingAssembly().GetName().Version; public static void InitLog() { + logger = DisableConsole ? new DummyLogger() : new ConsoleLogger(); + logdir = Path.Combine(Directory.GetCurrentDirectory(), "logs"); if (!Directory.Exists(logdir)) Directory.CreateDirectory(logdir); filename = $"log-{GetCurrentTime("yyyy-MM-dd")}.log"; - LogWriteLine($"App started! v{GetRunningVersion()}", LogType.Scheme, true); + // LogWriteLine($"App started! v{GetRunningVersion()}", LogType.Scheme, true); + } + public static void LogWriteLine() => logger.LogWriteLine(string.Empty, LogType.Empty); + public static void LogWriteLine( + string i = "", + LogType a = LogType.Default, + bool writeToLog = false) => + logger.LogWriteLine(i, a, writeToLog); + + public static void LogWrite( + string i = "", + LogType a = LogType.Default, + bool writeToLog = false, + bool overwriteCurLine = false) => + logger.LogWrite(i, a, writeToLog, overwriteCurLine); + + public static void WriteLog(string i = "", LogType a = LogType.Default) => + logger.WriteLog(i, a); + } + + public class DummyLogger : ILogger + { + public void LogWriteLine(string i = "", LogType a = LogType.Default, bool writeToLog = false) + { + if (writeToLog) + WriteLog(i, a); + } + public void LogWrite(string i = "", LogType a = LogType.Default, bool writeToLog = false, bool overwriteCurLine = false) + { + if (writeToLog) + WriteLog(i, a); + } + + public void WriteLog(string i, LogType a = LogType.Default) + { + using (Logger.logstream = new StreamWriter(Path.Combine(Logger.logdir, Logger.filename), true)) + Logger.logstream.WriteLine(GetLog(i, a)); + } + + string GetLog(string i, LogType a) + { + switch (a) + { + case LogType.Error: + i = $"[Erro]\t{i}"; + break; + case LogType.Warning: + i = $"[Warn]\t{i}"; + break; + default: + i = $"\t\t{i}"; + break; + case LogType.Default: + i = $"[Info]\t{i}"; + break; + case LogType.Scheme: + i = $"[Schm]\t{i}"; + break; + } + return $"[{Logger.GetCurrentTime("HH:mm:ss.fff")}] {i.Replace("\n", $"{new string(' ', 22)}\t")}"; } + } - private protected static string ColorizePrint(string i, LogType a) + public class ConsoleLogger : ILogger + { + + private protected string ColorizePrint(string i, LogType a) { switch (a) { @@ -57,10 +124,10 @@ private protected static string ColorizePrint(string i, LogType a) return i; } - private protected static void PrintLine(string i, LogType a) => Console.WriteLine(ColorizePrint(i, a)); - private protected static void Print(string i, LogType a) => Console.Write(ColorizePrint(i, a)); + private protected void PrintLine(string i, LogType a) => Console.WriteLine(ColorizePrint(i, a)); + private protected void Print(string i, LogType a) => Console.Write(ColorizePrint(i, a)); - public static void SetLabelAttrib(out Label i, string s, SolidColorBrush a) + public void SetLabelAttrib(out Label i, string s, SolidColorBrush a) { i = new Label { @@ -69,20 +136,20 @@ public static void SetLabelAttrib(out Label i, string s, SolidColorBrush a) }; } - public static void LogWriteLine(string i, LogType a = LogType.Default, bool writeToLog = false) + public void LogWriteLine(string i = "", LogType a = LogType.Default, bool writeToLog = false) { if (writeToLog) WriteLog(i, a); - if (!DisableConsole) + if (!Logger.DisableConsole) PrintLine(i, a); } - public static void LogWrite(string i, LogType a = LogType.Default, bool writeToLog = false, bool overwriteCurLine = false) + public void LogWrite(string i = "", LogType a = LogType.Default, bool writeToLog = false, bool overwriteCurLine = false) { if (writeToLog) WriteLog(i, a); - if (!DisableConsole) + if (!Logger.DisableConsole) { if (overwriteCurLine) Console.SetCursorPosition(0, Console.CursorTop); @@ -90,15 +157,13 @@ public static void LogWrite(string i, LogType a = LogType.Default, bool writeToL } } - public static void WriteLog(string i, LogType a = LogType.Default) + public void WriteLog(string i = "", LogType a = LogType.Default) { - using (logstream = new StreamWriter(Path.Combine(logdir, filename), true)) - logstream.WriteLine(GetLog(i, a)); + using (Logger.logstream = new StreamWriter(Path.Combine(Logger.logdir, Logger.filename), true)) + Logger.logstream.WriteLine(GetLog(i, a)); } - private protected static string GetCurrentTime(string format) => DateTime.Now.ToLocalTime().ToString(format); - - private protected static string GetLog(string i, LogType a) + private protected string GetLog(string i, LogType a) { switch (a) { @@ -118,7 +183,7 @@ private protected static string GetLog(string i, LogType a) i = $"[Schm]\t{i}"; break; } - return $"[{GetCurrentTime("HH:mm:ss.fff")}] {i.Replace("\n", $"{new string(' ', 22)}\t")}"; + return $"[{Logger.GetCurrentTime("HH:mm:ss.fff")}] {i.Replace("\n", $"{new string(' ', 22)}\t")}"; } } } diff --git a/Hi3HelperGUI/Classes/Preset/AppConfigLoader.cs b/Hi3HelperGUI/Classes/Preset/AppConfigLoader.cs index 5cb3d9275..493203760 100644 --- a/Hi3HelperGUI/Classes/Preset/AppConfigLoader.cs +++ b/Hi3HelperGUI/Classes/Preset/AppConfigLoader.cs @@ -30,6 +30,8 @@ public partial class MainWindow ShowConsoleWindow(); else HideConsoleWindow(); + + Logger.InitLog(); }); public void SaveAppConfig() diff --git a/Hi3HelperGUI/Hi3HelperGUI.csproj b/Hi3HelperGUI/Hi3HelperGUI.csproj index 948b79a9d..4b74c98b2 100644 --- a/Hi3HelperGUI/Hi3HelperGUI.csproj +++ b/Hi3HelperGUI/Hi3HelperGUI.csproj @@ -18,8 +18,7 @@ false false https://github.com/neon-nyan/Hi3Helper - 7.0 - embedded + none @@ -57,4 +56,19 @@ + + + True + True + Settings.settings + + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + diff --git a/Hi3HelperGUI/MainWindow.xaml b/Hi3HelperGUI/MainWindow.xaml index f1ae675cd..215902be3 100644 --- a/Hi3HelperGUI/MainWindow.xaml +++ b/Hi3HelperGUI/MainWindow.xaml @@ -6,7 +6,7 @@ xmlns:local="clr-namespace:Hi3HelperGUI" xmlns:Json="clr-namespace:Newtonsoft.Json;assembly=Newtonsoft.Json" x:Class="Hi3HelperGUI.MainWindow" mc:Ignorable="d" - Title="Hi3Helper" Height="528" Width="800" MinWidth="800" MinHeight="528" ResizeMode="CanMinimize"> + Title="Hi3Helper" Height="528" Width="800" MinWidth="800" MinHeight="528" ResizeMode="CanResize"> @@ -16,11 +16,6 @@ - - @@ -54,84 +49,115 @@ - - -