12 Commits
128 changed files with 6268 additions and 1513 deletions
+10
View File
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>_7zip</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
@@ -4,7 +4,7 @@ using System.Text;
namespace DiscUtils.CoreCompat namespace DiscUtils.CoreCompat
{ {
internal static class EncodingHelper public static class EncodingHelper
{ {
private static bool _registered; private static bool _registered;
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DiscUtils.Core" Version="0.16.13" />
</ItemGroup>
</Project>
@@ -28,7 +28,7 @@ using System.Text.RegularExpressions;
namespace DiscUtils.Internal namespace DiscUtils.Internal
{ {
internal static class Utilities public static class Utilities
{ {
/// <summary> /// <summary>
/// Converts between two arrays. /// Converts between two arrays.
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\DiscUtils.Core\DiscUtils.Core.WPI.csproj" />
</ItemGroup>
</Project>
+1789 -3
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -77,14 +77,14 @@ namespace WPinternals
Registration.CheckExpiration(); Registration.CheckExpiration();
string PatchDefintionsXml; string PatchDefintionsXml;
string PatchDefintionsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PatchDefintions.xml"); string PatchDefintionsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Assets", "PatchDefintions.xml");
if (File.Exists(PatchDefintionsPath)) if (File.Exists(PatchDefintionsPath))
{ {
PatchDefintionsXml = File.ReadAllText(PatchDefintionsPath); PatchDefintionsXml = File.ReadAllText(PatchDefintionsPath);
} }
else else
{ {
using Stream stream = System.Reflection.Assembly.GetEntryAssembly().GetManifestResourceStream("WPinternals.PatchDefinitions.xml"); using Stream stream = System.Reflection.Assembly.GetEntryAssembly().GetManifestResourceStream("WPinternals.Assets.PatchDefinitions.xml");
using StreamReader sr = new(stream); using StreamReader sr = new(stream);
PatchDefintionsXml = sr.ReadToEnd(); PatchDefintionsXml = sr.ReadToEnd();
} }

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

-257
View File
@@ -28,7 +28,6 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using WPinternals.Config; using WPinternals.Config;
using WPinternals.HelperClasses; using WPinternals.HelperClasses;
using WPinternals.Models.Lumia.MSR;
using WPinternals.Models.Lumia.NCSd; using WPinternals.Models.Lumia.NCSd;
using WPinternals.Models.UEFIApps.BootMgr; using WPinternals.Models.UEFIApps.BootMgr;
using WPinternals.Models.UEFIApps.Flash; using WPinternals.Models.UEFIApps.Flash;
@@ -1268,262 +1267,6 @@ namespace WPinternals
LogFile.Log("Root access enabled on image", LogType.FileAndConsole); LogFile.Log("Root access enabled on image", LogType.FileAndConsole);
break; break;
case "downloademergency":
LogFile.Log("Command: Download Emergency files", LogType.FileAndConsole);
Notifier = new PhoneNotifierViewModel();
UIContext.Send(s => Notifier.Start(), null);
if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Normal)
{
NormalModel = (NokiaCareSuiteModel)Notifier.CurrentModel;
ProductType = NormalModel.ExecuteJsonMethodAsString("ReadManufacturerModelName", "ManufacturerModelName");
if (ProductType.Contains('_'))
{
ProductType = ProductType.Substring(0, ProductType.IndexOf('_'));
}
}
else if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader)
{
BootMgrModel = (LumiaBootManagerAppModel)Notifier.CurrentModel;
BootManagerInfo = BootMgrModel.ReadPhoneInfo();
//ProductType = BootManagerInfo.Type; // TODO: FIXME
ProductType = "";
}
else if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Flash)
{
FlashModel = (LumiaFlashAppModel)Notifier.CurrentModel;
FlashInfo = FlashModel.ReadPhoneInfo();
//ProductType = FlashInfo.Type; // TODO: FIXME
ProductType = "";
}
else
{
NormalModel = (NokiaCareSuiteModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Normal);
ProductType = NormalModel.ExecuteJsonMethodAsString("ReadManufacturerModelName", "ManufacturerModelName");
if (ProductType.Contains('_'))
{
ProductType = ProductType.Substring(0, ProductType.IndexOf('_'));
}
}
URLs = LumiaDownloadModel.SearchEmergencyFiles(ProductType);
if (URLs != null)
{
DownloadFolder = args.Length >= 3
? args[2]
: Environment.ExpandEnvironmentVariables("%ALLUSERSPROFILE%\\WPInternals\\Repository\\" + ProductType.ToUpper());
if (!Directory.Exists(DownloadFolder))
{
Directory.CreateDirectory(DownloadFolder);
}
LogFile.Log("Download folder: " + DownloadFolder, LogType.FileAndConsole);
for (int i = 0; i < URLs.Length; i++)
{
LogFile.Log("URL: " + URLs[i], LogType.FileAndConsole);
URI = new Uri(URLs[i]);
EmergencyFileName = Path.GetFileName(URI.LocalPath);
LogFile.Log("File: " + EmergencyFileName, LogType.FileAndConsole);
EmergencyFilePath = Path.Combine(DownloadFolder, EmergencyFileName);
if (i == 0)
{
ProgrammerPath = EmergencyFilePath;
}
else
{
PayloadPath = EmergencyFilePath;
}
LogFile.Log("Downloading...", LogType.FileAndConsole);
using (System.Net.WebClient myWebClient = new())
{
await myWebClient.DownloadFileTaskAsync(URLs[i], EmergencyFilePath);
}
LogFile.Log("Download finished", LogType.FileAndConsole);
}
App.Config.AddEmergencyToRepository(ProductType, ProgrammerPath, PayloadPath);
}
Notifier.Stop();
break;
case "downloademergencybyproducttype":
LogFile.Log("Command: Download Emergency files", LogType.FileAndConsole);
if (args.Length < 3)
{
throw new WPinternalsException("Wrong number of arguments. Usage: WPinternals.exe -DownloadEmergencyByProductType <Product type> <Optional: Download folder>");
}
ProductType = args[2];
URLs = LumiaDownloadModel.SearchEmergencyFiles(ProductType);
if (URLs != null)
{
DownloadFolder = args.Length >= 4
? args[3]
: Environment.ExpandEnvironmentVariables("%ALLUSERSPROFILE%\\WPInternals\\Repository\\" + ProductType.ToUpper());
if (!Directory.Exists(DownloadFolder))
{
Directory.CreateDirectory(DownloadFolder);
}
LogFile.Log("Download folder: " + DownloadFolder, LogType.FileAndConsole);
for (int i = 0; i < URLs.Length; i++)
{
LogFile.Log("URL: " + URLs[i], LogType.FileAndConsole);
URI = new Uri(URLs[i]);
EmergencyFileName = Path.GetFileName(URI.LocalPath);
LogFile.Log("File: " + EmergencyFileName, LogType.FileAndConsole);
EmergencyFilePath = Path.Combine(DownloadFolder, EmergencyFileName);
if (i == 0)
{
ProgrammerPath = EmergencyFilePath;
}
else
{
PayloadPath = EmergencyFilePath;
}
LogFile.Log("Downloading...", LogType.FileAndConsole);
using (System.Net.WebClient myWebClient = new())
{
await myWebClient.DownloadFileTaskAsync(URLs[i], EmergencyFilePath);
}
LogFile.Log("Download finished", LogType.FileAndConsole);
}
App.Config.AddEmergencyToRepository(ProductType, ProgrammerPath, PayloadPath);
}
break;
case "downloadall":
LogFile.Log("Command: Download all", LogType.FileAndConsole);
Notifier = new PhoneNotifierViewModel();
UIContext.Send(s => Notifier.Start(), null);
if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Normal)
{
NormalModel = (NokiaCareSuiteModel)Notifier.CurrentModel;
ProductType = NormalModel.ExecuteJsonMethodAsString("ReadManufacturerModelName", "ManufacturerModelName");
if (ProductType.Contains('_'))
{
ProductType = ProductType.Substring(0, ProductType.IndexOf('_'));
}
}
else if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader)
{
BootMgrModel = (LumiaBootManagerAppModel)Notifier.CurrentModel;
BootManagerInfo = BootMgrModel.ReadPhoneInfo();
//ProductType = BootManagerInfo.Type; // TODO: FIXME
ProductType = "";
}
else if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Flash)
{
FlashModel = (LumiaFlashAppModel)Notifier.CurrentModel;
FlashInfo = FlashModel.ReadPhoneInfo();
//ProductType = FlashInfo.Type; // TODO: FIXME
ProductType = "";
}
else
{
NormalModel = (NokiaCareSuiteModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Normal);
ProductType = NormalModel.ExecuteJsonMethodAsString("ReadManufacturerModelName", "ManufacturerModelName");
if (ProductType.Contains('_'))
{
ProductType = ProductType.Substring(0, ProductType.IndexOf('_'));
}
}
DownloadFolder = args.Length >= 3
? args[2]
: Environment.ExpandEnvironmentVariables("%ALLUSERSPROFILE%\\WPInternals\\Repository\\" + ProductType.ToUpper());
if (!Directory.Exists(DownloadFolder))
{
Directory.CreateDirectory(DownloadFolder);
}
URLs = LumiaDownloadModel.SearchEmergencyFiles(ProductType);
if (URLs != null)
{
for (int i = 0; i < URLs.Length; i++)
{
LogFile.Log("URL: " + URLs[i], LogType.FileAndConsole);
URI = new Uri(URLs[i]);
EmergencyFileName = Path.GetFileName(URI.LocalPath);
LogFile.Log("File: " + EmergencyFileName, LogType.FileAndConsole);
EmergencyFilePath = Path.Combine(DownloadFolder, EmergencyFileName);
if (i == 0)
{
ProgrammerPath = EmergencyFilePath;
}
else
{
PayloadPath = EmergencyFilePath;
}
LogFile.Log("Downloading...", LogType.FileAndConsole);
using (System.Net.WebClient myWebClient = new())
{
await myWebClient.DownloadFileTaskAsync(URLs[i], EmergencyFilePath);
}
LogFile.Log("Download finished", LogType.FileAndConsole);
}
App.Config.AddEmergencyToRepository(ProductType, ProgrammerPath, PayloadPath);
}
if (!App.Config.FFURepository.Any(e => App.PatchEngine.PatchDefinitions.First(p => p.Name == "SecureBootHack-V2-EFIESP").TargetVersions.Any(v => v.Description == e.OSVersion)))
{
throw new WPinternalsException("Unable to find compatible FFU", "No donor-FFU has been found in the repository with a supported OS version. You can add a donor-FFU within the download section of the tool or by using the command line. A donor-FFU can be for a different device and a different CPU than your device. It is only used to gather Operating System specific binaries to be patched and used as part of the unlock process.");
}
Notifier.Stop();
break;
case "downloadallbyproducttype":
LogFile.Log("Command: Download all by Product Type", LogType.FileAndConsole);
if (args.Length < 3)
{
throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -DownloadAllByProductType <Product type> <Optional: Download folder>");
}
ProductType = args[2];
LogFile.Log("Product type: " + ProductType, LogType.FileAndConsole);
DownloadFolder = args.Length >= 4
? args[3]
: Environment.ExpandEnvironmentVariables("%ALLUSERSPROFILE%\\WPInternals\\Repository\\" + ProductType.ToUpper());
if (!Directory.Exists(DownloadFolder))
{
Directory.CreateDirectory(DownloadFolder);
}
URLs = LumiaDownloadModel.SearchEmergencyFiles(ProductType);
if (URLs != null)
{
for (int i = 0; i < URLs.Length; i++)
{
LogFile.Log("URL: " + URLs[i], LogType.FileAndConsole);
URI = new Uri(URLs[i]);
EmergencyFileName = Path.GetFileName(URI.LocalPath);
LogFile.Log("File: " + EmergencyFileName, LogType.FileAndConsole);
EmergencyFilePath = Path.Combine(DownloadFolder, EmergencyFileName);
if (i == 0)
{
ProgrammerPath = EmergencyFilePath;
}
else
{
PayloadPath = EmergencyFilePath;
}
LogFile.Log("Downloading...", LogType.FileAndConsole);
using (System.Net.WebClient myWebClient = new())
{
await myWebClient.DownloadFileTaskAsync(URLs[i], EmergencyFilePath);
}
LogFile.Log("Download finished", LogType.FileAndConsole);
}
App.Config.AddEmergencyToRepository(ProductType, ProgrammerPath, PayloadPath);
}
if (!App.Config.FFURepository.Any(e => App.PatchEngine.PatchDefinitions.First(p => p.Name == "SecureBootHack-V2-EFIESP").TargetVersions.Any(v => v.Description == e.OSVersion)))
{
throw new WPinternalsException("Unable to find compatible FFU", "No donor-FFU has been found in the repository with a supported OS version. You can add a donor-FFU within the download section of the tool or by using the command line. A donor-FFU can be for a different device and a different CPU than your device. It is only used to gather Operating System specific binaries to be patched and used as part of the unlock process.");
}
break;
case "rewritepartitionsfrommassstorage": case "rewritepartitionsfrommassstorage":
if (args.Length < 2) if (args.Length < 2)
{ {
+1 -1
View File
@@ -33,7 +33,7 @@ namespace WPinternals.Config
internal const bool IsPrerelease = false; internal const bool IsPrerelease = false;
#endif #endif
internal static readonly DateTime ExpirationDate = new(2025, 06, 21); internal static readonly DateTime ExpirationDate = new(2027, 4, 1);
internal static void CheckExpiration() internal static void CheckExpiration()
{ {
@@ -1,113 +0,0 @@
// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda
//
// 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.Net;
using System.Xml;
using WPinternals.HelperClasses;
namespace WPinternals.Models.Lumia.MSR
{
internal static class LumiaDownloadModel
{
internal static string[] SearchEmergencyFiles(string ProductType)
{
ProductType = ProductType.ToUpper();
if (ProductType.StartsWith("RM") && !ProductType.StartsWith("RM-"))
{
ProductType = "RM-" + ProductType[2..];
}
LogFile.Log("Getting Emergency files for: " + ProductType, LogType.FileAndConsole);
if (ProductType == "RM-1072" || ProductType == "RM-1073")
{
LogFile.Log("Due to mix-up in online-repository, redirecting to emergency files of RM-1113", LogType.FileAndConsole);
ProductType = "RM-1113";
}
List<string> Result = [];
WebClient Client = new();
string Src;
string FileName;
string Config = null;
try
{
Config = Client.DownloadString($"https://repairavoidance.blob.core.windows.net/packages/EmergencyFlash/{ProductType}/emergency_flash_config.xml");
}
catch (Exception ex)
{
LogFile.Log("An unexpected error happened", LogType.FileAndConsole);
LogFile.Log(ex.GetType().ToString(), LogType.FileAndConsole);
LogFile.Log(ex.Message, LogType.FileAndConsole);
LogFile.Log(ex.StackTrace, LogType.FileAndConsole);
LogFile.Log("Emergency files for " + ProductType + " not found", LogType.FileAndConsole);
return null;
}
Client.Dispose();
XmlDocument Doc = new();
Doc.LoadXml(Config);
// Hex
XmlNode Node = Doc.SelectSingleNode("//emergency_flash_config/hex_flasher");
if (Node != null)
{
FileName = Node.Attributes["image_path"].InnerText;
Src = $"https://repairavoidance.blob.core.windows.net/packages/EmergencyFlash/{ProductType}/{FileName}";
LogFile.Log("Hex-file: " + Src);
Result.Add(Src);
}
// Mbn
Node = Doc.SelectSingleNode("//emergency_flash_config/mbn_image");
if (Node != null)
{
FileName = Node.Attributes["image_path"].InnerText;
Src = $"https://repairavoidance.blob.core.windows.net/packages/EmergencyFlash/{ProductType}/{FileName}";
LogFile.Log("Mbn-file: " + Src);
Result.Add(Src);
}
// Ede
foreach (XmlNode SubNode in Doc.SelectNodes("//emergency_flash_config/first_boot_images/first_boot_image"))
{
FileName = SubNode.Attributes["image_path"].InnerText;
Src = $"https://repairavoidance.blob.core.windows.net/packages/EmergencyFlash/{ProductType}/{FileName}";
LogFile.Log("Firehose-programmer-file: " + Src);
Result.Add(Src);
}
// Edp
foreach (XmlNode SubNode in Doc.SelectNodes("//emergency_flash_config/second_boot_firehose_single_image/firehose_image"))
{
FileName = SubNode.Attributes["image_path"].InnerText;
Src = $"https://repairavoidance.blob.core.windows.net/packages/EmergencyFlash/{ProductType}/{FileName}";
LogFile.Log("Firehose-payload-file: " + Src);
Result.Add(Src);
}
return [.. Result];
}
}
}
+2 -10
View File
@@ -32,15 +32,7 @@ namespace WPinternals.Models.Lumia
public NokiaPhoneModel(string DevicePath) public NokiaPhoneModel(string DevicePath)
{ {
// Mass Storage device is not WinUSB Device = new USBDevice(DevicePath);
try
{
Device = new USBDevice(DevicePath);
}
catch (Exception ex)
{
LogFile.LogException(ex, LogType.FileOnly);
}
} }
public void ResetDevice() public void ResetDevice()
@@ -93,7 +85,7 @@ namespace WPinternals.Models.Lumia
if (disposing) if (disposing)
{ {
Device?.Dispose(); Device.Dispose();
} }
// Clean unmanaged resources here. // Clean unmanaged resources here.
+24 -5
View File
@@ -28,8 +28,9 @@ using WPinternals.Models.Lumia;
namespace WPinternals namespace WPinternals
{ {
internal class MassStorage : NokiaPhoneModel internal class MassStorage : IDisposable
{ {
protected bool Disposed = false;
internal string Drive = null; internal string Drive = null;
internal string PhysicalDrive = null; internal string PhysicalDrive = null;
internal string VolumeLabel = null; internal string VolumeLabel = null;
@@ -39,7 +40,7 @@ namespace WPinternals
private string Serial; private string Serial;
internal MassStorage(string DevicePath) : base(DevicePath) internal MassStorage(string DevicePath)
{ {
try try
{ {
@@ -131,7 +132,27 @@ namespace WPinternals
} }
} }
protected override void Dispose(bool disposing) /// <summary>
/// Disposes the UsbDevice including all unmanaged WinUSB handles. This function
/// should be called when the UsbDevice object is no longer in use, otherwise
/// unmanaged handles will remain open until the garbage collector finalizes the
/// object.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Finalizer for the UsbDevice. Disposes all unmanaged handles.
/// </summary>
~MassStorage()
{
Dispose(false);
}
protected void Dispose(bool disposing)
{ {
if (Disposed) if (Disposed)
{ {
@@ -141,8 +162,6 @@ namespace WPinternals
if (disposing) if (disposing)
{ {
CloseVolume(); CloseVolume();
base.Dispose(disposing);
} }
} }
@@ -0,0 +1,671 @@
/*
* MIT License
*
* Copyright (c) 2026 The DuoWOA authors
*
* 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 MadWizard.WinUSBNet;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Resources;
using System.Text;
namespace WPinternals.Models.SimpleIO
{
public partial class SimpleIOModel : IDisposable
{
// TODO: Use Locks!
// TODO: Timeouts!!
// TODO: Handle timeout
// TODO: Check functionality on true proto
// TODO: WIM Transfers!
// TODO: FFU Transfers!
// TODO: Missing command implementations nowhere to be found!
private bool Disposed = false;
private readonly USBDevice USBDevice;
private readonly USBPipe InputPipe;
private readonly USBPipe OutputPipe;
private readonly object UsbLock = new();
private bool HasCachedV2Results = false;
private (long curPosition, Guid guid, bool supportsFastFlash, bool supportsCompatFastFlash, int clientVersion, Guid DeviceUniqueID, string DeviceFriendlyName) CachedV2Results = (0, new Guid(), false, false, 0, new Guid(), "");
public SimpleIOModel(string DevicePath)
{
USBDevice = new USBDevice(DevicePath);
// LineSetState, 1
// Not sure why this is needed
byte LineSetState = 34;
USBDevice.ControlOut(33, LineSetState, 1, 0);
foreach (USBPipe Pipe in USBDevice.Pipes)
{
if (Pipe.IsIn)
{
InputPipe = Pipe;
}
if (Pipe.IsOut)
{
OutputPipe = Pipe;
}
}
if (InputPipe == null || OutputPipe == null)
{
throw new Exception("Invalid USB device!");
}
}
public byte[]? ExecuteRawMethod(byte[] RawMethod)
{
return ExecuteRawMethod(RawMethod, RawMethod.Length);
}
public byte[]? ExecuteRawMethod(byte[] RawMethod, int Length)
{
byte[] Buffer = new byte[0xF000]; // Should be at least 0x4408 for receiving the GPT packet.
byte[]? Result = null;
lock (UsbLock)
{
OutputPipe.Write(RawMethod, 0, Length);
try
{
int OutputLength = InputPipe.Read(Buffer);
Result = new byte[OutputLength];
System.Buffer.BlockCopy(Buffer, 0, Result, 0, OutputLength);
}
catch { } // Reboot command looses connection
}
return Result;
}
public void ExecuteRawVoidMethod(byte[] RawMethod)
{
ExecuteRawVoidMethod(RawMethod, RawMethod.Length);
}
public void ExecuteRawVoidMethod(byte[] RawMethod, int Length)
{
lock (UsbLock)
{
OutputPipe.Write(RawMethod, 0, Length);
}
}
public (long curPosition, Guid guid, bool supportsFastFlash, bool supportsCompatFastFlash, int clientVersion, Guid DeviceUniqueID, string DeviceFriendlyName) GetIdV2()
{
if (HasCachedV2Results)
{
return CachedV2Results;
}
int num = 0;
(long curPosition, Guid guid, bool supportsFastFlash, bool supportsCompatFastFlash, int clientVersion, Guid DeviceUniqueID, string DeviceFriendlyName) ID;
do
{
ID = GetId();
num++;
}
while (!ID.supportsFastFlash && !ID.supportsCompatFastFlash && ID.clientVersion < 2 && num < COMPATFLASH_MagicSequence);
CachedV2Results = ID;
HasCachedV2Results = true;
return ID;
}
private const int COMPATFLASH_MagicSequence = 1000;
private const byte INDEX_SUPPORTCOMPATFLASH = 15;
private const byte INDEX_SUPPORTV2CMDS = 14;
private const byte INDEX_SUPPORTFASTFLASH = 0;
// 1
public (long curPosition, Guid guid, bool supportsFastFlash, bool supportsCompatFastFlash, int clientVersion, Guid DeviceUniqueID, string DeviceFriendlyName) GetId()
{
bool supportsFastFlash = false;
bool supportsCompatFastFlash = false;
int clientVersion = 1;
byte[] buffer = ExecuteRawMethod([(byte)SioOpcode.SioId])!;
using MemoryStream stream = new(buffer);
using BinaryReader binaryReader = new(stream);
long curPosition = binaryReader.ReadInt64();
byte[] guidBuffer = binaryReader.ReadBytes(16);
Guid guid = new(guidBuffer);
if (guidBuffer[INDEX_SUPPORTFASTFLASH] >= 1)
{
supportsFastFlash = true;
}
else if (guidBuffer[INDEX_SUPPORTCOMPATFLASH] == 1)
{
supportsCompatFastFlash = true;
}
if (guidBuffer[INDEX_SUPPORTV2CMDS] >= 1)
{
clientVersion = guidBuffer[INDEX_SUPPORTV2CMDS] + 1;
}
Guid DeviceUniqueID = new(binaryReader.ReadBytes(16));
string DeviceFriendlyName = binaryReader.ReadString();
return (curPosition, guid, supportsFastFlash, supportsCompatFastFlash, clientVersion, DeviceUniqueID, DeviceFriendlyName);
}
// 7
public bool ContinueBoot()
{
byte[] buffer = ExecuteRawMethod([(byte)SioOpcode.SioSkip])!;
return buffer[0] == 3;
}
// 8
public bool EndTransfer()
{
HasCachedV2Results = false;
GetIdV2();
if (CachedV2Results.curPosition == 0L)
{
return true;
}
ExecuteRawVoidMethod([(byte)SioOpcode.SioSkip]);
byte[] array = new byte[16376];
do
{
InputPipe.Read(array, 0, array.Length);
}
while (array[0] == 5);
if (array[0] == 6)
{
HasCachedV2Results = false;
GetIdV2();
if (CachedV2Results.curPosition == 0L)
{
return true;
}
}
return false;
}
// 9
public void FlashDataFile(string path)
{
/*string fileName = Path.GetFileName(path);
this.InitFlashingStream();*/
ExecuteRawVoidMethod([(byte)SioOpcode.SioFile]);
/*this.packets.DataStream = this.GetStringStream(fileName);
this.TransferPackets(false);
this.WaitForEndResponse(false);
this.packets.DataStream = this.GetBufferedFileStream(path);
this.TransferPackets(false);
this.WaitForEndResponse(false);*/
}
// 10
public void Reboot()
{
ExecuteRawVoidMethod([(byte)SioOpcode.SioReboot]);
}
// 11
public bool EnterMassStorage()
{
byte[] buffer = ExecuteRawMethod([(byte)SioOpcode.SioMassStorage])!;
return buffer[0] == 3;
}
// 12
public void ReadDiskInfo(out int transferSize, out uint blockSize, out ulong lastBlock)
{
ExecuteRawVoidMethod([(byte)SioOpcode.SioGetDiskInfo]);
byte[] array = new byte[16];
lock (UsbLock)
{
InputPipe.Read(array, 0, array.Length);
}
int num = 0;
transferSize = BitConverter.ToInt32(array, num);
num += 4;
blockSize = BitConverter.ToUInt32(array, num);
num += 4;
lastBlock = BitConverter.ToUInt64(array, num);
num += 8;
}
// 13
public void ReadDataToBuffer(ulong diskOffset, byte[] buffer, int offset, int count, int diskTransferSize)
{
ExecuteRawVoidMethod([(byte)SioOpcode.SioReadDisk]);
byte[] buffer1 = new byte[16];
BitConverter.GetBytes(diskOffset).CopyTo(buffer1, 0);
BitConverter.GetBytes((ulong)count).CopyTo(buffer1, 8);
ExecuteRawVoidMethod(buffer1);
int offset1 = offset;
int count1;
for (int index = offset + count; offset1 < index; offset1 += count1)
{
count1 = diskTransferSize;
if (count1 > index - offset1)
{
count1 = index - offset1;
}
lock (UsbLock)
{
InputPipe.Read(buffer, offset1, count1);
}
byte[] singlebyte = new byte[1];
if (count1 % 512 == 0)
{
lock (UsbLock)
{
//InputPipe.Read(singlebyte, 0, singlebyte.Length);
}
}
}
}
// 14
public bool WriteDataFromBuffer(ulong diskOffset, byte[] buffer, int offset, int count, int diskTransferSize)
{
ExecuteRawVoidMethod([(byte)SioOpcode.SioWriteDisk]);
byte[] array = new byte[16];
BitConverter.GetBytes(diskOffset).CopyTo(array, 0);
BitConverter.GetBytes((ulong)((long)count)).CopyTo(array, 8);
ExecuteRawVoidMethod(array);
int i = offset;
int num2 = offset + count;
while (i < num2)
{
int num3 = diskTransferSize;
if (num3 > num2 - i)
{
num3 = num2 - i;
}
lock (UsbLock)
{
OutputPipe.Write(buffer, i, num3);
}
if (num3 % 512 == 0)
{
byte[] array2 = [];
lock (UsbLock)
{
OutputPipe.Write(array2, 0, array2.Length);
}
}
i += num3;
}
byte[] array3 = new byte[8];
lock (UsbLock)
{
InputPipe.Read(array3, 0, array3.Length);
}
if (count != (long)BitConverter.ToUInt64(array3, 0))
{
return false;
}
return true;
}
// 15
public bool ClearIdOverride()
{
byte[] buffer = ExecuteRawMethod([(byte)SioOpcode.SioClearIdOverride])!;
bool result = buffer[0] == 3;
// Refresh the ID
if (result)
{
HasCachedV2Results = false;
GetIdV2();
}
return result;
}
// 17
public Guid? GetSerialNumber()
{
byte[] buffer = ExecuteRawMethod([(byte)SioOpcode.SioSerialNumber])!;
return new Guid(buffer);
}
// 19
public uint SetBootMode(uint bootMode, string profileName)
{
uint num = 0x80000015U;
if (Encoding.Unicode.GetByteCount(profileName) >= 128)
{
num = 0x80000002U;
throw new Win32Exception(87);
}
uint num2 = 132U;
byte[] array = new byte[num2];
Array.Clear(array, 0, array.Length);
byte[] array2 = BitConverter.GetBytes(bootMode);
array2.CopyTo(array, 0);
array2 = Encoding.Unicode.GetBytes(profileName);
array2.CopyTo(array, 4);
ExecuteRawVoidMethod([(byte)SioOpcode.SioSetBootMode]);
OutputPipe.Write(array, 0, array.Length);
byte[] array3 = new byte[4];
InputPipe.Read(array3, 0, array3.Length);
num = BitConverter.ToUInt32(array3, 0);
return num;
}
// 22
public void GetDeviceVersion()
{
byte[] buffer = ExecuteRawMethod([(byte)SioOpcode.SioDeviceVersion])!;
Console.WriteLine(Convert.ToHexString(buffer));
}
// 23
public bool QueryForCommandAvailable(SioOpcode Cmd)
{
(_, Guid _, bool supportsFastFlash, bool _, int clientVersion, Guid _, string _) = GetIdV2();
if (clientVersion < 2)
{
return Cmd < SioOpcode.SioFastFlash || supportsFastFlash;
}
ExecuteRawVoidMethod([(byte)SioOpcode.SioQueryForCmd]);
byte[] buffer = ExecuteRawMethod([(byte)Cmd])!;
return buffer[0] != 0;
}
// 24
public string GetServicingLogs(string logFolderPath)
{
string? text = null;
if (!QueryForCommandAvailable(SioOpcode.SioGetUpdateLogs))
{
throw new Exception("Command not available");
}
if (string.IsNullOrEmpty(logFolderPath))
{
throw new ArgumentNullException(nameof(logFolderPath));
}
ExecuteRawVoidMethod([(byte)SioOpcode.SioGetUpdateLogs]);
byte[] array = new byte[262144];
int num = 0;
byte[] array2 = new byte[4];
int num2 = InputPipe.Read(array2, 0, array2.Length);
int num3 = BitConverter.ToInt32(array2, 0);
string text2 = Path.GetFullPath(logFolderPath);
Directory.CreateDirectory(text2);
text2 = Path.Combine(text2, Path.GetRandomFileName() + ".cab");
using (FileStream fileStream = File.Open(text2, FileMode.Create, FileAccess.Write))
{
do
{
Array.Clear(array, 0, array.Length);
num2 = InputPipe.Read(array, 0, array.Length);
num += num2;
fileStream.Write(array, 0, array.Length);
}
while (num != num3);
text = text2;
}
return text;
}
// 25
public void QueryDeviceUnlockId(out byte[] unlockId, out byte[] oemId, out byte[] platformId)
{
unlockId = new byte[32];
oemId = new byte[16];
platformId = new byte[16];
if (!QueryForCommandAvailable(SioOpcode.SioQueryDeviceUnlockId))
{
throw new Exception("Command not available");
}
ExecuteRawVoidMethod([(byte)SioOpcode.SioQueryDeviceUnlockId]);
byte[] numBuffer = new byte[4];
InputPipe.Read(numBuffer, 0, 4);
int num = BitConverter.ToInt32(numBuffer);
byte[] tmpBuffer = new byte[4];
InputPipe.Read(tmpBuffer, 0, 4);
InputPipe.Read(unlockId, 0, 32);
InputPipe.Read(oemId, 0, 16);
InputPipe.Read(platformId, 0, 16);
if (num != 0)
{
throw new Exception("Error while reading device unlock id, " + num);
}
}
// 26
public void RelockDeviceUnlockId()
{
if (!QueryForCommandAvailable(SioOpcode.SioRelockDeviceUnlockId))
{
throw new Exception("Command not available");
}
ExecuteRawVoidMethod([(byte)SioOpcode.SioRelockDeviceUnlockId]);
byte[] numBuffer = new byte[4];
InputPipe.Read(numBuffer, 0, 4);
int num = BitConverter.ToInt32(numBuffer);
if (num != 0)
{
throw new Exception("Error while relocking device unlock id, " + num);
}
}
// 27
public uint[] QueryUnlockTokenFiles()
{
byte[] array = new byte[16];
List<uint> list = [];
if (!QueryForCommandAvailable(SioOpcode.SioQueryUnlockTokenFiles))
{
throw new Exception("Command not available");
}
ExecuteRawVoidMethod([(byte)SioOpcode.SioQueryUnlockTokenFiles]);
byte[] numBuffer = new byte[4];
InputPipe.Read(numBuffer, 0, 4);
int num = BitConverter.ToInt32(numBuffer);
byte[] tmpBuffer = new byte[4];
InputPipe.Read(tmpBuffer, 0, 4);
InputPipe.Read(array, 0, 16);
BitArray bitArray = new BitArray(array);
uint num2 = 0U;
while (num2 < (ulong)((long)bitArray.Count))
{
if (bitArray.Get(Convert.ToInt32(num2)))
{
list.Add(num2);
}
num2 += 1U;
}
if (num != 0)
{
throw new Exception("Error while querying unlock token files, " + num);
}
return [.. list];
}
// 28
public void WriteUnlockTokenFile(uint unlockTokenId, byte[] fileData)
{
uint num = 0U;
uint num2 = (uint)fileData.Length;
if (1048576 < fileData.Length)
{
throw new ArgumentException("fileData");
}
if (127U < unlockTokenId)
{
throw new ArgumentException("unlockTokenId");
}
if (!QueryForCommandAvailable(SioOpcode.SioWriteUnlockTokenFile))
{
throw new Exception("Command not available");
}
ExecuteRawVoidMethod([(byte)SioOpcode.SioWriteUnlockTokenFile]);
OutputPipe.Write(BitConverter.GetBytes(num));
OutputPipe.Write(BitConverter.GetBytes(num2));
OutputPipe.Write(BitConverter.GetBytes(unlockTokenId));
OutputPipe.Write(fileData, 0, fileData.Length);
byte[] numBuffer = new byte[4];
InputPipe.Read(numBuffer, 0, 4);
int num3 = BitConverter.ToInt32(numBuffer);
if (num3 != 0)
{
throw new Exception("Error while writing unlock token files, " + num3);
}
}
// 29
public bool QueryBitlockerState()
{
if (!QueryForCommandAvailable(SioOpcode.SioQueryBitlockerState))
{
throw new Exception("Command not available");
}
ExecuteRawVoidMethod([(byte)SioOpcode.SioQueryBitlockerState]);
byte[] numBuffer = new byte[4];
InputPipe.Read(numBuffer, 0, 4);
int num = BitConverter.ToInt32(numBuffer);
byte[] flagBuffer = new byte[1];
InputPipe.Read(flagBuffer, 0, 1);
bool flag = flagBuffer[0] != 0;
if (num != 0)
{
throw new Exception("Error while reading bitlocker state, " + num);
}
return flag;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~SimpleIOModel()
{
Dispose(false);
}
public void Close()
{
USBDevice?.Dispose();
}
protected virtual void Dispose(bool disposing)
{
if (Disposed)
{
return;
}
if (disposing)
{
// Other disposables
}
// Clean unmanaged resources here.
Close();
Disposed = true;
}
}
}
+63
View File
@@ -0,0 +1,63 @@
/*
* MIT License
*
* Copyright (c) 2026 The DuoWOA authors
*
* 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.Text;
namespace WPinternals.Models.SimpleIO
{
public enum SioOpcode : byte
{
SioId = 1, //
SioFlash,
SioAck,
SioNack,
SioLog,
SioErr,
SioSkip, //
SioReset, //
SioFile, // WIP!
SioReboot, //
SioMassStorage, //
SioGetDiskInfo, //
SioReadDisk, //
SioWriteDisk, //
SioClearIdOverride, //
SioWim,
SioSerialNumber, //
SioExternalWim,
SioSetBootMode, //
SioFastFlash,
SioDeviceParams,
SioDeviceVersion,
SioQueryForCmd, //
SioGetUpdateLogs, //
SioQueryDeviceUnlockId, //
SioRelockDeviceUnlockId, //
SioQueryUnlockTokenFiles, //
SioWriteUnlockTokenFile, //
SioQueryBitlockerState, //
SioLast = 29
}
}
+33
View File
@@ -0,0 +1,33 @@
/*
* MIT License
*
* Copyright (c) 2024 The DuoWOA authors
*
* 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.
*/
namespace UnifiedFlashingPlatform
{
public enum AppType : byte
{
Min,
UEFI,
BOOT,
Max
};
}
+33
View File
@@ -0,0 +1,33 @@
/*
* MIT License
*
* Copyright (c) 2024 The DuoWOA authors
*
* 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.
*/
namespace UnifiedFlashingPlatform
{
public enum DeviceLogType
{
Min,
Flashing,
Servicing,
Max
}
}
@@ -0,0 +1,47 @@
/*
* MIT License
*
* Copyright (c) 2024 The DuoWOA authors
*
* 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.
*/
namespace UnifiedFlashingPlatform
{
public struct DeviceTargetingInfo
{
public string Manufacturer;
public string Family;
public string ProductName;
public string ProductVersion;
public string SKUNumber;
public string BaseboardManufacturer;
public string BaseboardProduct;
public override readonly string ToString()
{
return "Manufacturer: " + Manufacturer +
" - Family: " + Family +
" - Product Name: " + ProductName +
" - Product Version: " + ProductVersion +
" - SKU Number: " + SKUNumber +
" - Baseboard Manufacturer: " + BaseboardManufacturer +
" - Baseboard Product: " + BaseboardProduct;
}
}
}
+11
View File
@@ -0,0 +1,11 @@
namespace UnifiedFlashingPlatform
{
internal enum FfuProtocol
{
ProtocolSyncV1 = 1,
ProtocolAsyncV1 = 2,
ProtocolSyncV2 = 4,
ProtocolAsyncV2 = 8,
ProtocolAsyncV3 = 16
}
}
+41
View File
@@ -0,0 +1,41 @@
/*
* MIT License
*
* Copyright (c) 2024 The DuoWOA authors
*
* 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.
*/
namespace UnifiedFlashingPlatform
{
public struct FlashAppInfo
{
public byte ProtocolMajorVersion;
public byte ProtocolMinorVersion;
public byte ImplementationMajorVersion;
public byte ImplementationMinorVersion;
public override readonly string ToString()
{
return "ProtocolMajorVersion: " + ProtocolMajorVersion +
" - ProtocolMinorVersion: " + ProtocolMinorVersion +
" - ImplementationMajorVersion: " + ImplementationMajorVersion +
" - ImplementationMinorVersion: " + ImplementationMinorVersion;
}
}
}
+31
View File
@@ -0,0 +1,31 @@
/*
* MIT License
*
* Copyright (c) 2024 The DuoWOA authors
*
* 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.
*/
namespace UnifiedFlashingPlatform
{
public enum Mode : byte
{
DiagnosticMode,
Max
}
}
@@ -0,0 +1,39 @@
/*
* MIT License
*
* Copyright (c) 2024 The DuoWOA authors
*
* 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.
*/
namespace UnifiedFlashingPlatform
{
public struct ResetProtectionInfo
{
public bool IsResetProtectionEnabled;
public uint MajorVersion;
public uint MinorVersion;
public override readonly string ToString()
{
return "IsResetProtectionEnabled: " + IsResetProtectionEnabled +
" - MajorVersion: " + MajorVersion +
" - MinorVersion: " + MinorVersion;
}
}
}
@@ -0,0 +1,85 @@
using System;
using System.Drawing;
using System.Text;
using WPinternals.HelperClasses;
namespace UnifiedFlashingPlatform.UEFI
{
public struct BOOT_OPTION
{
public byte BootOrderIndex;
public ushort BootOption;
public LoadOptionAttribute Attributes;
public string Description;
public string DevicePath;
public string CommandLine;
public ushort DescriptionStringOffset;
public ushort DevicePathStringOffset;
public ushort CommandLineStringOffset;
public ushort TotalSize;
public static BOOT_OPTION ReadFromBuffer(byte[] Buffer, int offset)
{
byte BootOrderIndex = Buffer[offset];
var Attributes = (LoadOptionAttribute)BigEndian.ToUInt32(Buffer, offset + 1);
ushort BootOption = BigEndian.ToUInt16(Buffer, offset + 5);
// Latest
ushort DescriptionStringOffset = BigEndian.ToUInt16(Buffer, offset + 7);
ushort DevicePathStringOffset = BigEndian.ToUInt16(Buffer, offset + 9);
ushort CommandLineStringOffset = BigEndian.ToUInt16(Buffer, offset + 11);
ushort TotalSize = BigEndian.ToUInt16(Buffer, offset + 13);
string Description = Encoding.Unicode.GetString(Buffer, offset + DescriptionStringOffset, DevicePathStringOffset - DescriptionStringOffset);
string DevicePath;
string CommandLine = "";
if (CommandLineStringOffset == 0)
{
DevicePath = Encoding.Unicode.GetString(Buffer, offset + DevicePathStringOffset, TotalSize - DevicePathStringOffset);
}
else
{
DevicePath = Encoding.Unicode.GetString(Buffer, offset + DevicePathStringOffset, CommandLineStringOffset - DevicePathStringOffset);
CommandLine = Encoding.Unicode.GetString(Buffer, offset + CommandLineStringOffset, TotalSize - CommandLineStringOffset);
}
// Older
/*ushort DescriptionStringLength = BigEndian.ToUInt16(Buffer, offset + 7);
ushort DevicePathStringLength = BigEndian.ToUInt16(Buffer, offset + 9);
string Description = Encoding.Unicode.GetString(Buffer, offset + 11, DescriptionStringLength);
string DevicePath = Encoding.Unicode.GetString(Buffer, offset + 11 + DescriptionStringLength, DevicePathStringLength);
string CommandLine = "";
ushort DescriptionStringOffset = 11;
ushort DevicePathStringOffset = (ushort)(11 + DescriptionStringLength);
ushort CommandLineStringOffset = 0;
ushort TotalSize = (ushort)(11 + DescriptionStringLength + DevicePathStringLength);*/
return new BOOT_OPTION()
{
BootOrderIndex = BootOrderIndex,
Attributes = Attributes,
BootOption = BootOption,
Description = Description,
DevicePath = DevicePath,
CommandLine = CommandLine,
DescriptionStringOffset = DescriptionStringOffset,
DevicePathStringOffset = DevicePathStringOffset,
CommandLineStringOffset = CommandLineStringOffset,
TotalSize = TotalSize
};
}
public override readonly string ToString()
{
return "BootOrderIndex: " + BootOrderIndex +
" - Attributes: " + Attributes +
" - BootOption: " + BootOption +
" - Description: " + Description +
" - DevicePath: " + DevicePath +
" - CommandLine: " + CommandLine;
}
}
}
+53
View File
@@ -0,0 +1,53 @@
using System;
using System.Text;
namespace UnifiedFlashingPlatform.UEFI
{
public struct FILE_INFO
{
public ulong Size;
public ulong FileSize;
public ulong PhysicalSize;
public UefiDateTime CreateTime;
public UefiDateTime LastAccessTime;
public UefiDateTime ModificationTime;
public FileAttribute Attribute;
public string FileName;
public static FILE_INFO ReadFromBuffer(byte[] Buffer, int offset)
{
ulong size = BitConverter.ToUInt64(Buffer, offset);
ulong fileSize = BitConverter.ToUInt64(Buffer, offset + 8);
ulong physicalSize = BitConverter.ToUInt64(Buffer, offset + 16);
UefiDateTime createTime = UefiDateTime.ReadFromBuffer(Buffer, offset + 24);
UefiDateTime lastAccessTime = UefiDateTime.ReadFromBuffer(Buffer, offset + 40);
UefiDateTime modificationTime = UefiDateTime.ReadFromBuffer(Buffer, offset + 56);
ulong attribute = BitConverter.ToUInt64(Buffer, offset + 72);
string fileName = Encoding.Unicode.GetString(Buffer, offset + 80, (int)(size - 80));
return new FILE_INFO()
{
Size = size,
FileSize = fileSize,
PhysicalSize = physicalSize,
CreateTime = createTime,
LastAccessTime = lastAccessTime,
ModificationTime = modificationTime,
Attribute = (FileAttribute)attribute,
FileName = fileName
};
}
public override readonly string ToString()
{
return "Size: " + Size +
"- File Size: " + FileSize +
"- Physical Size: " + PhysicalSize +
"- Create Time: " + CreateTime.ToDateTime() +
"- Last Access Time: " + LastAccessTime.ToDateTime() +
"- Modification Time: " + ModificationTime.ToDateTime() +
"- Attribute: " + Attribute +
"- FileName: " + FileName;
}
}
}
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnifiedFlashingPlatform.UEFI
{
[Flags]
public enum FileAttribute : ulong
{
EfiFileReadOnly = 1UL,
EfiFileHidden = 2UL,
EfiFileSystem = 4UL,
EfiFileReserved = 8UL,
EfiFileDirectory = 16UL,
EfiFileArchive = 32UL,
EfiFileValidAttr = 55UL
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnifiedFlashingPlatform.UEFI
{
[Flags]
public enum LoadOptionAttribute : uint
{
LoadOptionCategoryBoot = 0U,
LoadOptionActive = 1U,
LoadOptionForceReconnect = 2U,
LoadOptionHidden = 8U,
LoadOptionCategoryApp = 256U,
LoadOptionCategory = 7936U
}
}
@@ -0,0 +1,62 @@
using System;
namespace UnifiedFlashingPlatform.UEFI
{
public struct UefiDateTime
{
public ushort year;
public byte month;
public byte day;
public byte hour;
public byte minute;
public byte second;
public uint nanosecond;
public short timezone;
public byte daylight;
public static UefiDateTime ReadFromBuffer(byte[] Buffer, int offset)
{
ushort year = BitConverter.ToUInt16(Buffer, offset);
byte month = Buffer[offset + 2];
byte day = Buffer[offset + 3];
byte hour = Buffer[offset + 4];
byte minute = Buffer[offset + 5];
byte second = Buffer[offset + 6];
uint nanosecond = BitConverter.ToUInt32(Buffer, offset + 8);
short timezone = BitConverter.ToInt16(Buffer, offset + 12);
byte daylight = Buffer[offset + 14];
return new UefiDateTime()
{
year = year,
month = month,
day = day,
hour = hour,
minute = minute,
second = second,
nanosecond = nanosecond,
timezone = timezone,
daylight = daylight
};
}
public readonly DateTime ToDateTime()
{
DateTime dateTime;
byte month = this.month == 0 ? (byte)1 : this.month;
byte day = this.day == 0 ? (byte)1 : this.day;
if (timezone == 2047)
{
dateTime = new DateTime(year, month, day, hour, minute, second, Convert.ToInt32(nanosecond / 1000000U), DateTimeKind.Local);
}
else
{
dateTime = new DateTime(year, month, day, hour, minute, second, Convert.ToInt32(nanosecond / 1000000U), DateTimeKind.Utc);
}
return dateTime;
}
}
}
@@ -0,0 +1,41 @@
/*
* MIT License
*
* Copyright (c) 2024 The DuoWOA authors
*
* 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;
namespace UnifiedFlashingPlatform.UEFI
{
[Flags]
public enum UefiVariableAttributes : uint
{
EfiVariableNone,
EfiVariableNonVolatile,
EfiVariableBootServiceAccess,
EfiVariableRuntimeAccess = 4U,
EfiVariableHardwareErrorRecord = 8U,
EfiVariableAuthenticatedWriteAccess = 16U,
EfiVariableTimeBasedAuthenticatedWriteAccess = 32U,
EfiVariableAppendWrite = 64U,
EfiVariableEnhancedAuthenticatedAccess = 128U
}
}
+37
View File
@@ -0,0 +1,37 @@
/*
* MIT License
*
* Copyright (c) 2024 The DuoWOA authors
*
* 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.
*/
namespace UnifiedFlashingPlatform
{
public struct USBSpeed
{
public byte CurrentUSBSpeed;
public byte MaxUSBSpeed;
public override readonly string ToString()
{
return "CurrentUSBSpeed: " + CurrentUSBSpeed +
" - MaxUSBSpeed: " + MaxUSBSpeed;
}
}
}
+42
View File
@@ -0,0 +1,42 @@
/*
* MIT License
*
* Copyright (c) 2024 The DuoWOA authors
*
* 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 UnifiedFlashingPlatform.UEFI;
namespace UnifiedFlashingPlatform
{
public struct UefiVariable
{
public UefiVariableAttributes Attributes;
public uint DataSize;
public byte[] Data;
public override readonly string ToString()
{
return "Attributes: " + Attributes +
" - DataSize: " + DataSize +
" - Data: " + BitConverter.ToString(Data);
}
}
}
@@ -0,0 +1,136 @@
namespace UnifiedFlashingPlatform
{
public partial class UnifiedFlashingPlatformModel
{
//
// Not valid commands
//
/* NOK */
private const string Signature = "NOK";
/* NOKX */
private const string ExtendedMessageSignature = $"{Signature}X";
/* NOKXC */
private const string CommonExtendedMessageSignature = $"{ExtendedMessageSignature}C";
/* NOKXF */
private const string UFPExtendedMessageSignature = $"{ExtendedMessageSignature}F";
//
// Normal commands
//
/* NOKF */
private const string FlashSignature = $"{Signature}F";
/* NOKI */
private const string HelloSignature = $"{Signature}I";
/* NOKM */
private const string MassStorageSignature = $"{Signature}M";
/* NOKN */
private const string TelemetryEndSignature = $"{Signature}N";
/* NOKR */
private const string RebootSignature = $"{Signature}R";
/* NOKS */
private const string TelemetryStartSignature = $"{Signature}S";
/* NOKT */
private const string GetGPTSignature = $"{Signature}T";
/* NOKV */
private const string InfoQuerySignature = $"{Signature}V";
/* NOKZ */
private const string ShutdownSignature = $"{Signature}Z";
//
// Common extended commands
//
/* NOKXCB */
private const string SwitchModeSignature = $"{CommonExtendedMessageSignature}B";
/* NOKXCC */
private const string ClearScreenSignature = $"{CommonExtendedMessageSignature}C";
/* NOKXCD */
private const string GetDirectoryEntriesSignature = $"{CommonExtendedMessageSignature}D";
/* NOKXCE */
private const string EchoSignature = $"{CommonExtendedMessageSignature}E";
/* NOKXCF */
private const string GetFileSignature = $"{CommonExtendedMessageSignature}F";
/* NOKXCM */
private const string DisplayCustomMessageSignature = $"{CommonExtendedMessageSignature}M";
/* NOKXCP */
private const string PutFileSignature = $"{CommonExtendedMessageSignature}P";
/* NOKXCT */
private const string BenchmarkTestsSignature = $"{CommonExtendedMessageSignature}T";
//
// UFP extended commands
//
/* NOKXFF */
private const string AsyncFlashModeSignature = $"{UFPExtendedMessageSignature}F";
/* NOKXFI */
private const string UnlockSignature = $"{UFPExtendedMessageSignature}I";
/* NOKXFO */
private const string RelockSignature = $"{UFPExtendedMessageSignature}O";
/* NOKXFR */
private const string ReadParamSignature = $"{UFPExtendedMessageSignature}R";
/* NOKXFS */
private const string SecureFlashSignature = $"{UFPExtendedMessageSignature}S";
/* NOKXFT */
private const string TelemetryReadSignature = $"{UFPExtendedMessageSignature}T";
/* NOKXFW */
private const string WriteParamSignature = $"{UFPExtendedMessageSignature}W";
/* NOKXFX */
private const string GetLogsSignature = $"{UFPExtendedMessageSignature}X";
//
// UFP Read Params
//
private const string AppTypeReadParamSignature = "APPT";
private const string ResetProtectionReadParamSignature = "ATRP";
private const string BitlockerStateReadParamSignature = "BITL";
private const string BuildInfoReadParamSignature = "BNFO";
private const string CurrentBootOptionReadParamSignature = "CUFO";
private const string AsyncProtocolSupportReadParamSignature = "DAS\0";
private const string DirectoryEntriesSizeReadParamSignature = "DES\0";
private const string DevicePlatformIDReadParamSignature = "DPI\0";
private const string DevicePropertiesReadParamSignature = "DPR\0";
private const string DeviceTargetInfoReadParamSignature = "DTI\0";
private const string DataVerifySpeedReadParamSignature = "DTSP";
private const string DeviceIDReadParamSignature = "DUI\0";
private const string EMMCTestResultReadParamSignature = "EMMT";
private const string EMMCSizeReadParamSignature = "EMS\0";
private const string EMMCWriteSpeedReadParamSignature = "EMWS";
private const string FlashAppInfoReadParamSignature = "FAI\0";
private const string FlashAppOptionsReadParamSignature = "FO\0\0";
private const string FlashingStatusReadParamSignature = "FS\0\0";
private const string FileSizeReadParamSignature = "FZ\0\0";
private const string SecureBootStatusReadParamSignature = "GSBS";
private const string GetUEFIVariableReadParamSignature = "GUFV";
private const string GetUEFIVariableSizeReadParamSignature = "GUVS";
private const string LargestMemoryRegionReadParamSignature = "LGMR";
private const string LogSizeReadParamSignature = "LZ\0\0";
private const string MACAddressReadParamSignature = "MAC\0";
private const string ModeDataReadParamSignature = "MODE";
private const string ProcessorManufacturerReadParamSignature = "pm\0\0";
private const string SDCardSizeReadParamSignature = "SDS\0";
private const string SupportedSecureFFUProtocolsReadParamSignature = "SFPI";
private const string SMBIOSDataReadParamSignature = "SMBD";
private const string SerialNumberReadParamSignature = "SN\0\0";
private const string SizeOfSystemMemoryReadParamSignature = "SOSM";
private const string SecurityStatusReadParamSignature = "SS\0\0";
private const string TelemetryLogSizeReadParamSignature = "TELS";
private const string TransferSizeReadParamSignature = "TS\0\0";
private const string UEFIBootFlagReadParamSignature = "UBF\0";
private const string UEFIBootOptionsReadParamSignature = "UEBO";
private const string UnlockIDReadParamSignature = "UKID";
private const string UnlockTokenFilesReadParamSignature = "UKTF";
private const string USBSpeedReadParamSignature = "USBS";
private const string WriteBufferSizeReadParamSignature = "WBS\0";
//
// UFP Write Params
//
private const string BootOptionOptionalDataWriteParamSignature = "BOCL";
private const string BootOptionAsFirstEntryWriteParamSignature = "BOF\0";
private const string BootOptionAsLastEntryWriteParamSignature = "BOL\0";
private const string FlashOptionsWriteParamSignature = "FO\0\0";
private const string LogInsertWriteParamSignature = "LI\0\0";
private const string ModeWriteParamSignature = "MODE";
private const string OneTimeBootSequenceWriteParamSignature = "OBU\0";
private const string SettingUEFIVariableWriteParamSignature = "SUFV";
}
}
@@ -0,0 +1,534 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnifiedFlashingPlatform.UEFI;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace UnifiedFlashingPlatform
{
public partial class UnifiedFlashingPlatformModel
{
public byte[]? ReadParam(string Param)
{
byte[] Request = new byte[0x0B];
const string Header = ReadParamSignature; // NOKXFR
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Param), 0, Request, 7, Param.Length);
byte[]? Response = ExecuteRawMethod(Request);
if ((Response == null) || (Response.Length < 0x10))
{
return null;
}
byte[] Result = new byte[Response[0x10]];
Buffer.BlockCopy(Response, 0x11, Result, 0, Response[0x10]);
return Result;
}
public string? ReadStringParam(string Param)
{
byte[]? Bytes = ReadParam(Param);
return Bytes == null ? null : Encoding.ASCII.GetString(Bytes).Trim('\0');
}
public AppType ReadAppType()
{
byte[]? Bytes = ReadParam(AppTypeReadParamSignature);
return Bytes == null ? AppType.Min : Bytes[0] == 1 ? AppType.UEFI : AppType.Min;
}
public ResetProtectionInfo? ReadResetProtection()
{
byte[]? Bytes = ReadParam(ResetProtectionReadParamSignature);
return Bytes == null
? null
: new ResetProtectionInfo()
{
IsResetProtectionEnabled = Bytes[0] == 1,
MajorVersion = BitConverter.ToUInt32(Bytes[1..5].Reverse().ToArray()),
MinorVersion = BitConverter.ToUInt32(Bytes[5..9].Reverse().ToArray())
};
}
public bool? ReadBitlocker()
{
byte[]? Bytes = ReadParam(BitlockerStateReadParamSignature);
return Bytes == null ? null : Bytes[0] == 1;
}
public string? ReadBuildInfo()
{
return ReadStringParam(BuildInfoReadParamSignature);
}
public ushort? ReadCurrentBootOption()
{
byte[]? Bytes = ReadParam(CurrentBootOptionReadParamSignature);
return Bytes == null || Bytes.Length != 2 ? null : BitConverter.ToUInt16(Bytes.Reverse().ToArray());
}
public bool? ReadDeviceAsyncSupport()
{
byte[]? Bytes = ReadParam(AsyncProtocolSupportReadParamSignature);
return Bytes == null || Bytes.Length != 2 ? null : BitConverter.ToUInt16(Bytes.Reverse().ToArray()) == 1;
}
public ulong? ReadDirectoryEntriesSize(string PartitionName, string DirectoryName)
{
if (PartitionName.Length > 35)
{
return null;
}
byte[] PartitionNameBuffer = Encoding.Unicode.GetBytes(PartitionName);
byte[] DirectoryNameBuffer = Encoding.Unicode.GetBytes(DirectoryName);
byte[] Request = new byte[87 + DirectoryNameBuffer.Length + 2];
const string Header = ReadParamSignature; // NOKXFR
const string Param = DirectoryEntriesSizeReadParamSignature;
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Param), 0, Request, 7, Param.Length);
Buffer.BlockCopy(PartitionNameBuffer, 0, Request, 15, PartitionNameBuffer.Length);
Buffer.BlockCopy(DirectoryNameBuffer, 0, Request, 87, DirectoryNameBuffer.Length);
byte[]? Response = ExecuteRawMethod(Request);
if ((Response == null) || (Response.Length < 0x10))
{
return null;
}
byte[] Result = new byte[Response[0x10]];
Buffer.BlockCopy(Response, 0x11, Result, 0, Response[0x10]);
return BitConverter.ToUInt64(Result.Reverse().ToArray());
}
public string? ReadDevicePlatformID()
{
return ReadStringParam(DevicePlatformIDReadParamSignature);
}
//
// Reads the device properties from the UEFI Variable "MSRuntimeDeviceProperties"
// in the g_guidMSRuntimeDeviceProperties namespace and returns it as a string.
//
public string? ReadDeviceProperties()
{
return ReadStringParam(DevicePropertiesReadParamSignature);
}
public DeviceTargetingInfo? ReadDeviceTargetInfo()
{
byte[]? Bytes = ReadParam(DeviceTargetInfoReadParamSignature);
if (Bytes == null)
{
return null;
}
ushort ManufacturerLength = BitConverter.ToUInt16(Bytes[0..2].Reverse().ToArray());
ushort FamilyLength = BitConverter.ToUInt16(Bytes[2..4].Reverse().ToArray());
ushort ProductNameLength = BitConverter.ToUInt16(Bytes[4..6].Reverse().ToArray());
ushort ProductVersionLength = BitConverter.ToUInt16(Bytes[6..8].Reverse().ToArray());
ushort SKUNumberLength = BitConverter.ToUInt16(Bytes[8..10].Reverse().ToArray());
ushort BaseboardManufacturerLength = BitConverter.ToUInt16(Bytes[10..12].Reverse().ToArray());
ushort BaseboardProductLength = BitConverter.ToUInt16(Bytes[12..14].Reverse().ToArray());
int CurrentOffset = 14;
string Manufacturer = Encoding.ASCII.GetString(Bytes[CurrentOffset..(CurrentOffset + ManufacturerLength)]);
CurrentOffset += ManufacturerLength;
string Family = Encoding.ASCII.GetString(Bytes[CurrentOffset..(CurrentOffset + FamilyLength)]);
CurrentOffset += FamilyLength;
string ProductName = Encoding.ASCII.GetString(Bytes[CurrentOffset..(CurrentOffset + ProductNameLength)]);
CurrentOffset += ProductNameLength;
string ProductVersion = Encoding.ASCII.GetString(Bytes[CurrentOffset..(CurrentOffset + ProductVersionLength)]);
CurrentOffset += ProductVersionLength;
string SKUNumber = Encoding.ASCII.GetString(Bytes[CurrentOffset..(CurrentOffset + SKUNumberLength)]);
CurrentOffset += SKUNumberLength;
string BaseboardManufacturer = Encoding.ASCII.GetString(Bytes[CurrentOffset..(CurrentOffset + BaseboardManufacturerLength)]);
CurrentOffset += BaseboardManufacturerLength;
string BaseboardProduct = Encoding.ASCII.GetString(Bytes[CurrentOffset..(CurrentOffset + BaseboardProductLength)]);
return new DeviceTargetingInfo()
{
Manufacturer = Manufacturer,
Family = Family,
ProductName = ProductName,
ProductVersion = ProductVersion,
SKUNumber = SKUNumber,
BaseboardManufacturer = BaseboardManufacturer,
BaseboardProduct = BaseboardProduct
};
}
//
// Gets the last FFU Flash Operation Data verify speed in KB/s
//
public uint? ReadDataVerifySpeed()
{
byte[]? Bytes = ReadParam(DataVerifySpeedReadParamSignature);
return Bytes == null || Bytes.Length != 4 ? null : BitConverter.ToUInt32(Bytes.Reverse().ToArray());
}
public Guid? ReadDeviceID()
{
byte[]? Bytes = ReadParam(DeviceIDReadParamSignature);
return Bytes == null || Bytes.Length != 16 ? null : new Guid(Bytes);
}
public uint? ReadEmmcTestResult()
{
byte[]? Bytes = ReadParam(EMMCTestResultReadParamSignature);
return Bytes == null || Bytes.Length != 4 ? null : BitConverter.ToUInt32(Bytes.Reverse().ToArray());
}
//
// Gets the eMMC Size in sectors, if present
//
public uint? ReadEmmcSize()
{
byte[]? Bytes = ReadParam(EMMCSizeReadParamSignature);
return Bytes == null || Bytes.Length != 4 ? null : BitConverter.ToUInt32(Bytes.Reverse().ToArray());
}
//
// Gets the eMMC Write speed in KB/s
//
public uint? ReadEmmcWriteSpeed()
{
byte[]? Bytes = ReadParam(EMMCWriteSpeedReadParamSignature);
return Bytes == null || Bytes.Length != 4 ? null : BitConverter.ToUInt32(Bytes.Reverse().ToArray());
}
public FlashAppInfo? ReadFlashAppInfo()
{
byte[]? Bytes = ReadParam(FlashAppInfoReadParamSignature);
return Bytes == null || Bytes.Length != 6 || Bytes[0] != 2
? null
: new FlashAppInfo()
{
ProtocolMajorVersion = Bytes[1],
ProtocolMinorVersion = Bytes[2],
ImplementationMajorVersion = Bytes[3],
ImplementationMinorVersion = Bytes[4]
};
}
//
// Reads the device properties from the UEFI Variable "FfuConfigurationOptions"
// in the g_guidLumiaGuid namespace and returns it as a string.
//
public string? ReadFlashOptions()
{
return ReadStringParam(FlashAppOptionsReadParamSignature);
}
public uint? ReadFlashingStatus()
{
byte[]? Bytes = ReadParam(FlashingStatusReadParamSignature);
return Bytes == null || Bytes.Length != 4 ? null : BitConverter.ToUInt32(Bytes.Reverse().ToArray());
}
public ulong? ReadFileSize(string PartitionName, string FileName)
{
if (PartitionName.Length > 35)
{
return null;
}
byte[] PartitionNameBuffer = Encoding.Unicode.GetBytes(PartitionName);
byte[] FileNameBuffer = Encoding.Unicode.GetBytes(FileName);
byte[] Request = new byte[87 + FileNameBuffer.Length + 2];
const string Header = ReadParamSignature; // NOKXFR
const string Param = FileSizeReadParamSignature;
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Param), 0, Request, 7, Param.Length);
Buffer.BlockCopy(PartitionNameBuffer, 0, Request, 15, PartitionNameBuffer.Length);
Buffer.BlockCopy(FileNameBuffer, 0, Request, 87, FileNameBuffer.Length);
byte[]? Response = ExecuteRawMethod(Request);
if ((Response == null) || (Response.Length < 0x10))
{
return null;
}
byte[] Result = new byte[Response[0x10]];
Buffer.BlockCopy(Response, 0x11, Result, 0, Response[0x10]);
return BitConverter.ToUInt64(Result.Reverse().ToArray());
}
public bool? ReadSecureBootStatus()
{
byte[]? Bytes = ReadParam(SecureBootStatusReadParamSignature);
return Bytes == null ? null : Bytes[0] == 1;
}
public UefiVariable? ReadUEFIVariable(Guid Guid, string Name, uint Size)
{
byte[] Request = new byte[39 + ((Name.Length + 1) * 2)];
const string Header = ReadParamSignature; // NOKXFR
string Param = GetUEFIVariableReadParamSignature;
byte[] VariableNameBuffer = Encoding.Unicode.GetBytes(Name);
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Param), 0, Request, 7, Param.Length);
Buffer.BlockCopy(Guid.ToByteArray(), 0, Request, 15, 16);
Buffer.BlockCopy(BitConverter.GetBytes(Size).Reverse().ToArray(), 0, Request, 31, 4);
Buffer.BlockCopy(BitConverter.GetBytes((Name.Length + 1) * 2).Reverse().ToArray(), 0, Request, 35, 4);
Buffer.BlockCopy(VariableNameBuffer, 0, Request, 39, VariableNameBuffer.Length);
byte[]? Response = ExecuteRawMethod(Request);
if ((Response == null) || (Response.Length < 0x10))
{
return null;
}
byte[] Result = new byte[Response[0x10]];
Buffer.BlockCopy(Response, 0x11, Result, 0, Response[0x10]);
return new UefiVariable()
{
Attributes = (UefiVariableAttributes)BitConverter.ToUInt32(Result[0..4].Reverse().ToArray()),
DataSize = BitConverter.ToUInt32(Result[4..8].Reverse().ToArray()),
Data = Result[8..^0]
};
}
public uint? ReadUEFIVariableSize(Guid Guid, string Name)
{
byte[] Request = new byte[39 + ((Name.Length + 1) * 2)];
const string Header = ReadParamSignature; // NOKXFR
string Param = GetUEFIVariableSizeReadParamSignature;
byte[] VariableNameBuffer = Encoding.Unicode.GetBytes(Name);
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Param), 0, Request, 7, Param.Length);
Buffer.BlockCopy(Guid.ToByteArray(), 0, Request, 15, 16);
Buffer.BlockCopy(BitConverter.GetBytes((Name.Length + 1) * 2).Reverse().ToArray(), 0, Request, 35, 4);
Buffer.BlockCopy(VariableNameBuffer, 0, Request, 39, VariableNameBuffer.Length);
byte[]? Response = ExecuteRawMethod(Request);
if ((Response == null) || (Response.Length < 0x10))
{
return null;
}
byte[] Result = new byte[Response[0x10]];
Buffer.BlockCopy(Response, 0x11, Result, 0, Response[0x10]);
return Result == null || Result.Length != 4 ? null : BitConverter.ToUInt32(Result.Reverse().ToArray());
}
//
// Returns the largest memory region in bytes available for use by UFP
//
public ulong? ReadLargestMemoryRegion()
{
byte[]? Bytes = ReadParam(LargestMemoryRegionReadParamSignature);
return Bytes == null || Bytes.Length != 8 ? null : BitConverter.ToUInt64(Bytes.Reverse().ToArray());
}
public ulong? ReadLogSize(DeviceLogType LogType)
{
byte[] Request = new byte[0x10];
const string Header = ReadParamSignature; // NOKXFR
const string Param = LogSizeReadParamSignature;
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Param), 0, Request, 7, Param.Length);
Request[15] = (byte)LogType;
byte[]? Response = ExecuteRawMethod(Request);
if ((Response == null) || (Response.Length < 0x10))
{
return 0;
}
byte[] Result = new byte[Response[0x10]];
Buffer.BlockCopy(Response, 0x11, Result, 0, Response[0x10]);
return BitConverter.ToUInt64([.. Result.Reverse()], 0);
}
//
// Reads the MAC Address in the following format: "%02x-%02x-%02x-%02x-%02x-%02x"
//
public string? ReadMacAddress()
{
return ReadStringParam(MACAddressReadParamSignature);
}
public uint? ReadModeData(Mode Mode)
{
byte[] Request = new byte[0x10];
const string Header = ReadParamSignature; // NOKXFR
string Param = ModeDataReadParamSignature;
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Param), 0, Request, 7, Param.Length);
Request[15] = (byte)Mode;
byte[]? Response = ExecuteRawMethod(Request);
if ((Response == null) || (Response.Length < 0x10))
{
return null;
}
byte[] Result = new byte[Response[0x10]];
Buffer.BlockCopy(Response, 0x11, Result, 0, Response[0x10]);
return Result == null || Result.Length != 4 ? null : BitConverter.ToUInt32(Result.Reverse().ToArray());
}
public string? ReadProcessorManufacturer()
{
return ReadStringParam(ProcessorManufacturerReadParamSignature);
}
//
// Gets the SD Card Size in sectors, if present
//
public uint? ReadSDCardSize()
{
byte[]? Bytes = ReadParam(SDCardSizeReadParamSignature);
return Bytes == null || Bytes.Length != 4 ? null : BitConverter.ToUInt32(Bytes.Reverse().ToArray());
}
public string? ReadSupportedFFUProtocolInfo()
{
// TODO
return ReadStringParam(SupportedSecureFFUProtocolsReadParamSignature);
}
public string? ReadSMBIOSData()
{
// TODO
return ReadStringParam(SMBIOSDataReadParamSignature);
}
public Guid? ReadSerialNumber()
{
byte[]? Bytes = ReadParam(SerialNumberReadParamSignature);
return Bytes == null || Bytes.Length != 16 ? null : new Guid(Bytes);
}
//
// Returns the size of system memory in kB
//
public ulong? ReadSizeOfSystemMemory()
{
byte[]? Bytes = ReadParam(SizeOfSystemMemoryReadParamSignature);
return Bytes == null || Bytes.Length != 8 ? null : BitConverter.ToUInt64(Bytes.Reverse().ToArray());
}
public string? ReadSecurityStatus()
{
// TODO
return ReadStringParam(SecurityStatusReadParamSignature);
}
public string? ReadTelemetryLogSize()
{
// TODO
return ReadStringParam(TelemetryLogSizeReadParamSignature);
}
public uint? ReadTransferSize()
{
byte[]? Bytes = ReadParam(TransferSizeReadParamSignature);
return Bytes == null || Bytes.Length != 4 ? null : BitConverter.ToUInt32(Bytes.Reverse().ToArray());
}
//
// Reads the UEFI Boot Flag variable content and returns it as a string.
//
public string? ReadUEFIBootFlag()
{
return ReadStringParam(UEFIBootFlagReadParamSignature);
}
public BOOT_OPTION[]? ReadUEFIBootOptions()
{
byte[] Request = new byte[0x0B];
const string Header = ReadParamSignature; // NOKXFR
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
Buffer.BlockCopy(Encoding.ASCII.GetBytes(UEFIBootOptionsReadParamSignature), 0, Request, 7, UEFIBootOptionsReadParamSignature.Length);
byte[]? Response = ExecuteRawMethod(Request);
if ((Response == null) || (Response.Length < 0x10))
{
return null;
}
byte[] ResponseBuffer = Response[0x19..];
List<BOOT_OPTION> bootOptions = [];
int j = 0;
while (j != ResponseBuffer.Length)
{
BOOT_OPTION bootOption = BOOT_OPTION.ReadFromBuffer(ResponseBuffer, j);
j += bootOption.TotalSize;
bootOptions.Add(bootOption);
}
return [.. bootOptions];
}
//
// Reads the device properties from the UEFI Variable "UnlockID"
// in the g_guidOfflineDUIdEfiNamespace namespace and returns it as a string.
//
public byte[] ReadUnlockID()
{
return ReadParam(UnlockIDReadParamSignature);
}
public string? ReadUnlockTokenFiles()
{
// TODO
return ReadStringParam(UnlockTokenFilesReadParamSignature);
}
public USBSpeed? ReadUSBSpeed()
{
byte[]? Bytes = ReadParam(USBSpeedReadParamSignature);
return Bytes == null || Bytes.Length != 2
? null
: new USBSpeed()
{
CurrentUSBSpeed = Bytes[0],
MaxUSBSpeed = Bytes[1]
};
}
public uint? ReadWriteBufferSize()
{
byte[]? Bytes = ReadParam(WriteBufferSizeReadParamSignature);
return Bytes == null || Bytes.Length != 4 ? null : BitConverter.ToUInt32(Bytes.Reverse().ToArray());
}
}
}
@@ -0,0 +1,543 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using WPinternals;
using WPinternals.HelperClasses;
namespace UnifiedFlashingPlatform
{
public partial class UnifiedFlashingPlatformModel
{
private readonly PhoneInfo Info = new();
public void FlashSectors(uint StartSector, byte[] Data, byte TargetDevice = 0, int Progress = 0)
{
// Start sector is in UInt32, so max size of eMMC is 2 TB.
byte[] Request = new byte[Data.Length + 0x40];
const string Header = FlashSignature; // NOKF
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
Request[0x05] = TargetDevice; // Target device: 0: eMMC, 1: SDIO, 2: Other ???, 3: ???
Buffer.BlockCopy(BigEndian.GetBytes(StartSector, 4), 0, Request, 0x0B, 4); // Start sector
Buffer.BlockCopy(BigEndian.GetBytes(Data.Length / 0x200, 4), 0, Request, 0x0F, 4); // Sector count
Request[0x13] = (byte)Progress; // Progress (0 - 100)
Request[0x18] = 0; // Verify needed
Request[0x19] = 0; // Skip write
Buffer.BlockCopy(Data, 0, Request, 0x40, Data.Length);
_ = ExecuteRawMethod(Request);
}
public void Hello()
{
byte[] Request = new byte[4];
ByteOperations.WriteAsciiString(Request, 0, HelloSignature);
byte[] Response = ExecuteRawMethod(Request) ?? throw new BadConnectionException();
if (ByteOperations.ReadAsciiString(Response, 0, 4) != HelloSignature)
{
throw new WPinternalsException("Bad response from phone!", "The phone did not answer properly to the Hello message sent.");
}
}
public void ResetPhone()
{
Debug.WriteLine("Rebooting phone");
try
{
byte[] Request = new byte[4];
ByteOperations.WriteAsciiString(Request, 0, RebootSignature);
ExecuteRawVoidMethod(Request);
}
catch
{
Debug.WriteLine("Sending reset-request failed");
Debug.WriteLine("Assuming automatic reset already in progress");
}
}
public GPT ReadGPT()
{
// If this function is used with a locked BootMgr v1,
// then the mode-switching should be done outside this function,
// because the context-switches that are used here are not supported on BootMgr v1.
// Only works in BootLoader-mode or on unlocked bootloaders in Flash-mode!!
/*PhoneInfo Info = ReadPhoneInfo(ExtendedInfo: false);
FlashAppType OriginalAppType = Info.App;
bool Switch = (Info.App != FlashAppType.BootManager) && Info.IsBootloaderSecure;
if (Switch)
{
SwitchToBootManagerContext();
}*/
byte[] Request = new byte[0x04];
const string Header = GetGPTSignature;
System.Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
byte[]? Buffer = ExecuteRawMethod(Request);
if ((Buffer == null) || (Buffer.Length < 0x4408))
{
throw new InvalidOperationException("Unable to read GPT!");
}
ushort Error = (ushort)((Buffer[6] << 8) + Buffer[7]);
if (Error > 0)
{
throw new NotSupportedException($"ReadGPT: Error 0x{Error:X4}");
}
// Length: 0x4400 for 512 (0x200) Sector Size (from sector 0 to sector 34)
// Length: 0x6000 for 4096 (0x1000) Sector Size (from sector 0 to sector 6)
uint ReturnedGPTBufferLength = (uint)Buffer.Length - 8;
uint SectorSize = Buffer.Length == 0x4408
? 512
: Buffer.Length == 0x6008
? (uint)4096
: throw new NotSupportedException($"ReadGPT: Unsupported output size! 0x{ReturnedGPTBufferLength:X4}");
byte[] GPTBuffer = new byte[ReturnedGPTBufferLength - SectorSize];
System.Buffer.BlockCopy(Buffer, 8 + (int)SectorSize, GPTBuffer, 0, (int)ReturnedGPTBufferLength - (int)SectorSize);
/*if (Switch)
{
if (OriginalAppType == FlashAppType.FlashApp)
{
SwitchToFlashAppContext();
}
else
{
SwitchToPhoneInfoAppContext();
}
}*/
return new GPT(GPTBuffer);//, SectorSize); // NOKT message header and MBR are ignored
}
private static void ThrowFlashError(int ErrorCode)
{
string SubMessage = ErrorCode switch
{
0x0008 => "Unsupported protocol / Invalid options",
0x000F => "Invalid sub block count",
0x0010 => "Invalid sub block length",
0x0012 => "Authentication required",
0x000E => "Invalid sub block type",
0x0013 => "Failed async message",
0x1000 => "Invalid header type",
0x1001 => "FFU header contain unknown extra data",
0x0001 => "Couldn't allocate memory",
0x1106 => "Security header validation failed",
0x1105 => "Invalid hash table size",
0x1104 => "Invalid catalog size",
0x1103 => "Invalid chunk size",
0x1102 => "Unsupported algorithm",
0x1101 => "Invalid struct size",
0x1100 => "Invalid signature",
0x1202 => "Invalid struct size",
0x1203 => "Unsupported algorithm",
0x1204 => "Invalid chunk size",
0x1005 => "Data not aligned correctly",
0x0009 => "Locate protocol failed",
0x1003 => "Hash mismatch",
0x1006 => "Couldn't find hash from security header for index",
0x1004 => "Security header import missing / All FFU headers have not been imported",
0x1304 => "Invalid platform ID",
0x1307 => "Invalid write descriptor info",
0x1306 => "Invalid write descriptor info",
0x1305 => "Invalid block size",
0x1303 => "Unsupported FFU version",
0x1302 => "Unsupported struct version",
0x1301 => "Invalid update type",
0x100B => "Too much payload data, all data has already been written",
0x1008 => "Internal error",
0x1007 => "Payload data does not contain all data",
0x0004 => "Flash write failed",
0x000D => "Flash verify failed",
0x0002 => "Flash read failed",
_ => "Unknown error",
};
WPinternalsException Ex = new("Flash failed!")
{
SubMessage = $"Error 0x{ErrorCode:X4}: {SubMessage}"
};
throw Ex;
}
public void SendFfuHeaderV1(byte[] FfuHeader, int Progress = 0, byte Options = 0)
{
byte[] Request = new byte[FfuHeader.Length + 0x20];
const string Header = SecureFlashSignature;
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
Buffer.BlockCopy(BigEndian.GetBytes(0x0001, 2), 0, Request, 0x06, 2); // Protocol version = 0x0001
Request[0x08] = (byte)Progress; // Progress = 0% (0 - 100)
Request[0x0B] = 1; // Subblock count = 1
Buffer.BlockCopy(BigEndian.GetBytes(0x0000000B, 4), 0, Request, 0x0C, 4); // Subblock type for header = 0x0B
Buffer.BlockCopy(BigEndian.GetBytes(FfuHeader.Length + 0x0C, 4), 0, Request, 0x10, 4); // Subblock length = length of header + 0x0C
Buffer.BlockCopy(BigEndian.GetBytes(0x00000000, 4), 0, Request, 0x14, 4); // Header type = 0
Buffer.BlockCopy(BigEndian.GetBytes(FfuHeader.Length, 4), 0, Request, 0x18, 4); // Payload length = length of header
Request[0x1C] = Options; // Header options = 0
Buffer.BlockCopy(FfuHeader, 0, Request, 0x20, FfuHeader.Length);
byte[] Response = ExecuteRawMethod(Request) ?? throw new BadConnectionException();
int ResultCode = (Response[6] << 8) + Response[7];
if (ResultCode != 0)
{
ThrowFlashError(ResultCode);
}
}
public void SendFfuHeaderV2(uint TotalHeaderLength, uint OffsetForThisPart, byte[] FfuHeader, int Progress = 0, byte Options = 0)
{
byte[] Request = new byte[FfuHeader.Length + 0x3C];
const string Header = SecureFlashSignature;
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
Buffer.BlockCopy(BigEndian.GetBytes(0x0002, 2), 0, Request, 0x06, 2); // Protocol version = 0x0002
Request[0x08] = (byte)Progress; // Progress = 0% (0 - 100)
Request[0x0B] = 1; // Subblock count = 1
Buffer.BlockCopy(BigEndian.GetBytes(0x00000021, 4), 0, Request, 0x0C, 4); // Subblock type for header v2 = 0x21
Buffer.BlockCopy(BigEndian.GetBytes(FfuHeader.Length + 0x28, 4), 0, Request, 0x10, 4); // Subblock starts at 0x14, payload starts at 0x3C.
Buffer.BlockCopy(BigEndian.GetBytes(0x00000000, 4), 0, Request, 0x14, 4); // Header type = 0
Buffer.BlockCopy(BigEndian.GetBytes(TotalHeaderLength, 4), 0, Request, 0x18, 4); // Payload length = length of header
Request[0x1C] = Options; // Header options = 0
Buffer.BlockCopy(BigEndian.GetBytes(OffsetForThisPart, 4), 0, Request, 0x1D, 4);
Buffer.BlockCopy(BigEndian.GetBytes(FfuHeader.Length, 4), 0, Request, 0x21, 4);
Request[0x25] = 0; // No Erase
Buffer.BlockCopy(FfuHeader, 0, Request, 0x3C, FfuHeader.Length);
byte[] Response = ExecuteRawMethod(Request) ?? throw new BadConnectionException();
if (Response.Length == 4)
{
throw new WPinternalsException("Flash protocol v2 not supported", "The device reported that the Flash protocol v2 was not supported while sending the FFU header.");
}
int ResultCode = (Response[6] << 8) + Response[7];
if (ResultCode != 0)
{
ThrowFlashError(ResultCode);
}
}
public void SendFfuPayloadV1(byte[] FfuChunk, int Progress = 0, byte Options = 0)
{
byte[] Request = new byte[FfuChunk.Length + 0x1C];
const string Header = SecureFlashSignature;
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
Buffer.BlockCopy(BigEndian.GetBytes((int)FfuProtocol.ProtocolSyncV1, 2), 0, Request, 0x06, 2); // Protocol version = 0x0001
Request[0x08] = (byte)Progress; // Progress = 0% (0 - 100)
Request[0x0B] = 1; // Subblock count = 1
Buffer.BlockCopy(BigEndian.GetBytes(0x0000000C, 4), 0, Request, 0x0C, 4); // Subblock type for ChunkData = 0x0C
Buffer.BlockCopy(BigEndian.GetBytes(FfuChunk.Length + 0x08, 4), 0, Request, 0x10, 4); // Subblock length = length of chunk + 0x08
Buffer.BlockCopy(BigEndian.GetBytes(FfuChunk.Length, 4), 0, Request, 0x14, 4); // Payload length = length of chunk
Request[0x18] = Options; // Data options = 0 (1 = verify)
Buffer.BlockCopy(FfuChunk, 0, Request, 0x1C, FfuChunk.Length);
byte[] Response = ExecuteRawMethod(Request) ?? throw new BadConnectionException();
int ResultCode = (Response[6] << 8) + Response[7];
if (ResultCode != 0)
{
ThrowFlashError(ResultCode);
}
}
public void SendFfuPayloadV2(byte[] FfuChunk, int Progress = 0, byte Options = 0)
{
byte[] Request = new byte[FfuChunk.Length + 0x20];
const string Header = SecureFlashSignature;
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
Buffer.BlockCopy(BigEndian.GetBytes((int)FfuProtocol.ProtocolSyncV2, 2), 0, Request, 0x06, 2); // Protocol
Request[0x08] = (byte)Progress; // Progress = 0% (0 - 100)
Request[0x0B] = 1; // Subblock count = 1
Buffer.BlockCopy(BigEndian.GetBytes(0x0000001B, 4), 0, Request, 0x0C, 4); // Subblock type for Payload v2 = 0x1B
Buffer.BlockCopy(BigEndian.GetBytes(FfuChunk.Length + 0x0C, 4), 0, Request, 0x10, 4); // Subblock length = length of chunk + 0x08
Buffer.BlockCopy(BigEndian.GetBytes(FfuChunk.Length, 4), 0, Request, 0x14, 4); // Payload length = length of chunk
Request[0x18] = Options; // Data options = 0 (1 = verify)
Buffer.BlockCopy(FfuChunk, 0, Request, 0x20, FfuChunk.Length);
byte[] Response = ExecuteRawMethod(Request) ?? throw new BadConnectionException();
int ResultCode = (Response[6] << 8) + Response[7];
if (ResultCode != 0)
{
ThrowFlashError(ResultCode);
}
}
public void SendFfuPayloadV3(byte[] FfuChunk, uint WriteDescriptorIndex, uint CRC, int Progress = 0, byte Options = 0)
{
byte[] Request = new byte[FfuChunk.Length + 0x20];
const string Header = SecureFlashSignature;
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
Buffer.BlockCopy(BigEndian.GetBytes((int)FfuProtocol.ProtocolAsyncV3, 2), 0, Request, 0x06, 2); // Protocol
Request[0x08] = (byte)Progress; // Progress = 0% (0 - 100)
Request[0x0B] = 1; // Subblock count = 1
Buffer.BlockCopy(BigEndian.GetBytes(0x0000001D, 4), 0, Request, 0x0C, 4); // Subblock type for Payload v2 = 0x1B
Buffer.BlockCopy(BigEndian.GetBytes(FfuChunk.Length + 0x2C, 4), 0, Request, 0x10, 4); // Subblock length = length of chunk + 0x08
Buffer.BlockCopy(BigEndian.GetBytes(FfuChunk.Length, 4), 0, Request, 0x14, 4); // Payload length = length of chunk
Request[0x18] = Options; // Data options = 0 (1 = verify)
Buffer.BlockCopy(BigEndian.GetBytes(WriteDescriptorIndex, 4), 0, Request, 0x19, 4); // Payload length = length of chunk
Buffer.BlockCopy(BigEndian.GetBytes(CRC, 4), 0, Request, 0x1D, 4); // Payload length = length of chunk
Buffer.BlockCopy(FfuChunk, 0, Request, 0x40, FfuChunk.Length);
byte[] Response = ExecuteRawMethod(Request) ?? throw new BadConnectionException();
int ResultCode = (Response[6] << 8) + Response[7];
if (ResultCode != 0)
{
ThrowFlashError(ResultCode);
}
}
public PhoneInfo ReadPhoneInfo()
{
// NOKV = Info Query
bool PhoneInfoLogged = Info.State != PhoneInfoState.Empty;
PhoneInfo Result = Info;
if (Result.State == PhoneInfoState.Empty)
{
byte[] Request = new byte[4];
ByteOperations.WriteAsciiString(Request, 0, InfoQuerySignature);
byte[]? Response = ExecuteRawMethod(Request);
if ((Response != null) && (ByteOperations.ReadAsciiString(Response, 0, 4) != "NOKU"))
{
Result.App = (FlashAppType)Response[5];
switch (Result.App)
{
case FlashAppType.FlashApp:
Result.FlashAppProtocolVersionMajor = Response[6];
Result.FlashAppProtocolVersionMinor = Response[7];
Result.FlashAppVersionMajor = Response[8];
Result.FlashAppVersionMinor = Response[9];
break;
}
byte SubblockCount = Response[10];
int SubblockOffset = 11;
for (int i = 0; i < SubblockCount; i++)
{
byte SubblockID = Response[SubblockOffset + 0x00];
ushort SubblockLength = BigEndian.ToUInt16(Response, SubblockOffset + 0x01);
int SubblockPayloadOffset = SubblockOffset + 3;
byte SubblockVersion;
switch (SubblockID)
{
case 0x01:
Result.TransferSize = BigEndian.ToUInt32(Response, SubblockPayloadOffset);
break;
case 0x02:
Result.WriteBufferSize = BigEndian.ToUInt32(Response, SubblockPayloadOffset);
break;
case 0x03:
Result.EmmcSizeInSectors = BigEndian.ToUInt32(Response, SubblockPayloadOffset);
break;
case 0x04:
if (Result.App == FlashAppType.FlashApp)
{
Result.SdCardSizeInSectors = BigEndian.ToUInt32(Response, SubblockPayloadOffset);
}
break;
case 0x05:
Result.PlatformID = ByteOperations.ReadAsciiString(Response, (uint)SubblockPayloadOffset, SubblockLength).Trim([' ', '\0']);
break;
case 0x0D:
Result.AsyncSupport = Response[SubblockPayloadOffset + 1] == 1;
break;
case 0x0F: // Supported but check parsing below pls
SubblockVersion = Response[SubblockPayloadOffset]; // 0x03
Result.PlatformSecureBootEnabled = Response[SubblockPayloadOffset + 0x01] == 0x01;
Result.SecureFfuEnabled = Response[SubblockPayloadOffset + 0x02] == 0x01;
Result.JtagDisabled = Response[SubblockPayloadOffset + 0x03] == 0x01;
Result.RdcPresent = Response[SubblockPayloadOffset + 0x04] == 0x01;
Result.Authenticated = (Response[SubblockPayloadOffset + 0x05] == 0x01) || (Response[SubblockPayloadOffset + 0x05] == 0x02);
Result.UefiSecureBootEnabled = Response[SubblockPayloadOffset + 0x06] == 0x01;
Result.SecondaryHardwareKeyPresent = Response[SubblockPayloadOffset + 0x07] == 0x01;
break;
case 0x10: // Also check to be sure
SubblockVersion = Response[SubblockPayloadOffset]; // 0x01
Result.SecureFfuSupportedProtocolMask = BigEndian.ToUInt16(Response, SubblockPayloadOffset + 0x01);
break;
case 0x1F: // Recheck too
Result.MmosOverUsbSupported = Response[SubblockPayloadOffset] == 1;
break;
case 0x20:
// CRC header info
break;
case 0x22:
uint SectorCount = BigEndian.ToUInt32(Response, SubblockPayloadOffset);
uint SectorSize = BigEndian.ToUInt32(Response, SubblockPayloadOffset + 4);
ushort FlashType = BigEndian.ToUInt16(Response, SubblockPayloadOffset + 8);
ushort FlashTypeIndex = BigEndian.ToUInt16(Response, SubblockPayloadOffset + 10);
uint Unknown = BigEndian.ToUInt32(Response, SubblockPayloadOffset + 12);
string DevicePath = ByteOperations.ReadUnicodeString(Response, (uint)SubblockPayloadOffset + 16, (uint)SubblockLength - 16).Trim([' ', '\0']);
Result.BootDevices.Add((SectorCount, SectorSize, FlashType, FlashTypeIndex, Unknown, DevicePath));
break;
case 0x23:
byte[] Bytes = Response[SubblockPayloadOffset..(SubblockPayloadOffset + SubblockLength)];
ushort ManufacturerLength = BitConverter.ToUInt16(Bytes[0..2].Reverse().ToArray());
ushort FamilyLength = BitConverter.ToUInt16(Bytes[2..4].Reverse().ToArray());
ushort ProductNameLength = BitConverter.ToUInt16(Bytes[4..6].Reverse().ToArray());
ushort ProductVersionLength = BitConverter.ToUInt16(Bytes[6..8].Reverse().ToArray());
ushort SKUNumberLength = BitConverter.ToUInt16(Bytes[8..10].Reverse().ToArray());
ushort BaseboardManufacturerLength = BitConverter.ToUInt16(Bytes[10..12].Reverse().ToArray());
ushort BaseboardProductLength = BitConverter.ToUInt16(Bytes[12..14].Reverse().ToArray());
int CurrentOffset = 14;
Result.Manufacturer = Encoding.ASCII.GetString(Bytes[CurrentOffset..(CurrentOffset + ManufacturerLength)]);
CurrentOffset += ManufacturerLength;
Result.Family = Encoding.ASCII.GetString(Bytes[CurrentOffset..(CurrentOffset + FamilyLength)]);
CurrentOffset += FamilyLength;
Result.ProductName = Encoding.ASCII.GetString(Bytes[CurrentOffset..(CurrentOffset + ProductNameLength)]);
CurrentOffset += ProductNameLength;
Result.ProductVersion = Encoding.ASCII.GetString(Bytes[CurrentOffset..(CurrentOffset + ProductVersionLength)]);
CurrentOffset += ProductVersionLength;
Result.SKUNumber = Encoding.ASCII.GetString(Bytes[CurrentOffset..(CurrentOffset + SKUNumberLength)]);
CurrentOffset += SKUNumberLength;
Result.BaseboardManufacturer = Encoding.ASCII.GetString(Bytes[CurrentOffset..(CurrentOffset + BaseboardManufacturerLength)]);
CurrentOffset += BaseboardManufacturerLength;
Result.BaseboardProduct = Encoding.ASCII.GetString(Bytes[CurrentOffset..(CurrentOffset + BaseboardProductLength)]);
break;
case 0x24:
Result.LargestMemoryRegion = BitConverter.ToUInt64(Response[SubblockPayloadOffset..(SubblockPayloadOffset + 8)].Reverse().ToArray());
break;
case 0x25:
Result.AppType = (AppType)Response[SubblockPayloadOffset];
break;
default:
Debug.WriteLine($"Unknown Subblock: ID: 0x{SubblockID:X2} Length: 0x{SubblockLength:X4}");
break;
}
SubblockOffset += SubblockLength + 3;
}
}
Result.State = PhoneInfoState.Basic;
}
Result.IsBootloaderSecure = !(Info.Authenticated || Info.RdcPresent || !Info.SecureFfuEnabled);
if (!PhoneInfoLogged)
{
Result.Log();
}
return Result;
}
public enum FlashAppType
{
FlashApp = 2
};
public enum PhoneInfoState
{
Empty,
Basic
};
public class PhoneInfo
{
public PhoneInfoState State = PhoneInfoState.Empty;
public FlashAppType App;
public byte FlashAppVersionMajor;
public byte FlashAppVersionMinor;
public byte FlashAppProtocolVersionMajor;
public byte FlashAppProtocolVersionMinor;
public uint TransferSize;
public bool MmosOverUsbSupported;
public uint SdCardSizeInSectors;
public uint WriteBufferSize;
public uint EmmcSizeInSectors;
public string? PlatformID;
public ushort SecureFfuSupportedProtocolMask;
public bool AsyncSupport;
public bool PlatformSecureBootEnabled;
public bool SecureFfuEnabled;
public bool JtagDisabled;
public bool RdcPresent;
public bool Authenticated;
public bool UefiSecureBootEnabled;
public bool SecondaryHardwareKeyPresent;
public string? Manufacturer;
public string? Family;
public string? ProductName;
public string? ProductVersion;
public string? SKUNumber;
public string? BaseboardManufacturer;
public string? BaseboardProduct;
public ulong LargestMemoryRegion;
public AppType AppType;
public List<(uint SectorCount, uint SectorSize, ushort FlashType, ushort FlashIndex, uint Unknown, string DevicePath)> BootDevices = [];
public bool IsBootloaderSecure;
public void Log()
{
switch (App)
{
case FlashAppType.FlashApp:
Debug.WriteLine($"Flash app: {FlashAppVersionMajor}.{FlashAppVersionMinor}");
Debug.WriteLine($"Flash protocol: {FlashAppProtocolVersionMajor}.{FlashAppProtocolVersionMinor}");
break;
}
Debug.WriteLine($"SecureBoot: {((!PlatformSecureBootEnabled || !UefiSecureBootEnabled) ? "Disabled" : "Enabled")} (Platform Secure Boot: {(PlatformSecureBootEnabled ? "Enabled" : "Disabled")}, UEFI Secure Boot: {(UefiSecureBootEnabled ? "Enabled" : "Disabled")})");
Debug.WriteLine($"Flash app security: {(!IsBootloaderSecure ? "Disabled" : "Enabled")}");
Debug.WriteLine($"Flash app security: {(!IsBootloaderSecure ? "Disabled" : "Enabled")} (FFU security: {(SecureFfuEnabled ? "Enabled" : "Disabled")}, RDC: {(RdcPresent ? "Present" : "Not found")}, Authenticated: {(Authenticated ? "True" : "False")})");
Debug.WriteLine($"JTAG: {(JtagDisabled ? "Disabled" : "Enabled")}");
}
}
public void Shutdown()
{
byte[] Request = new byte[4];
const string Header = ShutdownSignature;
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
ExecuteRawVoidMethod(Request);
}
}
}
@@ -0,0 +1,61 @@
using System;
using System.Linq;
using System.Text;
using UnifiedFlashingPlatform.UEFI;
using WPinternals.HelperClasses;
namespace UnifiedFlashingPlatform
{
public partial class UnifiedFlashingPlatformModel
{
public void WriteParam(string Param, byte[] Data)
{
byte[] Request = new byte[0x0F + Data.Length];
const string Header = WriteParamSignature; // NOKXFW
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Param), 0, Request, 7, Param.Length);
// 4 empty bytes here
Buffer.BlockCopy(Data, 0, Request, 15, Data.Length);
ExecuteRawMethod(Request);
}
// TODO: Verify proper functionality
public void SetUEFIVariable(Guid Guid, string Name, UefiVariableAttributes Attributes, byte[] Data)
{
byte[] ParamBuffer = new byte[540 + Data.Length];
/* 15..30 */ Buffer.BlockCopy(Guid.ToByteArray(), 0, ParamBuffer, 0, 16);
/* 31..34 */ Buffer.BlockCopy(BigEndian.GetBytes(Math.Min(512, Name.Length * 2), 4), 0, ParamBuffer, 16, 4);
/* 35.. */ Buffer.BlockCopy(Encoding.Unicode.GetBytes(Name), 0, ParamBuffer, 20, Math.Min(512, Name.Length * 2)); // 256 Max Size for name (unicode)
/* 547..550 */ Buffer.BlockCopy(BigEndian.GetBytes(Attributes, 4), 0, ParamBuffer, 532, 4);
/* 551..554 */ Buffer.BlockCopy(BigEndian.GetBytes(Data.Length, 4), 0, ParamBuffer, 536, 4);
/* 555.. */ Buffer.BlockCopy(Data, 0, ParamBuffer, 540, Data.Length);
WriteParam(SettingUEFIVariableWriteParamSignature, ParamBuffer);
}
public void SetOneTimeBootSequence(ushort BootEntryID)
{
//WriteParam(OneTimeBootSequenceWriteParamSignature, BigEndian.GetBytes(BootEntryID, 2));
WriteParam("UOBU", [.. BigEndian.GetBytes(BootEntryID, 2).Reverse()]);
}
public void SetBootOptionAsFirstEntry(ushort BootEntryID)
{
//WriteParam(BootOptionAsFirstEntryWriteParamSignature, BigEndian.GetBytes(BootEntryID, 2));
WriteParam("UBOF", [.. BigEndian.GetBytes(BootEntryID, 2).Reverse()]);
}
public void SetBootOptionAsLastEntry(ushort BootEntryID)
{
WriteParam(BootOptionAsLastEntryWriteParamSignature, BigEndian.GetBytes(BootEntryID, 2));
}
public void SetProgressBar(uint Percentage)
{
WriteParam("PBI\0", BigEndian.GetBytes(Percentage, 1));
}
}
}
@@ -0,0 +1,359 @@
/*
* MIT License
*
* Copyright (c) 2024 The DuoWOA authors
*
* 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 MadWizard.WinUSBNet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnifiedFlashingPlatform.UEFI;
using WPinternals.HelperClasses;
namespace UnifiedFlashingPlatform
{
public partial class UnifiedFlashingPlatformModel : IDisposable
{
private bool Disposed = false;
private readonly USBDevice USBDevice;
private readonly USBPipe InputPipe;
private readonly USBPipe OutputPipe;
private readonly object UsbLock = new();
public UnifiedFlashingPlatformModel(string DevicePath)
{
USBDevice = new USBDevice(DevicePath);
foreach (USBPipe Pipe in USBDevice.Pipes)
{
if (Pipe.IsIn)
{
InputPipe = Pipe;
}
if (Pipe.IsOut)
{
OutputPipe = Pipe;
}
}
if (InputPipe == null || OutputPipe == null)
{
throw new Exception("Invalid USB device!");
}
}
public byte[]? ExecuteRawMethod(byte[] RawMethod)
{
return ExecuteRawMethod(RawMethod, RawMethod.Length);
}
public byte[]? ExecuteRawMethod(byte[] RawMethod, int Length)
{
byte[] Buffer = new byte[0xF000]; // Should be at least 0x4408 for receiving the GPT packet.
byte[]? Result = null;
lock (UsbLock)
{
OutputPipe.Write(RawMethod, 0, Length);
try
{
int OutputLength = InputPipe.Read(Buffer);
Result = new byte[OutputLength];
System.Buffer.BlockCopy(Buffer, 0, Result, 0, OutputLength);
}
catch { } // Reboot command looses connection
}
return Result;
}
public void ExecuteRawVoidMethod(byte[] RawMethod)
{
ExecuteRawVoidMethod(RawMethod, RawMethod.Length);
}
public void ExecuteRawVoidMethod(byte[] RawMethod, int Length)
{
lock (UsbLock)
{
OutputPipe.Write(RawMethod, 0, Length);
}
}
public void Relock()
{
byte[] Request = new byte[7];
const string Header = RelockSignature; // NOKXFO
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
_ = ExecuteRawMethod(Request);
}
public void MassStorage()
{
byte[] Request = new byte[7];
const string Header = MassStorageSignature; // NOKM
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
_ = ExecuteRawMethod(Request);
}
public void RebootPhone()
{
byte[] Request = new byte[7];
const string Header = $"{SwitchModeSignature}R"; // NOKXCBR
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
_ = ExecuteRawMethod(Request);
}
public void SwitchToUFP()
{
byte[] Request = new byte[7];
const string Header = $"{SwitchModeSignature}U"; // NOKXCBU
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
_ = ExecuteRawMethod(Request);
}
public void ContinueBoot()
{
byte[] Request = new byte[7];
const string Header = $"{SwitchModeSignature}W"; // NOKXCBW
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
_ = ExecuteRawMethod(Request);
}
public void PowerOff()
{
byte[] Request = new byte[7];
const string Header = $"{SwitchModeSignature}Z"; // NOKXCBZ
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
ExecuteRawVoidMethod(Request);
}
public void TransitionToUFPBootApp()
{
byte[] Request = new byte[7];
const string Header = $"{SwitchModeSignature}T"; // NOKXCBT
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
ExecuteRawVoidMethod(Request);
}
public void DisplayCustomMessage(string Message, ushort Row)
{
byte[] MessageBuffer = Encoding.Unicode.GetBytes(Message);
byte[] Request = new byte[8 + MessageBuffer.Length];
const string Header = DisplayCustomMessageSignature; // NOKXCM
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
Buffer.BlockCopy(BitConverter.GetBytes(Row).Reverse().ToArray(), 0, Request, 6, 2);
Buffer.BlockCopy(MessageBuffer, 0, Request, 8, MessageBuffer.Length);
_ = ExecuteRawMethod(Request);
}
public void ClearScreen()
{
byte[] Request = new byte[6];
const string Header = ClearScreenSignature; // NOKXCC
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
_ = ExecuteRawMethod(Request);
}
public byte[]? Echo(byte[] DataPayload)
{
byte[] Request = new byte[10 + DataPayload.Length];
const string Header = EchoSignature; // NOKXCE
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
Buffer.BlockCopy(BitConverter.GetBytes(DataPayload.Length).Reverse().ToArray(), 0, Request, 6, 4);
Buffer.BlockCopy(DataPayload, 0, Request, 10, DataPayload.Length);
byte[]? Response = ExecuteRawMethod(Request);
if ((Response == null) || (Response.Length < 6 + DataPayload.Length))
{
return null;
}
byte[] Result = new byte[DataPayload.Length];
Buffer.BlockCopy(Response, 6, Result, 0, DataPayload.Length);
return Result;
}
public FILE_INFO[]? GetDirectoryEntries(string PartitionName, string DirectoryName)
{
ulong? size = ReadDirectoryEntriesSize(PartitionName, DirectoryName);
if (size == null)
{
return null;
}
return GetDirectoryEntries(PartitionName, DirectoryName, size.Value);
}
private FILE_INFO[]? GetDirectoryEntries(string PartitionName, string DirectoryName, ulong DataStructSize)
{
if (PartitionName.Length > 35)
{
return null;
}
byte[] PartitionNameBuffer = Encoding.Unicode.GetBytes(PartitionName);
byte[] DirectoryNameBuffer = Encoding.Unicode.GetBytes(DirectoryName);
byte[] Request = new byte[1114];
const string Header = GetDirectoryEntriesSignature; // NOKXCD
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
Buffer.BlockCopy(PartitionNameBuffer, 0, Request, 6, PartitionNameBuffer.Length);
Buffer.BlockCopy(DirectoryNameBuffer, 0, Request, 78, DirectoryNameBuffer.Length); // 512 max size (1024 for unicode)
Buffer.BlockCopy(BigEndian.GetBytes((int)DataStructSize, 4), 0, Request, 1102, 4);
// TODO: Investigate data size
//Buffer.BlockCopy(BigEndian.GetBytes(360, 8), 0, Request, 1106, 8);
byte[]? Response = ExecuteRawMethod(Request);
if ((Response == null) || (Response.Length < 0x10))
{
return null;
}
int ResultCode = (Response[6] << 8) + Response[7];
if (ResultCode != 0)
{
ThrowFlashError(ResultCode);
}
int ResponseLength = BigEndian.ToInt32(Response, 8);
byte[] Result = new byte[ResponseLength];
Buffer.BlockCopy(Response, 12, Result, 0, ResponseLength);
List<FILE_INFO> directoryEntries = [];
int j = 0;
while (j != Result.Length)
{
FILE_INFO directoryEntry = FILE_INFO.ReadFromBuffer(Result, j);
j += (int)directoryEntry.Size;
directoryEntries.Add(directoryEntry);
}
return [.. directoryEntries];
}
public void TelemetryStart()
{
byte[] Request = new byte[4];
const string Header = TelemetryStartSignature; // NOKS
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
ExecuteRawVoidMethod(Request);
}
public void TelemetryEnd()
{
byte[] Request = new byte[4];
const string Header = TelemetryEndSignature; // NOKN
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
ExecuteRawVoidMethod(Request);
}
// WIP!
public string? ReadLog()
{
byte[] Request = new byte[0x13];
const string Header = GetLogsSignature;
ulong BufferSize = 0xE000 - 0xC;
ulong Length = ReadLogSize(DeviceLogType.Flashing)!.Value;
if (Length == 0)
{
return null;
}
string LogContent = "";
for (ulong i = 0; i < Length; i += BufferSize)
{
if (i + BufferSize > Length)
{
BufferSize = Length - i;
}
uint BufferSizeInt = (uint)BufferSize;
Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length);
Request[6] = 1;
Buffer.BlockCopy(BitConverter.GetBytes(BufferSizeInt).Reverse().ToArray(), 0, Request, 7, 4);
Buffer.BlockCopy(BitConverter.GetBytes(i).Reverse().ToArray(), 0, Request, 11, 8);
byte[]? Response = ExecuteRawMethod(Request);
if ((Response == null) || (Response.Length < 0xC))
{
return null;
}
int ResultLength = Response.Length - 0xC;
byte[] Result = new byte[ResultLength];
Buffer.BlockCopy(Response, 0xC, Result, 0, ResultLength);
string PartialLogContent = Encoding.ASCII.GetString(Result);
LogContent += PartialLogContent;
}
return LogContent;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~UnifiedFlashingPlatformModel()
{
Dispose(false);
}
public void Close()
{
USBDevice?.Dispose();
}
protected virtual void Dispose(bool disposing)
{
if (Disposed)
{
return;
}
if (disposing)
{
// Other disposables
}
// Clean unmanaged resources here.
Close();
Disposed = true;
}
}
}
+1 -907
View File
@@ -20,18 +20,8 @@
using Microsoft.Win32; using Microsoft.Win32;
using System; using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Data;
using WPinternals.HelperClasses; using WPinternals.HelperClasses;
using WPinternals.Models.Lumia.MSR;
using WPinternals.Models.Lumia.NCSd; using WPinternals.Models.Lumia.NCSd;
using WPinternals.Models.UEFIApps.Flash; using WPinternals.Models.UEFIApps.Flash;
using WPinternals.Models.UEFIApps.PhoneInfo; using WPinternals.Models.UEFIApps.PhoneInfo;
@@ -41,7 +31,6 @@ namespace WPinternals
internal class DownloadsViewModel : ContextViewModel internal class DownloadsViewModel : ContextViewModel
{ {
private readonly PhoneNotifierViewModel Notifier; private readonly PhoneNotifierViewModel Notifier;
private bool IsSearching = false;
internal DownloadsViewModel(PhoneNotifierViewModel Notifier) internal DownloadsViewModel(PhoneNotifierViewModel Notifier)
{ {
@@ -50,11 +39,6 @@ namespace WPinternals
this.Notifier = Notifier; this.Notifier = Notifier;
Notifier.NewDeviceArrived += Notifier_NewDeviceArrived; Notifier.NewDeviceArrived += Notifier_NewDeviceArrived;
RegistryKey Key = Registry.CurrentUser.OpenSubKey(@"Software\WPInternals", true) ?? Registry.CurrentUser.CreateSubKey(@"Software\WPInternals");
DownloadFolder = (string)Key.GetValue("DownloadFolder", @"C:\ProgramData\WPinternals\Repository");
Key.Close();
AddFFUCommand = new DelegateCommand(() => AddFFUCommand = new DelegateCommand(() =>
{ {
string FFUPath = null; string FFUPath = null;
@@ -170,331 +154,11 @@ namespace WPinternals
} }
} }
internal static void TimerCallback(object State)
{
foreach (DownloadEntry Entry in App.DownloadManager.DownloadList)
{
if (Entry.SpeedIndex >= 0)
{
int ArrayIndex = (int)(Entry.SpeedIndex % 10);
Entry.Speeds[ArrayIndex] = Entry.BytesReceived - Entry.LastBytesReceived;
int Count = (int)((Entry.SpeedIndex + 1) > 10 ? 10 : (Entry.SpeedIndex + 1));
long Sum = 0;
for (int i = 0; i < Count; i++)
{
Sum += Entry.Speeds[i];
}
Entry.Speed = Sum / Count;
Entry.TimeLeft = Entry.Speed < 1000 ? Timeout.InfiniteTimeSpan : TimeSpan.FromSeconds((Entry.Size - Entry.BytesReceived) / Entry.Speed);
}
Entry.LastBytesReceived = Entry.BytesReceived;
Entry.SpeedIndex++;
}
}
private void Notifier_NewDeviceArrived(ArrivalEventArgs Args) private void Notifier_NewDeviceArrived(ArrivalEventArgs Args)
{ {
EvaluateViewState(); EvaluateViewState();
} }
internal static long GetFileLengthFromURL(string URL)
{
long Length = 0;
WebRequest webReq = WebRequest.Create(URL);
if (webReq is HttpWebRequest req)
{
req.Method = "HEAD";
req.ServicePoint.ConnectionLimit = 10;
using (WebResponse resp = req.GetResponse())
{
long.TryParse(resp.Headers.Get("Content-Length"), out 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)
{
string FileName = Path.GetFileName(URL);
int End = FileName.IndexOf('?');
if (End >= 0)
{
FileName = FileName.Substring(0, End);
}
return FileName;
}
private void Search()
{
if (IsSearching)
{
return;
}
IsSearching = true;
SynchronizationContext UIContext = SynchronizationContext.Current;
SearchResultList.Clear();
new Thread(() =>
{
string[] EmergencyURLs = null;
try
{
string TempProductType = ProductType.ToUpper();
if ((TempProductType?.StartsWith("RM") == true) && !TempProductType.StartsWith("RM-"))
{
TempProductType = "RM-" + TempProductType[2..];
}
ProductType = TempProductType;
if (TempProductType != null)
{
ProductType = TempProductType;
}
if (ProductType != null)
{
EmergencyURLs = LumiaDownloadModel.SearchEmergencyFiles(ProductType);
}
}
catch (Exception ex)
{
LogFile.LogException(ex, LogType.FileOnly);
}
UIContext.Post(s =>
{
if (EmergencyURLs != null)
{
SearchResultList.Add(new SearchResult($"{ProductType} emergency-files", EmergencyURLs, ProductType, EmergencyDownloaded, ProductType));
}
}, null);
IsSearching = false;
}).Start();
}
internal void Download(string URL, string Category, Action<string[], object> Callback, object State = null)
{
string Folder = Category == null ? DownloadFolder : Path.Combine(DownloadFolder, Category);
DownloadList.Add(new DownloadEntry(URL, Folder, null, Callback, State));
}
internal void Download(string[] URLs, string Category, Action<string[], object> Callback, object State = null)
{
string Folder = Category == null ? DownloadFolder : Path.Combine(DownloadFolder, Category);
foreach (string URL in URLs)
{
DownloadList.Add(new DownloadEntry(URL, Folder, URLs, Callback, State));
}
}
private void DownloadAll()
{
SynchronizationContext UIContext = SynchronizationContext.Current;
new Thread(() =>
{
string[] EmergencyURLs = null;
try
{
string TempProductType = ProductType.ToUpper();
if ((TempProductType?.StartsWith("RM") == true) && !TempProductType.StartsWith("RM-"))
{
TempProductType = "RM-" + TempProductType[2..];
}
ProductType = TempProductType;
if (TempProductType != null)
{
ProductType = TempProductType;
}
if (ProductType != null)
{
EmergencyURLs = LumiaDownloadModel.SearchEmergencyFiles(ProductType);
}
}
catch (Exception ex)
{
LogFile.LogException(ex, LogType.FileOnly);
}
UIContext.Post(s =>
{
if (EmergencyURLs != null)
{
Download(EmergencyURLs, ProductType, EmergencyDownloaded, ProductType);
}
}, null);
}).Start();
}
private void DownloadSelected()
{
foreach (SearchResult Result in SearchResultList.Where(r => r.IsSelected))
{
App.DownloadManager.Download(Result.URLs, Result.Category, Result.Callback, Result.State);
}
}
private void EmergencyDownloaded(string[] Files, object State)
{
string Type = (string)State;
string ProgrammerPath = null;
string PayloadPath = null;
for (int i = 0; i < Files.Length; i++)
{
if (Files[i].EndsWith(".ede", StringComparison.OrdinalIgnoreCase))
{
ProgrammerPath = Files[i];
}
if (Files[i].EndsWith(".edp", StringComparison.OrdinalIgnoreCase))
{
PayloadPath = Files[i];
}
}
if ((Type != null) && (ProgrammerPath != null) && (PayloadPath != null))
{
App.Config.AddEmergencyToRepository(Type, ProgrammerPath, PayloadPath);
}
}
public ObservableCollection<DownloadEntry> DownloadList { get; } = [];
public ObservableCollection<SearchResult> SearchResultList { get; } = [];
private DelegateCommand _DownloadSelectedCommand = null;
public DelegateCommand DownloadSelectedCommand
{
get
{
return _DownloadSelectedCommand ??= new DelegateCommand(() => DownloadSelected());
}
}
private DelegateCommand _SearchCommand = null;
public DelegateCommand SearchCommand
{
get
{
return _SearchCommand ??= new DelegateCommand(() => Search());
}
}
private DelegateCommand _DownloadAllCommand = null;
public DelegateCommand DownloadAllCommand
{
get
{
return _DownloadAllCommand ??= new DelegateCommand(() => DownloadAll());
}
}
private string _DownloadFolder = null;
public string DownloadFolder
{
get
{
return _DownloadFolder;
}
set
{
if (_DownloadFolder != value)
{
_DownloadFolder = value;
try
{
Directory.CreateDirectory(_DownloadFolder);
}
catch (Exception ex)
{
LogFile.LogException(ex, LogType.FileOnly);
}
if (!Directory.Exists(_DownloadFolder))
{
_DownloadFolder = @"C:\ProgramData\WPinternals\Repository";
Directory.CreateDirectory(_DownloadFolder);
}
RegistryKey Key = Registry.CurrentUser.OpenSubKey(@"Software\WPInternals", true);
if (_DownloadFolder == null)
{
if (Key.GetValue("DownloadFolder") != null)
{
Key.DeleteValue("DownloadFolder");
}
}
else
{
Key.SetValue("DownloadFolder", _DownloadFolder);
}
Key.Close();
OnPropertyChanged(nameof(DownloadFolder));
}
}
}
private string _ProductCode = null;
public string ProductCode
{
get
{
return _ProductCode;
}
set
{
if (_ProductCode != value)
{
_ProductCode = value;
OnPropertyChanged(nameof(ProductCode));
}
}
}
private string _ProductType = null;
public string ProductType
{
get
{
return _ProductType;
}
set
{
if (_ProductType != value)
{
_ProductType = value;
OnPropertyChanged(nameof(ProductType));
}
}
}
private string _FirmwareVersion = null; private string _FirmwareVersion = null;
public string FirmwareVersion public string FirmwareVersion
{ {
@@ -513,24 +177,6 @@ namespace WPinternals
} }
} }
private string _OperatorCode = null;
public string OperatorCode
{
get
{
return _OperatorCode;
}
set
{
if (_OperatorCode != value)
{
_OperatorCode = value;
OnPropertyChanged(nameof(OperatorCode));
}
}
}
internal override async void EvaluateViewState() internal override async void EvaluateViewState()
{ {
if (IsSwitchingInterface) if (IsSwitchingInterface)
@@ -574,9 +220,6 @@ namespace WPinternals
LumiaPhoneInfoAppModel LumiaPhoneInfoModel = (LumiaPhoneInfoAppModel)Notifier.CurrentModel; LumiaPhoneInfoAppModel LumiaPhoneInfoModel = (LumiaPhoneInfoAppModel)Notifier.CurrentModel;
LumiaPhoneInfoAppPhoneInfo Info = LumiaPhoneInfoModel.ReadPhoneInfo(); LumiaPhoneInfoAppPhoneInfo Info = LumiaPhoneInfoModel.ReadPhoneInfo();
ProductType = Info.Type;
OperatorCode = "";
ProductCode = Info.ProductCode;
ModernFlashApp = Info.PhoneInfoAppVersionMajor >= 2; ModernFlashApp = Info.PhoneInfoAppVersionMajor >= 2;
if (ModernFlashApp) if (ModernFlashApp)
@@ -609,9 +252,6 @@ namespace WPinternals
{ {
LumiaPhoneInfoAppModel LumiaPhoneInfoModel = (LumiaPhoneInfoAppModel)Notifier.CurrentModel; LumiaPhoneInfoAppModel LumiaPhoneInfoModel = (LumiaPhoneInfoAppModel)Notifier.CurrentModel;
LumiaPhoneInfoAppPhoneInfo Info = LumiaPhoneInfoModel.ReadPhoneInfo(); LumiaPhoneInfoAppPhoneInfo Info = LumiaPhoneInfoModel.ReadPhoneInfo();
ProductType = Info.Type;
OperatorCode = "";
ProductCode = Info.ProductCode;
IsSwitchingInterface = true; IsSwitchingInterface = true;
@@ -663,556 +303,10 @@ namespace WPinternals
else if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Normal) else if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Normal)
{ {
NokiaCareSuiteModel LumiaNormalModel = (NokiaCareSuiteModel)Notifier.CurrentModel; NokiaCareSuiteModel LumiaNormalModel = (NokiaCareSuiteModel)Notifier.CurrentModel;
OperatorCode = LumiaNormalModel.ExecuteJsonMethodAsString("ReadOperatorName", "OperatorName"); // Example: 000-NL
string TempProductType = LumiaNormalModel.ExecuteJsonMethodAsString("ReadManufacturerModelName", "ManufacturerModelName"); // RM-821_eu_denmark_251
if (TempProductType.Contains('_'))
{
TempProductType = TempProductType.Substring(0, TempProductType.IndexOf('_'));
}
ProductType = TempProductType;
ProductCode = LumiaNormalModel.ExecuteJsonMethodAsString("ReadProductCode", "ProductCode"); // 059Q9D7
FirmwareVersion = LumiaNormalModel.ExecuteJsonMethodAsString("ReadSwVersion", "SwVersion"); FirmwareVersion = LumiaNormalModel.ExecuteJsonMethodAsString("ReadSwVersion", "SwVersion");
} }
} }
public DelegateCommand AddFFUCommand { get; } = null; public DelegateCommand AddFFUCommand { get; } = null;
public DelegateCommand AddSecWIMCommand { get; } = null; public DelegateCommand AddSecWIMCommand { get; } = null;
} }
}
internal enum DownloadStatus
{
Downloading,
Ready,
Failed
};
internal class DownloadEntry : INotifyPropertyChanged, IProgress<GeneralDownloadProgress>
{
private readonly SynchronizationContext UIContext;
public event PropertyChangedEventHandler PropertyChanged = delegate { };
internal Action<string[], object> Callback;
internal object State;
internal string URL;
internal string[] URLCollection;
internal string Folder;
//internal HttpClient Client;
internal HttpDownloader Client;
internal long SpeedIndex = -1;
internal long[] Speeds = new long[10];
internal long LastBytesReceived;
internal long BytesReceived;
internal DownloadEntry(string URL, string Folder, string[] URLCollection, Action<string[], object> Callback, object State)
{
UIContext = SynchronizationContext.Current;
this.URL = URL;
this.Callback = Callback;
this.State = State;
this.URLCollection = URLCollection;
this.Folder = Folder;
Directory.CreateDirectory(Folder);
Name = DownloadsViewModel.GetFileNameFromURL(URL);
Uri Uri = new(URL);
Status = DownloadStatus.Downloading;
new Thread(() =>
{
Size = DownloadsViewModel.GetFileLengthFromURL(URL);
//Client = new HttpClient();
//_ = Client.DownloadFileAsync(Uri, Path.Combine(Folder, DownloadsViewModel.GetFileNameFromURL(Uri.LocalPath)), Client_DownloadProgressChanged, Client_DownloadFileCompleted);
Client = new(Folder, 4, false);
_ = 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()
{
Status = Error ? DownloadStatus.Failed : DownloadStatus.Ready;
App.DownloadManager.DownloadList.Remove(this);
if (Status == DownloadStatus.Ready)
{
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)
{
Files = new string[1];
Files[0] = Path.Combine(Folder, DownloadsViewModel.GetFileNameFromURL(URL));
}
else
{
Files = new string[URLCollection.Length];
for (int i = 0; i < URLCollection.Length; i++)
{
Files[i] = Path.Combine(Folder, DownloadsViewModel.GetFileNameFromURL(URLCollection[i]));
}
}
Callback(Files, State);
}
}
}
if (UIContext == null)
{
Finish();
}
else
{
UIContext?.Post(d => Finish(), null);
}
}
private void Client_DownloadProgressChanged(HttpClientDownloadProgress e)
{
BytesReceived = e.BytesReceived;
Progress = e.ProgressPercentage;
}
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
if (SynchronizationContext.Current == UIContext)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
else
{
UIContext.Post((s) => PropertyChanged(this, new PropertyChangedEventArgs(propertyName)), null);
}
}
}
private DownloadStatus _Status;
public DownloadStatus Status
{
get
{
return _Status;
}
set
{
if (_Status != value)
{
_Status = value;
OnPropertyChanged(nameof(Status));
}
}
}
private string _Name;
public string Name
{
get
{
return _Name;
}
set
{
if (_Name != value)
{
_Name = value;
OnPropertyChanged(nameof(Name));
}
}
}
private long _Size;
public long Size
{
get
{
return _Size;
}
set
{
if (_Size != value)
{
_Size = value;
OnPropertyChanged(nameof(Size));
}
}
}
private TimeSpan _TimeLeft;
public TimeSpan TimeLeft
{
get
{
return _TimeLeft;
}
set
{
if (_TimeLeft != value)
{
_TimeLeft = value;
OnPropertyChanged(nameof(TimeLeft));
}
}
}
private double _Speed;
public double Speed
{
get
{
return _Speed;
}
set
{
if (_Speed != value)
{
_Speed = value;
OnPropertyChanged(nameof(Speed));
}
}
}
private int _Progress;
public int Progress
{
get
{
return _Progress;
}
set
{
if (_Progress != value)
{
_Progress = value;
OnPropertyChanged(nameof(Progress));
}
}
}
}
internal class SearchResult : INotifyPropertyChanged
{
private readonly SynchronizationContext UIContext;
public event PropertyChangedEventHandler PropertyChanged;
internal string[] URLs;
internal Action<string[], object> Callback;
internal object State;
internal string Category;
internal SearchResult(string URL, string Category, Action<string[], object> Callback, object State)
{
UIContext = SynchronizationContext.Current;
URLs = new string[1];
URLs[0] = URL;
Name = DownloadsViewModel.GetFileNameFromURL(URL);
this.Callback = Callback;
this.State = State;
this.Category = Category;
GetSize();
}
internal SearchResult(string Name, string[] URLs, string Category, Action<string[], object> Callback, object State)
{
UIContext = SynchronizationContext.Current;
this.URLs = URLs;
this.Name = Name;
this.Callback = Callback;
this.State = State;
this.Category = Category;
GetSize();
}
internal SearchResult(string Name, string URL, string Category, Action<string[], object> Callback, object State)
{
UIContext = SynchronizationContext.Current;
URLs = new string[1];
URLs[0] = URL;
this.Name = Name;
this.Callback = Callback;
this.State = State;
this.Category = Category;
GetSize();
}
private void GetSize()
{
new Thread(() =>
{
long CalcSize = 0;
foreach (string URL in URLs)
{
CalcSize += DownloadsViewModel.GetFileLengthFromURL(URL);
}
Size = CalcSize;
}).Start();
}
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
if (SynchronizationContext.Current == UIContext)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
else
{
UIContext.Post((s) => PropertyChanged(this, new PropertyChangedEventArgs(propertyName)), null);
}
}
}
private string _Name;
public string Name
{
get
{
return _Name;
}
set
{
if (_Name != value)
{
_Name = value;
OnPropertyChanged(nameof(Name));
}
}
}
private long _Size;
public long Size
{
get
{
return _Size;
}
set
{
if (_Size != value)
{
_Size = value;
OnPropertyChanged(nameof(Size));
}
}
}
private bool _IsSelected;
public bool IsSelected
{
get
{
return _IsSelected;
}
set
{
if (_IsSelected != value)
{
_IsSelected = value;
OnPropertyChanged(nameof(IsSelected));
}
}
}
}
public class HttpClientDownloadProgress
{
//
// Summary:
// Gets the asynchronous task progress percentage.
//
// Returns:
// A percentage value indicating the asynchronous task progress.
public int ProgressPercentage { get; }
//
// Summary:
// Gets the number of bytes received.
//
// Returns:
// An System.Int64 value that indicates the number of bytes received.
public long BytesReceived { get; }
//
// Summary:
// Gets the total number of bytes in a System.Net.WebClient data download operation.
//
// Returns:
// An System.Int64 value that indicates the number of bytes that will be received.
public long TotalBytesToReceive { get; }
internal HttpClientDownloadProgress(long BytesReceived, long TotalBytesToReceive)
{
this.TotalBytesToReceive = TotalBytesToReceive;
this.BytesReceived = BytesReceived;
ProgressPercentage = (int)Math.Round((float)BytesReceived / TotalBytesToReceive * 100f);
}
}
public static class HttpClientProgressExtensions
{
public static async Task DownloadFileAsync(this HttpClient client, Uri address, string fileName, Action<HttpClientDownloadProgress> progress = null, Action<bool> completed = null, CancellationToken cancellationToken = default)
{
try
{
using FileStream destination = File.Create(fileName);
using HttpResponseMessage response = await client.GetAsync(address, HttpCompletionOption.ResponseHeadersRead);
long? contentLength = response.Content.Headers.ContentLength;
using Stream download = await response.Content.ReadAsStreamAsync();
if (progress is null || !contentLength.HasValue)
{
await download.CopyToAsync(destination);
completed?.Invoke(true);
return;
}
Progress<long> progressWrapper = new(totalBytes => progress(new HttpClientDownloadProgress(totalBytes, contentLength.Value)));
await download.CopyToAsync(destination, 81920, progressWrapper, cancellationToken);
completed?.Invoke(true);
}
catch (Exception ex)
{
LogFile.Log("An unexpected error happened", LogType.FileAndConsole);
LogFile.Log(ex.GetType().ToString(), LogType.FileAndConsole);
LogFile.Log(ex.Message, LogType.FileAndConsole);
LogFile.Log(ex.StackTrace, LogType.FileAndConsole);
completed?.Invoke(false);
}
}
private static async Task CopyToAsync(this Stream source, Stream destination, int bufferSize, IProgress<long> progress = null, CancellationToken cancellationToken = default)
{
if (bufferSize < 0)
throw new ArgumentOutOfRangeException(nameof(bufferSize));
if (source is null)
throw new ArgumentNullException(nameof(source));
if (!source.CanRead)
throw new InvalidOperationException($"'{nameof(source)}' is not readable.");
if (destination == null)
throw new ArgumentNullException(nameof(destination));
if (!destination.CanWrite)
throw new InvalidOperationException($"'{nameof(destination)}' is not writable.");
byte[] buffer = new byte[bufferSize];
long totalBytesRead = 0;
int bytesRead;
while ((bytesRead = await source.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) != 0)
{
await destination.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken).ConfigureAwait(false);
totalBytesRead += bytesRead;
progress?.Report(totalBytesRead);
}
}
}
public class DownloaderNameConvertor : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return Path.GetFileNameWithoutExtension((string)value);
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
public class DownloaderSizeConvertor : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
long? Size = value as long?;
if (Size < 1024)
{
return Size + " B";
}
if (Size < (1024 * 1024))
{
return Math.Round((double)Size / 1024, 0) + " KB";
}
return Math.Round((double)Size / 1024 / 1024, 0) + " MB";
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
public class DownloaderSpeedConvertor : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return ((int)((double)value / 1024)).ToString() + " KB/s";
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
public class DownloaderTimeRemainingConvertor : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
TimeSpan TimeLeft = (TimeSpan)value;
if (TimeLeft == Timeout.InfiniteTimeSpan)
{
return "";
}
return TimeLeft.ToString(@"h\:mm\:ss");
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
}
@@ -19,8 +19,10 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System; using System;
using UnifiedFlashingPlatform;
using WPinternals.HelperClasses; using WPinternals.HelperClasses;
using WPinternals.Models.Lumia.NCSd; using WPinternals.Models.Lumia.NCSd;
using WPinternals.Models.SimpleIO;
using WPinternals.Models.UEFIApps.BootMgr; using WPinternals.Models.UEFIApps.BootMgr;
using WPinternals.Models.UEFIApps.Flash; using WPinternals.Models.UEFIApps.Flash;
using WPinternals.Models.UEFIApps.PhoneInfo; using WPinternals.Models.UEFIApps.PhoneInfo;
@@ -91,6 +93,12 @@ namespace WPinternals
case PhoneInterfaces.Lumia_MassStorage: case PhoneInterfaces.Lumia_MassStorage:
ActivateSubContext(new NokiaMassStorageViewModel((MassStorage)CurrentModel)); ActivateSubContext(new NokiaMassStorageViewModel((MassStorage)CurrentModel));
break; break;
case PhoneInterfaces.SimpleIO:
ActivateSubContext(new SimpleIOViewModel((SimpleIOModel)CurrentModel, ModeSwitchRequestCallback));
break;
case PhoneInterfaces.UFP:
ActivateSubContext(new UFPViewModel((UnifiedFlashingPlatformModel)CurrentModel, ModeSwitchRequestCallback));
break;
} }
} }
} }
@@ -19,8 +19,10 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System; using System;
using UnifiedFlashingPlatform;
using WPinternals.HelperClasses; using WPinternals.HelperClasses;
using WPinternals.Models.Lumia.NCSd; using WPinternals.Models.Lumia.NCSd;
using WPinternals.Models.SimpleIO;
using WPinternals.Models.UEFIApps.BootMgr; using WPinternals.Models.UEFIApps.BootMgr;
using WPinternals.Models.UEFIApps.Flash; using WPinternals.Models.UEFIApps.Flash;
using WPinternals.Models.UEFIApps.PhoneInfo; using WPinternals.Models.UEFIApps.PhoneInfo;
@@ -109,6 +111,12 @@ namespace WPinternals
case PhoneInterfaces.Lumia_MassStorage: case PhoneInterfaces.Lumia_MassStorage:
ActivateSubContext(new NokiaModeMassStorageViewModel((MassStorage)CurrentModel, OnModeSwitchRequested)); ActivateSubContext(new NokiaModeMassStorageViewModel((MassStorage)CurrentModel, OnModeSwitchRequested));
break; break;
case PhoneInterfaces.SimpleIO:
ActivateSubContext(new SimpleIOModeViewModel((SimpleIOModel)CurrentModel, OnModeSwitchRequested));
break;
case PhoneInterfaces.UFP:
ActivateSubContext(new UFPModeViewModel((UnifiedFlashingPlatformModel)CurrentModel, OnModeSwitchRequested));
break;
} }
} }
@@ -2108,7 +2108,7 @@ namespace WPinternals
Part = new FlashPart(); Part = new FlashPart();
Partition TargetPartition = GPT.GetPartition("UEFI_BS_NV"); Partition TargetPartition = GPT.GetPartition("UEFI_BS_NV");
Part.StartSector = (UInt32)TargetPartition.FirstSector; // GPT is prepared for 64-bit sector-offset, but flash app isn't. Part.StartSector = (UInt32)TargetPartition.FirstSector; // GPT is prepared for 64-bit sector-offset, but flash app isn't.
string SBRes = IsSpecB ? "WPinternals.SB" : "WPinternals.SBA"; string SBRes = IsSpecB ? "WPinternals.Assets.SB" : "WPinternals.Assets.SBA";
Part.Stream = new SeekableStream(() => Part.Stream = new SeekableStream(() =>
{ {
System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly(); System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
@@ -197,7 +197,7 @@ namespace WPinternals
// It overwrites the variable in a different NV-partition than where this variable is stored usually. // It overwrites the variable in a different NV-partition than where this variable is stored usually.
// This normally leads to endless-loops when the NV-variables are enumerated. // This normally leads to endless-loops when the NV-variables are enumerated.
// But the partition contains an extra hack to break out the endless loops. // But the partition contains an extra hack to break out the endless loops.
Stream stream = assembly.GetManifestResourceStream("WPinternals.SB"); Stream stream = assembly.GetManifestResourceStream("WPinternals.Assets.SB");
return new DecompressedStream(stream); return new DecompressedStream(stream);
}); });
@@ -2293,7 +2293,7 @@ namespace WPinternals
// It overwrites the variable in a different NV-partition than where this variable is stored usually. // It overwrites the variable in a different NV-partition than where this variable is stored usually.
// This normally leads to endless-loops when the NV-variables are enumerated. // This normally leads to endless-loops when the NV-variables are enumerated.
// But the partition contains an extra hack to break out the endless loops. // But the partition contains an extra hack to break out the endless loops.
Stream stream = assembly.GetManifestResourceStream("WPinternals.SB"); Stream stream = assembly.GetManifestResourceStream("WPinternals.Assets.SB");
return new DecompressedStream(stream); return new DecompressedStream(stream);
}); });
@@ -2652,7 +2652,7 @@ namespace WPinternals
// It overwrites the variable in a different NV-partition than where this variable is stored usually. // It overwrites the variable in a different NV-partition than where this variable is stored usually.
// This normally leads to endless-loops when the NV-variables are enumerated. // This normally leads to endless-loops when the NV-variables are enumerated.
// But the partition contains an extra hack to break out the endless loops. // But the partition contains an extra hack to break out the endless loops.
Stream stream = assembly.GetManifestResourceStream("WPinternals.SB"); Stream stream = assembly.GetManifestResourceStream("WPinternals.Assets.SB");
return new DecompressedStream(stream); return new DecompressedStream(stream);
}); });
+3 -1
View File
@@ -49,7 +49,9 @@ namespace WPinternals
Qualcomm_Download, Qualcomm_Download,
Qualcomm_Flash, Qualcomm_Flash,
Lumia_BadMassStorage, Lumia_BadMassStorage,
Lumia_PhoneInfo Lumia_PhoneInfo,
SimpleIO,
UFP
}; };
// Create this class on the UI thread, after the main-window of the application is initialized. // Create this class on the UI thread, after the main-window of the application is initialized.
@@ -28,10 +28,10 @@ namespace WPinternals
private readonly MassStorage CurrentModel; private readonly MassStorage CurrentModel;
private readonly Action<PhoneInterfaces?> RequestModeSwitch; private readonly Action<PhoneInterfaces?> RequestModeSwitch;
internal NokiaModeMassStorageViewModel(NokiaPhoneModel CurrentModel, Action<PhoneInterfaces?> RequestModeSwitch) internal NokiaModeMassStorageViewModel(MassStorage CurrentModel, Action<PhoneInterfaces?> RequestModeSwitch)
: base() : base()
{ {
this.CurrentModel = (MassStorage)CurrentModel; this.CurrentModel = CurrentModel;
this.RequestModeSwitch = RequestModeSwitch; this.RequestModeSwitch = RequestModeSwitch;
} }
@@ -24,9 +24,11 @@ using System.Diagnostics.Eventing.Reader;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using UnifiedFlashingPlatform;
using WPinternals.HelperClasses; using WPinternals.HelperClasses;
using WPinternals.Models.Lumia.NCSd; using WPinternals.Models.Lumia.NCSd;
using WPinternals.Models.Lumia.UEFI; using WPinternals.Models.Lumia.UEFI;
using WPinternals.Models.SimpleIO;
using WPinternals.Models.UEFIApps.BootMgr; using WPinternals.Models.UEFIApps.BootMgr;
using WPinternals.Models.UEFIApps.Flash; using WPinternals.Models.UEFIApps.Flash;
using WPinternals.Models.UEFIApps.PhoneInfo; using WPinternals.Models.UEFIApps.PhoneInfo;
@@ -47,7 +49,10 @@ namespace WPinternals
private USBNotifier LumiaEmergencyNotifier; private USBNotifier LumiaEmergencyNotifier;
private USBNotifier LumiaLabelNotifier; private USBNotifier LumiaLabelNotifier;
private USBNotifier HidInterfaceNotifier; private USBNotifier HidInterfaceNotifier;
private USBNotifier SimpleIONotifier;
private USBNotifier UFPNotifier;
public string? CurrentDevicePath = null;
public PhoneInterfaces? CurrentInterface = null; public PhoneInterfaces? CurrentInterface = null;
private PhoneInterfaces? LastInterface = null; private PhoneInterfaces? LastInterface = null;
public IDisposable CurrentModel = null; public IDisposable CurrentModel = null;
@@ -67,6 +72,10 @@ namespace WPinternals
private Guid LumiaFlashInterfaceGuid = new("{9e3bd5f7-9690-4fcc-8810-3e2650cd6ecc}"); private Guid LumiaFlashInterfaceGuid = new("{9e3bd5f7-9690-4fcc-8810-3e2650cd6ecc}");
private Guid LumiaEmergencyInterfaceGuid = new("{71DE994D-8B7C-43DB-A27E-2AE7CD579A0C}"); private Guid LumiaEmergencyInterfaceGuid = new("{71DE994D-8B7C-43DB-A27E-2AE7CD579A0C}");
private Guid SimpleIOInterfaceGuid = new("{82809dd0-51f5-11e1-b86c-0800200c9a66}");
private Guid UFPInterfaceGuid = new("{9E3BD5F7-9690-4FCC-8810-3E2650CD6ECC}");
private readonly object ModelLock = new(); private readonly object ModelLock = new();
private readonly EventWaitHandle NewInterfaceWaitHandle = new(false, EventResetMode.AutoReset); private readonly EventWaitHandle NewInterfaceWaitHandle = new(false, EventResetMode.AutoReset);
@@ -113,6 +122,14 @@ namespace WPinternals
HidInterfaceNotifier.Arrival += LumiaNotifier_Arrival; HidInterfaceNotifier.Arrival += LumiaNotifier_Arrival;
HidInterfaceNotifier.Removal += LumiaNotifier_Removal; HidInterfaceNotifier.Removal += LumiaNotifier_Removal;
SimpleIONotifier = new USBNotifier(SimpleIOInterfaceGuid);
SimpleIONotifier.Arrival += LumiaNotifier_Arrival;
SimpleIONotifier.Removal += LumiaNotifier_Removal;
UFPNotifier = new USBNotifier(UFPInterfaceGuid);
UFPNotifier.Arrival += LumiaNotifier_Arrival;
UFPNotifier.Removal += LumiaNotifier_Removal;
try try
{ {
EventLogQuery LogQuery = new("Microsoft-Windows-Kernel-PnP/Configuration", PathType.LogName, "*[System[(EventID = 411)]]"); EventLogQuery LogQuery = new("Microsoft-Windows-Kernel-PnP/Configuration", PathType.LogName, "*[System[(EventID = 411)]]");
@@ -150,6 +167,8 @@ namespace WPinternals
ComPortNotifier.Dispose(); ComPortNotifier.Dispose();
LumiaEmergencyNotifier.Dispose(); LumiaEmergencyNotifier.Dispose();
HidInterfaceNotifier.Dispose(); HidInterfaceNotifier.Dispose();
SimpleIONotifier.Dispose();
UFPNotifier.Dispose();
LogWatcher.Dispose(); LogWatcher.Dispose();
} }
@@ -173,14 +192,44 @@ namespace WPinternals
{ {
try try
{ {
if (e.DevicePath == CurrentDevicePath)
{
return;
}
if (e.DevicePath.Contains("VID_0421&", StringComparison.OrdinalIgnoreCase) || if (e.DevicePath.Contains("VID_0421&", StringComparison.OrdinalIgnoreCase) ||
e.DevicePath.Contains("VID_045E&", StringComparison.OrdinalIgnoreCase)) e.DevicePath.Contains("VID_045E&", StringComparison.OrdinalIgnoreCase))
{ {
if (e.DevicePath.Contains("&PID_0660&MI_04", StringComparison.OrdinalIgnoreCase) || if (e.DevicePath.Contains("&PID_0658", StringComparison.OrdinalIgnoreCase))
{
CurrentModel = new UnifiedFlashingPlatformModel(e.DevicePath);
CurrentInterface = PhoneInterfaces.UFP;
CurrentDevicePath = e.DevicePath;
LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly);
LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly);
LogFile.Log("Connected device: Lumia", LogType.FileAndConsole);
LogFile.Log("Mode: UFP", LogType.FileAndConsole);
NewDeviceArrived(new ArrivalEventArgs((PhoneInterfaces)CurrentInterface, CurrentModel));
}
else if (e.DevicePath.Contains("&PID_062A", StringComparison.OrdinalIgnoreCase))
{
CurrentModel = new SimpleIOModel(e.DevicePath);
CurrentInterface = PhoneInterfaces.SimpleIO;
CurrentDevicePath = e.DevicePath;
LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly);
LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly);
LogFile.Log("Connected device: Lumia", LogType.FileAndConsole);
LogFile.Log("Mode: SimpleIO", LogType.FileAndConsole);
NewDeviceArrived(new ArrivalEventArgs((PhoneInterfaces)CurrentInterface, CurrentModel));
}
else if (e.DevicePath.Contains("&PID_0660&MI_04", StringComparison.OrdinalIgnoreCase) ||
e.DevicePath.Contains("&PID_0713&MI_04", StringComparison.OrdinalIgnoreCase) || // for Spec B e.DevicePath.Contains("&PID_0713&MI_04", StringComparison.OrdinalIgnoreCase) || // for Spec B
e.DevicePath.Contains("&PID_0A01&MI_04", StringComparison.OrdinalIgnoreCase)) // for Spec B (650) e.DevicePath.Contains("&PID_0A01&MI_04", StringComparison.OrdinalIgnoreCase)) // for Spec B (650)
{ {
CurrentInterface = PhoneInterfaces.Lumia_Label; CurrentInterface = PhoneInterfaces.Lumia_Label;
CurrentDevicePath = e.DevicePath;
CurrentModel = new NokiaCareSuiteModel(e.DevicePath); CurrentModel = new NokiaCareSuiteModel(e.DevicePath);
LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly); LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly);
LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly); LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly);
@@ -217,6 +266,7 @@ namespace WPinternals
// So we assume we need to talk to this old interface. // So we assume we need to talk to this old interface.
CurrentInterface = PhoneInterfaces.Lumia_Normal; CurrentInterface = PhoneInterfaces.Lumia_Normal;
CurrentDevicePath = e.DevicePath;
CurrentModel = new NokiaCareSuiteModel(DevicePath); CurrentModel = new NokiaCareSuiteModel(DevicePath);
LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly); LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly);
LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly); LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly);
@@ -232,6 +282,7 @@ namespace WPinternals
NewInterfaceWaitHandle.Set(); NewInterfaceWaitHandle.Set();
CurrentInterface = PhoneInterfaces.Lumia_Normal; CurrentInterface = PhoneInterfaces.Lumia_Normal;
CurrentDevicePath = e.DevicePath;
CurrentModel = new NokiaCareSuiteModel(e.DevicePath); CurrentModel = new NokiaCareSuiteModel(e.DevicePath);
LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly); LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly);
LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly); LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly);
@@ -246,11 +297,10 @@ namespace WPinternals
e.DevicePath.Contains("&PID_05EE", StringComparison.OrdinalIgnoreCase)) // VID_0421&PID_05EE is for early RX100 e.DevicePath.Contains("&PID_05EE", StringComparison.OrdinalIgnoreCase)) // VID_0421&PID_05EE is for early RX100
{ {
FlashAppType type = FlashAppType.FlashApp; FlashAppType type = FlashAppType.FlashApp;
NokiaUEFIModel tmpModel = new(e.DevicePath);
try try
{ {
NokiaUEFIModel tmpModel = new(e.DevicePath);
type = tmpModel.GetFlashAppType(); type = tmpModel.GetFlashAppType();
tmpModel.Dispose();
LogFile.Log("Flash App Type: " + type.ToString(), LogType.FileOnly); LogFile.Log("Flash App Type: " + type.ToString(), LogType.FileOnly);
} }
catch (Exception ex) catch (Exception ex)
@@ -262,6 +312,7 @@ namespace WPinternals
LogFile.Log("Flash App Type could not be determined, assuming " + type.ToString(), LogType.FileOnly); LogFile.Log("Flash App Type could not be determined, assuming " + type.ToString(), LogType.FileOnly);
} }
tmpModel.Dispose();
switch (type) switch (type)
{ {
@@ -271,6 +322,7 @@ namespace WPinternals
((NokiaUEFIModel)CurrentModel).InterfaceChanged += InterfaceChanged; ((NokiaUEFIModel)CurrentModel).InterfaceChanged += InterfaceChanged;
CurrentInterface = PhoneInterfaces.Lumia_Bootloader; CurrentInterface = PhoneInterfaces.Lumia_Bootloader;
CurrentDevicePath = e.DevicePath;
LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly); LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly);
LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly); LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly);
LogFile.Log("Connected device: Lumia", LogType.FileAndConsole); LogFile.Log("Connected device: Lumia", LogType.FileAndConsole);
@@ -285,6 +337,7 @@ namespace WPinternals
((NokiaUEFIModel)CurrentModel).DisableRebootTimeOut(); ((NokiaUEFIModel)CurrentModel).DisableRebootTimeOut();
CurrentInterface = PhoneInterfaces.Lumia_Flash; CurrentInterface = PhoneInterfaces.Lumia_Flash;
CurrentDevicePath = e.DevicePath;
LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly); LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly);
LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly); LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly);
LogFile.Log("Connected device: Lumia", LogType.FileAndConsole); LogFile.Log("Connected device: Lumia", LogType.FileAndConsole);
@@ -299,6 +352,7 @@ namespace WPinternals
((NokiaUEFIModel)CurrentModel).DisableRebootTimeOut(); ((NokiaUEFIModel)CurrentModel).DisableRebootTimeOut();
CurrentInterface = PhoneInterfaces.Lumia_PhoneInfo; CurrentInterface = PhoneInterfaces.Lumia_PhoneInfo;
CurrentDevicePath = e.DevicePath;
LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly); LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly);
LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly); LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly);
LogFile.Log("Connected device: Lumia", LogType.FileAndConsole); LogFile.Log("Connected device: Lumia", LogType.FileAndConsole);
@@ -339,6 +393,7 @@ namespace WPinternals
if (NewModel.Drive != null) // When logical drive is already known, we use this model. Or else we wait for the logical drive to arrive. if (NewModel.Drive != null) // When logical drive is already known, we use this model. Or else we wait for the logical drive to arrive.
{ {
CurrentInterface = PhoneInterfaces.Lumia_MassStorage; CurrentInterface = PhoneInterfaces.Lumia_MassStorage;
CurrentDevicePath = e.DevicePath;
CurrentModel = NewModel; CurrentModel = NewModel;
LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly); LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly);
LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly); LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly);
@@ -365,6 +420,7 @@ namespace WPinternals
if ((DeviceInfo.BusName == "QHSUSB_DLOAD") || (DeviceInfo.BusName == "QHSUSB__BULK") || ((DeviceInfo.BusName?.Length == 0) && (LastInterface != PhoneInterfaces.Qualcomm_Download))) // TODO: Separate for Sahara! if ((DeviceInfo.BusName == "QHSUSB_DLOAD") || (DeviceInfo.BusName == "QHSUSB__BULK") || ((DeviceInfo.BusName?.Length == 0) && (LastInterface != PhoneInterfaces.Qualcomm_Download))) // TODO: Separate for Sahara!
{ {
CurrentInterface = PhoneInterfaces.Qualcomm_Download; CurrentInterface = PhoneInterfaces.Qualcomm_Download;
CurrentDevicePath = e.DevicePath;
CurrentModel = new QualcommSerial(e.DevicePath); CurrentModel = new QualcommSerial(e.DevicePath);
NewDeviceArrived(new ArrivalEventArgs((PhoneInterfaces)CurrentInterface, CurrentModel)); NewDeviceArrived(new ArrivalEventArgs((PhoneInterfaces)CurrentInterface, CurrentModel));
LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly); LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly);
@@ -382,6 +438,7 @@ namespace WPinternals
else if ((DeviceInfo.BusName == "QHSUSB_ARMPRG") || ((DeviceInfo.BusName?.Length == 0) && (LastInterface == PhoneInterfaces.Qualcomm_Download))) else if ((DeviceInfo.BusName == "QHSUSB_ARMPRG") || ((DeviceInfo.BusName?.Length == 0) && (LastInterface == PhoneInterfaces.Qualcomm_Download)))
{ {
CurrentInterface = PhoneInterfaces.Qualcomm_Flash; CurrentInterface = PhoneInterfaces.Qualcomm_Flash;
CurrentDevicePath = e.DevicePath;
CurrentModel = new QualcommSerial(e.DevicePath); CurrentModel = new QualcommSerial(e.DevicePath);
NewDeviceArrived(new ArrivalEventArgs((PhoneInterfaces)CurrentInterface, CurrentModel)); NewDeviceArrived(new ArrivalEventArgs((PhoneInterfaces)CurrentInterface, CurrentModel));
LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly); LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly);
@@ -433,6 +490,7 @@ namespace WPinternals
LogFile.LogException(Ex); LogFile.LogException(Ex);
CurrentModel = null; CurrentModel = null;
CurrentInterface = null; CurrentInterface = null;
CurrentDevicePath = null;
} }
} }
@@ -441,6 +499,7 @@ namespace WPinternals
LastInterface = CurrentInterface; LastInterface = CurrentInterface;
CurrentInterface = null; CurrentInterface = null;
CurrentDevicePath = null;
if (CurrentModel != null) if (CurrentModel != null)
{ {
CurrentModel.Dispose(); CurrentModel.Dispose();
@@ -458,6 +517,7 @@ namespace WPinternals
((NokiaUEFIModel)CurrentModel).InterfaceChanged += InterfaceChanged; ((NokiaUEFIModel)CurrentModel).InterfaceChanged += InterfaceChanged;
CurrentInterface = PhoneInterfaces.Lumia_Bootloader; CurrentInterface = PhoneInterfaces.Lumia_Bootloader;
CurrentDevicePath = DevicePath;
LogFile.Log("Found device on interface: " + LumiaFlashInterfaceGuid.ToString(), LogType.FileOnly); LogFile.Log("Found device on interface: " + LumiaFlashInterfaceGuid.ToString(), LogType.FileOnly);
LogFile.Log("Device path: " + DevicePath, LogType.FileOnly); LogFile.Log("Device path: " + DevicePath, LogType.FileOnly);
LogFile.Log("Connected device: Lumia", LogType.FileAndConsole); LogFile.Log("Connected device: Lumia", LogType.FileAndConsole);
@@ -472,6 +532,7 @@ namespace WPinternals
((NokiaUEFIModel)CurrentModel).DisableRebootTimeOut(); ((NokiaUEFIModel)CurrentModel).DisableRebootTimeOut();
CurrentInterface = PhoneInterfaces.Lumia_Flash; CurrentInterface = PhoneInterfaces.Lumia_Flash;
CurrentDevicePath = DevicePath;
LogFile.Log("Found device on interface: " + LumiaFlashInterfaceGuid.ToString(), LogType.FileOnly); LogFile.Log("Found device on interface: " + LumiaFlashInterfaceGuid.ToString(), LogType.FileOnly);
LogFile.Log("Device path: " + DevicePath, LogType.FileOnly); LogFile.Log("Device path: " + DevicePath, LogType.FileOnly);
LogFile.Log("Connected device: Lumia", LogType.FileAndConsole); LogFile.Log("Connected device: Lumia", LogType.FileAndConsole);
@@ -486,6 +547,7 @@ namespace WPinternals
((NokiaUEFIModel)CurrentModel).DisableRebootTimeOut(); ((NokiaUEFIModel)CurrentModel).DisableRebootTimeOut();
CurrentInterface = PhoneInterfaces.Lumia_PhoneInfo; CurrentInterface = PhoneInterfaces.Lumia_PhoneInfo;
CurrentDevicePath = DevicePath;
LogFile.Log("Found device on interface: " + LumiaFlashInterfaceGuid.ToString(), LogType.FileOnly); LogFile.Log("Found device on interface: " + LumiaFlashInterfaceGuid.ToString(), LogType.FileOnly);
LogFile.Log("Device path: " + DevicePath, LogType.FileOnly); LogFile.Log("Device path: " + DevicePath, LogType.FileOnly);
LogFile.Log("Connected device: Lumia", LogType.FileAndConsole); LogFile.Log("Connected device: Lumia", LogType.FileAndConsole);
@@ -502,6 +564,7 @@ namespace WPinternals
((NokiaUEFIModel)CurrentModel).DisableRebootTimeOut(); ((NokiaUEFIModel)CurrentModel).DisableRebootTimeOut();
CurrentInterface = PhoneInterfaces.Lumia_Flash; CurrentInterface = PhoneInterfaces.Lumia_Flash;
CurrentDevicePath = DevicePath;
LogFile.Log("Found device on interface: " + LumiaFlashInterfaceGuid.ToString(), LogType.FileOnly); LogFile.Log("Found device on interface: " + LumiaFlashInterfaceGuid.ToString(), LogType.FileOnly);
LogFile.Log("Device path: " + DevicePath, LogType.FileOnly); LogFile.Log("Device path: " + DevicePath, LogType.FileOnly);
LogFile.Log("Connected device: Lumia", LogType.FileAndConsole); LogFile.Log("Connected device: Lumia", LogType.FileAndConsole);
@@ -530,6 +593,8 @@ namespace WPinternals
e.DevicePath.Contains("VID_0421&PID_05EE", StringComparison.OrdinalIgnoreCase) || e.DevicePath.Contains("VID_0421&PID_05EE", StringComparison.OrdinalIgnoreCase) ||
e.DevicePath.Contains("VID_045E&PID_0A00", StringComparison.OrdinalIgnoreCase) || e.DevicePath.Contains("VID_045E&PID_0A00", StringComparison.OrdinalIgnoreCase) ||
e.DevicePath.Contains("VID_045E&PID_0A02", StringComparison.OrdinalIgnoreCase) || e.DevicePath.Contains("VID_045E&PID_0A02", StringComparison.OrdinalIgnoreCase) ||
e.DevicePath.Contains("VID_045E&PID_062A", StringComparison.OrdinalIgnoreCase) ||
e.DevicePath.Contains("VID_045E&PID_0658", StringComparison.OrdinalIgnoreCase) ||
e.DevicePath.Contains("VID_05C6&PID_9008", StringComparison.OrdinalIgnoreCase) || e.DevicePath.Contains("VID_05C6&PID_9008", StringComparison.OrdinalIgnoreCase) ||
e.DevicePath.Contains("DISK&VEN_QUALCOMM&PROD_MMC_STORAGE", StringComparison.OrdinalIgnoreCase) || e.DevicePath.Contains("DISK&VEN_QUALCOMM&PROD_MMC_STORAGE", StringComparison.OrdinalIgnoreCase) ||
e.DevicePath.Contains("DISK&VEN_MSFT&PROD_PHONE_MMC_STOR", StringComparison.OrdinalIgnoreCase) e.DevicePath.Contains("DISK&VEN_MSFT&PROD_PHONE_MMC_STOR", StringComparison.OrdinalIgnoreCase)
@@ -541,6 +606,7 @@ namespace WPinternals
} }
CurrentInterface = null; CurrentInterface = null;
CurrentDevicePath = null;
if (CurrentModel != null) if (CurrentModel != null)
{ {
CurrentModel.Dispose(); CurrentModel.Dispose();
@@ -0,0 +1,54 @@
// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda
//
// 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 WPinternals.Models.Lumia;
using WPinternals.Models.SimpleIO;
namespace WPinternals
{
internal class SimpleIOModeViewModel : ContextViewModel
{
private readonly SimpleIOModel CurrentModel;
private readonly Action<PhoneInterfaces?> RequestModeSwitch;
internal SimpleIOModeViewModel(SimpleIOModel CurrentModel, Action<PhoneInterfaces?> RequestModeSwitch)
: base()
{
this.CurrentModel = CurrentModel;
this.RequestModeSwitch = RequestModeSwitch;
}
public void RebootTo(string Mode)
{
switch (Mode)
{
case "Normal":
RequestModeSwitch(PhoneInterfaces.Lumia_Normal);
break;
case "MassStorage":
RequestModeSwitch(PhoneInterfaces.Lumia_MassStorage);
break;
default:
return;
}
}
}
}
@@ -0,0 +1,88 @@
// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda
//
// 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.Threading;
using WPinternals.HelperClasses;
using WPinternals.Models.Lumia.NCSd;
using WPinternals.Models.SimpleIO;
using WPinternals.Terminal;
namespace WPinternals
{
// Create this class on the UI thread, after the main-window of the application is initialized.
// It is necessary to create the object on the UI thread, because notification events to the View need to be fired on that thread.
// The Model for this ViewModel communicates over USB and for that it uses the hWnd of the main window.
// Therefore the main window must be created before the ViewModel is created.
internal class SimpleIOViewModel : ContextViewModel
{
private readonly SimpleIOModel CurrentModel;
private readonly Action<PhoneInterfaces> RequestModeSwitch;
internal SimpleIOViewModel(SimpleIOModel CurrentModel, Action<PhoneInterfaces> RequestModeSwitch)
: base()
{
this.RequestModeSwitch = RequestModeSwitch;
this.CurrentModel = CurrentModel;
new Thread(() => StartLoadDeviceInfo()).Start();
}
private void StartLoadDeviceInfo()
{
(long curPosition, Guid guid, bool supportsFastFlash, bool supportsCompatFastFlash, int clientVersion, Guid DeviceUniqueID, string DeviceFriendlyName) ID = CurrentModel.GetIdV2();
PlatformName = ID.DeviceFriendlyName;
}
private string _PlatformName = null;
public string PlatformName
{
get
{
return _PlatformName;
}
set
{
_PlatformName = value;
OnPropertyChanged(nameof(PlatformName));
}
}
public void RebootTo(string Mode)
{
switch (Mode)
{
case "Normal":
RequestModeSwitch(PhoneInterfaces.Lumia_Normal);
break;
case "MassStorage":
RequestModeSwitch(PhoneInterfaces.Lumia_MassStorage);
break;
default:
return;
}
}
}
}
+56 -4
View File
@@ -23,10 +23,12 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using UnifiedFlashingPlatform;
using WPinternals.HelperClasses; using WPinternals.HelperClasses;
using WPinternals.Models.Lumia; using WPinternals.Models.Lumia;
using WPinternals.Models.Lumia.NCSd; using WPinternals.Models.Lumia.NCSd;
using WPinternals.Models.Lumia.UEFI; using WPinternals.Models.Lumia.UEFI;
using WPinternals.Models.SimpleIO;
using WPinternals.Models.UEFIApps.BootMgr; using WPinternals.Models.UEFIApps.BootMgr;
using WPinternals.Models.UEFIApps.Flash; using WPinternals.Models.UEFIApps.Flash;
using WPinternals.Models.UEFIApps.PhoneInfo; using WPinternals.Models.UEFIApps.PhoneInfo;
@@ -94,7 +96,7 @@ namespace WPinternals
else else
{ {
this.PhoneNotifier = PhoneNotifier; this.PhoneNotifier = PhoneNotifier;
this.CurrentModel = (NokiaPhoneModel)PhoneNotifier.CurrentModel; this.CurrentModel = PhoneNotifier.CurrentModel;
this.CurrentMode = PhoneNotifier.CurrentInterface; this.CurrentMode = PhoneNotifier.CurrentInterface;
this.TargetMode = TargetMode; this.TargetMode = TargetMode;
if (ModeSwitchProgress != null) if (ModeSwitchProgress != null)
@@ -217,6 +219,56 @@ namespace WPinternals
// Make switch and set message or navigate to error // Make switch and set message or navigate to error
switch (CurrentMode) switch (CurrentMode)
{ {
case PhoneInterfaces.UFP:
IsSwitchingInterface = true;
switch (TargetMode)
{
case null:
((UnifiedFlashingPlatformModel)PhoneNotifier.CurrentModel).Shutdown();
ModeSwitchProgressWrapper("Please disconnect your device. Waiting...", null);
LogFile.Log("Please disconnect your device. Waiting...", LogType.FileAndConsole);
new Thread(() =>
{
PhoneNotifier.WaitForRemoval().Wait();
ModeSwitchSuccessWrapper();
}).Start();
break;
case PhoneInterfaces.Lumia_Normal:
PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
((UnifiedFlashingPlatformModel)PhoneNotifier.CurrentModel).ResetPhone();
ModeSwitchProgressWrapper("Rebooting phone to Normal mode...", null);
LogFile.Log("Rebooting phone to Normal mode", LogType.FileAndConsole);
break;
case PhoneInterfaces.Lumia_Bootloader:
PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
((UnifiedFlashingPlatformModel)PhoneNotifier.CurrentModel).ResetPhone();
ModeSwitchProgressWrapper("Rebooting phone to Bootloader mode...", null);
LogFile.Log("Rebooting phone to Bootloader mode", LogType.FileAndConsole);
break;
default:
return;
}
break;
case PhoneInterfaces.SimpleIO:
IsSwitchingInterface = true;
switch (TargetMode)
{
case PhoneInterfaces.Lumia_Normal:
PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
((SimpleIOModel)CurrentModel).ContinueBoot();
ModeSwitchProgressWrapper("Rebooting phone to Normal mode...", null);
LogFile.Log("Rebooting phone to Normal mode", LogType.FileAndConsole);
break;
case PhoneInterfaces.Lumia_MassStorage:
PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
((SimpleIOModel)CurrentModel).EnterMassStorage();
ModeSwitchProgressWrapper("Rebooting phone to Mass Storage mode...", null);
LogFile.Log("Rebooting phone to Mass Storage mode", LogType.FileAndConsole);
break;
default:
return;
}
break;
case PhoneInterfaces.Lumia_Normal: case PhoneInterfaces.Lumia_Normal:
case PhoneInterfaces.Lumia_Label: case PhoneInterfaces.Lumia_Label:
string DeviceMode; string DeviceMode;
@@ -1013,7 +1065,7 @@ namespace WPinternals
// It overwrites the variable in a different NV-partition than where this variable is stored usually. // It overwrites the variable in a different NV-partition than where this variable is stored usually.
// This normally leads to endless-loops when the NV-variables are enumerated. // This normally leads to endless-loops when the NV-variables are enumerated.
// But the partition contains an extra hack to break out the endless loops. // But the partition contains an extra hack to break out the endless loops.
using (Stream stream = assembly.GetManifestResourceStream("WPinternals.SBMSM")) using (Stream stream = assembly.GetManifestResourceStream("WPinternals.Assets.SBMSM"))
{ {
using DecompressedStream dec = new(stream); using DecompressedStream dec = new(stream);
using MemoryStream SB = new(); // Must be a seekable stream! using MemoryStream SB = new(); // Must be a seekable stream!
@@ -1240,7 +1292,7 @@ namespace WPinternals
// It overwrites the variable in a different NV-partition than where this variable is stored usually. // It overwrites the variable in a different NV-partition than where this variable is stored usually.
// This normally leads to endless-loops when the NV-variables are enumerated. // This normally leads to endless-loops when the NV-variables are enumerated.
// But the partition contains an extra hack to break out the endless loops. // But the partition contains an extra hack to break out the endless loops.
using (Stream stream = assembly.GetManifestResourceStream("WPinternals.SBMSM")) using (Stream stream = assembly.GetManifestResourceStream("WPinternals.Assets.SBMSM"))
{ {
using DecompressedStream dec = new(stream); using DecompressedStream dec = new(stream);
using MemoryStream SB = new(); // Must be a seekable stream! using MemoryStream SB = new(); // Must be a seekable stream!
@@ -1441,7 +1493,7 @@ namespace WPinternals
// It overwrites the variable in a different NV-partition than where this variable is stored usually. // It overwrites the variable in a different NV-partition than where this variable is stored usually.
// This normally leads to endless-loops when the NV-variables are enumerated. // This normally leads to endless-loops when the NV-variables are enumerated.
// But the partition contains an extra hack to break out the endless loops. // But the partition contains an extra hack to break out the endless loops.
using (Stream stream = assembly.GetManifestResourceStream("WPinternals.SBMSM")) using (Stream stream = assembly.GetManifestResourceStream("WPinternals.Assets.SBMSM"))
{ {
using DecompressedStream dec = new(stream); using DecompressedStream dec = new(stream);
using MemoryStream SB = new(); // Must be a seekable stream! using MemoryStream SB = new(); // Must be a seekable stream!
@@ -0,0 +1,56 @@
// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda
//
// 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 UnifiedFlashingPlatform;
namespace WPinternals
{
internal class UFPModeViewModel : ContextViewModel
{
private readonly UnifiedFlashingPlatformModel CurrentModel;
private readonly Action<PhoneInterfaces?> RequestModeSwitch;
internal UFPModeViewModel(UnifiedFlashingPlatformModel CurrentModel, Action<PhoneInterfaces?> RequestModeSwitch)
: base()
{
this.CurrentModel = CurrentModel;
this.RequestModeSwitch = RequestModeSwitch;
}
public void RebootTo(string Mode)
{
switch (Mode)
{
case "Normal":
RequestModeSwitch(PhoneInterfaces.Lumia_Normal);
break;
case "MassStorage":
RequestModeSwitch(PhoneInterfaces.Lumia_MassStorage);
break;
case "Shutdown":
RequestModeSwitch(null);
break;
default:
return;
}
}
}
}
+258
View File
@@ -0,0 +1,258 @@
// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda
//
// 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.Threading;
using UnifiedFlashingPlatform;
using WPinternals.HelperClasses;
namespace WPinternals
{
// Create this class on the UI thread, after the main-window of the application is initialized.
// It is necessary to create the object on the UI thread, because notification events to the View need to be fired on that thread.
// The Model for this ViewModel communicates over USB and for that it uses the hWnd of the main window.
// Therefore the main window must be created before the ViewModel is created.
internal class UFPViewModel : ContextViewModel
{
private readonly UnifiedFlashingPlatformModel CurrentModel;
private readonly Action<PhoneInterfaces> RequestModeSwitch;
private readonly object LockDeviceInfo = new();
private bool DeviceInfoLoaded = false;
internal UFPViewModel(UnifiedFlashingPlatformModel CurrentModel, Action<PhoneInterfaces> RequestModeSwitch)
: base()
{
this.RequestModeSwitch = RequestModeSwitch;
this.CurrentModel = CurrentModel;
new Thread(() => StartLoadDeviceInfo()).Start();
}
private void StartLoadDeviceInfo()
{
lock (LockDeviceInfo)
{
if (!DeviceInfoLoaded)
{
try
{
PlatformName = CurrentModel.ReadStringParam("DPI");
LogFile.Log("Platform Name: " + PlatformName);
// Some phones do not support the Terminal interface! (928 verizon)
// Instead read param RRKH to get the RKH.
PublicID = null;
byte[] RawPublicID = CurrentModel.ReadParam("PID");
if (RawPublicID?.Length > 4)
{
PublicID = new byte[RawPublicID.Length - 4];
Array.Copy(RawPublicID, 4, PublicID, 0, RawPublicID.Length - 4);
LogFile.Log("Public ID: " + Converter.ConvertHexToString(PublicID, " "));
}
else
{
PublicID = new byte[20];
LogFile.Log("Public ID: " + Converter.ConvertHexToString(PublicID, " "));
}
RootKeyHash = CurrentModel.ReadParam("RRKH");
if (RootKeyHash != null)
{
LogFile.Log("Root Key Hash: " + Converter.ConvertHexToString(RootKeyHash, " "));
}
byte[] EMS = CurrentModel.ReadParam("EMS");
if (EMS != null)
{
UInt64 MemSize = (UInt64)(((UInt32)EMS[0] << 24) + ((UInt32)EMS[1] << 16) + ((UInt32)EMS[2] << 8) + EMS[3]) * 0x200;
double MemSizeDouble = (double)MemSize / 1024 / 1024 / 1024;
MemSizeDouble = (double)(int)(MemSizeDouble * 10) / 10;
string Manufacturer = null;
eMMC = Manufacturer == null ? MemSizeDouble.ToString() + " GB" : Manufacturer + " " + MemSizeDouble.ToString() + " GB";
}
else
{
eMMC = "Unknown";
SamsungWarningVisible = true;
}
UnifiedFlashingPlatformModel.PhoneInfo Info = CurrentModel.ReadPhoneInfo();
BootloaderDescription = Info.FlashAppProtocolVersionMajor < 2 ? "Lumia Bootloader Spec A" : "Lumia Bootloader Spec B";
LogFile.Log("Bootloader: " + BootloaderDescription);
ProductCode = "";//TODO: FIXME: Info.ProductCode;
LogFile.Log("ProductCode: " + ProductCode);
ProductType = "";//TODO: FIXME: Info.Type;
LogFile.Log("ProductType: " + ProductType);
if (PlatformName == null)
{
LogFile.Log("Platform Name was null. Gathering information from an alternative source.");
PlatformName = Info.PlatformID;
LogFile.Log("Platform Name: " + PlatformName);
}
}
catch (Exception ex)
{
LogFile.Log("An unexpected error happened", LogType.FileAndConsole);
LogFile.Log(ex.GetType().ToString(), LogType.FileAndConsole);
LogFile.Log(ex.Message, LogType.FileAndConsole);
LogFile.Log(ex.StackTrace, LogType.FileAndConsole);
LogFile.Log("Reading status from Flash interface was aborted.");
}
DeviceInfoLoaded = true;
}
}
}
private byte[] _PublicID = null;
public byte[] PublicID
{
get
{
return _PublicID;
}
set
{
_PublicID = value;
OnPropertyChanged(nameof(PublicID));
}
}
private byte[] _RootKeyHash = null;
public byte[] RootKeyHash
{
get
{
return _RootKeyHash;
}
set
{
_RootKeyHash = value;
OnPropertyChanged(nameof(RootKeyHash));
}
}
private string _PlatformName = null;
public string PlatformName
{
get
{
return _PlatformName;
}
set
{
_PlatformName = value;
OnPropertyChanged(nameof(PlatformName));
}
}
private string _ProductType = null;
public string ProductType
{
get
{
return _ProductType;
}
set
{
_ProductType = value;
OnPropertyChanged(nameof(ProductType));
}
}
private string _ProductCode = null;
public string ProductCode
{
get
{
return _ProductCode;
}
set
{
_ProductCode = value;
OnPropertyChanged(nameof(ProductCode));
}
}
private string _eMMC = null;
public string eMMC
{
get
{
return _eMMC;
}
set
{
_eMMC = value;
OnPropertyChanged(nameof(eMMC));
}
}
private string _BootloaderDescription = null;
public string BootloaderDescription
{
get
{
return _BootloaderDescription;
}
set
{
_BootloaderDescription = value;
OnPropertyChanged(nameof(BootloaderDescription));
}
}
private bool _SamsungWarningVisible = false;
public bool SamsungWarningVisible
{
get
{
return _SamsungWarningVisible;
}
set
{
_SamsungWarningVisible = value;
OnPropertyChanged(nameof(SamsungWarningVisible));
}
}
public void RebootTo(string Mode)
{
switch (Mode)
{
case "Normal":
RequestModeSwitch(PhoneInterfaces.Lumia_Normal);
break;
case "MassStorage":
RequestModeSwitch(PhoneInterfaces.Lumia_MassStorage);
break;
default:
return;
}
}
}
}
+1 -1
View File
@@ -30,7 +30,7 @@ DEALINGS IN THE SOFTWARE.
mc:Ignorable="d" mc:Ignorable="d"
d:DesignWidth="700"> d:DesignWidth="700">
<UserControl.Resources> <UserControl.Resources>
<BitmapImage x:Key="LogoImageSource" UriSource="..\Logo.png" /> <BitmapImage x:Key="LogoImageSource" UriSource="..\Assets\Logo.png" />
</UserControl.Resources> </UserControl.Resources>
<Border BorderThickness="1" BorderBrush="#FFD4D4D4" HorizontalAlignment="Stretch" VerticalAlignment="Center" Padding="25"> <Border BorderThickness="1" BorderBrush="#FFD4D4D4" HorizontalAlignment="Stretch" VerticalAlignment="Center" Padding="25">
<Grid> <Grid>
+1 -1
View File
@@ -30,7 +30,7 @@ DEALINGS IN THE SOFTWARE.
mc:Ignorable="d" mc:Ignorable="d"
d:DesignWidth="700"> d:DesignWidth="700">
<UserControl.Resources> <UserControl.Resources>
<BitmapImage x:Key="Busy" UriSource="..\aerobusy.gif" /> <BitmapImage x:Key="Busy" UriSource="..\Assets\aerobusy.gif" />
<helpers:ObjectToVisibilityConverter x:Key="ObjectToVisibilityConverter" /> <helpers:ObjectToVisibilityConverter x:Key="ObjectToVisibilityConverter" />
<helpers:InverseObjectToVisibilityConverter x:Key="InverseObjectToVisibilityConverter" /> <helpers:InverseObjectToVisibilityConverter x:Key="InverseObjectToVisibilityConverter" />
<helpers:BooleanConverter x:Key="VisibilityConverter" OnTrue="Visible" OnFalse="Collapsed" OnNull="Collapsed"/> <helpers:BooleanConverter x:Key="VisibilityConverter" OnTrue="Visible" OnFalse="Collapsed" OnNull="Collapsed"/>
+1 -1
View File
@@ -32,7 +32,7 @@ namespace WPinternals
InitializeComponent(); InitializeComponent();
// Setting these properties in XAML results in an error. Why? // Setting these properties in XAML results in an error. Why?
GifImage.GifSource = "/aerobusy.gif"; GifImage.GifSource = "/Assets/aerobusy.gif";
GifImage.AutoStart = true; GifImage.AutoStart = true;
} }
} }
+1 -1
View File
@@ -31,7 +31,7 @@ DEALINGS IN THE SOFTWARE.
d:DesignWidth="700" d:DesignWidth="700"
> >
<UserControl.Resources> <UserControl.Resources>
<BitmapImage x:Key="Busy" UriSource="..\aerobusy.gif" /> <BitmapImage x:Key="Busy" UriSource="..\Assets\aerobusy.gif" />
<helpers:BooleanConverter x:Key="VisibilityConverter" OnTrue="Visible" OnFalse="Collapsed" OnNull="Collapsed"/> <helpers:BooleanConverter x:Key="VisibilityConverter" OnTrue="Visible" OnFalse="Collapsed" OnNull="Collapsed"/>
<helpers:BooleanConverter x:Key="InverseVisibilityConverter" OnTrue="Collapsed" OnFalse="Visible" OnNull="Collapsed" /> <helpers:BooleanConverter x:Key="InverseVisibilityConverter" OnTrue="Collapsed" OnFalse="Visible" OnNull="Collapsed" />
</UserControl.Resources> </UserControl.Resources>
+1 -1
View File
@@ -46,7 +46,7 @@ namespace WPinternals
UIContext = SynchronizationContext.Current; UIContext = SynchronizationContext.Current;
// Setting these properties in XAML results in an error. Why? // Setting these properties in XAML results in an error. Why?
GifImage.GifSource = "/aerobusy.gif"; GifImage.GifSource = "/Assets/aerobusy.gif";
GifImage.AutoStart = true; GifImage.AutoStart = true;
Loaded += Empty_Loaded; Loaded += Empty_Loaded;
+10 -158
View File
@@ -31,10 +31,6 @@ DEALINGS IN THE SOFTWARE.
d:DesignWidth="700"> d:DesignWidth="700">
<UserControl.Resources> <UserControl.Resources>
<helpers:ObjectToVisibilityConverter x:Key="ObjectToVisibilityConverter" /> <helpers:ObjectToVisibilityConverter x:Key="ObjectToVisibilityConverter" />
<local:DownloaderNameConvertor x:Key="DownloaderNameConvertor" />
<local:DownloaderSizeConvertor x:Key="DownloaderSizeConvertor" />
<local:DownloaderSpeedConvertor x:Key="DownloaderSpeedConvertor" />
<local:DownloaderTimeRemainingConvertor x:Key="DownloaderTimeRemainingConvertor" />
<Style x:Key="HeaderLeftAligned" TargetType="{x:Type GridViewColumnHeader}"> <Style x:Key="HeaderLeftAligned" TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Left"></Setter> <Setter Property="HorizontalContentAlignment" Value="Left"></Setter>
<Setter Property="Padding" Value="6,0,0,0"></Setter> <Setter Property="Padding" Value="6,0,0,0"></Setter>
@@ -49,160 +45,6 @@ DEALINGS IN THE SOFTWARE.
</Style> </Style>
</UserControl.Resources> </UserControl.Resources>
<StackPanel VerticalAlignment="Center"> <StackPanel VerticalAlignment="Center">
<Border BorderThickness="1" BorderBrush="#FFD4D4D4" HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="0,0,0,20">
<StackPanel>
<helpers:FlowDocumentScrollViewerNoMouseWheel Grid.Column="1" Margin="20,0,20,0" VerticalScrollBarVisibility="Auto" >
<FlowDocument FontFamily="Segoe UI" FontSize="12" Loaded="Document_Loaded" TextAlignment="Left">
<FlowDocument.Resources>
<!-- This style is used to set the margins for all paragraphs in the FlowDocument to 0. -->
<Style TargetType="{x:Type Paragraph}">
<Setter Property="Margin" Value="0"/>
</Style>
<Style TargetType="{x:Type Section}">
<Setter Property="Margin" Value="0"/>
</Style>
</FlowDocument.Resources>
<helpers:Paragraph>
<Run Text="Download" FontSize="18" FontWeight="Bold" Foreground="#FF3753A6" />
<LineBreak />
<LineBreak />
<local:FolderPicker Caption="Download folder: " SelectionText="Select the destination location for the downloaded files..." Path="{Binding DownloadFolder, Mode=TwoWay}" AllowNull="False" HorizontalAlignment="Stretch" />
</helpers:Paragraph>
</FlowDocument>
</helpers:FlowDocumentScrollViewerNoMouseWheel>
<TextBlock Margin="36,0,36,6">Currently downloading:</TextBlock>
<ListView Grid.Row="1" ItemsSource="{Binding DownloadList}" Margin="36,0,36,24" MinHeight="66" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Visible" helpers:GridViewColumnResize.Enabled="True" >
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn helpers:GridViewColumnResize.Width="*" Header="Name" DisplayMemberBinding="{Binding Name}" HeaderContainerStyle="{StaticResource HeaderLeftAligned}" />
<GridViewColumn Width="80" Header="Size" HeaderContainerStyle="{StaticResource HeaderRightAligned}" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Style="{x:Null}" Text="{Binding Size, Converter={StaticResource DownloaderSizeConvertor}}" TextAlignment="Right" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="75" Header="Time Left" HeaderContainerStyle="{StaticResource HeaderRightAligned}" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Style="{x:Null}" Text="{Binding TimeLeft,Mode=OneWay,Converter={StaticResource DownloaderTimeRemainingConvertor}}" TextAlignment="Right" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="75" Header="Speed" HeaderContainerStyle="{StaticResource HeaderRightAligned}" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Style="{x:Null}" Text="{Binding Speed, Mode=OneWay,Converter={StaticResource DownloaderSpeedConvertor}}" TextAlignment="Right" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="123" Header="Progress">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ProgressBar Value="{Binding Progress, Mode=OneWay}" Width="120" Height="16" Maximum="100"></ProgressBar>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</Border>
<Border BorderThickness="1" BorderBrush="#FFD4D4D4" HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="0,0,0,20">
<StackPanel>
<helpers:FlowDocumentScrollViewerNoMouseWheel Grid.Column="1" Margin="20,0,20,0" VerticalScrollBarVisibility="Auto" >
<FlowDocument FontFamily="Segoe UI" FontSize="12" Loaded="Document_Loaded" TextAlignment="Left">
<FlowDocument.Resources>
<!-- This style is used to set the margins for all paragraphs in the FlowDocument to 0. -->
<Style TargetType="{x:Type Paragraph}">
<Setter Property="Margin" Value="0"/>
</Style>
<Style TargetType="{x:Type Section}">
<Setter Property="Margin" Value="0"/>
</Style>
</FlowDocument.Resources>
<helpers:Paragraph>
<Run Text="Model" FontSize="18" FontWeight="Bold" Foreground="#FF3753A6" />
<LineBreak/>
<LineBreak/>
<Run Text="When you choose &quot;Download all&quot;, Windows Phone Internals will download an FFU-file and emergency-files for your phone. When the FFU-file is downloaded, it will be analyzed. And if the OS-version is not a supported version, then Windows Phone Internals will start to download another FFU-file, which should have a supported OS-version. It will be for a different model, but Windows Phone Internals needs it extract some files from it." />
<LineBreak/>
<LineBreak/>
<Run Text="When you connect your phone, the search criteria will be detected automatically. For some older Lumia models this may not work when the phone is in Flash mode. To get the exact search criteria, you need to switch the phone to Normal mode first." />
<LineBreak/>
<LineBreak/>
<Run Text="In some cases the emergency files cannot be found and you will need to download the emergency files yourself. This " />
<Hyperlink NavigateUri="https://www.google.com/search?q=%22Lumia 650 emergency files%22">google search</Hyperlink>
<Run Text=" may yield some relevant results." />
<LineBreak/>
<LineBreak/>
<Run Text="For some older Lumia models the operatorcode search criterion doesn't work. Your search may not yield any results when use the Operatorcode search criterion. You should use the Productcode to find the files for your exact model." />
<LineBreak/>
</helpers:Paragraph>
</FlowDocument>
</helpers:FlowDocumentScrollViewerNoMouseWheel>
<Grid Margin="36,-6,36,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="120" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0">Producttype</TextBlock>
<TextBlock Grid.Column="0" Grid.Row="1">Productcode</TextBlock>
<TextBlock Grid.Column="0" Grid.Row="2">Operatorcode</TextBlock>
<TextBlock Grid.Column="0" Grid.Row="3">Firmwareversion</TextBlock>
<TextBox Grid.Column="1" Grid.Row="0" Width="Auto" Margin="0,0,0,8" Text="{Binding ProductType, Mode=TwoWay}"/>
<TextBox Grid.Column="1" Grid.Row="1" Width="Auto" Margin="0,0,0,8" Text="{Binding ProductCode, Mode=TwoWay}"/>
<TextBox Grid.Column="1" Grid.Row="2" Width="Auto" Margin="0,0,0,8" Text="{Binding OperatorCode, Mode=TwoWay}"/>
<TextBox Grid.Column="1" Grid.Row="3" Width="Auto" Text="{Binding FirmwareVersion, Mode=TwoWay}"/>
</Grid>
<StackPanel Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Bottom" Orientation="Horizontal">
<Button Content="Search" Padding="0,5" Width="120" Margin="0,0,20,0" Command="{Binding Path=SearchCommand, Mode=OneWay}" IsDefault="True"/>
<Button Content="Download all" Padding="0,5" Width="120" Command="{Binding Path=DownloadAllCommand, Mode=OneWay}" />
</StackPanel>
</Grid>
<TextBlock Margin="36,30,36,6">Search results:</TextBlock>
<ListView Grid.Row="1" Name="SearchResultsListView" ItemsSource="{Binding SearchResultList}" Margin="36,0,36,0" MinHeight="66" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Visible" helpers:GridViewColumnResize.Enabled="True">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn helpers:GridViewColumnResize.Width="*" Header="Name" DisplayMemberBinding="{Binding Name}" HeaderContainerStyle="{StaticResource HeaderLeftAligned}" />
<GridViewColumn Width="80" Header="Size" HeaderContainerStyle="{StaticResource HeaderRightAligned}" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Style="{x:Null}" Text="{Binding Size, Converter={StaticResource DownloaderSizeConvertor}}" TextAlignment="Right" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
<StackPanel Orientation="Horizontal" Width="Auto" Height="Auto" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,16,36,25">
<Button Command="{Binding Path=DownloadSelectedCommand, Mode=OneWay}" Content="Download selected" Width="Auto" Height="Auto" Padding="20,5" />
</StackPanel>
</StackPanel>
</Border>
<Border BorderThickness="1" BorderBrush="#FFD4D4D4" HorizontalAlignment="Stretch" VerticalAlignment="Center"> <Border BorderThickness="1" BorderBrush="#FFD4D4D4" HorizontalAlignment="Stretch" VerticalAlignment="Center">
<StackPanel> <StackPanel>
<helpers:FlowDocumentScrollViewerNoMouseWheel Grid.Column="1" Margin="20,0,20,0" VerticalScrollBarVisibility="Auto"> <helpers:FlowDocumentScrollViewerNoMouseWheel Grid.Column="1" Margin="20,0,20,0" VerticalScrollBarVisibility="Auto">
@@ -238,6 +80,16 @@ DEALINGS IN THE SOFTWARE.
</FlowDocument> </FlowDocument>
</helpers:FlowDocumentScrollViewerNoMouseWheel> </helpers:FlowDocumentScrollViewerNoMouseWheel>
<Button Height="30" Width="Auto" Content="Add existing FFU-file..." Padding="20,5,20,5" HorizontalAlignment="Right" Margin="0,0,36,20" Command="{Binding Path=AddFFUCommand}"/> <Button Height="30" Width="Auto" Content="Add existing FFU-file..." Padding="20,5,20,5" HorizontalAlignment="Right" Margin="0,0,36,20" Command="{Binding Path=AddFFUCommand}"/>
<StackPanel>
<Grid Margin="36,-6,36,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="120" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0">Firmwareversion</TextBlock>
<TextBox Grid.Column="1" Width="Auto" Text="{Binding FirmwareVersion, Mode=TwoWay}"/>
</Grid>
</StackPanel>
<helpers:FlowDocumentScrollViewerNoMouseWheel Grid.Column="1" Margin="20,-15,20,0" VerticalScrollBarVisibility="Auto" Visibility="{Binding Path=LastSecWIMStatusText, Converter={StaticResource ObjectToVisibilityConverter}}"> <helpers:FlowDocumentScrollViewerNoMouseWheel Grid.Column="1" Margin="20,-15,20,0" VerticalScrollBarVisibility="Auto" Visibility="{Binding Path=LastSecWIMStatusText, Converter={StaticResource ObjectToVisibilityConverter}}">
<FlowDocument FontFamily="Segoe UI" FontSize="12" Loaded="Document_Loaded" TextAlignment="Left"> <FlowDocument FontFamily="Segoe UI" FontSize="12" Loaded="Document_Loaded" TextAlignment="Left">
<FlowDocument.Resources> <FlowDocument.Resources>
+15 -3
View File
@@ -23,7 +23,7 @@ DEALINGS IN THE SOFTWARE.
<Window x:Class="WPinternals.MainWindow" <Window x:Class="WPinternals.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPinternals" Height="760" Width="1150" Icon="..\WPinternals.ico" Closed="Window_Closed"> xmlns:local="clr-namespace:WPinternals" Height="760" Width="1150" Icon="..\Assets\WPinternals.ico" Closed="Window_Closed">
<Window.Resources> <Window.Resources>
<DataTemplate DataType="{x:Type local:BootUnlockResourcesViewModel}"> <DataTemplate DataType="{x:Type local:BootUnlockResourcesViewModel}">
<local:FlashResourcesView /> <local:FlashResourcesView />
@@ -115,8 +115,20 @@ DEALINGS IN THE SOFTWARE.
<DataTemplate DataType="{x:Type local:ContextViewModel}"> <DataTemplate DataType="{x:Type local:ContextViewModel}">
<local:ContextView /> <local:ContextView />
</DataTemplate> </DataTemplate>
<BitmapImage x:Key="LogoImageSource" UriSource="..\Logo.png" /> <DataTemplate DataType="{x:Type local:SimpleIOViewModel}">
<BitmapImage x:Key="LogoSmallImageSource" UriSource="..\Logo-Small.png" /> <local:SimpleIOView />
</DataTemplate>
<DataTemplate DataType="{x:Type local:SimpleIOModeViewModel}">
<local:SimpleIOModeView />
</DataTemplate>
<DataTemplate DataType="{x:Type local:UFPViewModel}">
<local:UFPView />
</DataTemplate>
<DataTemplate DataType="{x:Type local:UFPModeViewModel}">
<local:UFPModeView />
</DataTemplate>
<BitmapImage x:Key="LogoImageSource" UriSource="..\Assets\Logo.png" />
<BitmapImage x:Key="LogoSmallImageSource" UriSource="..\Assets\Logo-Small.png" />
<Style x:Key="MenuButtonStyle" BasedOn="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" TargetType="{x:Type Button}"> <Style x:Key="MenuButtonStyle" BasedOn="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" TargetType="{x:Type Button}">
<Setter Property="Template"> <Setter Property="Template">
<Setter.Value> <Setter.Value>
+1 -1
View File
@@ -30,7 +30,7 @@ DEALINGS IN THE SOFTWARE.
mc:Ignorable="d" mc:Ignorable="d"
d:DesignWidth="700"> d:DesignWidth="700">
<UserControl.Resources> <UserControl.Resources>
<BitmapImage x:Key="Busy" UriSource="..\aerobusy.gif" /> <BitmapImage x:Key="Busy" UriSource="..\Assets\aerobusy.gif" />
<helpers:HexConverter x:Key="HexConverter" /> <helpers:HexConverter x:Key="HexConverter" />
<helpers:BooleanConverter x:Key="VisibilityConverter" OnTrue="Visible" OnFalse="Collapsed" OnNull="Collapsed"/> <helpers:BooleanConverter x:Key="VisibilityConverter" OnTrue="Visible" OnFalse="Collapsed" OnNull="Collapsed"/>
<helpers:BooleanConverter x:Key="InverseVisibilityConverter" OnTrue="Collapsed" OnFalse="Visible" OnNull="Collapsed" /> <helpers:BooleanConverter x:Key="InverseVisibilityConverter" OnTrue="Collapsed" OnFalse="Visible" OnNull="Collapsed" />
@@ -45,7 +45,7 @@ namespace WPinternals
UIContext = SynchronizationContext.Current; UIContext = SynchronizationContext.Current;
// Setting these properties in XAML results in an error. Why? // Setting these properties in XAML results in an error. Why?
GifImage.GifSource = "/aerobusy.gif"; GifImage.GifSource = "/Assets/aerobusy.gif";
GifImage.AutoStart = true; GifImage.AutoStart = true;
Loaded += NokiaBootloaderView_Loaded; Loaded += NokiaBootloaderView_Loaded;
+5 -5
View File
@@ -74,6 +74,11 @@ DEALINGS IN THE SOFTWARE.
<Run Text="This interface is meant for querying and provisioning the phone. This is normally used for configuring the phone during manufacturing." /> <Run Text="This interface is meant for querying and provisioning the phone. This is normally used for configuring the phone during manufacturing." />
<LineBreak /> <LineBreak />
<LineBreak /> <LineBreak />
<Hyperlink NavigateUri="Shutdown">Shutdown the phone</Hyperlink>
<LineBreak />
<Run Text="This will shutdown your phone. After selecting this option, you'll need to unplug your phone from your computer." />
<LineBreak />
<LineBreak />
<Hyperlink NavigateUri="MassStorage">Switch to Mass-Storage-mode</Hyperlink> <Hyperlink NavigateUri="MassStorage">Switch to Mass-Storage-mode</Hyperlink>
<LineBreak /> <LineBreak />
<Run Text="This mode allows you to access the complete file-system of the phone. " /> <Run Text="This mode allows you to access the complete file-system of the phone. " />
@@ -81,11 +86,6 @@ DEALINGS IN THE SOFTWARE.
<helpers:CollapsibleRun IsVisible="{Binding EffectiveBootloaderSecurityStatus, Converter={StaticResource InverseConverter}, Mode=OneWay}" Text="Your security flags indicate the this mode can be accessed. But this switch will only succeed if you took all measures to unlock Mass Storage mode." /> <helpers:CollapsibleRun IsVisible="{Binding EffectiveBootloaderSecurityStatus, Converter={StaticResource InverseConverter}, Mode=OneWay}" Text="Your security flags indicate the this mode can be accessed. But this switch will only succeed if you took all measures to unlock Mass Storage mode." />
<LineBreak /> <LineBreak />
<LineBreak /> <LineBreak />
<Hyperlink NavigateUri="Shutdown">Shutdown the phone</Hyperlink>
<LineBreak />
<Run Text="This will shutdown your phone. After selecting this option, you'll need to unplug your phone from your computer." />
<LineBreak />
<LineBreak />
<Run Text="Warning 1: " Foreground="Red" FontWeight="Bold"/> <Run Text="Warning 1: " Foreground="Red" FontWeight="Bold"/>
<Run Text="Once you've entered Mass Storage mode, be very careful with altering files. You can easily brick your phone, when you make invalid changes to the file-system of the phone." /> <Run Text="Once you've entered Mass Storage mode, be very careful with altering files. You can easily brick your phone, when you make invalid changes to the file-system of the phone." />
<LineBreak /> <LineBreak />
+1 -1
View File
@@ -30,7 +30,7 @@ DEALINGS IN THE SOFTWARE.
mc:Ignorable="d" mc:Ignorable="d"
d:DesignWidth="700"> d:DesignWidth="700">
<UserControl.Resources> <UserControl.Resources>
<BitmapImage x:Key="Busy" UriSource="..\aerobusy.gif" /> <BitmapImage x:Key="Busy" UriSource="..\Assets\aerobusy.gif" />
<helpers:HexConverter x:Key="HexConverter" /> <helpers:HexConverter x:Key="HexConverter" />
<helpers:BooleanConverter x:Key="VisibilityConverter" OnTrue="Visible" OnFalse="Collapsed" OnNull="Collapsed"/> <helpers:BooleanConverter x:Key="VisibilityConverter" OnTrue="Visible" OnFalse="Collapsed" OnNull="Collapsed"/>
<helpers:BooleanConverter x:Key="InverseVisibilityConverter" OnTrue="Collapsed" OnFalse="Visible" OnNull="Collapsed" /> <helpers:BooleanConverter x:Key="InverseVisibilityConverter" OnTrue="Collapsed" OnFalse="Visible" OnNull="Collapsed" />
+82
View File
@@ -0,0 +1,82 @@
<!--
Copyright (c) 2018, Rene Lergner - @Heathcliff74xda
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.
-->
<UserControl x:Class="WPinternals.SimpleIOModeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WPinternals"
xmlns:helpers="clr-namespace:WPinternals.HelperClasses"
mc:Ignorable="d"
d:DesignWidth="700">
<UserControl.Resources>
<helpers:BooleanConverter x:Key="VisibilityConverter" OnTrue="Visible" OnFalse="Collapsed" />
<helpers:BooleanConverter x:Key="InvisibilityConverter" OnTrue="Collapsed" OnFalse="Visible" />
<helpers:BooleanConverter x:Key="InverseConverter" OnTrue="False" OnFalse="True" />
</UserControl.Resources>
<Border BorderThickness="1" BorderBrush="#FFD4D4D4" HorizontalAlignment="Stretch" VerticalAlignment="Center" Padding="25">
<helpers:FlowDocumentScrollViewerNoMouseWheel Grid.Column="1" Margin="20,0,0,0" VerticalScrollBarVisibility="Auto" >
<FlowDocument x:Name="Document" FontFamily="Segoe UI" FontSize="12" Loaded="Document_Loaded" TextAlignment="Left">
<FlowDocument.Resources>
<!-- This style is used to set the margins for all paragraphs in the FlowDocument to 0. -->
<Style TargetType="{x:Type Paragraph}">
<Setter Property="Margin" Value="0"/>
</Style>
<Style TargetType="{x:Type Section}">
<Setter Property="Margin" Value="0"/>
</Style>
</FlowDocument.Resources>
<helpers:Paragraph>
<Run Text="Nokia Lumia - Switch mode" FontSize="18" FontWeight="Bold" Foreground="#FF3753A6" />
<LineBreak />
<LineBreak />
<Run Text="Current mode: " />
<Run Text="SimpleIO" Foreground="#FF3753A6" FontWeight="Bold" />
<LineBreak />
<LineBreak />
<Hyperlink NavigateUri="Normal">Switch to Normal-mode</Hyperlink>
<LineBreak />
<Run Text="This will switch back to Windows Phone OS." />
<LineBreak />
<LineBreak />
<Hyperlink NavigateUri="MassStorage">Switch to Mass-Storage-mode</Hyperlink>
<LineBreak />
<Run Text="This mode allows you to access the complete file-system of the phone. " />
<Run Text="To enter this mode the phone will first be booted to Flash mode. After that this tool will immediately attempt to boot the phone to Mass Storage mode. So you may see your phone reboot multiple times." />
<LineBreak />
<LineBreak />
<Run Text="Warning 1: " Foreground="Red" FontWeight="Bold"/>
<Run Text="Once you've entered Mass Storage mode, be very careful with altering files. You can easily brick your phone, when you make invalid changes to the file-system of the phone." />
<LineBreak />
<LineBreak />
<Run Text="Warning 2: " Foreground="Red" FontWeight="Bold"/>
<Run Text="Before switching to Mass Storage Mode, verify that you do not have any other Windows Phone disks or partitions mounted. The partitions may have equal identifiers, which will result in a conflict. The phone partitions will be mounted &quot;offline&quot; and if you try to switch them &quot;online&quot; in the Disk Manager, it will corrupt the partitions on the phone. Unmount any Windows Phone partitions before you continue." />
<LineBreak />
<LineBreak />
<Run Text="Warning 3: " Foreground="Red" FontWeight="Bold"/>
<Run Text="Switching to Mass Storage mode should take about 10 seconds. Phones with Bootloader Spec A should be unlocked using an Engineering SBL3 to enable Mass Storage mode. When you unlocked the bootloader, but you did not use an Engineering SBL3, an attempt to boot to Mass Storage mode may result in an unresponsive state. Installing drivers for this interface may also cause to hang the PC. So when this switch is taking too long, you should reboot both the PC and the phone." />
</helpers:Paragraph>
</FlowDocument>
</helpers:FlowDocumentScrollViewerNoMouseWheel>
</Border>
</UserControl>
@@ -0,0 +1,50 @@
// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda
//
// 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.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
namespace WPinternals
{
/// <summary>
/// Interaction logic for SimpleIOModeView.xaml
/// </summary>
public partial class SimpleIOModeView : UserControl
{
public SimpleIOModeView()
{
InitializeComponent();
}
private void HandleHyperlinkClick(object sender, RoutedEventArgs args)
{
if (args.Source is Hyperlink link)
{
(this.DataContext as SimpleIOModeViewModel)?.RebootTo(link.NavigateUri.ToString());
}
}
private void Document_Loaded(object sender, RoutedEventArgs e)
{
(sender as FlowDocument)?.AddHandler(Hyperlink.ClickEvent, new RoutedEventHandler(HandleHyperlinkClick));
}
}
}
+82
View File
@@ -0,0 +1,82 @@
<!--
Copyright (c) 2018, Rene Lergner - @Heathcliff74xda
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.
-->
<UserControl x:Class="WPinternals.SimpleIOView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WPinternals"
xmlns:helpers="clr-namespace:WPinternals.HelperClasses"
mc:Ignorable="d"
d:DesignWidth="700">
<UserControl.Resources>
<helpers:HexConverter x:Key="HexConverter" />
<helpers:BooleanConverter x:Key="VisibilityConverter" OnTrue="Visible" OnFalse="Collapsed" OnNull="Collapsed"/>
<helpers:BooleanConverter x:Key="InverseVisibilityConverter" OnTrue="Collapsed" OnFalse="Visible" OnNull="Collapsed" />
<helpers:ObjectToVisibilityConverter x:Key="ObjectToVisibilityConverter" />
</UserControl.Resources>
<StackPanel VerticalAlignment="Center">
<Border BorderThickness="1" BorderBrush="#FFD4D4D4" HorizontalAlignment="Stretch" VerticalAlignment="Center" Padding="25" Margin="0,0,0,20">
<StackPanel Orientation="Vertical">
<helpers:FlowDocumentScrollViewerNoMouseWheel Grid.Column="1" Margin="20,0" VerticalScrollBarVisibility="Auto">
<FlowDocument FontFamily="Segoe UI" FontSize="12" Loaded="Document_Loaded" PagePadding="1">
<helpers:Paragraph>
<Run Text="General info" FontSize="18" FontWeight="Bold" Foreground="#FF3753A6" />
<LineBreak />
<LineBreak />
<Grid IsHitTestVisible="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Platform name" />
<TextBlock Grid.Row="0" Grid.Column="1" FontWeight="Bold" Foreground="#FF3753A6" TextWrapping="Wrap" Text="{Binding PlatformName, Mode=OneWay}" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="Operating mode" />
<TextBlock Grid.Row="1" Grid.Column="1" Text="SimpleIO" FontWeight="Bold" Foreground="#FF3753A6" TextWrapping="Wrap" />
</Grid>
</helpers:Paragraph>
</FlowDocument>
</helpers:FlowDocumentScrollViewerNoMouseWheel>
<helpers:FlowDocumentScrollViewerNoMouseWheel Grid.Column="1" Margin="20,0" VerticalScrollBarVisibility="Auto">
<FlowDocument FontFamily="Segoe UI" FontSize="12" Loaded="Document_Loaded" PagePadding="1">
<helpers:Paragraph>
<LineBreak />
<Run Text="To let the phone go back to Windows, boot to " />
<Hyperlink NavigateUri="Normal">Normal</Hyperlink>
<Run Text=" mode." />
</helpers:Paragraph>
</FlowDocument>
</helpers:FlowDocumentScrollViewerNoMouseWheel>
</StackPanel>
</Border>
</StackPanel>
</UserControl>
+51
View File
@@ -0,0 +1,51 @@
// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda
//
// 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.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
namespace WPinternals
{
/// <summary>
/// Interaction logic for SimpleIOView.xaml
/// </summary>
public partial class SimpleIOView : UserControl
{
public SimpleIOView()
{
InitializeComponent();
}
private void HandleHyperlinkClick(object sender, RoutedEventArgs args)
{
Hyperlink link = args.Source as Hyperlink;
if (link?.NavigateUri != null)
{
(this.DataContext as SimpleIOViewModel)?.RebootTo(link.NavigateUri.ToString());
}
}
private void Document_Loaded(object sender, RoutedEventArgs e)
{
(sender as FlowDocument)?.AddHandler(Hyperlink.ClickEvent, new RoutedEventHandler(HandleHyperlinkClick));
}
}
}

Some files were not shown because too many files have changed in this diff Show More