// Copyright (c) 2018, Rene Lergner - wpinternals.net - @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.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace WPinternals
{
    internal static class CommandLine
    {
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool AllocConsole();

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool AttachConsole(int dwProcessId);

        [DllImport("kernel32.dll")]
        static extern IntPtr GetConsoleWindow();

        [DllImport("user32.dll")]
        static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

        private const UInt32 StdOutputHandle = 0xFFFFFFF5;

        [DllImport("kernel32.dll")]
        private static extern IntPtr GetStdHandle(UInt32 nStdHandle);

        [DllImport("kernel32.dll")]
        private static extern void SetStdHandle(UInt32 nStdHandle, IntPtr handle);

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, uint lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, uint hTemplateFile);

        [DllImport("user32.dll")]
        private static extern IntPtr GetForegroundWindow();

        const int SW_HIDE = 0;
        const int SW_SHOW = 5;

        private const int MY_CODE_PAGE = 437;
        private const uint GENERIC_WRITE = 0x40000000;
        private const uint FILE_SHARE_WRITE = 0x2;
        private const uint OPEN_EXISTING = 0x3;

        internal static bool IsConsoleVisible = false;
        internal static bool IsNewConsoleCreated = false;

        private static IntPtr hConsoleWnd;

        [DllImport("User32.Dll", EntryPoint = "PostMessageA")]
        private static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam);

        private const int VK_RETURN = 0x0D;
        private const int WM_KEYDOWN = 0x100;

        /// <summary>
        /// When the main window should not be shown, this function should call ExitProcess and it should not return.
        /// </summary>
        internal static async Task ParseCommandLine(System.Threading.SynchronizationContext UIContext)
        {
            FFU FFU = null;
            PhoneNotifierViewModel Notifier;
            NokiaFlashModel FlashModel;
            NokiaPhoneModel NormalModel;
            PhoneInfo Info;
            string ProductType;
            string ProductCode;
            string OperatorCode;
            string DownloadFolder;
            string FFUFilePath;
            string URL;
            string[] URLs;
            Uri URI;
            string FFUFileName;
            string EmergencyFileName;
            string EmergencyFilePath;
            string ProgrammerPath = "";
            string PayloadPath = "";
            string EfiEspImagePath = null;
            string MainOsImagePath = null;
            DiscUtils.Fat.FatFileSystem UnlockedEFIESPFileSystem;
            DiscUtils.Ntfs.NtfsFileSystem UnlockedMainOsFileSystem;
            bool PatchResult;

            try
            {
                string[] args = Environment.GetCommandLineArgs();

                if (args.Length == 1)
                    return;

                switch (args[1].ToLower().TrimStart(new char[] { '-', '/' }))
                {
#if DEBUG
                    case "test":
                        LogFile.BeginAction("Test");
                        await TestCode.Test(UIContext);
                        LogFile.EndAction("Test");
                        break;
#endif
                    case "flashpartition":
                        if (args.Length < 4)
                            throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -FlashPartition <Partition name> <Partition file> <Optional: FFU file>");
                        if (args.Length >= 5)
                            await LumiaV2UnlockBootViewModel.LumiaV2FlashPartition(UIContext, args[4], args[2], args[3]);
                        else
                            await LumiaV2UnlockBootViewModel.LumiaV2FlashPartition(UIContext, null, args[2], args[3]);
                        break;
                    case "flashraw":
                        if (args.Length < 4)
                            throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -FlashRaw <Start sector> <Raw file> <Optional: FFU file>");
                        UInt64 StartSector = 0;
                        try
                        {
                            if (args[2].StartsWith("0x", StringComparison.OrdinalIgnoreCase))
                                StartSector = Convert.ToUInt64(args[2].Substring(2), 16);
                            else
                                StartSector = Convert.ToUInt64(args[2], 10);
                        }
                        catch
                        {
                            LogFile.Log("Bad start sector", LogType.ConsoleOnly);
                            break;
                        }
                        if (args.Length >= 5)
                            await LumiaV2UnlockBootViewModel.LumiaV2FlashRaw(UIContext, StartSector, args[3], args[4]);
                        else
                            await LumiaV2UnlockBootViewModel.LumiaV2FlashRaw(UIContext, StartSector, args[3], null);
                        break;
                    case "flashpartitionimmediately":
                        if (args.Length < 5)
                            throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -FlashPartition <Partition name> <Partition file> <FFU Path>");
                        await LumiaV2UnlockBootViewModel.LumiaV2FlashPartition(UIContext, args[4], args[2], args[3], false);
                        break;
                    case "readgpt":
                        LogFile.BeginAction("ReadGPT");
                        try
                        {
                            Notifier = new PhoneNotifierViewModel();
                            UIContext.Send(s => Notifier.Start(), null);

                            FlashModel = (NokiaFlashModel)(await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Bootloader)); // This also works for Bootloader Spec A

                            GPT GPT = FlashModel.ReadGPT(); // May throw NotSupportedException
                            foreach (Partition Partition in GPT.Partitions)
                                LogFile.Log(Partition.Name.PadRight(20) + "0x" + Partition.FirstSector.ToString("X8") + " - 0x" + Partition.LastSector.ToString("X8") + "    " + Partition.Volume, LogType.ConsoleOnly);

                            if (FlashModel.ReadPhoneInfo(false).FlashAppProtocolVersionMajor >= 2)
                                FlashModel.SwitchToFlashAppContext();
                            else
                                await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash);

                            Notifier.Stop();
                        }
                        catch (Exception Ex)
                        {
                            LogFile.LogException(Ex);
                        }
                        finally
                        {
                            LogFile.EndAction("ReadGPT");
                        }
                        break;
                    case "backupgpt":
                        if (args.Length < 3)
                            throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -BackupGPT <Path to xml-file>");
                        LogFile.BeginAction("BackupGPT");
                        try
                        {
                            Notifier = new PhoneNotifierViewModel();
                            UIContext.Send(s => Notifier.Start(), null);
                            FlashModel = (NokiaFlashModel)(await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash));
                            GPT GPT = FlashModel.ReadGPT(); // May throw NotSupportedException
                            System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(args[2]));
                            GPT.WritePartitions(args[2]);
                            FlashModel.SwitchToFlashAppContext();
                            Notifier.Stop();
                        }
                        catch (Exception Ex)
                        {
                            LogFile.LogException(Ex);
                        }
                        finally
                        {
                            LogFile.EndAction("BackupGPT");
                        }
                        break;
                    case "restoregpt":
                        if (args.Length < 3)
                            throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -RestoreGPT <Path to xml-file>");
                        LogFile.BeginAction("RestoreGPT");
                        try
                        {
                            Notifier = new PhoneNotifierViewModel();
                            UIContext.Send(s => Notifier.Start(), null);
                            FlashModel = (NokiaFlashModel)(await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash));
                            byte[] GptChunk = LumiaV2UnlockBootViewModel.GetGptChunk(FlashModel, 0x20000);
                            GPT GPT = new GPT(GptChunk);
                            string Xml = File.ReadAllText(args[2]);
                            GPT.MergePartitions(Xml, false);
                            GPT.Rebuild();
                            await LumiaV2UnlockBootViewModel.LumiaV2CustomFlash(Notifier, null, false, false, 0, GptChunk, true, true);
                            FlashModel.SwitchToFlashAppContext();
                            Notifier.Stop();
                        }
                        catch (Exception Ex)
                        {
                            LogFile.LogException(Ex);
                        }
                        finally
                        {
                            LogFile.EndAction("RestoreGPT");
                        }
                        break;
                    case "mergegpt":
                        if (args.Length < 4)
                            throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -MergeGPT <Path to input-xml-file> <Path to input-xml-file> <Optional: Path to output-xml-file> Or use: WPinternals.exe -MergeGPT <Path to input-xml-file> <Path to ZIP-file including Partitions.xml> <Optional: Path to output-xml-file>");
                        LogFile.BeginAction("MergeGPT");
                        try
                        {
                            GPT GPT = GPT.ReadPartitions(args[2]);

                            ZipArchive Archive = null;
                            FileStream s = null;
                            try
                            {
                                s = new FileStream(args[3], FileMode.Open, FileAccess.Read);
                                Archive = new ZipArchive(s);
                            }
                            catch { }

                            if (Archive == null)
                            {
                                if (s != null)
                                    s.Close();

                                // Assume Xml-file
                                GPT.MergePartitionsFromFile(args[3], true);
                            }
                            else
                            {
                                ZipArchiveEntry PartitionEntry = Archive.GetEntry("Partitions.xml");
                                if (PartitionEntry == null)
                                    GPT.MergePartitions(null, true, Archive);
                                else
                                {
                                    using (Stream ZipStream = PartitionEntry.Open())
                                    {
                                        using (StreamReader ZipReader = new StreamReader(ZipStream))
                                        {
                                            string PartitionXml = ZipReader.ReadToEnd();
                                            GPT.MergePartitions(PartitionXml, true, Archive);
                                        }
                                    }
                                }
                            }

                            if (Archive != null)
                                Archive.Dispose();

                            if (args.Count() >= 5)
                                GPT.WritePartitions(args[4]);

                            foreach (Partition Partition in GPT.Partitions)
                                LogFile.Log(Partition.Name.PadRight(20) + "0x" + Partition.FirstSector.ToString("X8") + " - 0x" + Partition.LastSector.ToString("X8") + "    " + Partition.Volume, LogType.ConsoleOnly);
                        }
                        catch (Exception Ex)
                        {
                            LogFile.LogException(Ex);
                        }
                        finally
                        {
                            LogFile.EndAction("MergeGPT");
                        }
                        break;
                    case "dumpffu":
                        if (args.Length < 4)
                            throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -DumpFFU <FFU Path> <Destination Path> <Optional: Partition Name>");
                        FFU = new FFU(args[2]);
                        if (args.Length < 5)
                        {
                            foreach (Partition Partition in FFU.GPT.Partitions)
                            {
                                if (FFU.IsPartitionPresentInFFU(Partition.Name))
                                {
                                    FFU.WritePartition(Partition.Name, System.IO.Path.Combine(args[3], Partition.Name + ".bin"));
                                }
                            }
                        }
                        else
                        {
                            Partition Target = FFU.GPT.GetPartition(args[4]);
                            if ((Target == null) || (!FFU.IsPartitionPresentInFFU(Target.Name)))
                                throw new InvalidOperationException("Partition not found in FFU!");
                            FFU.WritePartition(Target.Name, System.IO.Path.Combine(args[3], Target.Name + ".bin"));
                        }
                        break;
                    case "dumpuefi":
                        if (args.Length < 4)
                            throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -DumpUEFI <UEFI binary or FFU file> <Destination Path>");

                        byte[] UefiBinary;
                        if (FFU.IsFFU(args[2]))
                        {
                            FFU = new FFU(args[2]);
                            UefiBinary = FFU.GetPartition("UEFI");
                        }
                        else
                        {
                            UefiBinary = File.ReadAllBytes(args[2]);
                        }

                        UEFI UEFI = new UEFI(UefiBinary);

                        foreach (EFI Efi in UEFI.EFIs)
                        {
                            byte[] EfiBinary = UEFI.GetFile(Efi.Guid);
                            string Name = Efi.Name;
                            if (Name == null)
                                Name = Efi.Guid.ToString();
                            if (!Name.Contains('.'))
                            {
                                switch (Efi.Type)
                                {
                                    case 5:
                                    case 7:
                                        Name += ".dll";
                                        break;
                                    case 9:
                                        Name += ".exe";
                                        break;
                                    default:
                                        Name += ".bin";
                                        break;
                                }
                            }
                            string EfiPath = Path.Combine(args[3], Name);
                            Directory.CreateDirectory(Path.GetDirectoryName(EfiPath));
                            File.WriteAllBytes(EfiPath, EfiBinary);
                        }
                        break;
                    case "testprogrammer":
                        if (args.Length < 3)
                            throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -TestProgrammer <Path to ede-file>");
                        await TestCode.TestProgrammer(UIContext, args[2]);
                        break;
                    case "findflashingprofile":
                        Notifier = new PhoneNotifierViewModel();
                        UIContext.Send(s => Notifier.Start(), null);
                        if (args.Length > 2)
                            await LumiaV2UnlockBootViewModel.LumiaV2FindFlashingProfile(Notifier, args[2]);
                        else
                            await LumiaV2UnlockBootViewModel.LumiaV2FindFlashingProfile(Notifier, null);
                        Notifier.Stop();
                        break;
                    case "findflashingprofileexperimental":
                        Notifier = new PhoneNotifierViewModel();
                        UIContext.Send(s => Notifier.Start(), null);
                        if (args.Length > 2)
                            await LumiaV2UnlockBootViewModel.LumiaV2FindFlashingProfile(Notifier, args[2], Experimental: true);
                        else
                            await LumiaV2UnlockBootViewModel.LumiaV2FindFlashingProfile(Notifier, null, Experimental: true);
                        Notifier.Stop();
                        break;
                    case "findflashingprofilenorestart":
                        Notifier = new PhoneNotifierViewModel();
                        UIContext.Send(s => Notifier.Start(), null);
                        if (args.Length > 2)
                            await LumiaV2UnlockBootViewModel.LumiaV2FindFlashingProfile(Notifier, args[2], false);
                        else
                            await LumiaV2UnlockBootViewModel.LumiaV2FindFlashingProfile(Notifier, null, false);
                        Notifier.Stop();
                        break;
                    case "enabletestsigning":
                        if (args.Length > 2)
                            await LumiaV2UnlockBootViewModel.LumiaV2EnableTestSigning(UIContext, args[2]);
                        else
                            await LumiaV2UnlockBootViewModel.LumiaV2EnableTestSigning(UIContext, null);
                        break;
                    case "enabletestsigningnorestart":
                        if (args.Length > 2)
                            await LumiaV2UnlockBootViewModel.LumiaV2EnableTestSigning(UIContext, args[2], false);
                        else
                            await LumiaV2UnlockBootViewModel.LumiaV2EnableTestSigning(UIContext, null, false);
                        break;
                    case "clearnv":
                        if (args.Length > 2)
                            await LumiaV2UnlockBootViewModel.LumiaV2ClearNV(UIContext, args[2]);
                        else
                            await LumiaV2UnlockBootViewModel.LumiaV2ClearNV(UIContext, null);
                        break;
                    case "switchtomassstoragemode":
                        LogFile.BeginAction("SwitchToMassStorageMode");
                        try
                        {
                            Notifier = new PhoneNotifierViewModel();
                            UIContext.Send(s => Notifier.Start(), null);
                            LogFile.Log("Command: Switch to Mass Storage Mode", LogType.FileAndConsole);
                            if (args.Length > 2)
                                await LumiaV2UnlockBootViewModel.LumiaV2SwitchToMassStorageMode(Notifier, args[2]);
                            else
                                await LumiaV2UnlockBootViewModel.LumiaV2SwitchToMassStorageMode(Notifier, null);
                            Notifier.Stop();
                        }
                        catch (Exception Ex)
                        {
                            LogFile.LogException(Ex);
                        }
                        finally
                        {
                            LogFile.EndAction("SwitchToMassStorageMode");
                        }
                        break;
                    case "relockphone":
                        Notifier = new PhoneNotifierViewModel();
                        UIContext.Send(s => Notifier.Start(), null);

                        if (args.Length > 2)
                            await LumiaV2UnlockBootViewModel.LumiaV2RelockPhone(Notifier, args[2]);
                        else
                            await LumiaV2UnlockBootViewModel.LumiaV2RelockPhone(Notifier, null);

                        Notifier.Stop();
                        break;
                    case "addffu":
                        if (args.Length < 3)
                            throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -AddFFU <Path to FFU-file>");
                        App.Config.AddFfuToRepository(args[2]);
                        break;
                    case "removeffu":
                        if (args.Length < 3)
                            throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -RemoveFFU <Path to FFU-file>");
                        App.Config.RemoveFfuFromRepository(args[2]);
                        break;
                    case "addemergency":
                        if (args.Length < 5)
                            throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -AddEmergnency <Type> <Path to Programmer-file> <Path to Payload-file>");
                        App.Config.AddEmergencyToRepository(args[2], args[3], args[4]);
                        break;
                    case "removeemergency":
                        if (args.Length < 3)
                            throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -RemoveEmergency <Path to Programmer-file>");
                        App.Config.RemoveEmergencyFromRepository(args[2]);
                        break;
                    case "listrepository":
                        int Count = 0;
                        LogFile.Log("", LogType.ConsoleOnly);
                        LogFile.Log("FFU Repository:", LogType.ConsoleOnly);
                        foreach (FFUEntry Entry in App.Config.FFURepository)
                        {
                            LogFile.Log("", LogType.ConsoleOnly);
                            LogFile.Log("FFU " + Count.ToString() + ":", LogType.ConsoleOnly);
                            LogFile.Log("File: " + Entry.Path + (Entry.Exists() ? "" : " (file is missing)"), LogType.ConsoleOnly);
                            LogFile.Log("Platform ID: " + Entry.PlatformID, LogType.ConsoleOnly);
                            if (Entry.FirmwareVersion != null)
                                LogFile.Log("Firmware version: " + Entry.FirmwareVersion, LogType.ConsoleOnly);
                            if (Entry.OSVersion != null)
                                LogFile.Log("OS version: " + Entry.OSVersion, LogType.ConsoleOnly);
                            Count++;
                        }
                        LogFile.Log("", LogType.ConsoleOnly);
                        LogFile.Log("Emergency Repository:", LogType.ConsoleOnly);
                        Count = 0;
                        foreach (EmergencyFileEntry Entry in App.Config.EmergencyRepository)
                        {
                            LogFile.Log("", LogType.ConsoleOnly);
                            LogFile.Log("Emergency " + Count.ToString() + ":", LogType.ConsoleOnly);
                            LogFile.Log("Type: " + Entry.Type, LogType.ConsoleOnly);
                            LogFile.Log("Programmer file: " + Entry.ProgrammerPath + (Entry.ProgrammerExists() ? "" : " (file is missing)"), LogType.ConsoleOnly);
                            if (Entry.PayloadPath != null)
                                LogFile.Log("Payload file: " + Entry.PayloadPath + (Entry.PayloadExists() ? "" : " (file is missing)"), LogType.ConsoleOnly);
                            Count++;
                        }
                        break;
                    case "showffu":
                        if (args.Length < 3)
                            throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -ShowFFU <Path to FFU-file>");
                        string FFUPath = args[2];
                        LogFile.Log("FFU: " + FFUPath, LogType.ConsoleOnly);
                        FFU = new FFU(FFUPath);

                        // Show basics
                        LogFile.Log("Platform ID: " + FFU.PlatformID, LogType.ConsoleOnly);
                        string Firmware = FFU.GetFirmwareVersion();
                        if (Firmware != null)
                            LogFile.Log("Firmware version: " + Firmware, LogType.ConsoleOnly);
                        string OSVersion = FFU.GetOSVersion();
                        if (OSVersion != null)
                            LogFile.Log("OS version: " + OSVersion, LogType.ConsoleOnly);

                        // Show partitions from GPT (also show which partitions are in the FFU payload)
                        LogFile.Log("", LogType.ConsoleOnly);
                        LogFile.Log("Partition table:", LogType.ConsoleOnly);
                        LogFile.Log("Name".PadRight(20) + "Start-sector".PadRight(20) + "End-sector".PadRight(20) + "Present in FFU", LogType.ConsoleOnly);
                        foreach (Partition p in FFU.GPT.Partitions)
                        {
                            LogFile.Log(p.Name.PadRight(20) + ("0x" + p.FirstSector.ToString("X16")).PadRight(20) + ("0x" + p.LastSector.ToString("X16")).PadRight(20) + (FFU.IsPartitionPresentInFFU(p.Name) ? "Yes" : "No"), LogType.ConsoleOnly);
                        }
                        break;
                    case "showphoneinfo":
                        LogFile.Log("Command: Show phone info", LogType.FileAndConsole);
                        Notifier = new PhoneNotifierViewModel();
                        UIContext.Send(s => Notifier.Start(), null);
                        FlashModel = (NokiaFlashModel)(await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash));
                        Info = FlashModel.ReadPhoneInfo();
                        Info.Log(LogType.ConsoleOnly);
                        Notifier.Stop();
                        break;
                    case "unlockbootloader":
                        LogFile.BeginAction("UnlockBootloader");
                        try
                        {
                            LogFile.Log("Command: Unlock Bootloader", LogType.FileAndConsole);
                            Notifier = new PhoneNotifierViewModel();
                            UIContext.Send(s => Notifier.Start(), null);
                            FlashModel = (NokiaFlashModel)(await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash));
                            Info = FlashModel.ReadPhoneInfo();
                            Info.Log(LogType.ConsoleOnly);

                            FFU ProfileFFU = null;
                            FFU SupportedFFU = null;
                            FFU CurrentFFU;
                            for (int i = 2; i <= 3; i++)
                            {
                                if (args.Length > i)
                                {
                                    CurrentFFU = new FFU(args[i]);
                                    string CurrentVersion = CurrentFFU.GetOSVersion();
                                    string PlatformID = CurrentFFU.PlatformID;

                                    // Check if the current FFU matches the connected phone, so that the FFU can be used for profiling.
                                    if (Info.PlatformID.StartsWith(PlatformID, StringComparison.OrdinalIgnoreCase))
                                        ProfileFFU = CurrentFFU;

                                    // Check if the current FFU is supported for unlocking.
                                    if (App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V2-EFIESP").First().TargetVersions.Any(v => v.Description == CurrentVersion))
                                        SupportedFFU = CurrentFFU;
                                }
                            }

                            if (ProfileFFU == null)
                            {
                                List<FFUEntry> FFUs = App.Config.FFURepository.Where(e => (Info.PlatformID.StartsWith(e.PlatformID, StringComparison.OrdinalIgnoreCase) && e.Exists())).ToList();
                                if (FFUs.Count() > 0)
                                    ProfileFFU = new FFU(FFUs[0].Path);
                                else
                                    throw new WPinternalsException("Profile FFU missing");
                            }
                            LogFile.Log("Profile FFU: " + ProfileFFU.Path);

                            if (SupportedFFU == null)
                            {
                                List<FFUEntry> FFUs = App.Config.FFURepository.Where(e => App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V2-EFIESP").First().TargetVersions.Any(v => v.Description == e.OSVersion)).ToList();
                                if (FFUs.Count() > 0)
                                    SupportedFFU = new FFU(FFUs[0].Path);
                                else
                                    throw new WPinternalsException("No donor-FFU found with supported OS version");
                            }

                            await LumiaV2UnlockBootViewModel.LumiaV2UnlockBootloader(Notifier, ProfileFFU.Path, null, SupportedFFU.Path);

                            Notifier.Stop();
                        }
                        catch (Exception Ex)
                        {
                            LogFile.LogException(Ex);
                        }
                        finally
                        {
                            LogFile.EndAction("UnlockBootloader");
                        }
                        break;
                    case "flashcustomrom":
                        LogFile.BeginAction("FlashCustomROM");
                        try
                        {
                            LogFile.Log("Command: Flash Custom ROM", LogType.FileAndConsole);
                            if (args.Length < 3)
                                throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -FlashCustomROM <Path to Custom ROM ZIP-file>");
                            string CustomRomPath = args[2];
                            LogFile.Log("Custom ROM: " + CustomRomPath, LogType.FileAndConsole);
                            Notifier = new PhoneNotifierViewModel();
                            UIContext.Send(s => Notifier.Start(), null);
                            FlashModel = (NokiaFlashModel)(await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash));
                            Info = FlashModel.ReadPhoneInfo();
                            Info.Log(LogType.ConsoleOnly);
                            LogFile.Log("Preparing to flash Custom ROM", LogType.FileAndConsole);
                            await LumiaV2UnlockBootViewModel.LumiaV2FlashArchive(Notifier, CustomRomPath);
                            LogFile.Log("Custom ROM flashed successfully", LogType.FileAndConsole);
                            Notifier.Stop();
                        }
                        catch (Exception Ex)
                        {
                            LogFile.LogException(Ex);
                        }
                        finally
                        {
                            LogFile.EndAction("FlashCustomROM");
                        }
                        break;
                    case "flashffu":
                        LogFile.BeginAction("FlashFFU");
                        try
                        {
                            LogFile.Log("Command: Flash FFU", LogType.FileAndConsole);
                            if (args.Length < 3)
                                throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -FlashFFU <Path to FFU-file>");
                            FFUPath = args[2];
                            LogFile.Log("FFU file: " + FFUPath, LogType.FileAndConsole);
                            Notifier = new PhoneNotifierViewModel();
                            UIContext.Send(s => Notifier.Start(), null);
                            FlashModel = (NokiaFlashModel)(await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash));
                            Info = FlashModel.ReadPhoneInfo();
                            Info.Log(LogType.ConsoleOnly);
                            LogFile.Log("Flashing FFU...", LogType.FileAndConsole);
                            await Task.Run(() => FlashModel.FlashFFU(new FFU(FFUPath), true, (byte)((Info.RdcPresent || !Info.SecureFfuEnabled || Info.Authenticated) ? FlashOptions.SkipSignatureCheck : 0)));
                            LogFile.Log("FFU flashed successfully", LogType.FileAndConsole);
                            Notifier.Stop();
                        }
                        catch (Exception Ex)
                        {
                            LogFile.LogException(Ex);
                        }
                        finally
                        {
                            LogFile.EndAction("FlashFFU");
                        }
                        break;
                    case "fixbootafterunlockingbootloader":
                        LogFile.BeginAction("FixBoot");
                        try
                        {
                            LogFile.Log("Command: Fix boot after unlocking bootloader", LogType.FileAndConsole);
                            Notifier = new PhoneNotifierViewModel();
                            UIContext.Send(s => Notifier.Start(), null);
                            string Drive = await LumiaV2UnlockBootViewModel.LumiaV2SwitchToMassStorageMode(Notifier, null);
                            Notifier.Stop();
                            App.PatchEngine.TargetPath = Drive + "\\";
                            PatchResult = App.PatchEngine.Patch("SecureBootHack-MainOS");
                            if (!PatchResult)
                                throw new WPinternalsException("Patch failed");
                            LogFile.Log("Fixed bootloader", LogType.FileAndConsole);
                            LogFile.Log("The phone is left in Mass Storage mode", LogType.FileAndConsole);
                            LogFile.Log("Press and hold the power-button of the phone for at least 10 seconds to reset the phone", LogType.FileAndConsole);
                        }
                        catch (Exception Ex)
                        {
                            LogFile.LogException(Ex);
                        }
                        finally
                        {
                            LogFile.EndAction("FixBoot");
                        }
                        break;
                    case "enablerootaccess":
                        LogFile.BeginAction("EnableRootAccess");
                        try
                        {
                            LogFile.Log("Command: Enable root access on the phone", LogType.FileAndConsole);
                            Notifier = new PhoneNotifierViewModel();
                            UIContext.Send(s => Notifier.Start(), null);
                            string Drive = await LumiaV2UnlockBootViewModel.LumiaV2SwitchToMassStorageMode(Notifier, null);
                            Notifier.Stop();
                            App.PatchEngine.TargetPath = Drive + "\\EFIESP\\";
                            PatchResult = App.PatchEngine.Patch("SecureBootHack-V2-EFIESP");
                            if (!PatchResult)
                                throw new WPinternalsException("Patch failed");
                            App.PatchEngine.TargetPath = Drive + "\\";
                            PatchResult = App.PatchEngine.Patch("SecureBootHack-MainOS");
                            if (!PatchResult)
                                throw new WPinternalsException("Patch failed");
                            PatchResult = App.PatchEngine.Patch("RootAccess-MainOS");
                            if (!PatchResult)
                                throw new WPinternalsException("Patch failed");
                            LogFile.Log("Root Access enabled!", LogType.FileAndConsole);
                            LogFile.Log("The phone is left in Mass Storage mode", LogType.FileAndConsole);
                            LogFile.Log("Press and hold the power-button of the phone for at least 10 seconds to reset the phone", LogType.FileAndConsole);
                        }
                        catch (Exception Ex)
                        {
                            LogFile.LogException(Ex);
                        }
                        finally
                        {
                            LogFile.EndAction("EnableRootAccess");
                        }
                        break;
                    case "unlockbootloaderonimage":
                        LogFile.Log("Command: Unlock bootloader on image", LogType.FileAndConsole);
                        if (args.Length < 3)
                            throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -UnlockBootLoaderOnImage <EFIESP image file> <Optional: MainOS image file> <Optional: donor-FFU file with supported version of bootfiles>");
                        FFUFilePath = null;
                        EfiEspImagePath = args[2];
                        if (args.Length > 3)
                        {
                            if (FFU.IsFFU(args[3]))
                                FFUFilePath = args[3];
                            else
                                MainOsImagePath = args[3];
                        }
                        if (args.Length > 4)
                            FFUFilePath = args[4];

                        using (FileStream FileSystemStream = new FileStream(EfiEspImagePath, FileMode.Open, FileAccess.ReadWrite))
                        {
                            UnlockedEFIESPFileSystem = new DiscUtils.Fat.FatFileSystem(FileSystemStream);

                            if (FFUFilePath != null)
                            {
                                FFU SupportedFFU = new FFU(FFUFilePath);
                                LogFile.Log("Donor FFU: " + SupportedFFU.Path);
                                byte[] SupportedEFIESP = SupportedFFU.GetPartition("EFIESP");
                                DiscUtils.Fat.FatFileSystem SupportedEFIESPFileSystem = new DiscUtils.Fat.FatFileSystem(new MemoryStream(SupportedEFIESP));
                                DiscUtils.SparseStream SupportedMobileStartupStream = SupportedEFIESPFileSystem.OpenFile(@"\Windows\System32\Boot\mobilestartup.efi", FileMode.Open);
                                MemoryStream SupportedMobileStartupMemStream = new MemoryStream();
                                SupportedMobileStartupStream.CopyTo(SupportedMobileStartupMemStream);
                                byte[] SupportedMobileStartup = SupportedMobileStartupMemStream.ToArray();
                                SupportedMobileStartupMemStream.Close();
                                SupportedMobileStartupStream.Close();

                                // Save supported mobilestartup.efi
                                LogFile.Log("Taking mobilestartup.efi from donor-FFU");
                                Stream MobileStartupStream = UnlockedEFIESPFileSystem.OpenFile(@"Windows\System32\Boot\mobilestartup.efi", FileMode.Create, FileAccess.Write);
                                MobileStartupStream.Write(SupportedMobileStartup, 0, SupportedMobileStartup.Length);
                                MobileStartupStream.Close();
                            }

                            App.PatchEngine.TargetImage = UnlockedEFIESPFileSystem;
                            PatchResult = App.PatchEngine.Patch("SecureBootHack-V2-EFIESP");
                            if (!PatchResult)
                                throw new WPinternalsException("Failed to patch bootloader");

                            // Edit BCD
                            LogFile.Log("Edit BCD");
                            using (Stream BCDFileStream = UnlockedEFIESPFileSystem.OpenFile(@"efi\Microsoft\Boot\BCD", FileMode.Open, FileAccess.ReadWrite))
                            {
                                using (DiscUtils.Registry.RegistryHive BCDHive = new DiscUtils.Registry.RegistryHive(BCDFileStream))
                                {
                                    DiscUtils.BootConfig.Store BCDStore = new DiscUtils.BootConfig.Store(BCDHive.Root);
                                    DiscUtils.BootConfig.BcdObject MobileStartupObject = BCDStore.GetObject(new Guid("{01de5a27-8705-40db-bad6-96fa5187d4a6}"));
                                    DiscUtils.BootConfig.Element NoCodeIntegrityElement = MobileStartupObject.GetElement(0x16000048);
                                    if (NoCodeIntegrityElement != null)
                                        NoCodeIntegrityElement.Value = DiscUtils.BootConfig.ElementValue.ForBoolean(true);
                                    else
                                        MobileStartupObject.AddElement(0x16000048, DiscUtils.BootConfig.ElementValue.ForBoolean(true));

                                    DiscUtils.BootConfig.BcdObject WinLoadObject = BCDStore.GetObject(new Guid("{7619dcc9-fafe-11d9-b411-000476eba25f}"));
                                    NoCodeIntegrityElement = WinLoadObject.GetElement(0x16000048);
                                    if (NoCodeIntegrityElement != null)
                                        NoCodeIntegrityElement.Value = DiscUtils.BootConfig.ElementValue.ForBoolean(true);
                                    else
                                        WinLoadObject.AddElement(0x16000048, DiscUtils.BootConfig.ElementValue.ForBoolean(true));
                                }
                            }
                        }

                        if (MainOsImagePath != null)
                        {
                            using (FileStream FileSystemStream = new FileStream(MainOsImagePath, FileMode.Open, FileAccess.ReadWrite))
                            {
                                UnlockedMainOsFileSystem = new DiscUtils.Ntfs.NtfsFileSystem(FileSystemStream);

                                App.PatchEngine.TargetImage = UnlockedMainOsFileSystem;
                                PatchResult = App.PatchEngine.Patch("SecureBootHack-MainOS");
                                if (!PatchResult)
                                    throw new WPinternalsException("Failed to patch MainOS");
                            }
                        }
                        LogFile.Log("Bootloader unlocked on image", LogType.FileAndConsole);
                        break;
                    case "enablerootaccessonimage":
                        LogFile.Log("Command: Enable root access on image", LogType.FileAndConsole);
                        if (args.Length < 4)
                            throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -EnableRootAccessOnImage <EFIESP image file> <MainOS image file> <Optional: donor-FFU file with supported version of bootfiles>");

                        FFUFilePath = null;
                        EfiEspImagePath = args[2];
                        MainOsImagePath = args[3];
                        if (args.Length > 4)
                            FFUFilePath = args[4];

                        using (FileStream FileSystemStream = new FileStream(EfiEspImagePath, FileMode.Open, FileAccess.ReadWrite))
                        {
                            UnlockedEFIESPFileSystem = new DiscUtils.Fat.FatFileSystem(FileSystemStream);

                            if (FFUFilePath != null)
                            {
                                FFU SupportedFFU = new FFU(FFUFilePath);
                                LogFile.Log("Supported FFU: " + SupportedFFU.Path);
                                byte[] SupportedEFIESP = SupportedFFU.GetPartition("EFIESP");
                                DiscUtils.Fat.FatFileSystem SupportedEFIESPFileSystem = new DiscUtils.Fat.FatFileSystem(new MemoryStream(SupportedEFIESP));
                                DiscUtils.SparseStream SupportedMobileStartupStream = SupportedEFIESPFileSystem.OpenFile(@"\Windows\System32\Boot\mobilestartup.efi", FileMode.Open);
                                MemoryStream SupportedMobileStartupMemStream = new MemoryStream();
                                SupportedMobileStartupStream.CopyTo(SupportedMobileStartupMemStream);
                                byte[] SupportedMobileStartup = SupportedMobileStartupMemStream.ToArray();
                                SupportedMobileStartupMemStream.Close();
                                SupportedMobileStartupStream.Close();

                                // Save supported mobilestartup.efi
                                LogFile.Log("Taking mobilestartup.efi from donor-FFU");
                                Stream MobileStartupStream = UnlockedEFIESPFileSystem.OpenFile(@"Windows\System32\Boot\mobilestartup.efi", FileMode.Create, FileAccess.Write);
                                MobileStartupStream.Write(SupportedMobileStartup, 0, SupportedMobileStartup.Length);
                                MobileStartupStream.Close();
                            }

                            App.PatchEngine.TargetImage = UnlockedEFIESPFileSystem;
                            PatchResult = App.PatchEngine.Patch("SecureBootHack-V2-EFIESP");
                            if (!PatchResult)
                                throw new WPinternalsException("Failed to patch bootloader");

                            // Edit BCD
                            LogFile.Log("Edit BCD");
                            using (Stream BCDFileStream = UnlockedEFIESPFileSystem.OpenFile(@"efi\Microsoft\Boot\BCD", FileMode.Open, FileAccess.ReadWrite))
                            {
                                using (DiscUtils.Registry.RegistryHive BCDHive = new DiscUtils.Registry.RegistryHive(BCDFileStream))
                                {
                                    DiscUtils.BootConfig.Store BCDStore = new DiscUtils.BootConfig.Store(BCDHive.Root);
                                    DiscUtils.BootConfig.BcdObject MobileStartupObject = BCDStore.GetObject(new Guid("{01de5a27-8705-40db-bad6-96fa5187d4a6}"));
                                    DiscUtils.BootConfig.Element NoCodeIntegrityElement = MobileStartupObject.GetElement(0x16000048);
                                    if (NoCodeIntegrityElement != null)
                                        NoCodeIntegrityElement.Value = DiscUtils.BootConfig.ElementValue.ForBoolean(true);
                                    else
                                        MobileStartupObject.AddElement(0x16000048, DiscUtils.BootConfig.ElementValue.ForBoolean(true));

                                    DiscUtils.BootConfig.BcdObject WinLoadObject = BCDStore.GetObject(new Guid("{7619dcc9-fafe-11d9-b411-000476eba25f}"));
                                    NoCodeIntegrityElement = WinLoadObject.GetElement(0x16000048);
                                    if (NoCodeIntegrityElement != null)
                                        NoCodeIntegrityElement.Value = DiscUtils.BootConfig.ElementValue.ForBoolean(true);
                                    else
                                        WinLoadObject.AddElement(0x16000048, DiscUtils.BootConfig.ElementValue.ForBoolean(true));
                                }
                            }
                        }

                        using (FileStream FileSystemStream = new FileStream(MainOsImagePath, FileMode.Open, FileAccess.ReadWrite))
                        {
                            UnlockedMainOsFileSystem = new DiscUtils.Ntfs.NtfsFileSystem(FileSystemStream);

                            App.PatchEngine.TargetImage = UnlockedMainOsFileSystem;
                            PatchResult = App.PatchEngine.Patch("SecureBootHack-MainOS");
                            if (!PatchResult)
                                throw new WPinternalsException("Failed to patch MainOS");
                            PatchResult = App.PatchEngine.Patch("RootAccess-MainOS");
                            if (!PatchResult)
                                throw new WPinternalsException("Failed to patch MainOS");
                        }
                        LogFile.Log("Root access enabled on image", LogType.FileAndConsole);
                        break;
                    case "unlockbootloaderonmountedimage":
                        LogFile.Log("Command: Unlock bootloader on mounted image", LogType.FileAndConsole);
                        if (args.Length < 3)
                            throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -UnlockBootLoaderOnMountedImage <Directory of mounted EFIESP image file> <Optional: Directory of mounted MainOS image file> <Optional: donor-FFU file with supported version of bootfiles>");
                        FFUFilePath = null;
                        EfiEspImagePath = args[2];
                        if (args.Length > 3)
                        {
                            if (FFU.IsFFU(args[3]))
                                FFUFilePath = args[3];
                            else
                                MainOsImagePath = args[3];
                        }
                        if (args.Length > 4)
                            FFUFilePath = args[4];

                        if (FFUFilePath != null)
                        {
                            FFU SupportedFFU = new FFU(FFUFilePath);
                            LogFile.Log("Donor-FFU: " + SupportedFFU.Path);
                            byte[] SupportedEFIESP = SupportedFFU.GetPartition("EFIESP");
                            DiscUtils.Fat.FatFileSystem SupportedEFIESPFileSystem = new DiscUtils.Fat.FatFileSystem(new MemoryStream(SupportedEFIESP));
                            DiscUtils.SparseStream SupportedMobileStartupStream = SupportedEFIESPFileSystem.OpenFile(@"\Windows\System32\Boot\mobilestartup.efi", FileMode.Open);
                            MemoryStream SupportedMobileStartupMemStream = new MemoryStream();
                            SupportedMobileStartupStream.CopyTo(SupportedMobileStartupMemStream);
                            byte[] SupportedMobileStartup = SupportedMobileStartupMemStream.ToArray();
                            SupportedMobileStartupMemStream.Close();
                            SupportedMobileStartupStream.Close();

                            // Save supported mobilestartup.efi
                            LogFile.Log("Taking mobilestartup.efi from donor-FFU");
                            Stream MobileStartupStream = File.Open(Path.Combine(EfiEspImagePath, @"Windows\System32\Boot\mobilestartup.efi"), FileMode.Create, FileAccess.Write);
                            MobileStartupStream.Write(SupportedMobileStartup, 0, SupportedMobileStartup.Length);
                            MobileStartupStream.Close();
                        }

                        App.PatchEngine.TargetPath = EfiEspImagePath;
                        PatchResult = App.PatchEngine.Patch("SecureBootHack-V2-EFIESP");
                        if (!PatchResult)
                            throw new WPinternalsException("Failed to patch bootloader");

                        // Edit BCD
                        LogFile.Log("Edit BCD");
                        using (Stream BCDFileStream = File.Open(Path.Combine(EfiEspImagePath, @"efi\Microsoft\Boot\BCD"), FileMode.Open, FileAccess.ReadWrite))
                        {
                            using (DiscUtils.Registry.RegistryHive BCDHive = new DiscUtils.Registry.RegistryHive(BCDFileStream))
                            {
                                DiscUtils.BootConfig.Store BCDStore = new DiscUtils.BootConfig.Store(BCDHive.Root);
                                DiscUtils.BootConfig.BcdObject MobileStartupObject = BCDStore.GetObject(new Guid("{01de5a27-8705-40db-bad6-96fa5187d4a6}"));
                                DiscUtils.BootConfig.Element NoCodeIntegrityElement = MobileStartupObject.GetElement(0x16000048);
                                if (NoCodeIntegrityElement != null)
                                    NoCodeIntegrityElement.Value = DiscUtils.BootConfig.ElementValue.ForBoolean(true);
                                else
                                    MobileStartupObject.AddElement(0x16000048, DiscUtils.BootConfig.ElementValue.ForBoolean(true));

                                DiscUtils.BootConfig.BcdObject WinLoadObject = BCDStore.GetObject(new Guid("{7619dcc9-fafe-11d9-b411-000476eba25f}"));
                                NoCodeIntegrityElement = WinLoadObject.GetElement(0x16000048);
                                if (NoCodeIntegrityElement != null)
                                    NoCodeIntegrityElement.Value = DiscUtils.BootConfig.ElementValue.ForBoolean(true);
                                else
                                    WinLoadObject.AddElement(0x16000048, DiscUtils.BootConfig.ElementValue.ForBoolean(true));
                            }
                        }

                        if (MainOsImagePath != null)
                        {
                            App.PatchEngine.TargetPath = MainOsImagePath;
                            PatchResult = App.PatchEngine.Patch("SecureBootHack-MainOS");
                            if (!PatchResult)
                                throw new WPinternalsException("Failed to patch MainOS");
                        }
                        LogFile.Log("Bootloader unlocked on image", LogType.FileAndConsole);
                        break;
                    case "enablerootaccessonmountedimage":
                        LogFile.Log("Command: Enable root access on mounted image", LogType.FileAndConsole);
                        if (args.Length < 4)
                            throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -EnableRootAccessOnMountedImage <Directory of mounted EFIESP image file> <Directory of mounted MainOS image file> <Optional: donor-FFU file with supported version of bootfiles>");

                        FFUFilePath = null;
                        EfiEspImagePath = args[2];
                        MainOsImagePath = args[3];
                        if (args.Length > 4)
                            FFUFilePath = args[4];

                        if (FFUFilePath != null)
                        {
                            FFU SupportedFFU = new FFU(FFUFilePath);
                            LogFile.Log("Supported FFU: " + SupportedFFU.Path);
                            byte[] SupportedEFIESP = SupportedFFU.GetPartition("EFIESP");
                            DiscUtils.Fat.FatFileSystem SupportedEFIESPFileSystem = new DiscUtils.Fat.FatFileSystem(new MemoryStream(SupportedEFIESP));
                            DiscUtils.SparseStream SupportedMobileStartupStream = SupportedEFIESPFileSystem.OpenFile(@"\Windows\System32\Boot\mobilestartup.efi", FileMode.Open);
                            MemoryStream SupportedMobileStartupMemStream = new MemoryStream();
                            SupportedMobileStartupStream.CopyTo(SupportedMobileStartupMemStream);
                            byte[] SupportedMobileStartup = SupportedMobileStartupMemStream.ToArray();
                            SupportedMobileStartupMemStream.Close();
                            SupportedMobileStartupStream.Close();

                            // Save supported mobilestartup.efi
                            LogFile.Log("Taking mobilestartup.efi from donor-FFU");
                            Stream MobileStartupStream = File.Open(Path.Combine(EfiEspImagePath, @"Windows\System32\Boot\mobilestartup.efi"), FileMode.Create, FileAccess.Write);
                            MobileStartupStream.Write(SupportedMobileStartup, 0, SupportedMobileStartup.Length);
                            MobileStartupStream.Close();
                        }

                        App.PatchEngine.TargetPath = EfiEspImagePath;
                        PatchResult = App.PatchEngine.Patch("SecureBootHack-V2-EFIESP");
                        if (!PatchResult)
                            throw new WPinternalsException("Failed to patch bootloader");

                        // Edit BCD
                        LogFile.Log("Edit BCD");
                        using (Stream BCDFileStream = File.Open(Path.Combine(EfiEspImagePath, @"efi\Microsoft\Boot\BCD"), FileMode.Open, FileAccess.ReadWrite))
                        {
                            using (DiscUtils.Registry.RegistryHive BCDHive = new DiscUtils.Registry.RegistryHive(BCDFileStream))
                            {
                                DiscUtils.BootConfig.Store BCDStore = new DiscUtils.BootConfig.Store(BCDHive.Root);
                                DiscUtils.BootConfig.BcdObject MobileStartupObject = BCDStore.GetObject(new Guid("{01de5a27-8705-40db-bad6-96fa5187d4a6}"));
                                DiscUtils.BootConfig.Element NoCodeIntegrityElement = MobileStartupObject.GetElement(0x16000048);
                                if (NoCodeIntegrityElement != null)
                                    NoCodeIntegrityElement.Value = DiscUtils.BootConfig.ElementValue.ForBoolean(true);
                                else
                                    MobileStartupObject.AddElement(0x16000048, DiscUtils.BootConfig.ElementValue.ForBoolean(true));

                                DiscUtils.BootConfig.BcdObject WinLoadObject = BCDStore.GetObject(new Guid("{7619dcc9-fafe-11d9-b411-000476eba25f}"));
                                NoCodeIntegrityElement = WinLoadObject.GetElement(0x16000048);
                                if (NoCodeIntegrityElement != null)
                                    NoCodeIntegrityElement.Value = DiscUtils.BootConfig.ElementValue.ForBoolean(true);
                                else
                                    WinLoadObject.AddElement(0x16000048, DiscUtils.BootConfig.ElementValue.ForBoolean(true));
                            }
                        }

                        App.PatchEngine.TargetPath = MainOsImagePath;
                        PatchResult = App.PatchEngine.Patch("SecureBootHack-MainOS");
                        if (!PatchResult)
                            throw new WPinternalsException("Failed to patch MainOS");
                        PatchResult = App.PatchEngine.Patch("RootAccess-MainOS");
                        if (!PatchResult)
                            throw new WPinternalsException("Failed to patch MainOS");
                        LogFile.Log("Root access enabled on image", LogType.FileAndConsole);
                        break;
                    case "downloadffu":
                        LogFile.Log("Command: Download FFU", LogType.FileAndConsole);
                        Notifier = new PhoneNotifierViewModel();
                        UIContext.Send(s => Notifier.Start(), null);
                        if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Normal)
                        {
                            NormalModel = (NokiaPhoneModel)Notifier.CurrentModel;
                            ProductCode = NormalModel.ExecuteJsonMethodAsString("ReadProductCode", "ProductCode");
                        }
                        else if ((Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) || (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Flash))
                        {
                            FlashModel = (NokiaFlashModel)Notifier.CurrentModel;
                            Info = FlashModel.ReadPhoneInfo();
                            ProductCode = Info.ProductCode;
                        }
                        else
                        {
                            NormalModel = (NokiaPhoneModel)(await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Normal));
                            ProductCode = NormalModel.ExecuteJsonMethodAsString("ReadProductCode", "ProductCode");
                        }
                        URL = LumiaDownloadModel.SearchFFU(null, ProductCode, null, out ProductType);
                        if (args.Length >= 3)
                            DownloadFolder = args[4];
                        else
                            DownloadFolder = Environment.ExpandEnvironmentVariables("%ALLUSERSPROFILE%\\WPInternals\\Repository\\" + ProductType.ToUpper());
                        if (!Directory.Exists(DownloadFolder))
                            Directory.CreateDirectory(DownloadFolder);
                        LogFile.Log("Download folder: " + DownloadFolder, LogType.FileAndConsole);
                        LogFile.Log("URL: " + URL, LogType.FileAndConsole);
                        URI = new Uri(URL);
                        FFUFileName = System.IO.Path.GetFileName(URI.LocalPath);
                        LogFile.Log("File: " + FFUFileName, LogType.FileAndConsole);
                        FFUFilePath = Path.Combine(DownloadFolder, FFUFileName);
                        LogFile.Log("Downloading...", LogType.FileAndConsole);
                        using (System.Net.WebClient myWebClient = new System.Net.WebClient())
                        {
                            await myWebClient.DownloadFileTaskAsync(URL, FFUFilePath);
                        }
                        LogFile.Log("Download finished", LogType.FileAndConsole);
                        App.Config.AddFfuToRepository(FFUFilePath);
                        Notifier.Stop();
                        break;
                    case "downloadffubyoperatorcode":
                        LogFile.Log("Command: Download FFU by Operator Code", LogType.FileAndConsole);
                        if (args.Length < 4)
                            throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -DownloadFFUbyOperatorCode <Product type> <Operator code> <Optional: Download folder>");
                        ProductType = args[2];
                        LogFile.Log("Product type: " + ProductType, LogType.FileAndConsole);
                        OperatorCode = args[3];
                        LogFile.Log("Operator code: " + OperatorCode, LogType.FileAndConsole);
                        if (args.Length >= 5)
                            DownloadFolder = args[4];
                        else
                            DownloadFolder = Environment.ExpandEnvironmentVariables("%ALLUSERSPROFILE%\\WPInternals\\Repository\\" + ProductType.ToUpper());
                        if (!Directory.Exists(DownloadFolder))
                            Directory.CreateDirectory(DownloadFolder);
                        LogFile.Log("Download folder: " + DownloadFolder, LogType.FileAndConsole);
                        URL = LumiaDownloadModel.SearchFFU(ProductType, null, OperatorCode);
                        LogFile.Log("URL: " + URL, LogType.FileAndConsole);
                        URI = new Uri(URL);
                        FFUFileName = System.IO.Path.GetFileName(URI.LocalPath);
                        LogFile.Log("File: " + FFUFileName, LogType.FileAndConsole);
                        FFUFilePath = Path.Combine(DownloadFolder, FFUFileName);
                        LogFile.Log("Downloading...", LogType.FileAndConsole);
                        using (System.Net.WebClient myWebClient = new System.Net.WebClient())
                        {
                            await myWebClient.DownloadFileTaskAsync(URL, FFUFilePath);
                        }
                        LogFile.Log("Download finished", LogType.FileAndConsole);
                        App.Config.AddFfuToRepository(FFUFilePath);
                        break;
                    case "downloadffubyproductcode":
                        LogFile.Log("Command: Download FFU by Product Code", LogType.FileAndConsole);
                        if (args.Length < 3)
                            throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -DownloadFFUbyProductCode <Product code> <Optional: Download folder>");
                        ProductCode = args[2];
                        LogFile.Log("Product code: " + ProductCode, LogType.FileAndConsole);
                        URL = LumiaDownloadModel.SearchFFU(null, ProductCode, null, out ProductType);
                        if (args.Length >= 4)
                            DownloadFolder = args[3];
                        else
                            DownloadFolder = Environment.ExpandEnvironmentVariables("%ALLUSERSPROFILE%\\WPInternals\\Repository\\" + ProductType.ToUpper());
                        if (!Directory.Exists(DownloadFolder))
                            Directory.CreateDirectory(DownloadFolder);
                        LogFile.Log("Download folder: " + DownloadFolder, LogType.FileAndConsole);
                        LogFile.Log("URL: " + URL, LogType.FileAndConsole);
                        URI = new Uri(URL);
                        FFUFileName = System.IO.Path.GetFileName(URI.LocalPath);
                        LogFile.Log("File: " + FFUFileName, LogType.FileAndConsole);
                        FFUFilePath = Path.Combine(DownloadFolder, FFUFileName);
                        LogFile.Log("Downloading...", LogType.FileAndConsole);
                        using (System.Net.WebClient myWebClient = new System.Net.WebClient())
                        {
                            await myWebClient.DownloadFileTaskAsync(URL, FFUFilePath);
                        }
                        LogFile.Log("Download finished", LogType.FileAndConsole);
                        App.Config.AddFfuToRepository(FFUFilePath);
                        break;
                    case "downloadffubyproducttype":
                        LogFile.Log("Command: Download FFU by Product Type", LogType.FileAndConsole);
                        if (args.Length < 3)
                            throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -DownloadFFUbyProductType <Product type> <Optional: Download folder>");
                        ProductType = args[2];
                        LogFile.Log("Product type: " + ProductType, LogType.FileAndConsole);
                        if (args.Length >= 4)
                            DownloadFolder = args[3];
                        else
                            DownloadFolder = Environment.ExpandEnvironmentVariables("%ALLUSERSPROFILE%\\WPInternals\\Repository\\" + ProductType.ToUpper());
                        if (!Directory.Exists(DownloadFolder))
                            Directory.CreateDirectory(DownloadFolder);
                        LogFile.Log("Download folder: " + DownloadFolder, LogType.FileAndConsole);
                        URL = LumiaDownloadModel.SearchFFU(ProductType, null, null);
                        LogFile.Log("URL: " + URL, LogType.FileAndConsole);
                        URI = new Uri(URL);
                        FFUFileName = System.IO.Path.GetFileName(URI.LocalPath);
                        LogFile.Log("File: " + FFUFileName, LogType.FileAndConsole);
                        FFUFilePath = Path.Combine(DownloadFolder, FFUFileName);
                        LogFile.Log("Downloading...", LogType.FileAndConsole);
                        using (System.Net.WebClient myWebClient = new System.Net.WebClient())
                        {
                            await myWebClient.DownloadFileTaskAsync(URL, FFUFilePath);
                        }
                        LogFile.Log("Download finished", LogType.FileAndConsole);
                        App.Config.AddFfuToRepository(FFUFilePath);
                        break;
                    case "searchffubyproducttype":
                        LogFile.Log("Command: Search FFU by Product Type", LogType.FileAndConsole);
                        if (args.Length < 3)
                            throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -SearchFFUbyProductType <Lumia model>");
                        ProductType = args[2];
                        LogFile.Log("Lumia model: " + ProductType, LogType.FileAndConsole);
                        URL = LumiaDownloadModel.SearchFFU(ProductType, null, null);
                        LogFile.Log("URL: " + URL, LogType.FileAndConsole);
                        URI = new Uri(URL);
                        FFUFileName = System.IO.Path.GetFileName(URI.LocalPath);
                        LogFile.Log("File: " + FFUFileName, LogType.FileAndConsole);
                        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 = (NokiaPhoneModel)Notifier.CurrentModel;
                            ProductType = NormalModel.ExecuteJsonMethodAsString("ReadManufacturerModelName", "ManufacturerModelName");
                            if (ProductType.IndexOf('_') >= 0) ProductType = ProductType.Substring(0, ProductType.IndexOf('_'));
                        }
                        else if ((Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) || (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Flash))
                        {
                            FlashModel = (NokiaFlashModel)Notifier.CurrentModel;
                            Info = FlashModel.ReadPhoneInfo();
                            ProductType = Info.Type;
                        }
                        else
                        {
                            NormalModel = (NokiaPhoneModel)(await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Normal));
                            ProductType = NormalModel.ExecuteJsonMethodAsString("ReadManufacturerModelName", "ManufacturerModelName");
                            if (ProductType.IndexOf('_') >= 0) ProductType = ProductType.Substring(0, ProductType.IndexOf('_'));
                        }
                        URLs = LumiaDownloadModel.SearchEmergencyFiles(ProductType);
                        if (URLs != null)
                        {
                            if (args.Length >= 3)
                                DownloadFolder = args[2];
                            else
                                DownloadFolder = 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 = System.IO.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 System.Net.WebClient())
                                {
                                    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)
                        {
                            if (args.Length >= 4)
                                DownloadFolder = args[3];
                            else
                                DownloadFolder = 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 = System.IO.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 System.Net.WebClient())
                                {
                                    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 = (NokiaPhoneModel)Notifier.CurrentModel;
                            ProductCode = NormalModel.ExecuteJsonMethodAsString("ReadProductCode", "ProductCode");
                        }
                        else if ((Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) || (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Flash))
                        {
                            FlashModel = (NokiaFlashModel)Notifier.CurrentModel;
                            Info = FlashModel.ReadPhoneInfo();
                            ProductCode = Info.ProductCode;
                        }
                        else
                        {
                            NormalModel = (NokiaPhoneModel)(await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Normal));
                            ProductCode = NormalModel.ExecuteJsonMethodAsString("ReadProductCode", "ProductCode");
                        }
                        URL = LumiaDownloadModel.SearchFFU(null, ProductCode, null, out ProductType);
                        if (args.Length >= 3)
                            DownloadFolder = args[2];
                        else
                            DownloadFolder = Environment.ExpandEnvironmentVariables("%ALLUSERSPROFILE%\\WPInternals\\Repository\\" + ProductType.ToUpper());
                        if (!Directory.Exists(DownloadFolder))
                            Directory.CreateDirectory(DownloadFolder);
                        LogFile.Log("Download folder: " + DownloadFolder, LogType.FileAndConsole);
                        LogFile.Log("URL: " + URL, LogType.FileAndConsole);
                        URI = new Uri(URL);
                        FFUFileName = System.IO.Path.GetFileName(URI.LocalPath);
                        LogFile.Log("File: " + FFUFileName, LogType.FileAndConsole);
                        FFUFilePath = Path.Combine(DownloadFolder, FFUFileName);
                        LogFile.Log("Downloading...", LogType.FileAndConsole);
                        using (System.Net.WebClient myWebClient = new System.Net.WebClient())
                        {
                            await myWebClient.DownloadFileTaskAsync(URL, FFUFilePath);
                        }
                        LogFile.Log("Download finished", LogType.FileAndConsole);
                        App.Config.AddFfuToRepository(FFUFilePath);

                        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 = System.IO.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 System.Net.WebClient())
                                {
                                    await myWebClient.DownloadFileTaskAsync(URLs[i], EmergencyFilePath);
                                }
                                LogFile.Log("Download finished", LogType.FileAndConsole);
                            }
                            App.Config.AddEmergencyToRepository(ProductType, ProgrammerPath, PayloadPath);
                        }

                        if (App.Config.FFURepository.Where(e => App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V2-EFIESP").First().TargetVersions.Any(v => v.Description == e.OSVersion)).Count() == 0)
                        {
                            ProductType = "RM-1085";

                            URL = LumiaDownloadModel.SearchFFU(ProductType, null, null);
                            if (args.Length >= 3)
                                DownloadFolder = args[2];
                            else
                                DownloadFolder = Environment.ExpandEnvironmentVariables("%ALLUSERSPROFILE%\\WPInternals\\Repository\\" + ProductType.ToUpper());
                            if (!Directory.Exists(DownloadFolder))
                                Directory.CreateDirectory(DownloadFolder);
                            LogFile.Log("Download folder: " + DownloadFolder, LogType.FileAndConsole);
                            LogFile.Log("URL: " + URL, LogType.FileAndConsole);
                            URI = new Uri(URL);
                            FFUFileName = System.IO.Path.GetFileName(URI.LocalPath);
                            LogFile.Log("File: " + FFUFileName, LogType.FileAndConsole);
                            FFUFilePath = Path.Combine(DownloadFolder, FFUFileName);
                            LogFile.Log("Downloading...", LogType.FileAndConsole);
                            using (System.Net.WebClient myWebClient = new System.Net.WebClient())
                            {
                                await myWebClient.DownloadFileTaskAsync(URL, FFUFilePath);
                            }
                            LogFile.Log("Download finished", LogType.FileAndConsole);
                            App.Config.AddFfuToRepository(FFUFilePath);

                            if (App.Config.FFURepository.Where(e => App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V2-EFIESP").First().TargetVersions.Any(v => v.Description == e.OSVersion)).Count() == 0)
                                throw new WPinternalsException("Unable to find compatible FFU");
                        }
                        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);
                        if (args.Length >= 4)
                            DownloadFolder = args[3];
                        else
                            DownloadFolder = Environment.ExpandEnvironmentVariables("%ALLUSERSPROFILE%\\WPInternals\\Repository\\" + ProductType.ToUpper());
                        if (!Directory.Exists(DownloadFolder))
                            Directory.CreateDirectory(DownloadFolder);
                        LogFile.Log("Download folder: " + DownloadFolder, LogType.FileAndConsole);
                        URL = LumiaDownloadModel.SearchFFU(ProductType, null, null);
                        LogFile.Log("URL: " + URL, LogType.FileAndConsole);
                        URI = new Uri(URL);
                        FFUFileName = System.IO.Path.GetFileName(URI.LocalPath);
                        LogFile.Log("File: " + FFUFileName, LogType.FileAndConsole);
                        FFUFilePath = Path.Combine(DownloadFolder, FFUFileName);
                        LogFile.Log("Downloading...", LogType.FileAndConsole);
                        using (System.Net.WebClient myWebClient = new System.Net.WebClient())
                        {
                            await myWebClient.DownloadFileTaskAsync(URL, FFUFilePath);
                        }
                        LogFile.Log("Download finished", LogType.FileAndConsole);
                        App.Config.AddFfuToRepository(FFUFilePath);

                        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 = System.IO.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 System.Net.WebClient())
                                {
                                    await myWebClient.DownloadFileTaskAsync(URLs[i], EmergencyFilePath);
                                }
                                LogFile.Log("Download finished", LogType.FileAndConsole);
                            }
                            App.Config.AddEmergencyToRepository(ProductType, ProgrammerPath, PayloadPath);
                        }

                        if (App.Config.FFURepository.Where(e => App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V2-EFIESP").First().TargetVersions.Any(v => v.Description == e.OSVersion)).Count() == 0)
                        {
                            ProductType = "RM-1085";

                            URL = LumiaDownloadModel.SearchFFU(ProductType, null, null);
                            if (args.Length >= 4)
                                DownloadFolder = args[3];
                            else
                                DownloadFolder = Environment.ExpandEnvironmentVariables("%ALLUSERSPROFILE%\\WPInternals\\Repository\\" + ProductType.ToUpper());
                            if (!Directory.Exists(DownloadFolder))
                                Directory.CreateDirectory(DownloadFolder);
                            LogFile.Log("Download folder: " + DownloadFolder, LogType.FileAndConsole);
                            LogFile.Log("URL: " + URL, LogType.FileAndConsole);
                            URI = new Uri(URL);
                            FFUFileName = System.IO.Path.GetFileName(URI.LocalPath);
                            LogFile.Log("File: " + FFUFileName, LogType.FileAndConsole);
                            FFUFilePath = Path.Combine(DownloadFolder, FFUFileName);
                            LogFile.Log("Downloading...", LogType.FileAndConsole);
                            using (System.Net.WebClient myWebClient = new System.Net.WebClient())
                            {
                                await myWebClient.DownloadFileTaskAsync(URL, FFUFilePath);
                            }
                            LogFile.Log("Download finished", LogType.FileAndConsole);
                            App.Config.AddFfuToRepository(FFUFilePath);

                            if (App.Config.FFURepository.Where(e => App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V2-EFIESP").First().TargetVersions.Any(v => v.Description == e.OSVersion)).Count() == 0)
                                throw new WPinternalsException("Unable to find compatible FFU");
                        }
                        break;
                    case "downloadallbyproductcode":
                        LogFile.Log("Command: Download all by Product Code", LogType.FileAndConsole);
                        if (args.Length < 3)
                            throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -DownloadAllByProductCode <Product code> <Optional: Download folder>");
                        ProductCode = args[2];
                        LogFile.Log("Product code: " + ProductCode, LogType.FileAndConsole);
                        URL = LumiaDownloadModel.SearchFFU(null, ProductCode, null, out ProductType);
                        if (args.Length >= 4)
                            DownloadFolder = args[3];
                        else
                            DownloadFolder = Environment.ExpandEnvironmentVariables("%ALLUSERSPROFILE%\\WPInternals\\Repository\\" + ProductType.ToUpper());
                        if (!Directory.Exists(DownloadFolder))
                            Directory.CreateDirectory(DownloadFolder);
                        LogFile.Log("Download folder: " + DownloadFolder, LogType.FileAndConsole);
                        LogFile.Log("URL: " + URL, LogType.FileAndConsole);
                        URI = new Uri(URL);
                        FFUFileName = System.IO.Path.GetFileName(URI.LocalPath);
                        LogFile.Log("File: " + FFUFileName, LogType.FileAndConsole);
                        FFUFilePath = Path.Combine(DownloadFolder, FFUFileName);
                        LogFile.Log("Downloading...", LogType.FileAndConsole);
                        using (System.Net.WebClient myWebClient = new System.Net.WebClient())
                        {
                            await myWebClient.DownloadFileTaskAsync(URL, FFUFilePath);
                        }
                        LogFile.Log("Download finished", LogType.FileAndConsole);
                        App.Config.AddFfuToRepository(FFUFilePath);

                        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 = System.IO.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 System.Net.WebClient())
                                {
                                    await myWebClient.DownloadFileTaskAsync(URLs[i], EmergencyFilePath);
                                }
                                LogFile.Log("Download finished", LogType.FileAndConsole);
                            }
                            App.Config.AddEmergencyToRepository(ProductType, ProgrammerPath, PayloadPath);
                        }

                        if (App.Config.FFURepository.Where(e => App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V2-EFIESP").First().TargetVersions.Any(v => v.Description == e.OSVersion)).Count() == 0)
                        {
                            ProductType = "RM-1085";

                            URL = LumiaDownloadModel.SearchFFU(ProductType, null, null);
                            if (args.Length >= 4)
                                DownloadFolder = args[3];
                            else
                                DownloadFolder = Environment.ExpandEnvironmentVariables("%ALLUSERSPROFILE%\\WPInternals\\Repository\\" + ProductType.ToUpper());
                            if (!Directory.Exists(DownloadFolder))
                                Directory.CreateDirectory(DownloadFolder);
                            LogFile.Log("Download folder: " + DownloadFolder, LogType.FileAndConsole);
                            LogFile.Log("URL: " + URL, LogType.FileAndConsole);
                            URI = new Uri(URL);
                            FFUFileName = System.IO.Path.GetFileName(URI.LocalPath);
                            LogFile.Log("File: " + FFUFileName, LogType.FileAndConsole);
                            FFUFilePath = Path.Combine(DownloadFolder, FFUFileName);
                            LogFile.Log("Downloading...", LogType.FileAndConsole);
                            using (System.Net.WebClient myWebClient = new System.Net.WebClient())
                            {
                                await myWebClient.DownloadFileTaskAsync(URL, FFUFilePath);
                            }
                            LogFile.Log("Download finished", LogType.FileAndConsole);
                            App.Config.AddFfuToRepository(FFUFilePath);

                            if (App.Config.FFURepository.Where(e => App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V2-EFIESP").First().TargetVersions.Any(v => v.Description == e.OSVersion)).Count() == 0)
                                throw new WPinternalsException("Unable to find compatible FFU");
                        }
                        break;
                    case "downloadallbyoperatorcode":
                        LogFile.Log("Command: Download FFU by Operator Code", LogType.FileAndConsole);
                        if (args.Length < 4)
                            throw new ArgumentException("Wrong number of arguments. Usage: WPinternals.exe -DownloadFFUbyOperatorCode <Product type> <Operator code> <Optional: Download folder>");
                        ProductType = args[2];
                        LogFile.Log("Product type: " + ProductType, LogType.FileAndConsole);
                        OperatorCode = args[3];
                        LogFile.Log("Operator code: " + OperatorCode, LogType.FileAndConsole);
                        if (args.Length >= 5)
                            DownloadFolder = args[4];
                        else
                            DownloadFolder = Environment.ExpandEnvironmentVariables("%ALLUSERSPROFILE%\\WPInternals\\Repository\\" + ProductType.ToUpper());
                        if (!Directory.Exists(DownloadFolder))
                            Directory.CreateDirectory(DownloadFolder);
                        LogFile.Log("Download folder: " + DownloadFolder, LogType.FileAndConsole);
                        URL = LumiaDownloadModel.SearchFFU(ProductType, null, OperatorCode);
                        LogFile.Log("URL: " + URL, LogType.FileAndConsole);
                        URI = new Uri(URL);
                        FFUFileName = System.IO.Path.GetFileName(URI.LocalPath);
                        LogFile.Log("File: " + FFUFileName, LogType.FileAndConsole);
                        FFUFilePath = Path.Combine(DownloadFolder, FFUFileName);
                        LogFile.Log("Downloading...", LogType.FileAndConsole);
                        using (System.Net.WebClient myWebClient = new System.Net.WebClient())
                        {
                            await myWebClient.DownloadFileTaskAsync(URL, FFUFilePath);
                        }
                        LogFile.Log("Download finished", LogType.FileAndConsole);
                        App.Config.AddFfuToRepository(FFUFilePath);

                        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 = System.IO.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 System.Net.WebClient())
                                {
                                    await myWebClient.DownloadFileTaskAsync(URLs[i], EmergencyFilePath);
                                }
                                LogFile.Log("Download finished", LogType.FileAndConsole);
                            }
                            App.Config.AddEmergencyToRepository(ProductType, ProgrammerPath, PayloadPath);
                        }

                        if (App.Config.FFURepository.Where(e => App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V2-EFIESP").First().TargetVersions.Any(v => v.Description == e.OSVersion)).Count() == 0)
                        {
                            ProductType = "RM-1085";

                            URL = LumiaDownloadModel.SearchFFU(ProductType, null, null);
                            if (args.Length >= 5)
                                DownloadFolder = args[4];
                            else
                                DownloadFolder = Environment.ExpandEnvironmentVariables("%ALLUSERSPROFILE%\\WPInternals\\Repository\\" + ProductType.ToUpper());
                            if (!Directory.Exists(DownloadFolder))
                                Directory.CreateDirectory(DownloadFolder);
                            LogFile.Log("Download folder: " + DownloadFolder, LogType.FileAndConsole);
                            LogFile.Log("URL: " + URL, LogType.FileAndConsole);
                            URI = new Uri(URL);
                            FFUFileName = System.IO.Path.GetFileName(URI.LocalPath);
                            LogFile.Log("File: " + FFUFileName, LogType.FileAndConsole);
                            FFUFilePath = Path.Combine(DownloadFolder, FFUFileName);
                            LogFile.Log("Downloading...", LogType.FileAndConsole);
                            using (System.Net.WebClient myWebClient = new System.Net.WebClient())
                            {
                                await myWebClient.DownloadFileTaskAsync(URL, FFUFilePath);
                            }
                            LogFile.Log("Download finished", LogType.FileAndConsole);
                            App.Config.AddFfuToRepository(FFUFilePath);

                            if (App.Config.FFURepository.Where(e => App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V2-EFIESP").First().TargetVersions.Any(v => v.Description == e.OSVersion)).Count() == 0)
                                throw new WPinternalsException("Unable to find compatible FFU");
                        }
                        break;
                    default:
                        LogFile.Log("", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals commandline usage:", LogType.ConsoleOnly);
                        LogFile.Log("", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -ShowPhoneInfo", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -AddFFU <FFU file>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -AddEmergency <Product type> <EDE file> <EDP file>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -RemoveFFU <FFU file>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -RemoveEmergency <EDE file>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -ListRepository", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -FindFlashingProfile <Optional: Profile FFU file>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -UnlockBootloader", LogType.ConsoleOnly);
                        LogFile.Log("                <Optional: donor-FFU file with supported version of bootfiles>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -FixBootAfterUnlockingBootloader", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -EnableRootAccess", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -UnlockBootLoaderOnImage <EFIESP image file>", LogType.ConsoleOnly);
                        LogFile.Log("                <Optional: MainOS image file>", LogType.ConsoleOnly);
                        LogFile.Log("                <Optional: donor-FFU file with supported version of bootfiles>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -EnableRootAccessOnImage <EFIESP image file> <MainOS image file>", LogType.ConsoleOnly);
                        LogFile.Log("                <Optional: donor-FFU file with supported version of bootfiles>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -UnlockBootLoaderOnMountedImage <Directory of mounted EFIESP image file>", LogType.ConsoleOnly);
                        LogFile.Log("                <Optional: Directory of mounted MainOS image file>", LogType.ConsoleOnly);
                        LogFile.Log("                <Optional: donor-FFU file with supported version of bootfiles>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -EnableRootAccessOnMountedImage <Directory of mounted EFIESP image file>", LogType.ConsoleOnly);
                        LogFile.Log("                <Directory of mounted MainOS image file>", LogType.ConsoleOnly);
                        LogFile.Log("                <Optional: donor-FFU file with supported version of bootfiles>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -EnableTestSigning", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -RelockPhone", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -SwitchToMassStorageMode", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -FlashFFU <FFU file>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -FlashCustomROM <ZIP file>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -FlashPartition <Partition name> <Partition file>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -FlashRaw <Start sector> <Raw file>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -ClearNV", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -ReadGPT", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -BackupGPT <Path to xml-file>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -RestoreGPT <Path to xml-file>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -MergeGPT <Path to input-xml-file> <Path to input-xml-file>", LogType.ConsoleOnly);
                        LogFile.Log("                <Optional: Path to output-xml-file>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -MergeGPT <Path to input-xml-file>", LogType.ConsoleOnly);
                        LogFile.Log("                <Path to ZIP file which includes Partitions.xml>", LogType.ConsoleOnly);
                        LogFile.Log("                <Optional: Path to output-xml-file>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -ShowFFU <FFU file>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -DumpFFU <FFU file> <Destination folder> <Optional: Partition Name>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -DownloadFFU <Optional: Destination folder>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -DownloadFFUbyProductType <Product type>", LogType.ConsoleOnly);
                        LogFile.Log("                <Optional: Destination folder>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -DownloadFFUbyProductCode <Product code>", LogType.ConsoleOnly);
                        LogFile.Log("                <Optional: Destination folder>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -DownloadFFUbyOperatorCode <Product type> <Operator code>", LogType.ConsoleOnly);
                        LogFile.Log("                <Optional: Destination folder>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -DownloadEmergency <Optional: Destination folder>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -DownloadEmergencyByProductType <Product type>", LogType.ConsoleOnly);
                        LogFile.Log("                <Optional: Destination folder>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -DownloadAll <Optional: Destination folder>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -DownloadAllByProductType <Product type>", LogType.ConsoleOnly);
                        LogFile.Log("                <Optional: Destination folder>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -DownloadAllByProductCode <Product code>", LogType.ConsoleOnly);
                        LogFile.Log("                <Optional: Destination folder>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -DownloadAllByOperatorCode <Product type> <Operator code>", LogType.ConsoleOnly);
                        LogFile.Log("                <Optional: Destination folder>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -DumpUEFI <UEFI binary or FFU file> <Destination folder>", LogType.ConsoleOnly);
                        LogFile.Log("WPinternals -TestProgrammer <EDE file>", LogType.ConsoleOnly);
                        break;
                }
            }
            catch (Exception Ex)
            {
                LogFile.LogException(Ex);
            }

            if (Environment.GetCommandLineArgs().Count() > 1)
                CloseConsole();
        }

        // http://stackoverflow.com/questions/472282/show-console-in-windows-application
        // https://stackoverflow.com/questions/807998/how-do-i-create-a-c-sharp-app-that-decides-itself-whether-to-show-as-a-console-o
        // http://www.csharp411.com/console-output-from-winforms-application/#comment-76
        // https://stackoverflow.com/questions/1305257/using-attachconsole-user-must-hit-enter-to-get-regular-command-line
        internal static void OpenConsole()
        {
            if (IsConsoleVisible)
                return;

            if (AttachConsole(-1))
            {
                Console.Write("\r" + new string(' ', Console.WindowWidth) + "\r"); // Prompt was already printed. Clear that line.

                /*
                Other possibility to clear line:

                System.Console.CursorLeft = 0;
                char[] bl = System.Linq.Enumerable.ToArray<char>(System.Linq.Enumerable.Repeat<char>(' ', System.Console.WindowWidth - 1));
                System.Console.Write(bl);
                System.Console.CursorLeft = 0;
                */

                hConsoleWnd = GetForegroundWindow();
            }
            else
            {
                AllocConsole();
                IsNewConsoleCreated = true;

                hConsoleWnd = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle;

                // Support VS 2017 debugger without VS host process:
                try
                {
                    // Console.OpenStandardOutput eventually calls into GetStdHandle. As per MSDN documentation of GetStdHandle: http://msdn.microsoft.com/en-us/library/windows/desktop/ms683231(v=vs.85).aspx will return the redirected handle and not the allocated console:
                    // "The standard handles of a process may be redirected by a call to  SetStdHandle, in which case  GetStdHandle returns the redirected handle. If the standard handles have been redirected, you can specify the CONIN$ value in a call to the CreateFile function to get a handle to a console's input buffer. Similarly, you can specify the CONOUT$ value to get a handle to a console's active screen buffer."
                    // Get the handle to CONOUT$.    
                    IntPtr stdHandle = CreateFile("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
                    Microsoft.Win32.SafeHandles.SafeFileHandle safeFileHandle = new Microsoft.Win32.SafeHandles.SafeFileHandle(stdHandle, true);
                    FileStream fileStream = new FileStream(safeFileHandle, FileAccess.Write);
                    Encoding encoding = System.Text.Encoding.GetEncoding(MY_CODE_PAGE);
                    StreamWriter standardOutput = new StreamWriter(fileStream, encoding);
                    standardOutput.AutoFlush = true;
                    Console.SetOut(standardOutput);
                }
                catch (Exception)
                {
                }
            }

            IsConsoleVisible = true;

            Console.CancelKeyPress += Console_CancelKeyPress;

            LogFile.LogApplicationVersion();
        }

        private static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
        {
            LogFile.Log("Operation canceled!", LogType.FileOnly);
            LogFile.EndAction();

#if PREVIEW
            Uploader.WaitForUploads();
#endif
        }

        internal static void CloseConsole(bool Exit = true)
        {
            if (IsConsoleVisible)
            {
                if (IsNewConsoleCreated)
                {
                    Console.WriteLine("Press any key to close...");
                    Console.ReadKey();
                }

                PostMessage(hConsoleWnd, WM_KEYDOWN, VK_RETURN, 0);

                IsConsoleVisible = false;

                if (Exit)
                {
#if PREVIEW
                    Uploader.WaitForUploads();
#endif

                    Environment.Exit(0);
                }
            }
        }
    }
}
