#!/usr/bin/env python3 # -*- coding: utf-8 -*- # This python script was built hacking 'pydfu.py', available at https://github.com/dhylands/stm32-test: # # This file is part of the OpenMV project. # Copyright (c) 2013/2014 Ibrahim Abdelkader # This work is licensed under the MIT license, see the file LICENSE for # details. # # # And python code available on the Travis Goodspeed's md380tools, available at https://github.com/travisgoodspeed/md380tools.git # # Copyright 2010, 2011 Michael Ossmann # Copyright 2015 Travis Goodspeed # # # Adapted & hacked for the OpenGD77 (for STM32) project by: # # Copyright (C) 2023 Daniel Caujolle-Bert, F1RMB # Roger Clark, VK3KYY / G4KYF # ################################################################################################################################# # ######################### Error codes ######################### # 0: No error # -1: Missing firmware file # -2: Wrong SGL file format # -3: Unsupported Firmware file format # -4: Firmware file is too large # -5: Unknown device model type # -6: Unable to connect the device # -7: Command line parsing error # -8: Unable to patch the firmware ############################################################### from __future__ import print_function import argparse import collections import inspect import re import struct import sys import usb.core import usb.util import zlib import os.path from pathlib import Path import configparser import hashlib from typing import Final import enum import time # USB request __TIMEOUT __TIMEOUT = 4000 # DFU commands __DFU_DETACH = 0 __DFU_DNLOAD = 1 __DFU_UPLOAD = 2 __DFU_GETSTATUS = 3 __DFU_CLRSTATUS = 4 __DFU_GETSTATE = 5 __DFU_ABORT = 6 # DFU status __DFU_STATE_APP_IDLE = 0x00 __DFU_STATE_APP_DETACH = 0x01 __DFU_STATE_DFU_IDLE = 0x02 __DFU_STATE_DFU_DOWNLOAD_SYNC = 0x03 __DFU_STATE_DFU_DOWNLOAD_BUSY = 0x04 __DFU_STATE_DFU_DOWNLOAD_IDLE = 0x05 __DFU_STATE_DFU_MANIFEST_SYNC = 0x06 __DFU_STATE_DFU_MANIFEST = 0x07 __DFU_STATE_DFU_MANIFEST_WAIT_RESET = 0x08 __DFU_STATE_DFU_UPLOAD_IDLE = 0x09 __DFU_STATE_DFU_ERROR = 0x0A _DFU_DESCRIPTOR_TYPE = 0x21 __DFU_STATUS_STR = { __DFU_STATE_APP_IDLE: "STATE_APP_IDLE", __DFU_STATE_APP_DETACH: "STATE_APP_DETACH", __DFU_STATE_DFU_IDLE: "STATE_DFU_IDLE", __DFU_STATE_DFU_DOWNLOAD_SYNC: "STATE_DFU_DOWNLOAD_SYNC", __DFU_STATE_DFU_DOWNLOAD_BUSY: "STATE_DFU_DOWNLOAD_BUSY", __DFU_STATE_DFU_DOWNLOAD_IDLE: "STATE_DFU_DOWNLOAD_IDLE", __DFU_STATE_DFU_MANIFEST_SYNC: "STATE_DFU_MANIFEST_SYNC", __DFU_STATE_DFU_MANIFEST: "STATE_DFU_MANIFEST", __DFU_STATE_DFU_MANIFEST_WAIT_RESET: "STATE_DFU_MANIFEST_WAIT_RESET", __DFU_STATE_DFU_UPLOAD_IDLE: "STATE_DFU_UPLOAD_IDLE", __DFU_STATE_DFU_ERROR: "STATE_DFU_ERROR", } # USB device handle __dev = None # Configuration descriptor of the device __cfg_descr = None __verbose = None # USB DFU interface __DFU_INTERFACE = 0 defaultVID = 0x0483 defaultPID = 0xDF11 version:Final = "1.0.0" class FWPlatformOutput(enum.Enum): MD_9600 = 0 MD_UV380 = 1 DM_1701 = 2 MD_2017 = 3 MD_380 = 4 UNKNOWN = 5 def __int__(self): return self.value config = [] FWOutputPlatforms = ["MD-9600", "MD-UV380", "DM-1701", "MD-2017", "MD-380", "Unknown"] FWPlatformFormat = FWPlatformOutput.UNKNOWN FW2645_SHA256_Checksum: Final = "d8a653307222e576ee416ab6d4704d14758fa71c1b0e826ffd840f26266cf11f" BLOCK_WRITE_SIZE:Final = 1024 MD9600_ENCODE_CIPHER:Final = [ 0xA2, 0xFA, 0xBB, 0x4B, 0x90, 0x8F, 0x17, 0x20, 0x96, 0x36, 0x43, 0x84, 0xF7, 0xAC, 0x4E, 0x55, 0xEA, 0xE5, 0xB4, 0x36, 0x55, 0xB9, 0x39, 0xE2, 0xD8, 0xDA, 0x18, 0xC0, 0x0D, 0x09, 0x5D, 0xB8, 0x0E, 0x89, 0x90, 0x46, 0x38, 0xD4, 0x93, 0xCC, 0x2F, 0x8E, 0xCD, 0x2D, 0x22, 0xB7, 0x89, 0x97, 0x51, 0x24, 0x98, 0xA0, 0xCC, 0x30, 0x3E, 0x95, 0x7D, 0xAF, 0x4C, 0x0E, 0x68, 0x23, 0x89, 0xC6, 0x32, 0x33, 0x56, 0xAA, 0xE0, 0x58, 0x92, 0x30, 0xE2, 0xDA, 0xBC, 0xEA, 0x50, 0xFB, 0x57, 0x5B, 0x73, 0x71, 0x93, 0x09, 0x87, 0x1A, 0x29, 0xD3, 0xBF, 0xEC, 0x87, 0x85, 0x8A, 0x2B, 0x2D, 0xAA, 0x15, 0xDE, 0x57, 0xA2, 0x11, 0x83, 0xDC, 0xF4, 0xB6, 0x02, 0x56, 0xE5, 0x08, 0xE0, 0x83, 0x49, 0x59, 0xB5, 0xEB, 0x99, 0x0F, 0xE0, 0xC3, 0x46, 0xA7, 0x79, 0x12, 0x4D, 0xFA, 0x87, 0x12, 0x0C, 0xBF, 0x73, 0xD9, 0x53, 0x52, 0xBD, 0x38, 0xBF, 0xB4, 0xEE, 0xE4, 0x43, 0xD2, 0xCE, 0xD3, 0x08, 0x0A, 0xD6, 0xE9, 0x77, 0xEB, 0xE8, 0xD4, 0x94, 0x3C, 0x3E, 0x35, 0x8D, 0x40, 0xA1, 0x00, 0x92, 0x39, 0xDB, 0x25, 0xE8, 0x2B, 0x6E, 0x70, 0x39, 0xE2, 0x86, 0xAD, 0x2F, 0x36, 0x2D, 0x11, 0x41, 0x8E, 0xBE, 0xD5, 0xCC, 0xA3, 0x9C, 0x24, 0x65, 0x87, 0x23, 0x37, 0x6E, 0xE5, 0xDF, 0xBF, 0xE7, 0x8A, 0xFC, 0x83, 0x87, 0x24, 0xFE, 0x4A, 0x0B, 0x4A, 0xB3, 0xFB, 0xCF, 0xBD, 0x65, 0x03, 0x9B, 0xEE, 0x53, 0xF7, 0xBF, 0xC0, 0x63, 0x7A, 0x62, 0x8E, 0x11, 0x62, 0x17, 0x70, 0xAB, 0x16, 0xB1, 0xBA, 0xC0, 0x3A, 0x59, 0xC6, 0xD6, 0x8F, 0xDD, 0xF4, 0x5B, 0x14, 0x4B, 0xEE, 0xDE, 0x72, 0xBF, 0x31, 0x7F, 0x96, 0x79, 0xC9, 0xA4, 0xA0, 0x32, 0x5B, 0xEE, 0xFC, 0xB0, 0x69, 0x6C, 0xCE, 0x99, 0xD2, 0x0E, 0x94, 0x85, 0x98, 0x5C, 0x07, 0x56, 0xE6, 0x67, 0x41, 0xCC, 0x52, 0x00, 0x25, 0x54, 0x5F, 0x29, 0xFC, 0x21, 0x46, 0xC9, 0x5C, 0x7E, 0xF6, 0xA4, 0x4E, 0x63, 0x59, 0x89, 0xAF, 0x46, 0xD9, 0xCD, 0xD7, 0x33, 0x23, 0xF9, 0x79, 0x1F, 0x2A, 0xC0, 0xCA, 0x7A, 0x6F, 0x34, 0xE6, 0x03, 0x81, 0x39, 0x6F, 0xE0, 0xBF, 0x39, 0x77, 0xEE, 0x65, 0x19, 0xA0, 0x56, 0xC7, 0x6C, 0x81, 0x61, 0xD7, 0xE7, 0x4C, 0x8D, 0xED, 0x15, 0xAE, 0xE0, 0xC8, 0x4C, 0xF7, 0x7C, 0xD0, 0xE0, 0x7B, 0x74, 0x9D, 0x96, 0x38, 0xDE, 0xBD, 0x5C, 0xB9, 0x29, 0xB2, 0x37, 0x3A, 0xB1, 0x3B, 0x7C, 0x0C, 0x91, 0xD5, 0x43, 0x3B, 0xB8, 0x80, 0x19, 0x6F, 0x40, 0xC6, 0xF5, 0x10, 0xFB, 0xFA, 0x6E, 0xAD, 0x4E, 0xBE, 0x2A, 0x9F, 0x42, 0xC7, 0x9A, 0xE9, 0xD8, 0xE5, 0xE4, 0x63, 0x9D, 0x3D, 0x21, 0x18, 0x7F, 0xD9, 0xC9, 0xEC, 0xDF, 0x64, 0x6B, 0x82, 0xE7, 0x2E, 0xA2, 0x5C, 0x1E, 0x77, 0x44, 0x44, 0x39, 0xE9, 0xDC, 0xEB, 0x35, 0x66, 0x5B, 0xD1, 0xA2, 0x04, 0x0A, 0x64, 0x42, 0x56, 0xC3, 0x6C, 0xD2, 0xEE, 0x61, 0xA6, 0x28, 0x1F, 0x75, 0xAF, 0x7E, 0x08, 0x3B, 0x24, 0x0E, 0xCD, 0xCC, 0x08, 0xDF, 0x28, 0x94, 0x66, 0xDE, 0x21, 0x07, 0x37, 0x30, 0x19, 0x90, 0x85, 0xC7, 0x0D, 0xCA, 0xD1, 0x33, 0x19, 0xF3, 0xB3, 0xBB, 0x3B, 0x9E, 0xC0, 0xAD, 0x5A, 0xA7, 0xB0, 0xF2, 0x87, 0x6C, 0xC1, 0xE5, 0x82, 0x3A, 0x56, 0x66, 0x80, 0x06, 0xE4, 0x29, 0x2B, 0x5E, 0x0E, 0x54, 0xEB, 0x9F, 0x0F, 0x4A, 0x64, 0x67, 0x59, 0xC1, 0x40, 0x4D, 0x7B, 0x1B, 0x2E, 0xD0, 0x48, 0xF3, 0x2A, 0x8E, 0x36, 0xF6, 0x00, 0xB7, 0x04, 0xF4, 0x0B, 0xC0, 0xA0, 0x36, 0x43, 0x5C, 0x47, 0x13, 0x77, 0xA8, 0xEE, 0xBE, 0xD6, 0xA5, 0xE1, 0x62, 0xB4, 0xEC, 0xAA, 0x71, 0x8B, 0x9D, 0x34, 0x39, 0x40, 0x99, 0x30, 0xB8, 0xA8, 0xF1, 0xB8, 0xB1, 0x4B, 0x9E, 0x32, 0xFF, 0x68, 0x72, 0x78, 0x2A, 0x39, 0x4E, 0x36, 0x38, 0x77, 0x96, 0x93, 0xC5, 0x21, 0xE2, 0x13, 0x56, 0x7A, 0xF6, 0xBB, 0xEB, 0x51, 0xF5, 0x77, 0xD3, 0x84, 0xD1, 0xBA, 0xC4, 0xC7, 0x06, 0x64, 0x2B, 0xA2, 0x88, 0xE8, 0xC1, 0xB9, 0xF9, 0xAE, 0x5F, 0x50, 0x20, 0xB6, 0x13, 0x0E, 0x97, 0x7F, 0x73, 0x01, 0xC3, 0x27, 0x31, 0xE3, 0x09, 0xD3, 0xF0, 0x9C, 0x3F, 0x51, 0x56, 0x07, 0x61, 0xFC, 0x63, 0xF9, 0x86, 0xE0, 0x01, 0x80, 0x12, 0x1F, 0xDC, 0x68, 0x2C, 0x94, 0x73, 0x04, 0x73, 0xB5, 0x70, 0x2B, 0xEC, 0xBE, 0x34, 0x80, 0x3F, 0x0C, 0xB7, 0xF6, 0x24, 0xC6, 0x8F, 0x94, 0x18, 0xC3, 0x4E, 0x76, 0x54, 0xA8, 0x11, 0x15, 0xFF, 0x51, 0x56, 0xC8, 0xA3, 0x73, 0x0E, 0x8A, 0xDE, 0x7F, 0xF4, 0xFD, 0x5A, 0xC9, 0x1C, 0xAF, 0xFE, 0xE9, 0xCF, 0x9C, 0x66, 0x61, 0x96, 0xF5, 0x91, 0x81, 0x95, 0x20, 0xDA, 0x88, 0x1A, 0x00, 0x2A, 0x0C, 0x76, 0x76, 0x6B, 0x9C, 0x0C, 0x28, 0x40, 0xA3, 0xA7, 0x81, 0xF3, 0x8F, 0x11, 0xF9, 0xAF, 0x33, 0xE1, 0x96, 0xEF, 0x6A, 0x94, 0xB2, 0x36, 0xFE, 0xDF, 0x00, 0x01, 0xC8, 0x44, 0xCA, 0xF9, 0x18, 0xE4, 0x7C, 0x6E, 0x57, 0x94, 0x66, 0x01, 0xEA, 0x32, 0xBE, 0xA0, 0x5A, 0x3A, 0xE4, 0xB8, 0xB2, 0x94, 0xEA, 0xA5, 0x29, 0xB0, 0x54, 0x6E, 0x01, 0xD5, 0x1C, 0xAF, 0xAF, 0xB6, 0xFA, 0xD6, 0x3C, 0x47, 0xE2, 0x92, 0xEB, 0xCE, 0xCD, 0x89, 0x1C, 0x3D, 0xBC, 0x4A, 0x70, 0xBF, 0xFA, 0x82, 0x2E, 0x91, 0xA2, 0x72, 0xE6, 0x13, 0x62, 0xA0, 0x54, 0x1F, 0x7E, 0xCD, 0x86, 0x99, 0x18, 0x28, 0x41, 0x47, 0xAE, 0xC1, 0xA2, 0xE3, 0xE4, 0x40, 0x01, 0x6F, 0x84, 0xD7, 0x1A, 0xC9, 0xC3, 0x75, 0x6F, 0x7F, 0xC6, 0x3D, 0xE8, 0xE4, 0x64, 0x36, 0xBD, 0x64, 0x2E, 0x44, 0x95, 0x14, 0xAC, 0x57, 0xF0, 0x8D, 0xEA, 0xE2, 0xC2, 0xFB, 0x33, 0x8F, 0x60, 0x71, 0x1D, 0x31, 0xA0, 0x80, 0xC6, 0xF9, 0x3C, 0x07, 0x5C, 0xEE, 0x78, 0x4C, 0xE3, 0x97, 0x05, 0x4C, 0x32, 0xFA, 0x24, 0x50, 0x3F, 0xCB, 0x0F, 0xC1, 0x9D, 0xDD, 0x94, 0x3D, 0x43, 0xDC, 0x03, 0xEA, 0x8F, 0x3E, 0x4A, 0x0B, 0x8B, 0x77, 0x5F, 0xD1, 0x6E, 0x6C, 0xDE, 0x73, 0x66, 0x2B, 0xF4, 0x81, 0x94, 0xD9, 0x7B, 0x75, 0x58, 0xEB, 0x66, 0x8B, 0xD0, 0x9A, 0x60, 0xD2, 0x9B, 0x90, 0xB0, 0x83, 0xE3, 0xE8, 0x60, 0x92, 0x9A, 0x55, 0x9E, 0x84, 0x03, 0xA1, 0x62, 0x80, 0x75, 0x5A, 0x51, 0xA8, 0x5C, 0xC8, 0xE2, 0xAA, 0x80, 0x21, 0xBF, 0x91, 0x8A, 0x00, 0x6E, 0xE2, 0xC4, 0x14, 0x30, 0xE4, 0x20, 0x15, 0x29, 0x3F, 0x7C, 0xFD, 0xC2, 0xC8, 0x24, 0x74, 0x4C, 0x9C, 0x98, 0x8C, 0xE6, 0x6C, 0x90, 0xAE, 0xA0, 0x17, 0x3E, 0xD5, 0xE0, 0x7E, 0xD3, 0xF9, 0x05, 0x94, 0x44, 0xCF, 0x4B, 0xB4, 0x4E, 0xAF, 0xEE, 0x38, 0xB8, 0xD5, 0x93, 0x47, 0xD8, 0xCD, 0xE3, 0xEE, 0x58, 0x29, 0x79, 0x72, 0x3A, 0x75, 0xFE, 0xE5, 0x1A, 0x6D, 0x92, 0xF8, 0xB3, 0x6D, 0x6E, 0x10, 0xA5, 0x28, 0xC8, 0x9C, 0x76, 0x9D, 0xF7, 0xA5, 0xD6, 0x47, 0xD8, 0xA6, 0x27, 0x94, 0x70, 0x9F, 0x3C, 0x99, 0xD3, 0x65, 0x61, 0x04, 0x44, 0x3C, 0x9C, 0x52, 0x9D, 0xA7, 0x33, 0x42, 0xF2, 0x7F, 0x6E, 0x89, 0x71, 0x43, 0x9E, 0xC7, 0x8C, 0xAF, 0x5E, 0xBA, 0x5B, 0x90, 0x19, 0xB1, 0x3B, 0xD6, 0xCD, 0x44, 0xBC, 0xEB, 0x0E, 0x43, 0xBA, 0x43, 0x4D, 0xEC, 0xC9, 0x35 ] MDUV380_ENCODE_CIPHER:Final = [ 0x00, 0xaa, 0x89, 0x89, 0x1f, 0x4b, 0xec, 0xcf, 0x42, 0x45, 0x14, 0x54, 0x00, 0x65, 0xeb, 0x66, 0x41, 0x7d, 0x4c, 0x88, 0x49, 0x5a, 0x21, 0x0d, 0xf2, 0xf5, 0xc8, 0xe6, 0x38, 0xed, 0xbc, 0xb9, 0xfb, 0x35, 0x71, 0x33, 0x01, 0x0a, 0x7f, 0x9e, 0x3b, 0x29, 0x03, 0xb6, 0x49, 0x3e, 0x42, 0xb8, 0x3f, 0x9f, 0x90, 0xbd, 0xaa, 0x3a, 0x71, 0x46, 0xce, 0xcd, 0xfd, 0x18, 0x32, 0x55, 0x89, 0x4a, 0x5f, 0xc8, 0x83, 0x9c, 0xe4, 0x06, 0x9e, 0x0a, 0x9d, 0x0d, 0x2f, 0xa1, 0x35, 0x6d, 0xd7, 0x92, 0xea, 0xfd, 0x63, 0x85, 0x90, 0xcb, 0xf0, 0x2f, 0xd9, 0x59, 0x53, 0x27, 0xd3, 0x06, 0xb8, 0xf5, 0xb2, 0xca, 0x88, 0x6c, 0xd0, 0x26, 0x91, 0x3b, 0xf2, 0x5b, 0x61, 0xbe, 0xcd, 0xdb, 0xf2, 0x1a, 0xc9, 0xfd, 0x8d, 0x88, 0x04, 0xf4, 0xe8, 0xf1, 0x9a, 0x02, 0x92, 0xbc, 0x24, 0xe9, 0x90, 0xe4, 0x7e, 0xa3, 0x49, 0x4d, 0xce, 0x52, 0x9f, 0x58, 0xc1, 0x7a, 0x5f, 0xb5, 0x18, 0x6e, 0xdb, 0x78, 0x64, 0x08, 0xd5, 0x6f, 0x0d, 0x9d, 0x9f, 0xb3, 0x99, 0x30, 0x81, 0x7e, 0x2b, 0xe6, 0x5b, 0x3c, 0x4a, 0xba, 0x8b, 0xe5, 0xe4, 0x72, 0x11, 0x89, 0x93, 0xd2, 0xf1, 0x2d, 0x1e, 0x0f, 0xd9, 0xd5, 0x43, 0x87, 0x04, 0xe2, 0xb4, 0xae, 0x5e, 0x9e, 0x5f, 0x4c, 0xe9, 0x16, 0xf2, 0xe6, 0x5f, 0x28, 0x9f, 0x79, 0x19, 0xdc, 0x1d, 0x6f, 0x2f, 0xf8, 0xef, 0xcb, 0xe1, 0xce, 0xe8, 0xa7, 0x36, 0x59, 0xef, 0xe0, 0xe2, 0x88, 0x00, 0x10, 0x6d, 0xda, 0x73, 0xbc, 0x92, 0x2b, 0x81, 0xcf, 0xe6, 0xce, 0x04, 0x47, 0xba, 0xdb, 0x7e, 0x2f, 0x41, 0xca, 0x5d, 0xcd, 0xf6, 0x41, 0x7d, 0x1c, 0x38, 0x2b, 0xef, 0x7d, 0x37, 0x0a, 0xf9, 0xa9, 0x14, 0x8e, 0x5d, 0xea, 0x44, 0x66, 0xde, 0x8b, 0x36, 0x56, 0x01, 0x8c, 0x35, 0x8a, 0x11, 0x9b, 0x8f, 0x2a, 0x65, 0x40, 0xf7, 0x2e, 0xe6, 0x58, 0x28, 0x74, 0xcb, 0xc4, 0xcb, 0x0f, 0xa7, 0x62, 0x9b, 0xe3, 0xa6, 0x3c, 0xc7, 0x6f, 0x13, 0x00, 0x97, 0xe9, 0x1e, 0xb1, 0x53, 0x90, 0xdd, 0x9b, 0x61, 0x3e, 0x90, 0x8c, 0xad, 0x3c, 0x29, 0x41, 0x4e, 0x5b, 0x0c, 0x1f, 0x66, 0x40, 0x13, 0x24, 0x49, 0x00, 0xd5, 0x1c, 0xe2, 0xed, 0x28, 0x18, 0x53, 0xaf, 0xe4, 0x1c, 0xdc, 0x96, 0xea, 0x18, 0xfe, 0x2e, 0x65, 0x19, 0xe0, 0x14, 0x50, 0xc1, 0xf1, 0x09, 0x39, 0xf5, 0xcf, 0x45, 0x44, 0xd5, 0x68, 0x0d, 0x72, 0xf1, 0x5f, 0x88, 0x23, 0xb9, 0xb1, 0xcf, 0xda, 0x36, 0x98, 0x43, 0x41, 0xf8, 0xaf, 0x23, 0x6d, 0x50, 0x57, 0x5e, 0x62, 0xbf, 0x5a, 0xa5, 0xda, 0xad, 0xcf, 0xc5, 0x42, 0x5e, 0x3e, 0x34, 0x06, 0x23, 0x04, 0xe9, 0x0e, 0xcd, 0xf8, 0x71, 0x89, 0x67, 0x4e, 0x40, 0xe9, 0x25, 0xbc, 0x45, 0x2e, 0x97, 0xdc, 0xc1, 0x68, 0x22, 0xd2, 0x58, 0x77, 0xb1, 0x2e, 0x69, 0x16, 0xa8, 0x14, 0x9b, 0x18, 0x1a, 0x9a, 0xb8, 0xf0, 0x3b, 0x71, 0xbf, 0x77, 0x18, 0xc8, 0x34, 0xea, 0x85, 0x6d, 0xbb, 0x32, 0x57, 0x35, 0xe5, 0x69, 0xd4, 0x9f, 0x4a, 0x99, 0x68, 0xb4, 0xd8, 0xc7, 0x9a, 0x31, 0x6a, 0x30, 0x3d, 0xe8, 0x9c, 0xd2, 0xeb, 0x64, 0xde, 0x2e, 0xaf, 0xcc, 0xc8, 0x4d, 0x02, 0x09, 0xae, 0x01, 0xf9, 0x2b, 0x73, 0x6d, 0xbc, 0x09, 0xa2, 0xc7, 0x3a, 0x28, 0xba, 0x5d, 0x1b, 0xdf, 0xca, 0xd6, 0xf6, 0xb8, 0x3e, 0xbb, 0xc5, 0x18, 0xf9, 0x36, 0x96, 0x23, 0xa4, 0x19, 0x83, 0xda, 0x45, 0x21, 0xe3, 0x86, 0x13, 0x7d, 0xc2, 0x5a, 0x89, 0x8a, 0x8f, 0x54, 0xb9, 0xe1, 0x15, 0x64, 0xe3, 0x93, 0xad, 0xd0, 0x46, 0xb3, 0xb1, 0xd7, 0x36, 0x15, 0x33, 0x95, 0x6f, 0x56, 0xef, 0x26, 0xa9, 0x1c, 0x7f, 0x0e, 0x6c, 0x9f, 0xce, 0xd8, 0x26, 0x69, 0xcf, 0xfe, 0x7b, 0x5a, 0x6f, 0x09, 0xdc, 0xee, 0xc8, 0xf9, 0x5b, 0xc3, 0x97, 0xe7, 0xbd, 0x55, 0xf0, 0xe9, 0xd1, 0x0c, 0x30, 0x36, 0x01, 0x7a, 0x34, 0x8b, 0x27, 0xdd, 0xc8, 0xcd, 0xa2, 0xec, 0x62, 0xef, 0xa8, 0xd0, 0x11, 0x16, 0xdd, 0x70, 0xb0, 0xfb, 0x25, 0xf1, 0x5f, 0x91, 0xb7, 0x7d, 0x34, 0xe9, 0x74, 0x44, 0x2d, 0x52, 0x76, 0xc1, 0x69, 0xc4, 0xeb, 0x3f, 0x98, 0x7f, 0x24, 0x9b, 0xb1, 0xef, 0xe9, 0x4b, 0xe3, 0xd3, 0x10, 0x9f, 0xcd, 0x9e, 0x4e, 0x47, 0xf1, 0x1d, 0x4c, 0x16, 0x66, 0x5b, 0xfd, 0x06, 0xce, 0xc2, 0x30, 0x7b, 0x88, 0x82, 0x61, 0xcc, 0x27, 0x37, 0xd5, 0xff, 0x22, 0xc6, 0xe6, 0xd4, 0xcc, 0x87, 0x9b, 0x06, 0x87, 0xaa, 0x7b, 0xcd, 0x35, 0xd3, 0xa3, 0xa7, 0xf0, 0x08, 0x17, 0x58, 0xfb, 0xcd, 0x56, 0x2f, 0xf8, 0x8d, 0x31, 0x8c, 0x5b, 0x3c, 0xdc, 0x9f, 0x1e, 0x3b, 0x46, 0x72, 0xb7, 0x7c, 0xa6, 0x2a, 0x47, 0xe6, 0x56, 0x8a, 0x14, 0xfb, 0xe5, 0xb8, 0x39, 0xb8, 0x68, 0x44, 0x9c, 0xbc, 0x10, 0x66, 0x21, 0xad, 0x02, 0x87, 0x1d, 0xd8, 0x62, 0x03, 0x0e, 0x17, 0xb1, 0x2e, 0x89, 0xf8, 0x5a, 0x95, 0x73, 0x1b, 0x87, 0x86, 0x74, 0xdc, 0x39, 0xd2, 0xa9, 0x32, 0x98, 0xd1, 0x99, 0xd7, 0x88, 0xa7, 0x6b, 0xaa, 0x7c, 0xc6, 0x56, 0x51, 0x8f, 0xb4, 0x58, 0x22, 0xd1, 0x0f, 0x2b, 0x44, 0xde, 0xce, 0x75, 0x11, 0xb6, 0xc9, 0x3f, 0xbf, 0xc8, 0x7c, 0xa8, 0x40, 0x50, 0x07, 0xda, 0x66, 0xe3, 0x7a, 0x3e, 0x4b, 0x48, 0x50, 0xec, 0xf0, 0x8a, 0x39, 0x66, 0x24, 0x4b, 0x1d, 0x85, 0xa8, 0x5b, 0x5d, 0xb3, 0x90, 0x8a, 0x5c, 0x5b, 0xec, 0xba, 0x3e, 0x9e, 0xa8, 0x38, 0xef, 0x48, 0xb1, 0x4c, 0x67, 0x02, 0x59, 0x0e, 0x2d, 0xc9, 0xfd, 0x7c, 0x1a, 0x9e, 0xe5, 0xca, 0x60, 0x7f, 0x6b, 0xf9, 0xcb, 0x97, 0x60, 0xab, 0x46, 0xb2, 0xab, 0x36, 0xa0, 0xf3, 0x33, 0xf7, 0x90, 0xc9, 0x00, 0xe9, 0xf7, 0x1f, 0x9d, 0x75, 0x66, 0xd3, 0xc0, 0x8c, 0xe0, 0x6a, 0x2c, 0xf4, 0xe1, 0x02, 0xd7, 0xdf, 0x9e, 0x87, 0x48, 0xc2, 0x8f, 0x2a, 0x44, 0x64, 0x2b, 0x0f, 0xa9, 0x36, 0xf3, 0x46, 0x9a, 0xe2, 0xb1, 0xfd, 0xdc, 0x26, 0x02, 0xf4, 0x80, 0xe3, 0x12, 0x31, 0xc3, 0x71, 0xa7, 0xf4, 0x32, 0x36, 0x61, 0xed, 0x12, 0x77, 0x40, 0xad, 0xfe, 0x6d, 0x66, 0x5b, 0xd2, 0x9c, 0x1e, 0xa8, 0xc8, 0x60, 0x1e, 0x04, 0xe1, 0xc9, 0x09, 0x13, 0x87, 0xa8, 0x38, 0x5a, 0x70, 0xea, 0xba, 0x3f, 0xc5, 0x25, 0x99, 0x30, 0x84, 0x71, 0x5f, 0x22, 0x23, 0x79, 0xd9, 0x3d, 0x76, 0xd2, 0x1b, 0xd5, 0xd2, 0x8b, 0xc4, 0x9d, 0x73, 0x05, 0x84, 0x17, 0x1b, 0x04, 0xdb, 0x4f, 0xfc, 0x07, 0x23, 0xc9, 0xd8, 0xd5, 0xd0, 0xb8, 0x67, 0x59, 0xf7, 0x70, 0xf9, 0xaf, 0x0d, 0x1e, 0x5c, 0x7f, 0xf2, 0xb7, 0x00, 0x8a, 0x2d, 0x2e, 0x59, 0x82, 0x7a, 0xea, 0x85, 0x1f, 0x82, 0x77, 0x2f, 0x6f, 0xe9, 0x7c, 0xb3, 0x6e, 0x8d, 0xed, 0x82, 0xd6, 0x0d, 0x81, 0xc9, 0x38, 0x89, 0x67, 0x4d, 0x4c, 0xa9, 0x35, 0x99, 0x86, 0xe1, 0x21, 0x5c, 0xe9, 0xf3, 0x73, 0x0d, 0x20, 0xb5, 0x3a, 0xd0, 0xcb, 0x14, 0x3e, 0x9d, 0x17, 0x59, 0x37, 0x9f, 0x91, 0xab, 0x3c, 0xda, 0x3c, 0xd5, 0x7e, 0x11, 0xe0, 0x4a, 0x36, 0xe7, 0xa6, 0x66, 0xdc, 0x44, 0xe2, 0xf7, 0x9a, 0xfa, 0x30, 0xfc, 0x00, 0xa9, 0xc2, 0xad, 0xf9, 0xe0, 0xf8, 0xbb, 0xfe, 0x84, 0x31, 0xd8, 0x89, 0x76, 0xe2 ] MD380_ENCODE_CIPHER:Final = [ 0x2e, 0xdf, 0x40, 0xb5, 0xbd, 0xda, 0x91, 0x35, 0x21, 0x42, 0xe3, 0xe2, 0x6d, 0xa9, 0x0b, 0x90, 0x31, 0x30, 0x3a, 0xfa, 0x4f, 0x05, 0x74, 0x64, 0x0a, 0x29, 0x44, 0x7e, 0x60, 0x77, 0xad, 0x8c, 0x9a, 0xe2, 0x63, 0xc4, 0x21, 0xfe, 0x3c, 0xf7, 0x93, 0xc2, 0xe1, 0x74, 0x16, 0x8c, 0xc9, 0x2a, 0xed, 0x65, 0x68, 0x0c, 0x49, 0x86, 0xa3, 0xba, 0x61, 0x1c, 0x88, 0x5d, 0xc4, 0x49, 0x3c, 0xd2, 0xee, 0x6b, 0x34, 0x0c, 0x1a, 0xa0, 0xa8, 0xb3, 0x58, 0x8a, 0x45, 0x11, 0xdf, 0x4f, 0x23, 0x2f, 0xa4, 0xe4, 0xf6, 0x3b, 0x2c, 0x8c, 0x88, 0x2d, 0x9e, 0x9b, 0x67, 0xab, 0x1c, 0x80, 0xda, 0x29, 0x53, 0x02, 0x1a, 0x54, 0x51, 0xca, 0xbf, 0xb1, 0x97, 0x22, 0x79, 0x81, 0x70, 0xfc, 0x00, 0xe9, 0x81, 0x36, 0x4e, 0x4f, 0xa0, 0x1c, 0x0b, 0x07, 0xea, 0x2f, 0x49, 0x2f, 0x0f, 0x25, 0x71, 0xd7, 0xf1, 0x30, 0x7d, 0x66, 0x6e, 0x83, 0x68, 0x38, 0x79, 0x13, 0xe3, 0x8c, 0x70, 0x9a, 0x4a, 0x9e, 0xa9, 0xe2, 0xd6, 0x10, 0x4f, 0x40, 0x14, 0x8e, 0x6c, 0x5e, 0x96, 0xb2, 0x46, 0x3e, 0xe8, 0x25, 0xef, 0x7c, 0xc5, 0x08, 0x18, 0xd4, 0x8b, 0x92, 0x26, 0xe3, 0xed, 0xfa, 0x88, 0x32, 0xe8, 0x97, 0x47, 0x70, 0xf8, 0x46, 0xde, 0xff, 0x8b, 0x0c, 0x4d, 0xb3, 0xb6, 0xfc, 0x69, 0xd6, 0x27, 0x5b, 0x76, 0x6f, 0x5b, 0x03, 0xf7, 0xc3, 0x11, 0x05, 0xc5, 0x1d, 0xfe, 0x92, 0x5f, 0xcb, 0xc2, 0x1c, 0x81, 0x69, 0x1b, 0xb8, 0xf8, 0x62, 0x58, 0xc7, 0xb4, 0xb3, 0x11, 0xd5, 0x1f, 0xf2, 0x16, 0xc1, 0xad, 0x8f, 0xa5, 0x1e, 0xb4, 0x5b, 0xe0, 0xda, 0x7f, 0x46, 0x7d, 0x1d, 0x9e, 0x6d, 0xc0, 0x74, 0x7f, 0x54, 0xa6, 0x2f, 0x43, 0x6f, 0x64, 0x08, 0xca, 0xe8, 0x0f, 0x05, 0x10, 0x9c, 0x9d, 0x9f, 0xbd, 0x67, 0x0c, 0x23, 0xf7, 0xa1, 0xe1, 0x59, 0x7b, 0xe8, 0xd4, 0x64, 0xec, 0x20, 0xca, 0xe9, 0x6a, 0xb9, 0x03, 0x73, 0x67, 0x30, 0x95, 0x16, 0xb6, 0xd9, 0x19, 0x53, 0xe5, 0xdb, 0xa4, 0x3c, 0xcd, 0x7c, 0xf9, 0xd8, 0x67, 0x9f, 0xfc, 0xc9, 0xe2, 0x8a, 0x6a, 0x2c, 0xf2, 0xed, 0xc8, 0xc1, 0x6a, 0x20, 0x99, 0x4c, 0x0d, 0xad, 0xd4, 0x3b, 0xa1, 0x0e, 0x95, 0x88, 0x46, 0xb8, 0x13, 0xe1, 0x06, 0x58, 0xd2, 0x07, 0xad, 0x5c, 0x1a, 0x74, 0xdb, 0xb5, 0xa7, 0x40, 0x57, 0xdb, 0xa2, 0x45, 0xa6, 0x12, 0xd0, 0x82, 0xdd, 0xed, 0x0a, 0xbd, 0xb3, 0x10, 0xed, 0x6c, 0xda, 0x39, 0xd2, 0xd6, 0x90, 0x82, 0x00, 0x76, 0x71, 0xe0, 0x21, 0xa0, 0x8f, 0xf0, 0xf3, 0x67, 0xc4, 0xf3, 0x40, 0xbd, 0x47, 0x16, 0x10, 0xdc, 0x7e, 0xf8, 0x1d, 0xe5, 0x13, 0x66, 0x87, 0xc7, 0x4a, 0x69, 0xc9, 0x63, 0x92, 0x82, 0xec, 0xee, 0x5a, 0x34, 0xfb, 0x96, 0x25, 0xc3, 0xb6, 0x68, 0xe1, 0x3c, 0x8a, 0x71, 0x74, 0xb5, 0xc1, 0x23, 0x99, 0xd6, 0xf7, 0xfb, 0xea, 0x98, 0xcd, 0x61, 0x3d, 0x4d, 0xe1, 0xd0, 0x34, 0xe1, 0xfd, 0x36, 0x10, 0x5f, 0x8e, 0x9e, 0xc6, 0xb6, 0x58, 0x0c, 0x55, 0xbe, 0x69, 0xa8, 0x56, 0x76, 0x4b, 0x1f, 0xd5, 0x90, 0x7e, 0x47, 0x5f, 0x2f, 0x25, 0x02, 0x5c, 0xef, 0x00, 0x64, 0xa0, 0x26, 0x9a, 0x18, 0x3c, 0x69, 0xc4, 0xff, 0x9a, 0x52, 0x41, 0x1b, 0xc9, 0x81, 0xc3, 0xac, 0x15, 0xe1, 0x17, 0x98, 0xdb, 0x2c, 0x9c, 0x10, 0x9b, 0xb2, 0xf9, 0x71, 0x4f, 0x56, 0x0f, 0x68, 0xfb, 0xd9, 0x2d, 0x5a, 0x86, 0x5b, 0x83, 0x03, 0xc8, 0x1e, 0xda, 0x5d, 0xe4, 0x8e, 0x82, 0xc3, 0xd8, 0x7e, 0x8b, 0x56, 0x52, 0xb5, 0x38, 0xa0, 0xc6, 0xa9, 0xb0, 0x77, 0xbd, 0x8a, 0xf7, 0x24, 0x70, 0x82, 0x1d, 0xc5, 0x95, 0x3c, 0xb5, 0xf0, 0x79, 0xa3, 0x89, 0x99, 0x4f, 0xec, 0x8c, 0x36, 0xc7, 0xd6, 0x10, 0x20, 0xe3, 0x30, 0x39, 0x3d, 0x07, 0x9c, 0xb2, 0xdc, 0x4f, 0x94, 0x9e, 0xe0, 0x24, 0xaa, 0xd2, 0x21, 0x12, 0x14, 0x41, 0x0f, 0xd4, 0x67, 0xb7, 0x99, 0xb1, 0xa3, 0xcb, 0x4d, 0x0c, 0x70, 0x0f, 0xc0, 0x36, 0xa7, 0x89, 0x30, 0x86, 0x14, 0x67, 0x68, 0xac, 0x7b, 0xee, 0xe4, 0x42, 0xd8, 0xb4, 0x36, 0xa4, 0xeb, 0x0f, 0xa8, 0x02, 0xf4, 0xcd, 0x23, 0xb3, 0xbc, 0x25, 0x4f, 0xcc, 0xd4, 0xee, 0xfc, 0xf2, 0x21, 0x0f, 0xc1, 0x6c, 0x99, 0x37, 0xe2, 0x7c, 0x47, 0xce, 0x77, 0xf0, 0x95, 0x2b, 0xcb, 0xf4, 0xca, 0x07, 0x03, 0x2a, 0xd2, 0x31, 0x00, 0xfd, 0x3e, 0x84, 0x86, 0x32, 0x8b, 0x17, 0x9d, 0xbf, 0xa7, 0xb3, 0x37, 0xe1, 0xb1, 0x8a, 0x14, 0x69, 0x00, 0x25, 0xe3, 0x56, 0x68, 0x9f, 0xaa, 0xa9, 0xb8, 0x11, 0x67, 0x75, 0x87, 0x4d, 0xf8, 0x36, 0x31, 0xcf, 0x38, 0x63, 0x1c, 0xf0, 0x6b, 0x47, 0x40, 0x5d, 0xdc, 0x0c, 0xe6, 0xc8, 0xc4, 0x19, 0xaf, 0xdd, 0x6e, 0x9e, 0xd9, 0x78, 0x99, 0x6c, 0xbe, 0x15, 0x1e, 0x0b, 0x9d, 0x88, 0xd2, 0x06, 0x9d, 0xee, 0xae, 0x8a, 0x0f, 0xe3, 0x2d, 0x2f, 0xf4, 0xf5, 0xf6, 0x16, 0xbf, 0x59, 0xbb, 0x34, 0x5c, 0xdd, 0x61, 0xed, 0x70, 0x1e, 0x61, 0xe5, 0xe3, 0xfb, 0x6e, 0x13, 0x9c, 0x49, 0x58, 0x17, 0x8b, 0xc8, 0x30, 0xcd, 0xed, 0x56, 0xad, 0x22, 0xcb, 0x63, 0xce, 0x26, 0xc4, 0xa5, 0xc1, 0x63, 0x0d, 0x0d, 0x04, 0x6e, 0xb6, 0xf9, 0xca, 0xbb, 0x2f, 0xab, 0xa0, 0xb5, 0x0a, 0xfa, 0x50, 0x0e, 0x02, 0x47, 0x05, 0x54, 0x3d, 0xb3, 0xb1, 0xc6, 0xce, 0x8f, 0xac, 0x65, 0x7e, 0x15, 0x9e, 0x4e, 0xcc, 0x55, 0x9e, 0x46, 0x32, 0x71, 0x9b, 0x97, 0xaa, 0x0d, 0xfb, 0x1b, 0x71, 0x02, 0x83, 0x96, 0x0b, 0x52, 0x77, 0x48, 0x87, 0x61, 0x02, 0xc3, 0x04, 0x62, 0xd7, 0xfb, 0x74, 0x0f, 0x19, 0x9c, 0xa0, 0x9d, 0x79, 0xa0, 0x6d, 0xef, 0x9e, 0x20, 0x5d, 0x0a, 0xc9, 0x6a, 0x58, 0xc9, 0xb9, 0x55, 0xad, 0xd1, 0xcc, 0xd1, 0x54, 0xc8, 0x68, 0xc2, 0x76, 0xc2, 0x99, 0x0f, 0x2e, 0xfc, 0xfb, 0xf5, 0x92, 0xcd, 0xdb, 0xa2, 0xed, 0xd9, 0x99, 0xff, 0x4f, 0x88, 0x50, 0xcd, 0x48, 0xb7, 0xb9, 0xf3, 0xf0, 0xad, 0x4d, 0x16, 0x2a, 0x50, 0xaa, 0x6b, 0x2a, 0x98, 0x38, 0xc9, 0x35, 0x45, 0x0c, 0x03, 0xa8, 0xcd, 0x0d, 0x74, 0x3c, 0x99, 0x55, 0xdb, 0x88, 0x70, 0xda, 0x6a, 0xc8, 0x34, 0x4d, 0x19, 0xdc, 0xcc, 0x42, 0x40, 0x94, 0x61, 0x92, 0x65, 0x2a, 0xcd, 0xfd, 0x52, 0x10, 0x50, 0x14, 0x6b, 0xec, 0x85, 0x57, 0x3f, 0xe2, 0x95, 0x9a, 0x5d, 0x11, 0xab, 0xad, 0x69, 0x60, 0xa8, 0x3b, 0x6f, 0x7a, 0x17, 0xf3, 0x76, 0x17, 0x63, 0xe6, 0x59, 0x7e, 0x47, 0x30, 0xd2, 0x47, 0x87, 0xdb, 0xd8, 0x66, 0xde, 0x00, 0x2b, 0x65, 0x37, 0x2f, 0x2d, 0xf1, 0x20, 0x11, 0xf3, 0x98, 0x7b, 0x4c, 0x9c, 0xd1, 0x76, 0xa7, 0xe1, 0x3d, 0xbe, 0x6f, 0xee, 0x2c, 0xf0, 0x19, 0x70, 0x63, 0x51, 0x28, 0xf0, 0x1d, 0xbe, 0x52, 0x5f, 0x4f, 0xe6, 0xde, 0xf2, 0x30, 0xb6, 0x50, 0x30, 0xf9, 0x15, 0x48, 0x49, 0xe9, 0xd2, 0xa8, 0xa9, 0x8d, 0xda, 0xf5, 0xcd, 0x3e, 0xaf, 0x00, 0x55, 0xeb, 0x15, 0xc5, 0x5b, 0x19, 0x0f, 0x93, 0x04, 0x27, 0x09, 0x6d, 0x54, 0xd7, 0x57, 0xb1, 0x47, 0x0a, 0xde, 0xf7, 0x1d, 0xcb, 0x11, 0x3c, 0xf5, 0x8f, 0x20, 0x40, 0x9d, 0xbb, 0x6b, 0x2c, 0xa9, 0x67, 0x3d, 0x78, 0xc2, 0x62, 0xb7, 0x0c ] DM1801_ENCODE_CIPHER:Final = [ 0x3a, 0x04, 0x74, 0xad, 0x90, 0xae, 0xb3, 0x78, 0xde, 0xfa, 0x73, 0x8d, 0xdc, 0x8c, 0x53, 0x67, 0x27, 0x61, 0xf3, 0x6f, 0x95, 0xc8, 0xf1, 0x1f, 0xc5, 0xad, 0x17, 0x68, 0xfb, 0x1d, 0xd2, 0x51, 0x2b, 0x07, 0xe6, 0xe2, 0x54, 0x40, 0x61, 0x40, 0x10, 0xca, 0xcd, 0x19, 0x68, 0x56, 0xaa, 0x42, 0xec, 0x07, 0x1c, 0x9f, 0x04, 0x79, 0xe0, 0x44, 0x81, 0x02, 0x84, 0xd9, 0x77, 0x38, 0xd8, 0x43, 0x4f, 0xb3, 0xa0, 0x81, 0x18, 0x14, 0x8b, 0xd3, 0x1e, 0x45, 0x69, 0x22, 0xbe, 0x03, 0x98, 0x9c, 0x78, 0x9a, 0xbf, 0x9f, 0x46, 0xf0, 0xbf, 0xd8, 0x2c, 0xc5, 0xe7, 0xac, 0x11, 0x39, 0x68, 0xd6, 0xcd, 0x8f, 0x08, 0x52, 0x83, 0x30, 0x1a, 0x7a, 0x31, 0xf3, 0xad, 0x70, 0x85, 0x9b, 0x04, 0xbb, 0xf3, 0xa2, 0x46, 0x35, 0x04, 0x35, 0x77, 0x24, 0xf1, 0x7f, 0xa7, 0xa7, 0x70, 0x2a, 0x69, 0x54, 0xce, 0x23, 0x87, 0x1f, 0x3e, 0x9e, 0xf4, 0x7d, 0x71, 0x5b, 0x02, 0xca, 0x66, 0x27, 0xd5, 0xe8, 0x84, 0xa5, 0x18, 0x2a, 0xe6, 0x4e, 0xee, 0x70, 0xf6, 0xb7, 0x2c, 0x93, 0x3d, 0x12, 0xc5, 0x03, 0x79, 0xf8, 0x86, 0xae, 0xf0, 0x66, 0x03, 0x24, 0x05, 0x05, 0xd1, 0xf9, 0x09, 0xae, 0xf5, 0x6b, 0x53, 0x2d, 0x9d, 0x45, 0x93, 0x45, 0x0e, 0x04, 0x63, 0xf5, 0xde, 0x37, 0x1f, 0xfa, 0x63, 0x2c, 0xf7, 0x95, 0x6b, 0xc8, 0x42, 0x8e, 0x2d, 0xb7, 0x15, 0x79, 0x80, 0xc6, 0x15, 0x38, 0x4b, 0x8c, 0x89, 0xc1, 0x3d, 0x50, 0xb4, 0x22, 0xbe, 0x27, 0x61, 0xc2, 0x25, 0x5d, 0xbf, 0xe9, 0x2b, 0x16, 0x6f, 0x82, 0x9f, 0x35, 0xdc, 0x20, 0x5c, 0x7d, 0xcb, 0x40, 0x79, 0xf6, 0x33, 0xce, 0xbf, 0x93, 0x4e, 0xea, 0x60, 0x11, 0xf0, 0xeb, 0xe5, 0x22, 0x18, 0xa4, 0x69, 0xcb, 0xc4, 0xe7, 0x05, 0x0b, 0x0a, 0x48, 0x8b, 0xbd, 0x65, 0x23, 0x77, 0xbf, 0x4d, 0xe1, 0x22, 0x54, 0x0a, 0x76, 0x39, 0xc7, 0xc9, 0x2e, 0x6e, 0x52, 0xf0, 0xaa, 0x6d, 0x3d, 0xaf, 0x25, 0x12, 0x4a, 0xd7, 0xfd, 0xd9, 0x51, 0xf0, 0x6e, 0x96, 0x28, 0x86, 0xa0, 0x66, 0xc5, 0xc3, 0xe4, 0xe5, 0xa7, 0x43, 0x3a, 0xa1, 0x72, 0x23, 0x18, 0xcf, 0xd9, 0x5b, 0x66, 0x3d, 0xc0, 0x4e, 0xcd, 0x88, 0xa2, 0xa0, 0x31, 0x8f, 0x31, 0x48, 0x7c, 0x27, 0x3d, 0xe6, 0x9e, 0x11, 0xd7, 0x56, 0xd2, 0x29, 0xb6, 0x85, 0x22, 0xdf, 0xda, 0x83, 0x2d, 0xeb, 0x6e, 0xda, 0x28, 0x3d, 0xf3, 0x1f, 0x23, 0x33, 0x9b, 0xc6, 0x8d, 0x0f, 0xf3, 0x3a, 0xfb, 0xa8, 0xc5, 0x2e, 0x25, 0x60, 0x3d, 0x2d, 0x31, 0x55, 0x4a, 0x79, 0x35, 0xdc, 0x47, 0x12, 0xf7, 0x2b, 0xdb, 0x15, 0xf7, 0x55, 0x1e, 0x47, 0xaf, 0x7c, 0xfd, 0xf2, 0x19, 0x41, 0xdf, 0xf0, 0x72, 0x80, 0x89, 0x05, 0x3e, 0x3b, 0x3e, 0x72, 0x8c, 0xd3, 0x2b, 0xc6, 0x7b, 0x7e, 0x03, 0xf8, 0xfd, 0xf5, 0xe7, 0xb3, 0xdb, 0x6d, 0x88, 0xf1, 0xf9, 0xc9, 0x8f, 0xcb, 0xdc, 0x0e, 0x3d, 0x8f, 0x69, 0x17, 0x4e, 0x14, 0xef, 0x8a, 0x24, 0x4a, 0x68, 0x0a, 0x21, 0x15, 0xfc, 0xae, 0x55, 0x5c, 0xc7, 0xb2, 0x59, 0x5d, 0xdc, 0x6d, 0x7a, 0x43, 0x8a, 0x83, 0x1a, 0xfa, 0xde, 0x5c, 0x54, 0x42, 0x68, 0xd5, 0xdf, 0x02, 0x42, 0x35, 0x35, 0xdf, 0x4f, 0x62, 0xf4, 0x0e, 0xc1, 0x55, 0x84, 0x66, 0xde, 0xcb, 0xfa, 0xba, 0x03, 0x3d, 0x3c, 0x65, 0xe9, 0x13, 0x66, 0x26, 0x27, 0x15, 0x6d, 0x2f, 0xf8, 0x22, 0x03, 0x78, 0x3f, 0x24, 0xba, 0x59, 0xc8, 0x43, 0x6b, 0x58, 0xd1, 0x59, 0xd9, 0x40, 0xc9, 0xa6, 0x92, 0x73, 0x57, 0xc6, 0x16, 0x80, 0x9e, 0xdf, 0x3b, 0xf8, 0xc0, 0x1f, 0xd0, 0x7e, 0xa0, 0x66, 0x81, 0x1e, 0xed, 0x3f, 0xfa, 0xe0, 0x5c, 0x15, 0x50, 0x9c, 0x34, 0xa4, 0x9c, 0x0f, 0x10, 0xad, 0xe9, 0x2f, 0xe0, 0xee, 0x50, 0xbc, 0x32, 0x51, 0x61, 0x18, 0xb0, 0x64, 0xc5, 0x58, 0xe9, 0x09, 0x22, 0x9b, 0x54, 0x6f, 0x3f, 0x9a, 0x91, 0x40, 0x69, 0x81, 0xf3, 0x1c, 0x15, 0xfe, 0x3c, 0x47, 0xc6, 0x97, 0xa7, 0x9e, 0x31, 0x40, 0x2c, 0xd0, 0x9f, 0x2d, 0xff, 0xca, 0x94, 0xe5, 0x5a, 0x73, 0xae, 0x98, 0x7c, 0x9a, 0xcf, 0xb2, 0xf2, 0x2c, 0x7f, 0xb0, 0x15, 0xab, 0x8b, 0x32, 0xd4, 0xdb, 0xf2, 0x52, 0xb3, 0xbf, 0x02, 0x35, 0x14, 0xc3, 0xbf, 0xdf, 0xb5, 0x3b, 0x84, 0x4c, 0x7b, 0x0c, 0xed, 0xbc, 0x6e, 0xa9, 0xf3, 0x4e, 0x04, 0x42, 0x59, 0xd0, 0xa2, 0x38, 0x48, 0xd6, 0x60, 0xd3, 0x36, 0x09, 0x0d, 0x37, 0x0c, 0xc2, 0x73, 0x94, 0x87, 0xd8, 0xdb, 0x9e, 0xde, 0xb6, 0xd4, 0x3d, 0xa6, 0xb0, 0x31, 0x85, 0xf3, 0x97, 0x51, 0xe8, 0xc1, 0x8a, 0xa3, 0xaa, 0x92, 0x10, 0x68, 0x96, 0x58, 0x64, 0xbb, 0xf0, 0x94, 0x10, 0xcf, 0xaa, 0xc0, 0xbd, 0x79, 0xda, 0xeb, 0x4a, 0xee, 0x6c, 0xa3, 0x1b, 0xcd, 0x14, 0x17, 0xb4, 0x60, 0x87, 0x7d, 0x86, 0x1f, 0xeb, 0xb2, 0x08, 0x75, 0x8c, 0x20, 0x0a, 0xc7, 0xd0, 0xe5, 0x47, 0xb3, 0x6d, 0x32, 0x39, 0x95, 0xd9, 0xf0, 0x31, 0x50, 0x03, 0xaa, 0xa6, 0x4b, 0x40, 0xa7, 0xcd, 0xb9, 0x88, 0x56, 0x6b, 0x1e, 0xe2, 0xf0, 0xe8, 0x0e, 0x1d, 0x58, 0xa4, 0x38, 0xc1, 0x46, 0x8e, 0xa4, 0x45, 0xa4, 0xf2, 0x39, 0x82, 0x38, 0x92, 0x82, 0x68, 0x84, 0xf9, 0xb2, 0xf0, 0xea, 0x0c, 0xe5, 0x51, 0x14, 0xe2, 0xa8, 0x77, 0x93, 0xd5, 0xbc, 0xb1, 0xc6, 0xd9, 0x17, 0xaa, 0xfe, 0x0d, 0x2c, 0x9a, 0xdf, 0x90, 0x6c, 0xbd, 0x0a, 0x96, 0x0d, 0x05, 0xf9, 0xbb, 0x0a, 0x0c, 0x29, 0x97, 0x6b, 0x4c, 0x7f, 0x92, 0xc6, 0x92, 0xe5, 0xf9, 0x06, 0xb0, 0x34, 0x52, 0x6b, 0x72, 0x56, 0xed, 0xd2, 0xd4, 0xac, 0xbc, 0x37, 0x72, 0xad, 0x64, 0x78, 0x40, 0xd0, 0x94, 0x5b, 0x7b, 0xac, 0x96, 0xd3, 0xe0, 0x5e, 0x24, 0x7f, 0x1b, 0x2c, 0x7c, 0x74, 0x82, 0x68, 0xb7, 0x3c, 0x03, 0x96, 0x56, 0x1e, 0x5b, 0xd1, 0x1e, 0xa0, 0x89, 0x6a, 0x26, 0x4b, 0x83, 0xd3, 0x2e, 0xae, 0x27, 0xbb, 0x32, 0xa6, 0x74, 0x7b, 0x3f, 0xdc, 0xfa, 0xb1, 0x86, 0x8e, 0x8f, 0x2a, 0xaf, 0x93, 0x44, 0x06, 0x6f, 0x99, 0x98, 0x16, 0x5d, 0xb2, 0xeb, 0x89, 0x01, 0x0f, 0x35, 0xc8, 0x2e, 0x0a, 0xf7, 0x9e, 0x92, 0x6a, 0x72, 0x9c, 0x8c, 0xe3, 0x18, 0xbc, 0x3d, 0xdd, 0x40, 0x44, 0xe2, 0x77, 0x1d, 0xed, 0x61, 0xca, 0xf1, 0x45, 0x21, 0x72, 0x7e, 0x52, 0x1f, 0x4b, 0xbd, 0x78, 0x3f, 0x78, 0xd3, 0x9c, 0xe0, 0xaa, 0x41, 0x8a, 0xb2, 0x9e, 0x5b, 0x94, 0xcc, 0xe8, 0xfb, 0x7c, 0xf9, 0xf0, 0x76, 0x95, 0x53, 0x3a, 0xcf, 0x24, 0x14, 0xea, 0x2b, 0x0c, 0xa8, 0x87, 0x85, 0xab, 0x07, 0xff, 0xa3, 0xff, 0x41, 0xeb, 0x49, 0x0d, 0x5a, 0x15, 0xac, 0x83, 0x59, 0x37, 0x29, 0x9c, 0x9c, 0x06, 0x37, 0x44, 0x6e, 0x6f, 0x9b, 0x7d, 0xdc, 0x21, 0xdb, 0x01, 0xc4, 0x4b, 0xf4, 0x28, 0x2e, 0xa7, 0x4f, 0x0d, 0xdf, 0xb7, 0xf2, 0xec, 0x2c, 0x4f, 0xf4, 0xcf, 0x0d, 0x53, 0x33, 0x6a, 0x72, 0xc2, 0x49, 0x43, 0xda, 0xf3, 0xbb, 0x16, 0x21, 0x1f, 0x74, 0x77, 0x99, 0x20, 0x72, 0xb9, 0x5d, 0x78, 0xc0, 0x0f, 0xe2, 0x95, 0xa4, 0xf1, 0xcf, 0x54, 0x19, 0xc1, 0x0f, 0xc3, 0x7f, 0xaf, 0x24, 0x2b, 0x92, 0xda, 0xbe, 0x4e, 0x99, 0xb7, 0x8c, 0xed, 0xdf, 0xb7 ] # Python 3 deprecated getargspec in favour of getfullargspec, but # Python 2 doesn't have the latter, so detect which one to use getargspec = getattr(inspect, "getfullargspec", inspect.getargspec) if "length" in getargspec(usb.util.get_string).args: # PyUSB 1.0.0.b1 has the length argument def get_string(dev, index): return usb.util.get_string(dev, 255, index) else: # PyUSB 1.0.0.b2 dropped the length argument def get_string(dev, index): return usb.util.get_string(dev, index) def GetSHA256Checksum(filename): fHash = '' try: with open(filename,"rb") as f: bytes = f.read() fHash = hashlib.sha256(bytes).hexdigest() except: return '' return fHash def dfu_get_string(i=0): try: # Mac calling convention. return usb.util.get_string(__dev, 255, i, None) except: # Linux calling convention. return usb.util.get_string(__dev, i, None) def check_if_in_bootloader(): bootl = dfu_get_string(1) return (bootl == u'AnyRoad Technology') def find_dfu_cfg_descr(descr): if len(descr) == 9 and descr[0] == 9 and descr[1] == _DFU_DESCRIPTOR_TYPE: nt = collections.namedtuple( "CfgDescr", [ "bLength", "bDescriptorType", "bmAttributes", "wDetachTimeOut", "wTransferSize", "bcdDFUVersion", ], ) return nt(*struct.unpack(" 1: raise ValueError(" !!! " + "Multiple DFU devices found") __dev = devices[0] __dev.set_configuration() # Claim DFU interface usb.util.claim_interface(__dev, __DFU_INTERFACE) # Find the DFU configuration descriptor, either in the device or interfaces __cfg_descr = None for cfg in __dev.configurations(): __cfg_descr = find_dfu_cfg_descr(cfg.extra_descriptors) if __cfg_descr: break for itf in cfg.interfaces(): __cfg_descr = find_dfu_cfg_descr(itf.extra_descriptors) if __cfg_descr: break # Get device into idle state for attempt in range(4): status = get_status() if status == __DFU_STATE_DFU_IDLE: break elif status == __DFU_STATE_DFU_DOWNLOAD_IDLE or status == __DFU_STATE_DFU_UPLOAD_IDLE: abort_request() else: clr_status() def abort_request(): """Sends an abort request.""" __dev.ctrl_transfer(0x21, __DFU_ABORT, 0, __DFU_INTERFACE, None, __TIMEOUT) def clr_status(): """Clears any error status (perhaps left over from a previous session).""" __dev.ctrl_transfer(0x21, __DFU_CLRSTATUS, 0, __DFU_INTERFACE, None, __TIMEOUT) def get_status(): """Get the status of the last operation.""" try: stat = __dev.ctrl_transfer(0xA1, __DFU_GETSTATUS, 0, __DFU_INTERFACE, 6, 20000) except: return # firmware can provide an optional string for any error if stat[5]: message = get_string(__dev, stat[5]) if message: print(message) return stat[4] def check_status(stage, expected): status = get_status() if status != expected: raise SystemExit(" !!! DFU: %s failed (%s)" % (stage, __DFU_STATUS_STR.get(status, status))) def wait_for_idle(): # Seems to be something strange with the GetStatus command that effects the internal logic state of the bootloader # These commands can't be optimised to not pre-read the GetStatus, possibly if the status is Idle it should not be cleared. ##STDFU_GetStatus(ref hDevice, ref status); status = get_status() while (status != __DFU_STATE_DFU_IDLE): clr_status() status = get_status() def page_erase(addr): """Erases a single page.""" if __verbose: print("Erasing page: 0x%x..." % (addr)) # Send DNLOAD with first byte=0x41 and page address buf = struct.pack("= len(encBuf) + 0x6937c): for j in range(0, len(encBuf)): openFirmwareData[0x6937c + j] = encBuf[j]; else: print(" *** " + "Flashing FM Only firmware") ## Encode the open firmware print(" *** " + "Flashing a ", end="") if (platform == FWPlatformOutput.MD_9600): print("MD-9600", end="") for j in range(0, len(openFirmwareData)): openFirmwareData[j] ^= MD9600_ENCODE_CIPHER[j % 1024] elif (platform == FWPlatformOutput.MD_UV380) or (platform == FWPlatformOutput.MD_2017): print(("MD-UV380" if platform == FWPlatformOutput.MD_UV380 else "MD-2017"), end="") for j in range(0, len(openFirmwareData)): openFirmwareData[j] ^= MDUV380_ENCODE_CIPHER[j % 1024] elif (platform == FWPlatformOutput.MD_380): print("MD-380", end="") for j in range(0, len(openFirmwareData)): openFirmwareData[j] ^= MD380_ENCODE_CIPHER[j % 1024] elif (platform == FWPlatformOutput.DM_1701): print("DM-1701", end="") for j in range(0, len(openFirmwareData)): openFirmwareData[j] ^= DM1801_ENCODE_CIPHER[j % 1024]; print(":") wait_for_idle() send_custom_command(0x91, 0x01) send_custom_command(0x91, 0x31) for address in addresses: print("\r *** > Erasing address@ 0x%x" % address,end="") sys.stdout.flush() page_erase(address) bytesWrote = 0 totalBlocks = len(openFirmwareData) / BLOCK_WRITE_SIZE totalBytes = totalBlocks * BLOCK_WRITE_SIZE blockStart = 2 addressIndex = 0 numAddresses = len(addresses) while (addressIndex < numAddresses): # for each section address = addresses[addressIndex] size = sizes[addressIndex] if progress: progress(address, 0, size, " *** > ") set_address(address) if addressIndex != len(addresses) - 1: assert address + size == addresses[addressIndex + 1] dataWritten = 0 blockNumber = blockStart while len(openFirmwareData) > 0 and size > dataWritten: # for each block assert blockNumber <= blockEnds[addressIndex] chunk, openFirmwareData = openFirmwareData[:BLOCK_WRITE_SIZE], openFirmwareData[BLOCK_WRITE_SIZE:] ## Filling data chunk with 0xFF to reach BLOCK_WRITE_SIZE length if len(chunk) < BLOCK_WRITE_SIZE: chunk += b'\xFF' * (BLOCK_WRITE_SIZE - len(chunk)) write_firmware_data(blockNumber, chunk) if progress and blockNumber % 2 == 0: progress(address, dataWritten, size, " *** > ") chunkLen = len(chunk) dataWritten += chunkLen bytesWrote += chunkLen blockNumber += 1 addressIndex += 1 print("\r *** 100% complete, now safe to disconnect and/or reboot the radio.") exit_dfu(True) def main(): """Test program for verifying this files functionality.""" global __verbose global config home = str(Path.home()) configFilename = home + "/.gd77firmwareloader.ini" config = configparser.ConfigParser() config.read(configFilename) try: officialFirmware = config['GLOBAL']['SourceSTM32Firmware'] except KeyError: officialFirmware = "" # Parse CMD args parser = argparse.ArgumentParser( prog="OpenGD77 firmware loader for STM32 transceivers based", description="OpenGD77 STM32 FW Loader v{}".format(version), epilog=" -- DMR Enabled -- " if (officialFirmware != '' and os.path.isfile(officialFirmware) == True) else " -- FM Only -- " ) parser.add_argument( "-l", "--list", help="list available DFU devices.", action="store_true", default=False ) parser.add_argument("--vid", help="USB Vendor ID (default: 0x{:X}).".format(defaultVID), type=lambda x: int(x, 0), default=defaultVID) parser.add_argument("--pid", help="USB Product ID (default: 0x{:X}).".format(defaultPID), type=lambda x: int(x, 0), default=defaultPID) parser.add_argument( "-s", "--source", help="official firmware file used as codec source (**Note**: you have to select a source firmware at least once, the file location is stored and used by default).", dest="source", default=False ) parser.add_argument( "-f", "--firmware", help="flash the specified firmware.", dest="firmware", default=False ) models="Select transceiver model. Models are: {}".format(", ".join(str(x) for x in FWOutputPlatforms[:-1])) + "." parser.add_argument( "-m", "--model", help=models, dest="model", default=False ) #parser.add_argument( # "-v", "--verbose", help="increase output verbosity", action="store_true", default=False #) args = parser.parse_args() #__verbose = args.verbose print("OpenGD77 STM32 FW Loader v{}\n".format(version)) kwargs = {} if args.vid: kwargs["idVendor"] = args.vid if args.pid: kwargs["idProduct"] = args.pid if args.list: list_dfu_devices(**kwargs) return if args.source: fwFile = args.source hash256 = GetSHA256Checksum(fwFile) # The checksum doesn't match, ignoring this SGL file if (hash256 != FW2645_SHA256_Checksum): print(" !!! " + "The specified codec source firmware isn't valid...") fwFile = '' try: config.set('GLOBAL', 'SourceSTM32Firmware', fwFile) except: config['GLOBAL'] = { 'SourceSTM32Firmware': fwFile } # Save configuration file if (fwFile != ''): print(" *** " + "Use {} as codec source.".format(fwFile)) else: print(" *** " + "Unset codec source firmware file.") with open(configFilename, 'w') as cfgFile: config.write(cfgFile) # Re-read the configuration file config.read(configFilename) officialFirmware = fwFile if args.model: try: index = FWOutputPlatforms.index(args.model) except ValueError as e: print(" !!! " + "Model \"{}\" is unknown. Available models are: {}. Exiting...".format(args.model, ", ".join(str(x) for x in FWOutputPlatforms[:-1]))) sys.exit(-5) global FWPlatformFormat FWPlatformFormat = FWPlatformOutput(index) if (FWPlatformFormat == FWPlatformOutput.UNKNOWN): print(" !!! " + "Unsupported model. Exiting...") sys.exit(-5) ## Try to connect the device deviceConnected = True try: init(**kwargs) except: deviceConnected = False command_run = False ## Upload the firmware (optionnaly, patch it first) if args.firmware and deviceConnected == True: ## A radio model HAS to be specified, there is no default here. if (FWPlatformFormat == FWPlatformOutput.UNKNOWN): print(" !!! " + "You should specify a radio model (available models are: {}). Exiting...".format(", ".join(str(x) for x in FWOutputPlatforms[:-1]))) sys.exit(-5) ## Download the firmware (and optionnaly patch it for the codec) to the device. patch_and_download_firmware(args.firmware, FWPlatformFormat, cli_progress) command_run = True if command_run: print(" *** " + "Finished") else: if deviceConnected == False: print(" !!! " + "Failed to connect to the device.") sys.exit(-6) else: print(" !!! " + "No command specified") sys.exit(-7) sys.exit(0) if __name__ == "__main__": main()