mirror of
https://github.com/ReneLergner/WPinternals.git
synced 2026-06-14 03:16:40 +10:00
fix: Download issues with ENOSW
This commit is contained in:
@@ -927,36 +927,35 @@ namespace WPinternals
|
|||||||
uint length = uint.Parse(info.Length.ToString());
|
uint length = uint.Parse(info.Length.ToString());
|
||||||
|
|
||||||
int offset = 0;
|
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))
|
using FileStream MMOSFile = new(MMOSPath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
|
|
||||||
|
for (int i = 1; i <= (uint)Math.Truncate((decimal)length / maximumBufferSize); i++)
|
||||||
{
|
{
|
||||||
for (int i = 1; i <= (uint)Math.Truncate((decimal)length / maximumbuffersize); i++)
|
Progress.IncreaseProgress(1);
|
||||||
{
|
byte[] data = new byte[maximumBufferSize];
|
||||||
Progress.IncreaseProgress(1);
|
MMOSFile.Read(data, 0, maximumBufferSize);
|
||||||
byte[] data = new byte[maximumbuffersize];
|
|
||||||
MMOSFile.Read(data, 0, maximumbuffersize);
|
|
||||||
|
|
||||||
LoadMmosBinary(length, (uint)offset, false, data);
|
LoadMmosBinary(length, (uint)offset, false, data);
|
||||||
|
|
||||||
offset += maximumbuffersize;
|
offset += maximumBufferSize;
|
||||||
}
|
|
||||||
|
|
||||||
if (length - offset != 0)
|
|
||||||
{
|
|
||||||
Progress.IncreaseProgress(1);
|
|
||||||
|
|
||||||
byte[] data = new byte[length - offset];
|
|
||||||
MMOSFile.Read(data, 0, (int)(length - offset));
|
|
||||||
LoadMmosBinary(length, (uint)offset, false, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
SwitchToMmosContext();
|
|
||||||
ResetPhone();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (length - offset != 0)
|
||||||
|
{
|
||||||
|
Progress.IncreaseProgress(1);
|
||||||
|
|
||||||
|
byte[] data = new byte[length - offset];
|
||||||
|
MMOSFile.Read(data, 0, (int)(length - offset));
|
||||||
|
LoadMmosBinary(length, (uint)offset, false, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitchToMmosContext();
|
||||||
|
ResetPhone();
|
||||||
|
|
||||||
LogFile.EndAction("FlashMMOS");
|
LogFile.EndAction("FlashMMOS");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
|
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
@@ -136,14 +135,30 @@ namespace WPinternals
|
|||||||
internal static long GetFileLengthFromURL(string URL)
|
internal static long GetFileLengthFromURL(string URL)
|
||||||
{
|
{
|
||||||
long Length = 0;
|
long Length = 0;
|
||||||
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(URL);
|
|
||||||
req.Method = "HEAD";
|
WebRequest webReq = WebRequest.Create(URL);
|
||||||
req.ServicePoint.ConnectionLimit = 10;
|
|
||||||
using (WebResponse resp = req.GetResponse())
|
if (webReq is HttpWebRequest req)
|
||||||
{
|
{
|
||||||
long.TryParse(resp.Headers.Get("Content-Length"), out Length);
|
req.Method = "HEAD";
|
||||||
|
req.ServicePoint.ConnectionLimit = 10;
|
||||||
|
using (WebResponse resp = req.GetResponse())
|
||||||
|
{
|
||||||
|
long.TryParse(resp.Headers.Get("Content-Length"), out Length);
|
||||||
|
}
|
||||||
|
return Length;
|
||||||
}
|
}
|
||||||
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)
|
internal static string GetFileNameFromURL(string URL)
|
||||||
@@ -672,7 +687,7 @@ namespace WPinternals
|
|||||||
Failed
|
Failed
|
||||||
};
|
};
|
||||||
|
|
||||||
internal class DownloadEntry : INotifyPropertyChanged
|
internal class DownloadEntry : INotifyPropertyChanged, IProgress<GeneralDownloadProgress>
|
||||||
{
|
{
|
||||||
private readonly SynchronizationContext UIContext;
|
private readonly SynchronizationContext UIContext;
|
||||||
public event PropertyChangedEventHandler PropertyChanged = delegate { };
|
public event PropertyChangedEventHandler PropertyChanged = delegate { };
|
||||||
@@ -681,7 +696,8 @@ namespace WPinternals
|
|||||||
internal string URL;
|
internal string URL;
|
||||||
internal string[] URLCollection;
|
internal string[] URLCollection;
|
||||||
internal string Folder;
|
internal string Folder;
|
||||||
internal HttpClient Client;
|
//internal HttpClient Client;
|
||||||
|
internal HttpDownloader Client;
|
||||||
internal long SpeedIndex = -1;
|
internal long SpeedIndex = -1;
|
||||||
internal long[] Speeds = new long[10];
|
internal long[] Speeds = new long[10];
|
||||||
internal long LastBytesReceived;
|
internal long LastBytesReceived;
|
||||||
@@ -703,11 +719,42 @@ namespace WPinternals
|
|||||||
{
|
{
|
||||||
Size = DownloadsViewModel.GetFileLengthFromURL(URL);
|
Size = DownloadsViewModel.GetFileLengthFromURL(URL);
|
||||||
|
|
||||||
Client = new HttpClient();
|
//Client = new HttpClient();
|
||||||
_ = Client.DownloadFileAsync(Uri, Path.Combine(Folder, DownloadsViewModel.GetFileNameFromURL(Uri.LocalPath)), Client_DownloadProgressChanged, Client_DownloadFileCompleted);
|
|
||||||
|
//_ = 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();
|
}).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)
|
private void Client_DownloadFileCompleted(bool Error)
|
||||||
{
|
{
|
||||||
void Finish()
|
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.
|
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;
|
string[] Files;
|
||||||
if (URLCollection == null)
|
if (URLCollection == null)
|
||||||
{
|
{
|
||||||
@@ -738,7 +788,14 @@ namespace WPinternals
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UIContext?.Post(d => Finish(), null);
|
if (UIContext == null)
|
||||||
|
{
|
||||||
|
Finish();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UIContext?.Post(d => Finish(), null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Client_DownloadProgressChanged(HttpClientDownloadProgress e)
|
private void Client_DownloadProgressChanged(HttpClientDownloadProgress e)
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Security.Policy;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@@ -724,34 +725,34 @@ namespace WPinternals
|
|||||||
|
|
||||||
LumiaPhoneInfoAppPhoneInfo PhoneInfoAppInfo = ((LumiaPhoneInfoAppModel)PhoneNotifier.CurrentModel).ReadPhoneInfo(ExtendedInfo: true);
|
LumiaPhoneInfoAppPhoneInfo PhoneInfoAppInfo = ((LumiaPhoneInfoAppModel)PhoneNotifier.CurrentModel).ReadPhoneInfo(ExtendedInfo: true);
|
||||||
|
|
||||||
new Thread(async () =>
|
bool ModernFlashApp = ((LumiaPhoneInfoAppModel)PhoneNotifier.CurrentModel).ReadPhoneInfo().PhoneInfoAppVersionMajor >= 2;
|
||||||
|
if (ModernFlashApp)
|
||||||
{
|
{
|
||||||
bool ModernFlashApp = ((LumiaPhoneInfoAppModel)PhoneNotifier.CurrentModel).ReadPhoneInfo().PhoneInfoAppVersionMajor >= 2;
|
((LumiaPhoneInfoAppModel)PhoneNotifier.CurrentModel).SwitchToFlashAppContext();
|
||||||
if (ModernFlashApp)
|
}
|
||||||
{
|
else
|
||||||
((LumiaPhoneInfoAppModel)PhoneNotifier.CurrentModel).SwitchToFlashAppContext();
|
{
|
||||||
}
|
((LumiaPhoneInfoAppModel)PhoneNotifier.CurrentModel).ContinueBoot();
|
||||||
else
|
}
|
||||||
{
|
|
||||||
((LumiaPhoneInfoAppModel)PhoneNotifier.CurrentModel).ContinueBoot();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
if (PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_Flash)
|
if (PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_Flash)
|
||||||
{
|
{
|
||||||
await PhoneNotifier.WaitForArrival();
|
await PhoneNotifier.WaitForArrival();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_Flash)
|
void Finish()
|
||||||
{
|
{
|
||||||
throw new WPinternalsException("Unexpected Mode");
|
if (PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_Flash)
|
||||||
}
|
{
|
||||||
|
throw new WPinternalsException("Unexpected Mode");
|
||||||
|
}
|
||||||
|
|
||||||
LumiaFlashAppModel FlashModel = (LumiaFlashAppModel)PhoneNotifier.CurrentModel;
|
LumiaFlashAppModel FlashModel = (LumiaFlashAppModel)PhoneNotifier.CurrentModel;
|
||||||
LumiaFlashAppPhoneInfo Info = FlashModel.ReadPhoneInfo(ExtendedInfo: true);
|
LumiaFlashAppPhoneInfo Info = FlashModel.ReadPhoneInfo(ExtendedInfo: true);
|
||||||
|
|
||||||
if (Info.MmosOverUsbSupported)
|
if (Info.MmosOverUsbSupported)
|
||||||
{
|
|
||||||
new Thread(() =>
|
|
||||||
{
|
{
|
||||||
LogFile.BeginAction("SwitchToLabelMode");
|
LogFile.BeginAction("SwitchToLabelMode");
|
||||||
|
|
||||||
@@ -768,30 +769,9 @@ namespace WPinternals
|
|||||||
|
|
||||||
(string ENOSWFileUrl, string DPLFileUrl) = LumiaDownloadModel.SearchENOSW(PhoneInfoAppInfo.Type, Info.Firmware);
|
(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) =>
|
DownloadEntry downloadEntry = new(ENOSWFileUrl, TempFolder, [ENOSWFileUrl], ENOSWDownloadCompleted, Info.Firmware);
|
||||||
{
|
|
||||||
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.PropertyChanged += (object sender, System.ComponentModel.PropertyChangedEventArgs e) =>
|
downloadEntry.PropertyChanged += (object sender, System.ComponentModel.PropertyChangedEventArgs e) =>
|
||||||
{
|
{
|
||||||
@@ -799,7 +779,7 @@ namespace WPinternals
|
|||||||
{
|
{
|
||||||
int progress = (sender as DownloadEntry)?.Progress ?? 0;
|
int progress = (sender as DownloadEntry)?.Progress ?? 0;
|
||||||
ulong.TryParse(progress.ToString(), out ulong progressret);
|
ulong.TryParse(progress.ToString(), out ulong progressret);
|
||||||
UpdateWorkingStatus(null, CurrentProgressValue: progressret);
|
UIContext?.Post(d => UpdateWorkingStatus(null, CurrentProgressValue: progressret), null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -810,22 +790,24 @@ namespace WPinternals
|
|||||||
}
|
}
|
||||||
|
|
||||||
LogFile.EndAction("SwitchToLabelMode");
|
LogFile.EndAction("SwitchToLabelMode");
|
||||||
}).Start();
|
}
|
||||||
}
|
else
|
||||||
else
|
{
|
||||||
{
|
PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
|
||||||
PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
|
|
||||||
|
|
||||||
byte[] BootModeFlagCommand = [0x4E, 0x4F, 0x4B, 0x58, 0x46, 0x57, 0x00, 0x55, 0x42, 0x46, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00]; // NOKFW UBF
|
byte[] BootModeFlagCommand = [0x4E, 0x4F, 0x4B, 0x58, 0x46, 0x57, 0x00, 0x55, 0x42, 0x46, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00]; // NOKFW UBF
|
||||||
byte[] RebootCommand = [0x4E, 0x4F, 0x4B, 0x52]; // NOKR
|
byte[] RebootCommand = [0x4E, 0x4F, 0x4B, 0x52]; // NOKR
|
||||||
|
|
||||||
BootModeFlagCommand[0x0F] = 0x59;
|
BootModeFlagCommand[0x0F] = 0x59;
|
||||||
((LumiaFlashAppModel)PhoneNotifier.CurrentModel).ExecuteRawMethod(BootModeFlagCommand);
|
((LumiaFlashAppModel)PhoneNotifier.CurrentModel).ExecuteRawMethod(BootModeFlagCommand);
|
||||||
((LumiaFlashAppModel)PhoneNotifier.CurrentModel).ExecuteRawVoidMethod(RebootCommand);
|
((LumiaFlashAppModel)PhoneNotifier.CurrentModel).ExecuteRawVoidMethod(RebootCommand);
|
||||||
ModeSwitchProgressWrapper("Rebooting phone to Label mode", null);
|
ModeSwitchProgressWrapper("Rebooting phone to Label mode", null);
|
||||||
LogFile.Log("Rebooting phone to Label mode", LogType.FileAndConsole);
|
LogFile.Log("Rebooting phone to Label mode", LogType.FileAndConsole);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}).Start();
|
|
||||||
|
UIContext?.Post(d => Finish(), null);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SwitchFromFlashToLabelMode(bool Continuation = false)
|
private void SwitchFromFlashToLabelMode(bool Continuation = false)
|
||||||
@@ -839,18 +821,18 @@ namespace WPinternals
|
|||||||
|
|
||||||
LumiaFlashAppPhoneInfo Info = ((LumiaFlashAppModel)PhoneNotifier.CurrentModel).ReadPhoneInfo(ExtendedInfo: true);
|
LumiaFlashAppPhoneInfo Info = ((LumiaFlashAppModel)PhoneNotifier.CurrentModel).ReadPhoneInfo(ExtendedInfo: true);
|
||||||
|
|
||||||
new Thread(async () =>
|
bool ModernFlashApp = ((LumiaFlashAppModel)PhoneNotifier.CurrentModel).ReadPhoneInfo().FlashAppProtocolVersionMajor >= 2;
|
||||||
|
if (ModernFlashApp)
|
||||||
{
|
{
|
||||||
bool ModernFlashApp = ((LumiaFlashAppModel)PhoneNotifier.CurrentModel).ReadPhoneInfo().FlashAppProtocolVersionMajor >= 2;
|
((LumiaFlashAppModel)PhoneNotifier.CurrentModel).SwitchToPhoneInfoAppContext();
|
||||||
if (ModernFlashApp)
|
}
|
||||||
{
|
else
|
||||||
((LumiaFlashAppModel)PhoneNotifier.CurrentModel).SwitchToPhoneInfoAppContext();
|
{
|
||||||
}
|
((LumiaFlashAppModel)PhoneNotifier.CurrentModel).SwitchToPhoneInfoAppContextLegacy();
|
||||||
else
|
}
|
||||||
{
|
|
||||||
((LumiaFlashAppModel)PhoneNotifier.CurrentModel).SwitchToPhoneInfoAppContextLegacy();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
if (PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_PhoneInfo)
|
if (PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_PhoneInfo)
|
||||||
{
|
{
|
||||||
await PhoneNotifier.WaitForArrival();
|
await PhoneNotifier.WaitForArrival();
|
||||||
@@ -879,16 +861,16 @@ namespace WPinternals
|
|||||||
await PhoneNotifier.WaitForArrival();
|
await PhoneNotifier.WaitForArrival();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_Flash)
|
void Finish()
|
||||||
{
|
{
|
||||||
throw new WPinternalsException("Unexpected Mode");
|
if (PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_Flash)
|
||||||
}
|
{
|
||||||
|
throw new WPinternalsException("Unexpected Mode");
|
||||||
|
}
|
||||||
|
|
||||||
LumiaFlashAppModel FlashModel = (LumiaFlashAppModel)PhoneNotifier.CurrentModel;
|
LumiaFlashAppModel FlashModel = (LumiaFlashAppModel)PhoneNotifier.CurrentModel;
|
||||||
|
|
||||||
if (Info.MmosOverUsbSupported)
|
if (Info.MmosOverUsbSupported)
|
||||||
{
|
|
||||||
new Thread(() =>
|
|
||||||
{
|
{
|
||||||
LogFile.BeginAction("SwitchToLabelMode");
|
LogFile.BeginAction("SwitchToLabelMode");
|
||||||
|
|
||||||
@@ -896,7 +878,7 @@ namespace WPinternals
|
|||||||
{
|
{
|
||||||
ModeSwitchProgressWrapper(ProgressText, null);
|
ModeSwitchProgressWrapper(ProgressText, null);
|
||||||
|
|
||||||
string TempFolder = Environment.GetEnvironmentVariable("TEMP") + @"\WPInternals";
|
string TempFolder = $@"{Environment.GetEnvironmentVariable("TEMP")}\WPInternals";
|
||||||
|
|
||||||
if (PhoneInfoAppInfo.Type == "RM-1152")
|
if (PhoneInfoAppInfo.Type == "RM-1152")
|
||||||
{
|
{
|
||||||
@@ -905,30 +887,9 @@ namespace WPinternals
|
|||||||
|
|
||||||
(string ENOSWFileUrl, string DPLFileUrl) = LumiaDownloadModel.SearchENOSW(PhoneInfoAppInfo.Type, Info.Firmware);
|
(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) =>
|
DownloadEntry downloadEntry = new(ENOSWFileUrl, TempFolder, [ENOSWFileUrl], ENOSWDownloadCompleted, Info.Firmware);
|
||||||
{
|
|
||||||
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.PropertyChanged += (object sender, System.ComponentModel.PropertyChangedEventArgs e) =>
|
downloadEntry.PropertyChanged += (object sender, System.ComponentModel.PropertyChangedEventArgs e) =>
|
||||||
{
|
{
|
||||||
@@ -936,7 +897,7 @@ namespace WPinternals
|
|||||||
{
|
{
|
||||||
int progress = (sender as DownloadEntry)?.Progress ?? 0;
|
int progress = (sender as DownloadEntry)?.Progress ?? 0;
|
||||||
ulong.TryParse(progress.ToString(), out ulong progressret);
|
ulong.TryParse(progress.ToString(), out ulong progressret);
|
||||||
UpdateWorkingStatus(null, CurrentProgressValue: progressret);
|
UIContext?.Post(d => UpdateWorkingStatus(null, CurrentProgressValue: progressret), null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -947,22 +908,54 @@ namespace WPinternals
|
|||||||
}
|
}
|
||||||
|
|
||||||
LogFile.EndAction("SwitchToLabelMode");
|
LogFile.EndAction("SwitchToLabelMode");
|
||||||
}).Start();
|
}
|
||||||
}
|
else
|
||||||
else
|
{
|
||||||
{
|
PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
|
||||||
PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
|
|
||||||
|
|
||||||
byte[] BootModeFlagCommand = [0x4E, 0x4F, 0x4B, 0x58, 0x46, 0x57, 0x00, 0x55, 0x42, 0x46, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00]; // NOKFW UBF
|
byte[] BootModeFlagCommand = [0x4E, 0x4F, 0x4B, 0x58, 0x46, 0x57, 0x00, 0x55, 0x42, 0x46, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00]; // NOKFW UBF
|
||||||
byte[] RebootCommand = [0x4E, 0x4F, 0x4B, 0x52]; // NOKR
|
byte[] RebootCommand = [0x4E, 0x4F, 0x4B, 0x52]; // NOKR
|
||||||
|
|
||||||
BootModeFlagCommand[0x0F] = 0x59;
|
BootModeFlagCommand[0x0F] = 0x59;
|
||||||
((LumiaFlashAppModel)PhoneNotifier.CurrentModel).ExecuteRawMethod(BootModeFlagCommand);
|
((LumiaFlashAppModel)PhoneNotifier.CurrentModel).ExecuteRawMethod(BootModeFlagCommand);
|
||||||
((LumiaFlashAppModel)PhoneNotifier.CurrentModel).ExecuteRawVoidMethod(RebootCommand);
|
((LumiaFlashAppModel)PhoneNotifier.CurrentModel).ExecuteRawVoidMethod(RebootCommand);
|
||||||
ModeSwitchProgressWrapper("Rebooting phone to Label mode", null);
|
ModeSwitchProgressWrapper("Rebooting phone to Label mode", null);
|
||||||
LogFile.Log("Rebooting phone to Label mode", LogType.FileAndConsole);
|
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)
|
private void SwitchFromPhoneInfoToMassStorageMode(bool Continuation = false)
|
||||||
|
|||||||
Reference in New Issue
Block a user