#!/usr/bin/env python3
# -*- coding: utf-8 -*-
################################################################################################################################################
#
# GD-77 Firmware uploader. By Roger VK3KYY
# 
#
# This script has only been tested on Windows and Linux, it may or may not work on OSX
#
# On Windows,..
# the driver the system installs for the GD-77, which is the HID driver, needs to be replaced by the LibUSB-win32 using Zadig
# for USB device with idVendor=0x15a2, idProduct=0x0073
# Once this driver is installed the CPS and official firmware loader will no longer work as they can't find the device
# To use the CPS etc again, use the DeviceManager to uninstall the driver associated with idVendor=0x15a2, idProduct=0x0073 (this will appear as a libusb-win32 device)
# Then unplug the GD-77 and reconnect, and the HID driver will be re-installed
#
#
# On Linux, depending of you distro, you need to install a special udev rule to automatically unbind the USB HID device to usbhid driver.
#
#
# You also need python3-usb, enum34 and urllib3
#
################################################################################################################################################

######################### Error codes #########################
#
# -1:  Missing firmware file
# -2:  Wrong SGL file format
# -3:  Unencrypted firmware
# -4:  Firmware file is too large
# -5:  Unknown HT model type
# -6:  Command line parsing error
# -7:  Online download firmware location error
# -8:  Online download firmware binary error
# -9:  Online firmware download failure
# -10: Firmare/HT mismatch
# -99: Unsupported GD-77S (will be removed in the futur)
#
###############################################################

import usb
import getopt, sys
import ntpath
import os.path
from array import array
import enum
import urllib3
import re
import tempfile

class SGLFormatOutput(enum.Enum):
    GD_77 = 0
    GD_77S = 1
    DM_1801 = 2
    RD_5R = 3
    UNKNOWN = 4

    def __int__(self):
        return self.value


# Globals
responseOK = [0x41]
outputModes = ["GD-77", "GD-77S", "DM-1801", "RD-5R", "Unknown"]
outputFormat = SGLFormatOutput.GD_77
downloadedFW = ""

########################################################################
# Utilities to dump hex for testing
########################################################################
def hexdump(buf):
    cbuf = ""
    for b in buf:
        cbuf = cbuf + "0x%0.2X " % ord(b)
    return cbuf

def hexdumpArray(buf):
    cbuf = ""
    for b in buf:
        cbuf = cbuf + "0x%0.2X " % b
    return cbuf

def hexdumpArray2(buf):
    cbuf = ""
    for b in buf:
        cbuf = cbuf + "%0.2X-" % b
    return cbuf[:-1]

def strdumpArray(buf):
    cbuf = ""
    for b in buf:
        cbuf = cbuf + chr(b)
    return cbuf

def downloadFirmware(downloadStable):
    url = "https://github.com/rogerclarkmelbourne/OpenGD77/releases"
    urlBase = "http://github.com"
    httpPool = urllib3.PoolManager()
    pattern = ""
    fwVersion = "UNKNOWN"
    fwVersionPatternFormat = r'/{}([0-9\.]+)/'
    urlFW = ""
    webContent = ""
        
    print(" - " + "Try to download the firmware for your {} from the project page".format(outputModes[int(outputFormat)]))
    print(" - " + "Retrieve firmware location");

    try:
        response = httpPool.request('GET', url)
    except urllib3.URLError as e:
        print("".format(e.reason))
        sys.exit(-7)
        
    webContent = str(response.data)
    
    if (outputFormat == SGLFormatOutput.GD_77):
        patternFormat = r'/rogerclarkmelbourne/OpenGD77/releases/download/{}([0-9\.]+)/OpenGD77\.sgl'
    elif (outputFormat == SGLFormatOutput.GD_77S):
        patternFormat = r'/rogerclarkmelbourne/OpenGD77/releases/download/{}([0-9\.]+)/OpenGD77S\.sgl'
    elif (outputFormat == SGLFormatOutput.DM_1801):
        patternFormat = r'/rogerclarkmelbourne/OpenGD77/releases/download/{}([0-9\.]+)/OpenDM1801\.sgl'
    elif (outputFormat == SGLFormatOutput.RD_5R):
        patternFormat = r'/rogerclarkmelbourne/OpenGD77/releases/download/{}([0-9\.]+)/OpenDM5R\.sgl'

    pattern = patternFormat.format("R" if downloadStable == True else "D")
    fwVersionPattern = fwVersionPatternFormat.format("R" if downloadStable == True else "D") 
    contentArray = webContent.split("\n")    
    
    for l in contentArray:
        m = re.search(pattern, l)
        if (m != None):
            urlFW = urlBase + m.group(0)
            
            m = re.search(fwVersionPattern, urlFW)
            if (m != None):
                fwVersion = m.group(0).strip('/')
            
            break
    
    if (len(urlFW)):
        global downloadedFW
        downloadedFW = os.path.join(tempfile.gettempdir(), next(tempfile._get_candidate_names()) + '.sgl')
        
        print(" - " + "Downloading the firmware version {}, please wait".format(fwVersion));
        
        try:
            response = httpPool.request('GET', urlFW, preload_content=False)
        except urllib3.URLError as e:
            print("".format(e.reason))
            sys.exit(-8)

        length = response.getheader('content-length')
        
        if (length != None):
            length = int(length)
            blocksize = max(4096, (length//100))
        else:
            blocksize = 4096
        
        # Download data        
        with open(downloadedFW, "w+b") as f:
            while True:
                data = response.read(blocksize)
                
                if not data:
                    break
                
                f.write(data)
        f.close()
        return True
    
    return False
        
########################################################################
# Send the data packet to the GD-77 and return response
########################################################################
def sendAndGetResponse(dev, cmd):
    USB_WRITE_ENDPOINT  = 0x02
    USB_READ_ENDPOINT   = 0x81
    TRANSFER_LENGTH     = 38
    headerData = [0x0] * 4
        
    headerData[0] = 1
    headerData[1] = 0
    headerData[2] = ((len(cmd) >> 0) & 0xff)
    headerData[3] = ((len(cmd) >> 8) & 0xff)

    cmd = headerData + cmd
    
    #print("TX: " + hexdumpArray2(cmd))
    #print("TX: '{}'".format(strdumpArray(cmd[4:])))

    ret = dev.write(USB_WRITE_ENDPOINT, cmd)
    ret = dev.read(USB_READ_ENDPOINT, TRANSFER_LENGTH + 4, 5000)
    
    #print("RX: " + hexdumpArray2(ret[4:]))
    #print("RX: '{}'".format(strdumpArray(ret[4:])))

    return ret[4:]

########################################################################
# Send the data packet to the GD-77 and confirm the response is correct
########################################################################
def sendAndCheckResponse(dev, cmd, resp):
    USB_WRITE_ENDPOINT  = 0x02
    USB_READ_ENDPOINT   = 0x81
    TRANSFER_LENGTH     = 38
    zeroPad = [0x0] * TRANSFER_LENGTH
    headerData = [0x0] * 4
        
    headerData[0] = 1
    headerData[1] = 0
    headerData[2] = ((len(cmd) >> 0) & 0xff)
    headerData[3] = ((len(cmd) >> 8) & 0xff)

    if (len(resp) < TRANSFER_LENGTH):
        resp = resp + zeroPad[0:TRANSFER_LENGTH - len(resp)]

    cmd = headerData + cmd
    
    #print("TX: " + hexdumpArray2(cmd))
    #print("TX: '{}'".format(strdumpArray(cmd[4:])))

    ret = dev.write(USB_WRITE_ENDPOINT, cmd)
    ret = dev.read(USB_READ_ENDPOINT, TRANSFER_LENGTH + 4, 5000)
    expected = array("B", resp)
    
    #print("RX: " + hexdumpArray2(ret[4:]))
    #print("RX: '{}'".format(strdumpArray(ret[4:])))

    if (expected == ret[4:]):
        return True
    else:
        print("Error. Read returned: " + str(ret))
        return False

 
##############################
# Create checksum data packet
##############################
def createChecksumData(buf, startAddress, endAddress):
    #checksum data starts with a small header, followed by the 32 bit checksum value, least significant byte first
    checkSumData = [ 0x45, 0x4e, 0x44, 0xff, 0xDE, 0xAD, 0xBE, 0xEF ]
    cs = 0
    
    for i in range(startAddress, endAddress):
        cs = cs + buf[i]
     
    checkSumData[4] = (cs % 256) & 0xff
    checkSumData[5] = ((cs >> 8) % 256) & 0xff
    checkSumData[6] = ((cs >> 16) % 256) & 0xff
    checkSumData[7] = ((cs >> 24) % 256) & 0xff
    return checkSumData


def updateBlockAddressAndLength(buf, address, length):
    buf[5] = ((length) % 256) & 0xff
    buf[4] = ((length >> 8) % 256) & 0xff
    buf[3] = ((address) % 256) & 0xff
    buf[2] = ((address >> 8) % 256) & 0xff
    buf[1] = ((address >> 16) % 256) & 0xff
    buf[0] = ((address >> 24) % 256) & 0xff
    return buf


#####################################################
# Open firmware file on disk and sent it to the GD-77
###########################################b##########
def sendFileData(fileBuf, dev):
    dataHeader = [0x00] * (0x20 + 0x06)
    BLOCK_LENGTH = 1024 #1k
    DATA_TRANSFER_SIZE = 0x20
    checksumStartAddress = 0
    address = 0
         
    fileLength = len(fileBuf)
    totalBlocks = (fileLength // BLOCK_LENGTH) + 1

    while address < fileLength:
        if ((address % BLOCK_LENGTH) == 0):
            checksumStartAddress = address
            
        dataHeader = updateBlockAddressAndLength(dataHeader, address, DATA_TRANSFER_SIZE)
        
        if ((address + DATA_TRANSFER_SIZE) < fileLength):
            
            for i in range(DATA_TRANSFER_SIZE):
                dataHeader[6 + i] = fileBuf[address + i]
 
            if  (sendAndCheckResponse(dev, dataHeader, responseOK) == False):
                print("Error sending data")
                return False
                break
            
            address = address + DATA_TRANSFER_SIZE
            
            if ((address % 0x400) == 0):
                print("\r - Sent block " + str(address // BLOCK_LENGTH) + " of "+ str(totalBlocks), end='')
                sys.stdout.flush()

                if (sendAndCheckResponse(dev, createChecksumData(fileBuf, checksumStartAddress, address), responseOK) == False):
                    print("Error sending checksum")
                    return False
                    break
                
        else:
            print("\r - Sending last block                   ", end='')
            sys.stdout.flush()
            
            DATA_TRANSFER_SIZE = fileLength - address

            dataHeader = updateBlockAddressAndLength(dataHeader, address, DATA_TRANSFER_SIZE)
            
            for i in range(DATA_TRANSFER_SIZE):
                dataHeader[6 + i] = fileBuf[address + i]
            
            if (sendAndCheckResponse(dev, dataHeader, responseOK) == False):
                print("Error sending data")
                return False
                break
            
            address = address + DATA_TRANSFER_SIZE

            if (sendAndCheckResponse(dev, createChecksumData(fileBuf, checksumStartAddress, address), responseOK) == False):
                print("Error sending checksum")
                return False
                break

            print("")
    return True

#####################################################
# Probe connected model
###########################################b##########
def probeModel(dev):
    commandLetterA = [ 0x41 ] # 'A'
    command0       = [[ 0x44, 0x4f, 0x57, 0x4e, 0x4c, 0x4f, 0x41, 0x44 ], [ 0x23, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x3f ]] # 'DOWNLOAD'
    command1       = [ commandLetterA, responseOK ] 
    commandDummy   = [ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ]
    #commandMOD     = [ 0x46, 0x2D, 0x4D, 0x4F, 0x44, 0xff, 0xff, 0xff ] # F-MOD...
    #commandEND     = [ 0x45, 0x4E, 0x44, 0xFF ] # END.
    commandID      = [ command0, command1 ]
    models         = [[ 'DV01', SGLFormatOutput.GD_77 ], [ 'DV02', SGLFormatOutput.GD_77S ], [ 'DV03', SGLFormatOutput.DM_1801 ]]
    # RD-5R also have "DV02" id

    commandNumber = 0
    while commandNumber < len(commandID):
        if sendAndCheckResponse(dev, commandID[commandNumber][0], commandID[commandNumber][1]) == False:
            return SGLFormatOutput.UNKNOWN
        commandNumber = commandNumber + 1

    resp = sendAndGetResponse(dev, commandDummy)
    ##dummy = sendAndGetResponse(dev, command0[0])

    for x in models:
        if (x[0] == str(resp[:4].tobytes().decode("ascii"))):
            return x[1]

    return SGLFormatOutput.UNKNOWN

###########################################################################################################################################
# Send commands to the GD-77 to verify we are the updater, prepare to program including erasing the internal program flash memory
###########################################################################################################################################
def sendInitialCommands(dev, encodeKey):
    commandLetterA      =[ 0x41] #A
    command0            =[[0x44,0x4f,0x57,0x4e,0x4c,0x4f,0x41,0x44],[0x23,0x55,0x50,0x44,0x41,0x54,0x45,0x3f]] # DOWNLOAD
    command1            =[commandLetterA,responseOK] 
    command3            =[[0x46, 0x2d, 0x50, 0x52, 0x4f, 0x47, 0xff, 0xff],responseOK] #... F-PROG..

    if (outputFormat == SGLFormatOutput.GD_77):
        command2            =[[0x44, 0x56, 0x30, 0x31, (0x61 + 0x00), (0x61 + 0x0C), (0x61 + 0x0D), (0x61 + 0x01)],[0x44, 0x56, 0x30, 0x31]] #.... DV01enhi (DV01enhi comes from deobfuscated sgl file)
        command4            =[[0x53, 0x47, 0x2d, 0x4d, 0x44, 0x2d, 0x37, 0x36, 0x30, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],responseOK] #SG-MD-760
        command5            =[[0x4d, 0x44, 0x2d, 0x37, 0x36, 0x30, 0xff, 0xff],responseOK] #MD-760..
    elif (outputFormat == SGLFormatOutput.GD_77S):
        command2            =[[0x44, 0x56, 0x30, 0x32, 0x6D, 0x40, 0x7D, 0x63],[0x44, 0x56, 0x30, 0x32]] #.... DV02Gpmj (thanks Wireshark)
        command4            =[[0x53, 0x47, 0x2d, 0x4d, 0x44, 0x2d, 0x37, 0x33, 0x30, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],responseOK] #SG-MD-730
        command5            =[[0x4d, 0x44, 0x2d, 0x37, 0x33, 0x30, 0xff, 0xff],responseOK] #MD-730..
    elif (outputFormat == SGLFormatOutput.DM_1801):
        command2            =[[0x44, 0x56, 0x30, 0x33, 0x74, 0x21, 0x44, 0x39],[0x44, 0x56, 0x30, 0x33]] #.... last 4 bytes of the command are the offset encoded as letters a - p (hard coded fr
        command4            =[[0x42, 0x46, 0x2d, 0x44, 0x4d, 0x52, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],responseOK] #BF-DMR
        command5            =[[0x31, 0x38, 0x30, 0x31, 0xff, 0xff, 0xff, 0xff],responseOK] # MD-1801
    elif (outputFormat == SGLFormatOutput.RD_5R):
        command2            =[[0x44, 0x56, 0x30, 0x32, 0x53, 0x36, 0x37, 0x62],[0x44, 0x56, 0x30, 0x32]] #.... last 4 bytes of the command are the offset encoded as letters a - p (hard coded fr
        command4            =[[0x42, 0x46, 0x2D, 0x35, 0x52, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],responseOK] #BF-5R
        command5            =[[0x42, 0x46, 0x2D, 0x35, 0x52, 0xff, 0xff, 0xff],responseOK] # BF-5R

    command6            =[[0x56, 0x31, 0x2e, 0x30, 0x30, 0x2e, 0x30, 0x31],responseOK] #V1.00.01
    commandErase        =[[0x46, 0x2d, 0x45, 0x52, 0x41, 0x53, 0x45, 0xff],responseOK] #F-ERASE
    commandPostErase    =[commandLetterA,responseOK] 
    commandProgram      =[[0x50, 0x52, 0x4f, 0x47, 0x52, 0x41, 0x4d, 0xf],responseOK] #PROGRAM
    commands            =[command0,command1,command2,command3,command4,command5,command6,commandErase,commandPostErase,commandProgram]
    commandNames        =["Sending Download command", "Sending ACK", "Sending encryption key", "Sending F-PROG command", "Sending radio modem number", "Sending radio modem number 2", "Sending version", "Sending erase command", "Send post erase command", "Sending Program command"]
    commandNumber = 0
    
    # Buffer.BlockCopy(encodeKey, 0, command2[0], 4, 4);
    command2[0] = command2[0][0:4] + encodeKey
    
    # Send the commands which the GD-77 expects before the start of the data
    while commandNumber < len(commands):
        print(" - " + commandNames[commandNumber])

        if sendAndCheckResponse(dev, commands[commandNumber][0], commands[commandNumber][1]) == False:
            print("Error sending command")
            return False
            break
        commandNumber = commandNumber + 1
    return True

###########################################################################################################################################
#
###########################################################################################################################################
def checkForSGLAndReturnEncryptedData(fileBuf):
    header_tag = list("SGL!")
    headerModel = []
    
    buf_in_4 = list("".join(map(chr, fileBuf[0:4])))
    headerModel.append(fileBuf[11])

    if buf_in_4 == header_tag:
        # read and decode offset and xor tag
        buf_in_4 = list(fileBuf[0x000C : 0x000C + 4])
        
        for i in range(0, 4):
            buf_in_4[i] = buf_in_4[i] ^ ord(header_tag[i])
        
        offset = buf_in_4[0] + 256 * buf_in_4[1]
        xor_data = [ buf_in_4[2], buf_in_4[3] ]
        
	    # read and decode part of the header
        buf_in_512 = list(fileBuf[offset + 0x0006 : offset + 0x0006 + 512])
    
        xor_idx = 0;
        for i in range(0, 512):
            buf_in_512[i] = buf_in_512[i] ^ xor_data[xor_idx]
            
            xor_idx += 1
            if xor_idx == 2:
                xor_idx = 0

        #
        encodeKey = buf_in_512[0x005D : 0x005D + 4]

        # extract length
        length1 = buf_in_512[0x0000]
        length2 = buf_in_512[0x0001]
        length3 = buf_in_512[0x0002]
        length4 = buf_in_512[0x0003]
        length = (length4 << 24) + (length3 << 16) + (length2 << 8) + length1

        # extract encoded raw firmware
        retBuf = [0x00] * length;
        retBuf = fileBuf[len(fileBuf) - length : len(fileBuf) - length + len(retBuf) ]

        return retBuf, encodeKey, headerModel
    
    print("ERROR: SGL! header is missing.")
    return None, None, None

###########################################################################################################################################
#
###########################################################################################################################################
def usage():
    print("Usage:")
    print("       " + ntpath.basename(sys.argv[0]) + " [OPTION]")
    print("")
    print("    -h, --help                     : Display this help text")
    print("    -f, --firmware=<filename.sgl>  : Flash <filename.sgl> instead of default file \"firmware.sgl\"")
    print("    -m, --model=<type>             : Select transceiver model. Models are: {}".format(", ".join(str(x) for x in outputModes[:-1])) + ".")
    print("    -d, --download                 : Download firmware from the project website")
    print("    -S, --stable                   : Select the stable version while downloading from the project page")
    print("    -U, --unstable                 : Select the development version while downloading from the project page")
    print("")

#####################################################
# Main function.
#####################################################
def main():
    global outputFormat
    sglFile = "firmware.sgl"
    downloadStable = True
    doDownload = False
    doForce = False

    # Command line argument parsing
    try:                                
        opts, args = getopt.getopt(sys.argv[1:], "hf:m:dSUF", ["help", "firmware=", "model=", "download", "stable", "unstable", "force"])
    except getopt.GetoptError as err:
        print(str(err))
        usage()
        sys.exit(-6)

    for opt, arg in opts:
        if opt in ("-h", "--help"):
            usage()
            sys.exit(0)
        elif opt in ("-f", "--firmware"):
            sglFile = arg
        elif opt in ("-m", "--model"):
            try:
                index = outputModes.index(arg)
            except ValueError as e:
                print("Model \"{}\" is unknown".format(arg))
                sys.exit(-5)

            outputFormat = SGLFormatOutput(index)
            
            if (outputFormat == SGLFormatOutput.UNKNOWN):
                print("Unsupported model")
                sys.exit(-5)
                
        elif opt in ["-d", "--download"]:
            doDownload = True
        elif opt in ["-S", "--stable"]:
            downloadStable = True
        elif opt in ["-U", "--unstable"]:
            downloadStable = False
        elif opt in ["-F", "--force"]:
            doForce = True
        else:
            assert False, "Unhandled option"

    # Try to connect USB device
    dev = usb.core.find(idVendor=0x15a2, idProduct=0x0073)
    if (dev):
        # Needed on Linux
        try:
            if dev.is_kernel_driver_active(0):
                dev.detach_kernel_driver(0)
        except NotImplementedError as e:
            pass
        
        #seems to be needed for the usb to work !
        dev.set_configuration()

        if (outputFormat == SGLFormatOutput.UNKNOWN):
            outputFormat = probeModel(dev)
            if (outputFormat == SGLFormatOutput.UNKNOWN):
                print("Error. Failed to detect you transceiver model.")
                sys.exit(-5)
            print(" - Detected model: {}".format(outputModes[int(outputFormat)]))
        
        # Try to download the firmware
        if (doDownload == True):
            if (downloadFirmware(downloadStable) == True):
                sglFile = downloadedFW
            else:
                print("Firmware download failed")
                sys.exit(-9)

        if (os.path.isfile(sglFile) == False):
            print("Firmware file \"" + sglFile + "\" is missing.")
            sys.exit(-1)

        print(" - " + "Now flashing your {} with \"{}\"".format(outputModes[int(outputFormat)], sglFile))
        
        with open(sglFile, 'rb') as f:
            fileBuf = f.read()
            
        # Check firmware        
        filename, file_extension = os.path.splitext(sglFile)

        # Define encodeKey according to HT model
        if (outputFormat == SGLFormatOutput.GD_77):
            encodeKey = [ (0x61 + 0x00), (0x61 + 0x0C), (0x61 + 0x0D), (0x61 + 0x01) ]
        elif (outputFormat == SGLFormatOutput.GD_77S):
            encodeKey = [ (0x6D), (0x40), (0x7D), (0x63) ] ## Original header (smaller filelength): was (0x47), (0x70), (0x6d), (0x4a)
        elif (outputFormat == SGLFormatOutput.DM_1801):
            encodeKey = [ (0x74), (0x21), (0x44), (0x39) ]
        elif (outputFormat == SGLFormatOutput.RD_5R):
            encodeKey = [ (0x53), (0x36), (0x37), (0x62) ]

        if (file_extension == ".sgl"):
            firmwareModelTag = { SGLFormatOutput.GD_77: 0x1B , SGLFormatOutput.GD_77S: 0x70, SGLFormatOutput.DM_1801: 0x4F, SGLFormatOutput.RD_5R: 0x5C}
            
            ## Could be a SGL file !
            fileBuf, encodeKey, headerModel = checkForSGLAndReturnEncryptedData(fileBuf)

            if (fileBuf == None):
                print("Error. Missing SGL in .sgl file header")
                sys.exit(-2)
                
            print(" - " + "Firmware file confirmed as SGL")

            if (doForce == False):
                # Check if the firmware matches the transceiver model
                if (headerModel[0] != firmwareModelTag[outputFormat]):
                    print("Error. The firmware doesn't match the transceiver model.")
                    sys.exit(-10)
                    
        else:
            print("Firmware file is an unencrypted binary. Exiting")
            sys.exit(-3)

        if len(fileBuf) > 0x7b000:
            print("Error. Firmware file too large.")
            sys.exit(-4)

        if (sendInitialCommands(dev, encodeKey) == True):
            if (sendFileData(fileBuf, dev) == True):
                print("Firmware update complete. Please reboot the {}.".format(outputModes[int(outputFormat)]))
            else:
                print("Error while sending data")
        else:
            print("Error while sending initial commands")
       
        usb.util.dispose_resources(dev) #free up the USB device
        
    else:
        print("Error. Can't find your transceiver.")
        
    # Remove downloaded firmware, if any
    if (len(downloadedFW)):
        if (os.path.isfile(downloadedFW)):
            os.remove(downloadedFW)


## Run the program
main()
sys.exit(0)