fix: Download issues with ENOSW

This commit is contained in:
Gustave Monce
2024-09-01 10:38:47 +02:00
parent b03b561724
commit 29c6ab06d4
4 changed files with 813 additions and 145 deletions
@@ -927,21 +927,21 @@ namespace WPinternals
uint length = uint.Parse(info.Length.ToString());
int offset = 0;
const int maximumbuffersize = 0x00240000;
const int maximumBufferSize = 0x00240000;
uint totalcounts = (uint)Math.Truncate((decimal)length / maximumbuffersize);
uint chunkCount = (uint)Math.Truncate((decimal)length / maximumBufferSize);
using (FileStream MMOSFile = new(MMOSPath, FileMode.Open, FileAccess.Read))
{
for (int i = 1; i <= (uint)Math.Truncate((decimal)length / maximumbuffersize); i++)
using FileStream MMOSFile = new(MMOSPath, FileMode.Open, FileAccess.Read, FileShare.Read);
for (int i = 1; i <= (uint)Math.Truncate((decimal)length / maximumBufferSize); i++)
{
Progress.IncreaseProgress(1);
byte[] data = new byte[maximumbuffersize];
MMOSFile.Read(data, 0, maximumbuffersize);
byte[] data = new byte[maximumBufferSize];
MMOSFile.Read(data, 0, maximumBufferSize);
LoadMmosBinary(length, (uint)offset, false, data);
offset += maximumbuffersize;
offset += maximumBufferSize;
}
if (length - offset != 0)
@@ -955,7 +955,6 @@ namespace WPinternals
SwitchToMmosContext();
ResetPhone();
}
LogFile.EndAction("FlashMMOS");
}
+63 -6
View File
@@ -20,7 +20,6 @@
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
@@ -136,7 +135,11 @@ namespace WPinternals
internal static long GetFileLengthFromURL(string URL)
{
long Length = 0;
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(URL);
WebRequest webReq = WebRequest.Create(URL);
if (webReq is HttpWebRequest req)
{
req.Method = "HEAD";
req.ServicePoint.ConnectionLimit = 10;
using (WebResponse resp = req.GetResponse())
@@ -145,6 +148,18 @@ namespace WPinternals
}
return Length;
}
else if (webReq is FileWebRequest filereq)
{
webReq.Method = "HEAD";
using (WebResponse resp = webReq.GetResponse())
{
long.TryParse(resp.Headers.Get("Content-Length"), out Length);
}
return Length;
}
return 0;
}
internal static string GetFileNameFromURL(string URL)
{
@@ -672,7 +687,7 @@ namespace WPinternals
Failed
};
internal class DownloadEntry : INotifyPropertyChanged
internal class DownloadEntry : INotifyPropertyChanged, IProgress<GeneralDownloadProgress>
{
private readonly SynchronizationContext UIContext;
public event PropertyChangedEventHandler PropertyChanged = delegate { };
@@ -681,7 +696,8 @@ namespace WPinternals
internal string URL;
internal string[] URLCollection;
internal string Folder;
internal HttpClient Client;
//internal HttpClient Client;
internal HttpDownloader Client;
internal long SpeedIndex = -1;
internal long[] Speeds = new long[10];
internal long LastBytesReceived;
@@ -703,11 +719,42 @@ namespace WPinternals
{
Size = DownloadsViewModel.GetFileLengthFromURL(URL);
Client = new HttpClient();
_ = Client.DownloadFileAsync(Uri, Path.Combine(Folder, DownloadsViewModel.GetFileNameFromURL(Uri.LocalPath)), Client_DownloadProgressChanged, Client_DownloadFileCompleted);
//Client = new HttpClient();
//_ = Client.DownloadFileAsync(Uri, Path.Combine(Folder, DownloadsViewModel.GetFileNameFromURL(Uri.LocalPath)), Client_DownloadProgressChanged, Client_DownloadFileCompleted);
Client = new(Folder, 4);
_ = Client.DownloadAsync([new FileDownloadInformation(URL, DownloadsViewModel.GetFileNameFromURL(Uri.LocalPath), Size, null, null)], this);
}).Start();
}
public void Report(GeneralDownloadProgress e)
{
foreach (FileDownloadStatus status in e.DownloadedStatus)
{
if (status == null)
{
continue;
}
if (status.FileStatus == FileStatus.Failed || status.FileStatus == FileStatus.Failed)
{
Client_DownloadFileCompleted(true);
}
if (status.FileStatus == FileStatus.Completed)
{
Client_DownloadFileCompleted(false);
}
if (status.FileStatus == FileStatus.Downloading)
{
Client_DownloadProgressChanged(new HttpClientDownloadProgress(status.DownloadedBytes, status.File.FileSize));
}
}
}
private void Client_DownloadFileCompleted(bool Error)
{
void Finish()
@@ -718,6 +765,9 @@ namespace WPinternals
{
if (URLCollection?.Any(c => App.DownloadManager.DownloadList.Any(d => d.URL == c)) != true) // if there are no files left to download from this collection, then call the callback-function.
{
Client.Dispose();
Client = null;
string[] Files;
if (URLCollection == null)
{
@@ -738,8 +788,15 @@ namespace WPinternals
}
}
if (UIContext == null)
{
Finish();
}
else
{
UIContext?.Post(d => Finish(), null);
}
}
private void Client_DownloadProgressChanged(HttpClientDownloadProgress e)
{
+619
View File
@@ -0,0 +1,619 @@
/*
* Copyright (c) Gustave Monce and Contributors
* Copyright (c) ADeltaX and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
namespace WPinternals
{
public class GeneralDownloadProgress
{
public long EstimatedTotalBytes;
public long DownloadedTotalBytes;
public int NumFilesDownloadedSuccessfully;
public int NumFilesDownloadedUnsuccessfully;
public int NumFiles;
public FileDownloadStatus[] DownloadedStatus;
}
public enum FileStatus
{
Downloading,
Verifying,
Completed,
Expired,
Failed
}
public class FileDownloadStatus
{
public FileStatus FileStatus;
public long DownloadedBytes;
public long HashedBytes;
public FileDownloadInformation File;
public FileDownloadStatus(FileDownloadInformation file)
{
File = file;
}
}
public class FileDownloadInformation
{
public string DownloadUrl
{
get; set;
}
public string FileName
{
get; set;
}
public long FileSize
{
get; set;
}
public string Hash
{
get; set;
}
public string HashAlgorithm
{
get; set;
}
public FileDownloadInformation(string DownloadUrl, string FileName, long FileSize, string Hash, string HashAlgorithm)
{
this.DownloadUrl = DownloadUrl;
this.FileName = FileName;
this.FileSize = FileSize;
this.Hash = Hash;
this.HashAlgorithm = HashAlgorithm;
}
}
public class HttpDownloader : IDisposable
{
private const long CHUNK_SIZE = 8_388_608 + 65_536; //Slice 8MB+64KB
private const string TEMP_DOWNLOAD_EXTENSION = ".dlTmp";
private readonly HttpClient _hc;
public string DownloadFolderPath
{
get; set;
}
public int DownloadThreads
{
get; set;
}
public int DownloadRetries
{
get; set;
}
public bool VerifyFiles
{
get; set;
}
public HttpDownloader(string downloadFolderPath, int downloadThreads = 4, bool verifyFiles = true, IWebProxy proxy = null, bool useSystemProxy = true)
{
HttpClientHandler filter = new()
{
AutomaticDecompression = DecompressionMethods.All,
MaxConnectionsPerServer = 512,
};
if (proxy != null || !useSystemProxy)
{
filter.Proxy = proxy;
}
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
filter.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; //For Linux, MS cert isn't trusted lol ¯\_(ツ)_/¯
}
_hc = new HttpClient(filter)
{
Timeout = TimeSpan.FromSeconds(10)
};
_hc.DefaultRequestHeaders.Connection.Add("keep-alive");
DownloadThreads = downloadThreads;
DownloadFolderPath = downloadFolderPath;
VerifyFiles = verifyFiles;
}
public async Task<bool> DownloadAsync(List<FileDownloadInformation> Files, IProgress<GeneralDownloadProgress> generalDownloadProgress, CancellationToken cancellationToken = default)
{
return await ParallelQueue(Files, DownloadFile, generalDownloadProgress, DownloadThreads, cancellationToken);
}
public async Task<bool> DownloadAsync(FileDownloadInformation File, IProgress<FileDownloadStatus> downloadProgress = null, CancellationToken cancellationToken = default)
{
return await DownloadFile(File, downloadProgress, cancellationToken);
}
private static async ValueTask<bool> ParallelQueue(List<FileDownloadInformation> items, Func<FileDownloadInformation, IProgress<FileDownloadStatus>, CancellationToken, ValueTask<bool>> func,
IProgress<GeneralDownloadProgress> generalProgress, int threads, CancellationToken cancellationToken)
{
Queue<FileDownloadInformation> pending = new(items);
Task<bool>[] workingSlots = new Task<bool>[threads];
int workingThreadsCount = 0;
GeneralDownloadProgress generalDownloadProgress = new()
{
DownloadedStatus = new FileDownloadStatus[threads],
NumFiles = items.Count
};
bool result = true;
while (pending.Count + workingThreadsCount != 0)
{
if (workingThreadsCount < threads && pending.Count != 0)
{
FileDownloadInformation item = pending.Dequeue();
FileDownloadStatus fileStatus = new(item);
Progress<FileDownloadStatus> progress = new();
workingThreadsCount++;
GetFreeSlotIndex(workingSlots, out int freeSlotIndex);
generalDownloadProgress.DownloadedStatus[freeSlotIndex] = fileStatus;
progress.ProgressChanged += (s, e) =>
{
generalDownloadProgress.DownloadedTotalBytes += e.DownloadedBytes - generalDownloadProgress.DownloadedStatus[freeSlotIndex].DownloadedBytes;
generalDownloadProgress.DownloadedStatus[freeSlotIndex].DownloadedBytes = e.DownloadedBytes;
generalDownloadProgress.DownloadedStatus[freeSlotIndex].HashedBytes = e.HashedBytes;
generalDownloadProgress.DownloadedStatus[freeSlotIndex].FileStatus = e.FileStatus;
generalProgress?.Report(generalDownloadProgress);
};
workingSlots[freeSlotIndex] = Task.Run(async () => await func(item, progress, cancellationToken));
}
else
{
_ = await Task.WhenAny(workingSlots.Where(t => t != null));
for (int i = 0; i < workingSlots.Length; i++)
{
if (workingSlots[i]?.IsCompleted == true)
{
if (workingSlots[i].Result)
{
generalDownloadProgress.NumFilesDownloadedSuccessfully++;
}
else
{
generalDownloadProgress.NumFilesDownloadedUnsuccessfully++;
result = false;
}
workingThreadsCount--;
workingSlots[i].Dispose();
workingSlots[i] = null;
}
}
}
cancellationToken.ThrowIfCancellationRequested();
}
return result;
}
private async ValueTask<bool> DownloadFile(FileDownloadInformation downloadFile, IProgress<FileDownloadStatus> progress, CancellationToken cancellationToken)
{
return await HttpDownload(DownloadFolderPath, downloadFile, _hc, VerifyFiles, progress, cancellationToken: cancellationToken);
}
private static async ValueTask<bool> HttpDownload(string basePath, FileDownloadInformation downloadFile, HttpClient httpClient, bool verifyFiles,
IProgress<FileDownloadStatus> downloadProgress = null, int bufferSize = 65_536, CancellationToken cancellationToken = default)
{
long currRange = 0;
long chunk = CHUNK_SIZE;
long totalBytesRead = 0;
long hashedBytes = 0;
int blockBufferSize = bufferSize;
try
{
//This path will be used for downloaded and validated files, moved/renamed
string filePath = Path.Combine(basePath, downloadFile.FileName);
//This path will be used for downloading files
string tempFilePath = filePath + TEMP_DOWNLOAD_EXTENSION;
//If we have an already completed file, prefer this under certain conditions.
if (File.Exists(tempFilePath) && File.Exists(filePath))
{
FileInfo tmpFileInfo = new(tempFilePath);
FileInfo fileInfo = new(filePath);
if (tmpFileInfo.Length == downloadFile.FileSize)
{
File.Delete(filePath);
}
else if (fileInfo.Length == downloadFile.FileSize)
{
File.Delete(tempFilePath);
}
else
{
File.Delete(filePath);
}
}
if (File.Exists(tempFilePath))
{
FileInfo tmpFileInfo = new(tempFilePath);
if (tmpFileInfo.Length == downloadFile.FileSize)
{
/*//Decrypted file should match estimated bytes.
//Imagine if it crashed during hashing, the file may be valid.
//So... lets rename this file so it can be verified.
File.Move(tempFilePath, filePath);
totalBytesRead = tmpFileInfo.Length;*/
File.Delete(tempFilePath);
}
else if (tmpFileInfo.Length < downloadFile.FileSize)
{
//Download the remaining part
currRange = tmpFileInfo.Length;
totalBytesRead = tmpFileInfo.Length;
}
else
{
//If it's bigger then we have a problem.
//Just... delete the file.
File.Delete(tempFilePath);
}
}
if (File.Exists(filePath))
{
//If the filename was renamed then the download must have been completed successfully,
//we just hash to be sure that the file hasn't been tampered
if (verifyFiles)
{
FileStream fileStreamToHash = File.Open(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
totalBytesRead = fileStreamToHash.Length;
bool hashResult = await HashWithProgress(fileStreamToHash);
fileStreamToHash.Dispose();
if (hashResult)
{
//Nice, the file is ok, return success.
return true;
}
else
{
//The file is not ok.
//It's better to delete this file and redownload from scratch
//instead of partially downloading the missing parts and found later that the entire file was corrupted.
File.Delete(filePath);
//This range may have been changed before (e.g. when assuming for temp. file), so let's set it to 0
currRange = 0;
totalBytesRead = 0;
hashedBytes = 0;
}
}
else
{
//At your own risk lol
FileInfo fileInfo = new(filePath);
downloadProgress?.Report(new FileDownloadStatus(downloadFile)
{
DownloadedBytes = fileInfo.Length,
FileStatus = FileStatus.Completed
});
return true;
}
}
//Before we need to create a directory.
_ = Directory.CreateDirectory(Path.GetDirectoryName(filePath));
//Open the file as stream.
using FileStream streamToWriteTo = File.Open(tempFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read);
//Set the seek position to current range position (via totalBytesRead or currRange). This is needed.
_ = streamToWriteTo.Seek(totalBytesRead, SeekOrigin.Begin);
using HttpRequestMessage httpRequestMessageHead = new(HttpMethod.Head, new Uri(downloadFile.DownloadUrl));
using HttpResponseMessage response = await httpClient.SendAsync(httpRequestMessageHead, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
//Technically the server reports both content-length and Accept-Ranges.
long? contentLength = response.Content.Headers.ContentLength;
bool hasAcceptRanges = response.Headers.AcceptRanges.Contains("bytes");
//This is just a fallback in case the file delivery server goes nuts.
if (!hasAcceptRanges)
{
//We don't have a way to download from a specific range, hence we set the position to 0
//and download everything from scratch... Sadly.
//TODO
_ = streamToWriteTo.Seek(0, SeekOrigin.Begin);
downloadProgress?.Report(new FileDownloadStatus(downloadFile)
{
DownloadedBytes = 0,
FileStatus = FileStatus.Downloading
});
//TODO: add download reporting for this
//TODO: may throw an exception if the server suddently closes the connection (e.g. file expired.)
using HttpResponseMessage fullFileResp = await httpClient.GetAsync(downloadFile.DownloadUrl, cancellationToken);
using Stream streamToReadFrom = await fullFileResp.Content.ReadAsStreamAsync();
await streamToReadFrom.CopyToAsync(streamToWriteTo);
downloadProgress?.Report(new FileDownloadStatus(downloadFile)
{
DownloadedBytes = contentLength.Value,
FileStatus = FileStatus.Downloading
});
//just an assumption
_ = streamToWriteTo.Seek(0, SeekOrigin.Begin);
return await HashWithProgress(streamToWriteTo);
}
while (currRange < contentLength.Value)
{
//Calculate range values
if (currRange + chunk >= contentLength.Value)
{
chunk = contentLength.Value - currRange - 1;
}
//Create request for range and send it, return asap (we just need the header to see if the status code is ok)
using HttpRequestMessage requestMessageRange = CreateRequestHeaderForRange(HttpMethod.Get, downloadFile.DownloadUrl, currRange, currRange + chunk);
using HttpResponseMessage filePartResp = await httpClient.SendAsync(requestMessageRange, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
//increment to the next range
currRange += chunk + 1;
//If the server has replied with 200 ok/206 partial content
if (filePartResp.IsSuccessStatusCode)
{
//get the underlying stream
using Stream streamToReadFrom = await filePartResp.Content.ReadAsStreamAsync();
int bytesRead;
byte[] buffer = new byte[blockBufferSize];
//read the content
//TODO: it may throw an exception (stream closed because file expired?)
//In that case we would wrap into another try catch and try to read the reason behind this
while ((bytesRead = await streamToReadFrom.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0)
{
totalBytesRead += bytesRead;
//simply write to the file
await streamToWriteTo.WriteAsync(buffer, 0, bytesRead, cancellationToken);
//report progress
downloadProgress?.Report(new FileDownloadStatus(downloadFile)
{
DownloadedBytes = totalBytesRead,
FileStatus = FileStatus.Downloading
});
}
}
else
{
if (filePartResp.StatusCode is HttpStatusCode.Forbidden or
HttpStatusCode.NotFound or
HttpStatusCode.Unauthorized)
{
//The url is expired.
//Report that is expired and return false
//We need to keep the file, because it's just incomplete, not corrupted.
downloadProgress?.Report(new FileDownloadStatus(downloadFile)
{
DownloadedBytes = totalBytesRead,
FileStatus = FileStatus.Expired
});
return false;
}
else
{
throw new Exception(filePartResp.ReasonPhrase);
}
}
}
//last left block if any
if (verifyFiles)
{
_ = streamToWriteTo.Seek(0, SeekOrigin.Begin);
bool hashResult = await HashWithProgress(streamToWriteTo);
if (hashResult)
{
streamToWriteTo.Close();
File.Move(tempFilePath, filePath);
}
return hashResult;
}
else
{
streamToWriteTo.Close();
File.Move(tempFilePath, filePath);
downloadProgress?.Report(new FileDownloadStatus(downloadFile)
{
DownloadedBytes = totalBytesRead,
FileStatus = FileStatus.Completed
});
return true;
}
}
catch //(Exception ex)
{
downloadProgress?.Report(new FileDownloadStatus(downloadFile)
{
DownloadedBytes = totalBytesRead,
FileStatus = FileStatus.Failed
});
return false;
}
//C# 7 - Local Functions
async ValueTask<bool> HashWithProgress(Stream strm)
{
Progress<long> progressHashedBytes = new();
progressHashedBytes.ProgressChanged += (s, e) =>
{
hashedBytes = e;
downloadProgress?.Report(new FileDownloadStatus(downloadFile)
{
DownloadedBytes = totalBytesRead,
HashedBytes = hashedBytes,
FileStatus = FileStatus.Verifying
});
};
bool hashMatches = true;
switch (downloadFile.HashAlgorithm?.ToLower())
{
case "sha1":
hashMatches = await IsDownloadedFileValidSHA1(strm, downloadFile.Hash,
progressHashedBytes, cancellationToken);
break;
case "sha256":
hashMatches = await IsDownloadedFileValidSHA256(strm, downloadFile.Hash,
progressHashedBytes, cancellationToken);
break;
}
if (hashMatches)
{
downloadProgress?.Report(new FileDownloadStatus(downloadFile)
{
DownloadedBytes = totalBytesRead,
HashedBytes = hashedBytes,
FileStatus = FileStatus.Completed
});
return true;
}
else
{
downloadProgress?.Report(new FileDownloadStatus(downloadFile)
{
DownloadedBytes = totalBytesRead,
HashedBytes = hashedBytes,
FileStatus = FileStatus.Failed
});
return false;
}
}
}
#region Helpers
private static async ValueTask<bool> IsDownloadedFileValidSHA256(Stream fileStream, string base64Hash, IProgress<long> progress = null, CancellationToken cancellationToken = default)
{
using SHA256 hashAlgo = SHA256.Create();
byte[] hashByte = await ComputeHashAsyncT(hashAlgo, fileStream, progress, cancellationToken: cancellationToken);
return ByteArraySpanCompare(Convert.FromBase64String(base64Hash), hashByte);
}
private static async ValueTask<bool> IsDownloadedFileValidSHA1(Stream fileStream, string base64Hash, IProgress<long> progress = null, CancellationToken cancellationToken = default)
{
using SHA1 hashAlgo = SHA1.Create();
byte[] hashByte = await ComputeHashAsyncT(hashAlgo, fileStream, progress, cancellationToken: cancellationToken);
return ByteArraySpanCompare(Convert.FromBase64String(base64Hash), hashByte);
}
public static async ValueTask<byte[]> ComputeHashAsyncT(HashAlgorithm hashAlgorithm, Stream fileStream, IProgress<long> progress = null,
int bufferSize = 1_048_576, CancellationToken cancellationToken = default)
{
int readBytes;
long totalBytesRead = 0;
long bufSizeEffective = Math.Min(bufferSize, fileStream.Length);
byte[] buffer = new byte[bufSizeEffective];
using MemoryStream ms = new(buffer);
using CryptoStream cs = new(ms, hashAlgorithm, CryptoStreamMode.Write);
while ((readBytes = await fileStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0)
{
await cs.WriteAsync(buffer, 0, readBytes, cancellationToken);
ms.Position = 0;
totalBytesRead += readBytes;
progress?.Report(totalBytesRead);
cancellationToken.ThrowIfCancellationRequested();
}
cs.FlushFinalBlock();
return hashAlgorithm.Hash;
}
private static bool ByteArraySpanCompare(ReadOnlySpan<byte> a1, ReadOnlySpan<byte> a2)
{
return a1.SequenceEqual(a2);
}
private static void GetFreeSlotIndex<T>(T[] array, out int firstFreeTaskIndex)
{
firstFreeTaskIndex = -1;
for (int i = 0; i < array.Length; i++)
{
if (array[i] == null)
{
firstFreeTaskIndex = i;
break;
}
}
}
private static HttpRequestMessage CreateRequestHeaderForRange(HttpMethod method, string url, long from, long to)
{
HttpRequestMessage request = new(method, new Uri(url));
request.Headers.Range = new System.Net.Http.Headers.RangeHeaderValue(from, to);
return request;
}
#endregion
public void Dispose()
{
//Release all resources that have been instanced and used
_hc.Dispose();
GC.SuppressFinalize(this);
}
}
}
+54 -61
View File
@@ -21,6 +21,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Policy;
using System.Threading;
using System.Threading.Tasks;
@@ -724,8 +725,6 @@ namespace WPinternals
LumiaPhoneInfoAppPhoneInfo PhoneInfoAppInfo = ((LumiaPhoneInfoAppModel)PhoneNotifier.CurrentModel).ReadPhoneInfo(ExtendedInfo: true);
new Thread(async () =>
{
bool ModernFlashApp = ((LumiaPhoneInfoAppModel)PhoneNotifier.CurrentModel).ReadPhoneInfo().PhoneInfoAppVersionMajor >= 2;
if (ModernFlashApp)
{
@@ -736,11 +735,15 @@ namespace WPinternals
((LumiaPhoneInfoAppModel)PhoneNotifier.CurrentModel).ContinueBoot();
}
Task.Run(async () =>
{
if (PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_Flash)
{
await PhoneNotifier.WaitForArrival();
}
void Finish()
{
if (PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_Flash)
{
throw new WPinternalsException("Unexpected Mode");
@@ -750,8 +753,6 @@ namespace WPinternals
LumiaFlashAppPhoneInfo Info = FlashModel.ReadPhoneInfo(ExtendedInfo: true);
if (Info.MmosOverUsbSupported)
{
new Thread(() =>
{
LogFile.BeginAction("SwitchToLabelMode");
@@ -768,30 +769,9 @@ namespace WPinternals
(string ENOSWFileUrl, string DPLFileUrl) = LumiaDownloadModel.SearchENOSW(PhoneInfoAppInfo.Type, Info.Firmware);
SetWorkingStatus($"Downloading {PhoneInfoAppInfo.Type} Test Mode package...", MaxProgressValue: 100);
UIContext?.Post(d => SetWorkingStatus($"Downloading {PhoneInfoAppInfo.Type} Test Mode package...", MaxProgressValue: 100), null);
DownloadEntry downloadEntry = new(ENOSWFileUrl, TempFolder, null, (string[] Files, object State) =>
{
App.Config.AddSecWimToRepository(ENOSWFileUrl, Info.Firmware);
ModeSwitchProgressWrapper("Initializing Flash...", null);
string MMOSPath = $"{TempFolder}\\{DownloadsViewModel.GetFileNameFromURL(ENOSWFileUrl)}";
PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
FileInfo info = new(MMOSPath);
uint length = uint.Parse(info.Length.ToString());
const int maximumbuffersize = 0x00240000;
uint totalcounts = (uint)Math.Truncate((decimal)length / maximumbuffersize);
SetWorkingStatus("Flashing Test Mode package...", MaxProgressValue: 100);
ProgressUpdater progressUpdater = new(totalcounts + 1, (int i, TimeSpan? time) => UpdateWorkingStatus(null, CurrentProgressValue: (ulong)i));
FlashModel.FlashMMOS(MMOSPath, progressUpdater);
SetWorkingStatus("And now booting phone to MMOS...", "If the phone stays on the lightning cog screen for a while, you may need to unplug and replug the phone to continue the boot process.");
}, null);
DownloadEntry downloadEntry = new(ENOSWFileUrl, TempFolder, [ENOSWFileUrl], ENOSWDownloadCompleted, Info.Firmware);
downloadEntry.PropertyChanged += (object sender, System.ComponentModel.PropertyChangedEventArgs e) =>
{
@@ -799,7 +779,7 @@ namespace WPinternals
{
int progress = (sender as DownloadEntry)?.Progress ?? 0;
ulong.TryParse(progress.ToString(), out ulong progressret);
UpdateWorkingStatus(null, CurrentProgressValue: progressret);
UIContext?.Post(d => UpdateWorkingStatus(null, CurrentProgressValue: progressret), null);
}
};
}
@@ -810,7 +790,6 @@ namespace WPinternals
}
LogFile.EndAction("SwitchToLabelMode");
}).Start();
}
else
{
@@ -825,7 +804,10 @@ namespace WPinternals
ModeSwitchProgressWrapper("Rebooting phone to Label mode", null);
LogFile.Log("Rebooting phone to Label mode", LogType.FileAndConsole);
}
}).Start();
}
UIContext?.Post(d => Finish(), null);
});
}
private void SwitchFromFlashToLabelMode(bool Continuation = false)
@@ -839,8 +821,6 @@ namespace WPinternals
LumiaFlashAppPhoneInfo Info = ((LumiaFlashAppModel)PhoneNotifier.CurrentModel).ReadPhoneInfo(ExtendedInfo: true);
new Thread(async () =>
{
bool ModernFlashApp = ((LumiaFlashAppModel)PhoneNotifier.CurrentModel).ReadPhoneInfo().FlashAppProtocolVersionMajor >= 2;
if (ModernFlashApp)
{
@@ -851,6 +831,8 @@ namespace WPinternals
((LumiaFlashAppModel)PhoneNotifier.CurrentModel).SwitchToPhoneInfoAppContextLegacy();
}
Task.Run(async () =>
{
if (PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_PhoneInfo)
{
await PhoneNotifier.WaitForArrival();
@@ -879,6 +861,8 @@ namespace WPinternals
await PhoneNotifier.WaitForArrival();
}
void Finish()
{
if (PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_Flash)
{
throw new WPinternalsException("Unexpected Mode");
@@ -887,8 +871,6 @@ namespace WPinternals
LumiaFlashAppModel FlashModel = (LumiaFlashAppModel)PhoneNotifier.CurrentModel;
if (Info.MmosOverUsbSupported)
{
new Thread(() =>
{
LogFile.BeginAction("SwitchToLabelMode");
@@ -896,7 +878,7 @@ namespace WPinternals
{
ModeSwitchProgressWrapper(ProgressText, null);
string TempFolder = Environment.GetEnvironmentVariable("TEMP") + @"\WPInternals";
string TempFolder = $@"{Environment.GetEnvironmentVariable("TEMP")}\WPInternals";
if (PhoneInfoAppInfo.Type == "RM-1152")
{
@@ -905,30 +887,9 @@ namespace WPinternals
(string ENOSWFileUrl, string DPLFileUrl) = LumiaDownloadModel.SearchENOSW(PhoneInfoAppInfo.Type, Info.Firmware);
SetWorkingStatus($"Downloading {PhoneInfoAppInfo.Type} Test Mode package...", MaxProgressValue: 100);
UIContext?.Post(d => SetWorkingStatus($"Downloading {PhoneInfoAppInfo.Type} Test Mode package...", MaxProgressValue: 100), null);
DownloadEntry downloadEntry = new(ENOSWFileUrl, TempFolder, null, (string[] Files, object State) =>
{
App.Config.AddSecWimToRepository(ENOSWFileUrl, Info.Firmware);
ModeSwitchProgressWrapper("Initializing Flash...", null);
string MMOSPath = $"{TempFolder}\\{DownloadsViewModel.GetFileNameFromURL(ENOSWFileUrl)}";
PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
FileInfo info = new(MMOSPath);
uint length = uint.Parse(info.Length.ToString());
const int maximumbuffersize = 0x00240000;
uint totalcounts = (uint)Math.Truncate((decimal)length / maximumbuffersize);
SetWorkingStatus("Flashing Test Mode package...", MaxProgressValue: 100);
ProgressUpdater progressUpdater = new(totalcounts + 1, (int i, TimeSpan? time) => UpdateWorkingStatus(null, CurrentProgressValue: (ulong)i));
FlashModel.FlashMMOS(MMOSPath, progressUpdater);
SetWorkingStatus("And now booting phone to MMOS...", "If the phone stays on the lightning cog screen for a while, you may need to unplug and replug the phone to continue the boot process.");
}, null);
DownloadEntry downloadEntry = new(ENOSWFileUrl, TempFolder, [ENOSWFileUrl], ENOSWDownloadCompleted, Info.Firmware);
downloadEntry.PropertyChanged += (object sender, System.ComponentModel.PropertyChangedEventArgs e) =>
{
@@ -936,7 +897,7 @@ namespace WPinternals
{
int progress = (sender as DownloadEntry)?.Progress ?? 0;
ulong.TryParse(progress.ToString(), out ulong progressret);
UpdateWorkingStatus(null, CurrentProgressValue: progressret);
UIContext?.Post(d => UpdateWorkingStatus(null, CurrentProgressValue: progressret), null);
}
};
}
@@ -947,7 +908,6 @@ namespace WPinternals
}
LogFile.EndAction("SwitchToLabelMode");
}).Start();
}
else
{
@@ -962,7 +922,40 @@ namespace WPinternals
ModeSwitchProgressWrapper("Rebooting phone to Label mode", null);
LogFile.Log("Rebooting phone to Label mode", LogType.FileAndConsole);
}
}).Start();
}
UIContext?.Post(d => Finish(), null);
});
}
private void ENOSWDownloadCompleted(string[] URLs, object State)
{
string Firmware = (string)State;
string ENOSWFileUrl = URLs[0];
string Name = DownloadsViewModel.GetFileNameFromURL(ENOSWFileUrl);
string TempFolder = $@"{Environment.GetEnvironmentVariable("TEMP")}\WPInternals";
LumiaFlashAppModel FlashModel = (LumiaFlashAppModel)PhoneNotifier.CurrentModel;
ModeSwitchProgressWrapper("Initializing Flash...", null);
string MMOSPath = Path.Combine(TempFolder, Name);
App.Config.AddSecWimToRepository(MMOSPath, Firmware);
PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
FileInfo info = new(MMOSPath);
uint fileLength = uint.Parse(info.Length.ToString());
const int maximumBufferSize = 0x00240000;
uint chunkCount = (uint)Math.Truncate((decimal)fileLength / maximumBufferSize);
UIContext?.Post(d => SetWorkingStatus("Flashing Test Mode package...", MaxProgressValue: 100), null);
ProgressUpdater progressUpdater = new(chunkCount + 1, (int i, TimeSpan? time) => UIContext?.Post(d => UpdateWorkingStatus(null, CurrentProgressValue: (ulong)i), null));
FlashModel.FlashMMOS(MMOSPath, progressUpdater);
ModeSwitchProgressWrapper("And now booting phone to MMOS...", "If the phone stays on the lightning cog screen for a while, you may need to unplug and replug the phone to continue the boot process.");
}
private void SwitchFromPhoneInfoToMassStorageMode(bool Continuation = false)