internal void FlashPartitionsTask(string EFIESPPath, string MainOSPath, string DataPath)
        {
            new Thread(() =>
            {
                bool Result = true;

                ActivateSubContext(new BusyViewModel("Initializing flash..."));

                NokiaFlashModel Phone = (NokiaFlashModel)PhoneNotifier.CurrentModel;

                GPT GPT = Phone.ReadGPT();

                ulong TotalSizeSectors     = 0;
                int PartitionCount         = 0;
                ulong MainOSOldSectorCount = 0;
                ulong MainOSNewSectorCount = 0;
                ulong DataOldSectorCount   = 0;
                ulong DataNewSectorCount   = 0;
                ulong FirstMainOSSector    = 0;

                try
                {
                    if (EFIESPPath != null)
                    {
                        using (Stream Stream = new DecompressedStream(File.Open(EFIESPPath, FileMode.Open)))
                        {
                            ulong StreamLengthInSectors = (ulong)Stream.Length / 0x200;
                            TotalSizeSectors           += StreamLengthInSectors;
                            PartitionCount++;
                            Partition Partition = GPT.Partitions.Where(p => string.Compare(p.Name, "EFIESP", true) == 0).FirstOrDefault();
                            if (StreamLengthInSectors > Partition.SizeInSectors)
                            {
                                LogFile.Log("Flash failed! Size of partition 'EFIESP' is too big.");
                                ExitFailure("Flash failed!", "Size of partition 'EFIESP' is too big.");
                                return;
                            }
                        }
                    }

                    if (MainOSPath != null)
                    {
                        using (Stream Stream = new DecompressedStream(File.Open(MainOSPath, FileMode.Open)))
                        {
                            ulong StreamLengthInSectors = (ulong)Stream.Length / 0x200;
                            TotalSizeSectors           += StreamLengthInSectors;
                            PartitionCount++;
                            Partition Partition  = GPT.Partitions.Where(p => string.Compare(p.Name, "MainOS", true) == 0).FirstOrDefault();
                            MainOSOldSectorCount = Partition.SizeInSectors;
                            MainOSNewSectorCount = StreamLengthInSectors;
                            FirstMainOSSector    = Partition.FirstSector;
                        }
                    }

                    if (DataPath != null)
                    {
                        using (Stream Stream = new DecompressedStream(File.Open(DataPath, FileMode.Open)))
                        {
                            ulong StreamLengthInSectors = (ulong)Stream.Length / 0x200;
                            TotalSizeSectors           += StreamLengthInSectors;
                            PartitionCount++;
                            Partition Partition = GPT.Partitions.Where(p => string.Compare(p.Name, "Data", true) == 0).FirstOrDefault();
                            DataOldSectorCount  = Partition.SizeInSectors;
                            DataNewSectorCount  = StreamLengthInSectors;
                        }
                    }
                }
                catch (Exception Ex)
                {
                    LogFile.LogException(Ex);
                    Result = false;
                }

                if ((MainOSNewSectorCount > 0) && (DataNewSectorCount > 0))
                {
                    if ((MainOSNewSectorCount > MainOSOldSectorCount) || (DataNewSectorCount > DataOldSectorCount))
                    {
                        UInt64 OSSpace = GPT.LastUsableSector - FirstMainOSSector + 1;
                        if ((MainOSNewSectorCount + DataNewSectorCount) <= OSSpace)
                        {
                            // MainOS and Data partitions need to be re-aligned!
                            Partition MainOSPartition  = GPT.Partitions.Where(p => string.Compare(p.Name, "MainOS", true) == 0).Single();
                            Partition DataPartition    = GPT.Partitions.Where(p => string.Compare(p.Name, "Data", true) == 0).Single();
                            MainOSPartition.LastSector = MainOSPartition.FirstSector + MainOSNewSectorCount - 1;
                            DataPartition.FirstSector  = MainOSPartition.LastSector + 1;
                            DataPartition.LastSector   = DataPartition.FirstSector + DataNewSectorCount - 1;
                            Phone.WriteGPT(GPT);
                        }
                        else
                        {
                            LogFile.Log("Flash failed! Size of partitions 'MainOS' and 'Data' together are too big.");
                            ExitFailure("Flash failed!", "Sizes of partitions 'MainOS' and 'Data' together are too big.");
                            return;
                        }
                    }
                }
                else if ((MainOSNewSectorCount > 0) && (MainOSNewSectorCount > MainOSOldSectorCount))
                {
                    LogFile.Log("Flash failed! Size of partition 'MainOS' is too big.");
                    ExitFailure("Flash failed!", "Size of partition 'MainOS' is too big.");
                    return;
                }
                else if ((DataNewSectorCount > 0) && (DataNewSectorCount > DataOldSectorCount))
                {
                    LogFile.Log("Flash failed! Size of partition 'Data' is too big.");
                    ExitFailure("Flash failed!", "Size of partition 'Data' together is too big.");
                    return;
                }

                BusyViewModel Busy      = new BusyViewModel("Flashing...", MaxProgressValue: TotalSizeSectors, UIContext: UIContext);
                ProgressUpdater Updater = Busy.ProgressUpdater;
                ActivateSubContext(Busy);

                int i = 0;
                if (Result)
                {
                    try
                    {
                        if (EFIESPPath != null)
                        {
                            i++;
                            Busy.Message = "Flashing partition EFIESP (" + i.ToString() + @"/" + PartitionCount.ToString() + ")";
                            Phone.FlashRawPartition(EFIESPPath, "EFIESP", Updater);
                        }
                    }
                    catch (Exception Ex)
                    {
                        LogFile.LogException(Ex);
                        Result = false;
                    }
                }

                if (Result)
                {
                    try
                    {
                        if (MainOSPath != null)
                        {
                            i++;
                            Busy.Message = "Flashing partition MainOS (" + i.ToString() + @"/" + PartitionCount.ToString() + ")";
                            Phone.FlashRawPartition(MainOSPath, "MainOS", Updater);
                        }
                    }
                    catch (Exception Ex)
                    {
                        LogFile.LogException(Ex);
                        Result = false;
                    }
                }

                if (Result)
                {
                    try
                    {
                        if (DataPath != null)
                        {
                            i++;
                            Busy.Message = "Flashing partition Data (" + i.ToString() + @"/" + PartitionCount.ToString() + ")";
                            Phone.FlashRawPartition(DataPath, "Data", Updater);
                        }
                    }
                    catch (Exception Ex)
                    {
                        LogFile.LogException(Ex);
                        Result = false;
                    }
                }

                if (!Result)
                {
                    ExitFailure("Flash failed!", null);
                    return;
                }

                ExitSuccess("Flash successful! Make sure you disable Windows Update on the phone!", null);
            }).Start();
        }
        internal void FlashArchiveTask(string ArchivePath)
        {
            new Thread(() =>
            {
                ActivateSubContext(new BusyViewModel("Initializing flash..."));

                NokiaFlashModel Phone = (NokiaFlashModel)PhoneNotifier.CurrentModel;

                ulong TotalSizeSectors     = 0;
                int PartitionCount         = 0;
                ulong MainOSOldSectorCount = 0;
                ulong MainOSNewSectorCount = 0;
                ulong DataOldSectorCount   = 0;
                ulong DataNewSectorCount   = 0;
                ulong FirstMainOSSector    = 0;
                bool GPTChanged            = false;

                try
                {
                    GPT GPT = Phone.ReadGPT();

                    using (FileStream FileStream = new FileStream(ArchivePath, FileMode.Open))
                    {
                        using (ZipArchive Archive = new ZipArchive(FileStream, ZipArchiveMode.Read))
                        {
                            foreach (ZipArchiveEntry Entry in Archive.Entries)
                            {
                                // Determine if there is a partition layout present
                                ZipArchiveEntry PartitionEntry = Archive.GetEntry("Partitions.xml");
                                if (PartitionEntry == null)
                                {
                                    GPT.MergePartitions(null, false, Archive);
                                    GPTChanged |= GPT.HasChanged;
                                }
                                else
                                {
                                    using (Stream ZipStream = PartitionEntry.Open())
                                    {
                                        using (StreamReader ZipReader = new StreamReader(ZipStream))
                                        {
                                            string PartitionXml = ZipReader.ReadToEnd();
                                            GPT.MergePartitions(PartitionXml, false, Archive);
                                            GPTChanged |= GPT.HasChanged;
                                        }
                                    }
                                }

                                // First determine if we need a new GPT!
                                if (!Entry.FullName.Contains("/")) // No subfolders
                                {
                                    string PartitionName = System.IO.Path.GetFileNameWithoutExtension(Entry.Name);
                                    int P = PartitionName.IndexOf('.');
                                    if (P >= 0)
                                    {
                                        PartitionName = PartitionName.Substring(0, P); // Example: Data.bin.gz -> Data
                                    }
                                    Partition Partition = GPT.Partitions.Where(p => string.Compare(p.Name, PartitionName, true) == 0).FirstOrDefault();
                                    if (Partition != null)
                                    {
                                        DecompressedStream DecompressedStream = new DecompressedStream(Entry.Open());
                                        ulong StreamLengthInSectors           = (ulong)Entry.Length / 0x200;
                                        try
                                        {
                                            StreamLengthInSectors = (ulong)DecompressedStream.Length / 0x200;
                                        }
                                        catch { }

                                        TotalSizeSectors += StreamLengthInSectors;
                                        PartitionCount++;

                                        if (string.Compare(PartitionName, "MainOS", true) == 0)
                                        {
                                            MainOSOldSectorCount = Partition.SizeInSectors;
                                            MainOSNewSectorCount = StreamLengthInSectors;
                                            FirstMainOSSector    = Partition.FirstSector;
                                        }
                                        else if (string.Compare(PartitionName, "Data", true) == 0)
                                        {
                                            DataOldSectorCount = Partition.SizeInSectors;
                                            DataNewSectorCount = StreamLengthInSectors;
                                        }
                                        else if (StreamLengthInSectors > Partition.SizeInSectors)
                                        {
                                            LogFile.Log("Flash failed! Size of partition '" + PartitionName + "' is too big.");
                                            ExitFailure("Flash failed!", "Size of partition '" + PartitionName + "' is too big.");
                                            return;
                                        }
                                    }
                                }
                            }

                            if ((MainOSNewSectorCount > 0) && (DataNewSectorCount > 0))
                            {
                                if ((MainOSNewSectorCount > MainOSOldSectorCount) || (DataNewSectorCount > DataOldSectorCount))
                                {
                                    UInt64 OSSpace = GPT.LastUsableSector - FirstMainOSSector + 1;
                                    if ((MainOSNewSectorCount + DataNewSectorCount) <= OSSpace)
                                    {
                                        // MainOS and Data partitions need to be re-aligned!
                                        Partition MainOSPartition  = GPT.Partitions.Where(p => string.Compare(p.Name, "MainOS", true) == 0).Single();
                                        Partition DataPartition    = GPT.Partitions.Where(p => string.Compare(p.Name, "Data", true) == 0).Single();
                                        MainOSPartition.LastSector = MainOSPartition.FirstSector + MainOSNewSectorCount - 1;
                                        DataPartition.FirstSector  = MainOSPartition.LastSector + 1;
                                        DataPartition.LastSector   = DataPartition.FirstSector + DataNewSectorCount - 1;

                                        GPTChanged = true;
                                    }
                                    else
                                    {
                                        LogFile.Log("Flash failed! Size of partitions 'MainOS' and 'Data' together are too big.");
                                        ExitFailure("Flash failed!", "Sizes of partitions 'MainOS' and 'Data' together are too big.");
                                        return;
                                    }
                                }
                            }
                            else if ((MainOSNewSectorCount > 0) && (MainOSNewSectorCount > MainOSOldSectorCount))
                            {
                                LogFile.Log("Flash failed! Size of partition 'MainOS' is too big.");
                                ExitFailure("Flash failed!", "Size of partition 'MainOS' is too big.");
                                return;
                            }
                            else if ((DataNewSectorCount > 0) && (DataNewSectorCount > DataOldSectorCount))
                            {
                                LogFile.Log("Flash failed! Size of partition 'Data' is too big.");
                                ExitFailure("Flash failed!", "Size of partition 'Data' is too big.");
                                return;
                            }

                            if (GPTChanged)
                            {
                                Phone.WriteGPT(GPT);
                            }

                            if (PartitionCount > 0)
                            {
                                BusyViewModel Busy      = new BusyViewModel("Flashing...", MaxProgressValue: TotalSizeSectors, UIContext: UIContext);
                                ProgressUpdater Updater = Busy.ProgressUpdater;
                                ActivateSubContext(Busy);

                                int i = 0;

                                foreach (ZipArchiveEntry Entry in Archive.Entries)
                                {
                                    // "MainOS.bin.gz" => "MainOS"
                                    string PartitionName = Entry.Name;
                                    int Pos = PartitionName.IndexOf('.');
                                    if (Pos >= 0)
                                    {
                                        PartitionName = PartitionName.Substring(0, Pos);
                                    }

                                    Partition Partition = GPT.Partitions.Where(p => string.Compare(p.Name, PartitionName, true) == 0).FirstOrDefault();
                                    if (Partition != null)
                                    {
                                        Stream DecompressedStream   = new DecompressedStream(Entry.Open());
                                        ulong StreamLengthInSectors = (ulong)Entry.Length / 0x200;
                                        try
                                        {
                                            StreamLengthInSectors = (ulong)DecompressedStream.Length / 0x200;
                                        }
                                        catch { }

                                        if (StreamLengthInSectors <= Partition.SizeInSectors)
                                        {
                                            i++;
                                            Busy.Message = "Flashing partition " + Partition.Name + " (" + i.ToString() + @"/" + PartitionCount.ToString() + ")";
                                            Phone.FlashRawPartition(DecompressedStream, Partition.Name, Updater);
                                        }
                                        DecompressedStream.Close();
                                    }
                                }
                            }
                            else
                            {
                                LogFile.Log("Flash failed! No valid partitions found in the archive.");
                                ExitFailure("Flash failed!", "No valid partitions found in the archive");
                                return;
                            }
                        }
                    }
                }
                catch (Exception Ex)
                {
                    LogFile.LogException(Ex);
                    if (Ex is WPinternalsException)
                    {
                        ExitFailure("Flash failed!", ((WPinternalsException)Ex).SubMessage);
                    }
                    else
                    {
                        ExitFailure("Flash failed!", null);
                    }
                    return;
                }

                ExitSuccess("Flash successful! Make sure you disable Windows Update on the phone!", null);
            }).Start();
        }