public bool ConnectToProgrammerInTestMode()
        {
            // Behaviour of old firehose:
            // Takes about 20 ms to be started.
            // Then PC has to start talking to the phone.
            // Behaviour of new firehose:
            // After 2000 ms the firehose starts talking to the PC
            //
            // For the duration of 2.5 seconds we will send Hello packages
            // And also wait for incoming messages
            // An incoming message can be a response to our outgoing Hello packet (read incoming until "response value")
            // Or it can be an incoming Hello-packet from the programmer (always 2 packets, starting with "Chip serial num")
            // Sending the hello-packet can succeed immediately, or it can timeout.
            // When sending succeeds, an answer should be incoming immediately to complete the handshake.
            // When an incoming Hello was received, the phone still expects to receive another Hello.

            byte[] HelloPacketFromPcToProgrammer = new byte[0x20C];
            ByteOperations.WriteUInt32(HelloPacketFromPcToProgrammer, 0, 0x57503730);
            ByteOperations.WriteUInt32(HelloPacketFromPcToProgrammer, 0x28, 0x57503730);
            ByteOperations.WriteUInt32(HelloPacketFromPcToProgrammer, 0x208, 0x57503730);
            ByteOperations.WriteUInt16(HelloPacketFromPcToProgrammer, 0x48, 0x4445);

            int    HelloSendCount     = 0;
            bool   HandshakeCompleted = false;
            string Incoming;

            do
            {
                Serial.SetTimeOut(200);
                HelloSendCount++;
                try
                {
                    LogFile.Log("Send Hello to programmer (" + HelloSendCount.ToString() + ")", LogType.FileOnly);
                    Serial.SendData(HelloPacketFromPcToProgrammer);
                    LogFile.Log("Hello packet accepted", LogType.FileOnly);
                }
                catch
                {
                    LogFile.Log("Hello packet not accepted", LogType.FileOnly);
                }

                try
                {
                    Serial.SetTimeOut(500);
                    Incoming = System.Text.Encoding.ASCII.GetString(Serial.GetResponse(null));
                    LogFile.Log("In: " + Incoming, LogType.FileOnly);
                    Serial.SetTimeOut(200);
                    if (Incoming.Contains("Chip serial num"))
                    {
                        Incoming = System.Text.Encoding.ASCII.GetString(Serial.GetResponse(null));
                        LogFile.Log("In: " + Incoming, LogType.FileOnly);
                        LogFile.Log("Incoming Hello-packets received", LogType.FileOnly);
                    }

                    while (Incoming.IndexOf("response value") < 0)
                    {
                        Incoming = System.Text.Encoding.ASCII.GetString(Serial.GetResponse(null));
                        LogFile.Log("In: " + Incoming, LogType.FileOnly);
                    }
                    ;

                    LogFile.Log("Incoming Hello-response received", LogType.FileOnly);
                    HandshakeCompleted = true;
                }
                catch { }
            }while (!HandshakeCompleted && (HelloSendCount < 6));

            if (HandshakeCompleted)
            {
                LogFile.Log("Handshake completed with programmer in testmode", LogType.FileOnly);
            }
            else
            {
                LogFile.Log("Handshake with programmer failed", LogType.FileOnly);
            }

            return(HandshakeCompleted);
        }
        public async Task <bool> SendEdPayload(string ProgrammerPath, string PayloadPath)
        {
            // First, let's read the Emergency Download payload header and verify its validity
            FileStream PayloadStream = File.OpenRead(PayloadPath);

            byte[] ValidReferencePayloadHeader = new byte[] { 0x45, 0x6D, 0x65, 0x72, 0x67, 0x65, 0x6E, 0x63, 0x79, 0x20, 0x50, 0x61, 0x79, 0x6C, 0x6F, 0x61, 0x64 };

            byte[] PayloadHeader = new byte[17];
            PayloadStream.Read(PayloadHeader, 0, 17);

            bool IsValidEdPayloadImage = StructuralComparisons.StructuralEqualityComparer.Equals(PayloadHeader, ValidReferencePayloadHeader);

            if (!IsValidEdPayloadImage)
            {
                return(false);
            }

            // Now let's read the information block
            PayloadStream.Seek(0x64, SeekOrigin.Begin);
            byte[] PayloadInformationBlock = new byte[0x64];

            PayloadStream.Read(PayloadInformationBlock, 0, 0x64);
            string buildtime = Encoding.ASCII.GetString(PayloadInformationBlock).Trim('\0');

            PayloadStream.Read(PayloadInformationBlock, 0, 0x64);
            string builddate = Encoding.ASCII.GetString(PayloadInformationBlock).Trim('\0');

            PayloadStream.Read(PayloadInformationBlock, 0, 0x64);
            string version = Encoding.ASCII.GetString(PayloadInformationBlock).Trim('\0');

            PayloadInformationBlock = new byte[0x670];
            PayloadStream.Read(PayloadInformationBlock, 0, 0x670);
            string Info = Encoding.ASCII.GetString(PayloadInformationBlock).Trim('\0');

            // Print some information about the payload
            LogFile.Log("Emerency flasher version 0.1", LogType.FileAndConsole);
            LogFile.Log("Programmer information:", LogType.FileAndConsole);
            LogFile.Log("Build time: " + buildtime, LogType.FileAndConsole);
            LogFile.Log("Build date: " + builddate, LogType.FileAndConsole);
            LogFile.Log("Version: " + version, LogType.FileAndConsole);
            LogFile.Log("Info: " + Info, LogType.FileAndConsole);

            // Send the emergency programmer to the phone
            bool SendImageResult = await Task.Run(() => SendImage(ProgrammerPath));

            if (!SendImageResult)
            {
                return(false);
            }

            // Start the emergency programmer on the phone
            await Task.Run(() => StartProgrammer());

            // Wait a few seconds before sending commands
            LogFile.Log("Waiting...", LogType.FileAndConsole);
            Thread.Sleep(2000);
            LogFile.Log("Waiting...OK", LogType.FileAndConsole);

            bool Terminated        = false;
            bool Connected         = false;
            bool ProgrammerRawMode = false;

            string Incoming;

            while (!Terminated)
            {
                PayloadInformationBlock = new byte[0x200];
                PayloadStream.Read(PayloadInformationBlock, 0, 0x200);
                string ProgrammerCommand = Encoding.ASCII.GetString(PayloadInformationBlock.Skip(0xC).ToArray()).Trim('\0');

                LogFile.Log(ProgrammerCommand, LogType.FileAndConsole);

                byte[] PacketFromPcToProgrammer = Array.Empty <byte>();
                byte[] temp = new byte[0x200];

                while (true)
                {
                    if (PayloadStream.Position == PayloadStream.Length)
                    {
                        Terminated = true;
                        break;
                    }

                    PayloadStream.Read(temp, 0, 0x200);

                    if (temp[12] == 77 && temp[13] == 83 && temp[14] == 71 && temp[15] == 95)
                    {
                        PayloadStream.Seek(-0x200, SeekOrigin.Current);
                        break;
                    }

                    PacketFromPcToProgrammer = PacketFromPcToProgrammer.Concat(temp).ToArray();
                }

                bool ExpectingReplyFromProgrammer = false;

                if (ProgrammerCommand.Contains("XML"))
                {
                    string Outgoing = Encoding.ASCII.GetString(PacketFromPcToProgrammer).Trim('\0');
                    PacketFromPcToProgrammer = Encoding.ASCII.GetBytes(Outgoing);
                    LogFile.Log("Out: " + Outgoing, LogType.FileAndConsole);
                }

                if (!ProgrammerCommand.Contains("RAW_DATA") && !ProgrammerRawMode)
                {
                    ExpectingReplyFromProgrammer = true;
                }

                if (ProgrammerCommand.Contains("LAST") && ProgrammerRawMode)
                {
                    ExpectingReplyFromProgrammer = true;
                }

                if (ProgrammerCommand.Contains("DATA_ALL") && ProgrammerRawMode)
                {
                    ExpectingReplyFromProgrammer = true;
                }

                if (ProgrammerCommand.Contains("RAW_DATA") && !ProgrammerRawMode)
                {
                    LogFile.Log("Phone is not in raw mode ON, leaving...", LogType.FileAndConsole);

                    // Workaround for problem
                    // SerialPort is sometimes not disposed correctly when the device is already removed.
                    // So explicitly dispose here
                    Serial.Close();

                    LogFile.Log("Phone has been emergency flashed unsuccessfully!", LogType.FileAndConsole);
                    PayloadStream.Dispose();

                    return(false);
                }

                if (!ProgrammerCommand.Contains("RAW_DATA") && ProgrammerRawMode)
                {
                    LogFile.Log("Phone is not in raw mode ON, leaving...", LogType.FileAndConsole);

                    // Workaround for problem
                    // SerialPort is sometimes not disposed correctly when the device is already removed.
                    // So explicitly dispose here
                    Serial.Close();

                    LogFile.Log("Phone has been emergency flashed unsuccessfully!", LogType.FileAndConsole);
                    PayloadStream.Dispose();

                    return(false);
                }

                if (Connected)
                {
                    Serial.SendData(PacketFromPcToProgrammer);
                }

                if (ExpectingReplyFromProgrammer)
                {
                    if (!Connected)
                    {
                        Connected = ConnectToProgrammer(PacketFromPcToProgrammer);

                        if (Connected)
                        {
                            LogFile.Log("Handshake completed with programmer in validated image programming (VIP) mode", LogType.FileAndConsole);
                        }
                        else
                        {
                            LogFile.Log("Handshake with programmer failed", LogType.FileAndConsole);
                        }

                        if (!Connected)
                        {
                            LogFile.Log("Phone programmer is now ignoring us, leaving...", LogType.FileAndConsole);

                            // Workaround for problem
                            // SerialPort is sometimes not disposed correctly when the device is already removed.
                            // So explicitly dispose here
                            Serial.Close();

                            LogFile.Log("Phone has been emergency flashed unsuccessfully!", LogType.FileAndConsole);
                            PayloadStream.Dispose();

                            return(false);
                        }
                    }
                    else
                    {
                        do
                        {
                            Serial.SetTimeOut(500);
                            Incoming = Encoding.ASCII.GetString(Serial.GetResponse(null));
                            Serial.SetTimeOut(200);
                            LogFile.Log("In: " + Incoming, LogType.FileAndConsole);
                        }while (Incoming.IndexOf("response value") < 0);

                        if (Incoming.Contains("rawmode=\"false\""))
                        {
                            ProgrammerRawMode = false;
                            LogFile.Log("Raw mode: OFF", LogType.FileAndConsole);
                        }

                        if (Incoming.Contains("rawmode=\"true\""))
                        {
                            ProgrammerRawMode = true;
                            LogFile.Log("Raw mode: ON", LogType.FileAndConsole);
                        }

                        if (!Incoming.Contains("ACK"))
                        {
                            LogFile.Log("Phone programmer is now ignoring us, leaving...", LogType.FileAndConsole);

                            // Workaround for problem
                            // SerialPort is sometimes not disposed correctly when the device is already removed.
                            // So explicitly dispose here
                            Serial.Close();

                            LogFile.Log("Phone has been emergency flashed unsuccessfully!", LogType.FileAndConsole);
                            PayloadStream.Dispose();

                            return(false);
                        }
                    }
                }
            }

            // Workaround for problem
            // SerialPort is sometimes not disposed correctly when the device is already removed.
            // So explicitly dispose here
            Serial.Close();

            LogFile.Log("Phone has been emergency flashed successfully!", LogType.FileAndConsole);
            PayloadStream.Dispose();

            return(true);
        }