FreeTRX/MDUV380_firmware/tools/opengd77_stm32_firmware_loader.py
2026-07-06 08:45:33 +02:00

1077 lines
52 KiB
Python

#!/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 <i.abdalkader@gmail.com>
# 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-2025 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.1.1"
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
]
DM1701_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
# F1RMB: make it work with Python >= 3.11
try:
getargspec = getattr(inspect, "getfullargspec", inspect.getargspec)
except AttributeError:
getargspec = getattr(inspect, "getfullargspec", inspect.getfullargspec)
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("<BBBHHH", bytearray(descr)))
return None
def init(**kwargs):
"""Initializes the found DFU device so that we can program it."""
global __dev, __cfg_descr
devices = get_dfu_devices(**kwargs)
if not devices:
raise ValueError(" !!! " + "No DFU device found")
if len(devices) > 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("<BI", 0x41, addr)
__dev.ctrl_transfer(0x21, __DFU_DNLOAD, 0, __DFU_INTERFACE, buf, __TIMEOUT)
# Execute last command
check_status("erase", __DFU_STATE_DFU_DOWNLOAD_BUSY)
# Check command state
check_status("erase", __DFU_STATE_DFU_DOWNLOAD_IDLE)
def set_address(addr):
"""Sets the address for the next operation."""
# Send DNLOAD with first byte=0x21 and page address
buf = struct.pack("<BI", 0x21, addr)
__dev.ctrl_transfer(0x21, __DFU_DNLOAD, 0, __DFU_INTERFACE, buf, __TIMEOUT)
# Execute last command
check_status("set address", __DFU_STATE_DFU_DOWNLOAD_BUSY)
# Check command state
check_status("set address", __DFU_STATE_DFU_DOWNLOAD_IDLE)
def send_custom_command(a, b):
a &= 0xFF
b &= 0xFF
__dev.ctrl_transfer(0x21, __DFU_DNLOAD, 0, 0, [a, b])
status = get_status() # this changes state
time.sleep(0.1)
status = get_status() # this gets the status
wait_for_idle()
def write_firmware_data(blockNumber, chunk):
"""Download a chunk of bytes, for block number. This routine assumes that memory has
already been erased.
"""
__dev.ctrl_transfer(0x21, __DFU_DNLOAD, blockNumber, __DFU_INTERFACE, chunk, __TIMEOUT)
# Execute last command
check_status("write memory", __DFU_STATE_DFU_DOWNLOAD_BUSY)
# Check command state
check_status("write memory", __DFU_STATE_DFU_DOWNLOAD_IDLE)
def exit_dfu(bootloaderHT=False):
"""Exit DFU mode, and start running the program."""
# Set jump address
set_address(0x08000000)
# Send DNLOAD with 0 length to exit DFU
__dev.ctrl_transfer(0x21, __DFU_DNLOAD, 0, __DFU_INTERFACE, None, __TIMEOUT)
try:
# Execute last command
status = get_status()
if (((bootloaderHT == False) and (status != __DFU_STATE_DFU_MANIFEST)) or (status != None)):
print("Failed to reset device {}".format(status))
# Release device
usb.util.dispose_resources(__dev)
except:
pass
class FilterDFU(object):
"""Class for filtering USB devices to identify devices which are in DFU
mode.
"""
def __call__(self, device):
for cfg in device:
for intf in cfg:
return intf.bInterfaceClass == 0xFE and intf.bInterfaceSubClass == 1
def get_dfu_devices(*args, **kwargs):
"""Returns a list of USB devices which are currently in DFU mode.
Additional filters (like idProduct and idVendor) can be passed in
to refine the search.
"""
# Convert to list for compatibility with newer PyUSB
return list(usb.core.find(*args, find_all=True, custom_match=FilterDFU(), **kwargs))
def named(values, names):
"""Creates a dict with `names` as fields, and `values` as values."""
return dict(zip(names.split(), values))
def get_memory_layout(device):
"""Returns an array which identifies the memory layout. Each entry
of the array will contain a dictionary with the following keys:
addr - Address of this memory segment.
last_addr - Last address contained within the memory segment.
size - Size of the segment, in bytes.
num_pages - Number of pages in the segment.
page_size - Size of each page, in bytes.
"""
cfg = device[0]
intf = cfg[(0, 0)]
mem_layout_str = get_string(device, intf.iInterface)
mem_layout = mem_layout_str.split("/")
result = []
for mem_layout_index in range(1, len(mem_layout), 2):
addr = int(mem_layout[mem_layout_index], 0)
segments = mem_layout[mem_layout_index + 1].split(",")
seg_re = re.compile(r"(\d+)\*(\d+)(.)(.)")
for segment in segments:
seg_match = seg_re.match(segment)
num_pages = int(seg_match.groups()[0], 10)
page_size = int(seg_match.groups()[1], 10)
multiplier = seg_match.groups()[2]
if multiplier == "K":
page_size *= 1024
if multiplier == "M":
page_size *= 1024 * 1024
size = num_pages * page_size
last_addr = addr + size - 1
result.append(
named(
(addr, last_addr, size, num_pages, page_size),
"addr last_addr size num_pages page_size",
)
)
addr += size
return result
def list_dfu_devices(*args, **kwargs):
"""Prints a lits of devices detected in DFU mode."""
devices = get_dfu_devices(*args, **kwargs)
if not devices:
raise SystemExit(" !!! No DFU capable devices found")
for device in devices:
print(
"Bus {} Device {:03d}: ID {:04x}:{:04x}".format(
device.bus, device.address, device.idVendor, device.idProduct
)
)
layout = get_memory_layout(device)
print("Memory Layout")
for entry in layout:
print(
" 0x{:x} {:2d} pages of {:3d}K bytes".format(
entry["addr"], entry["num_pages"], entry["page_size"] // 1024
)
)
def cli_progress(addr, offset, size, strPrefix=None):
"""Prints a progress report suitable for use on the command line."""
width = 25
done = offset * width // size
print(
"\r{}0x{:08x} {:7d} [{}{}] {:3d}% ".format((strPrefix if strPrefix != None else ""),
addr, size, "=" * done, " " * (width - done), offset * 100 // size
),
end="",
)
try:
sys.stdout.flush()
except OSError:
pass # Ignore Windows CLI "WinError 87" on Python 3.6
if offset == size:
print("")
def patch_and_download_firmware(firmwareFile, langFile, platform, progress=None):
"""Patch the open firmware using the Official one as codec source, then download to the device."""
encBuf = []
officialFirmware = ""
firmwareSize = 0
foundOfficialFirmware = True
OFFICIAL_FIRMWARE_HEADER = [ 0x4F, 0x75, 0x74, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x42, 0x69, 0x6E, 0x00, 0x00 ]
addresses = [
0x0800C000,
0x08010000,
0x08020000,
0x08040000,
0x08060000,
0x08080000,
0x080A0000,
0x080C0000,
0x080E0000,
0x08100000
]
sizes = [
0x4000, # 0c
0x10000, # 1
0x20000, # 2
0x20000, # 4
0x20000, # 6
0x20000, # 8
0x20000, # a
0x20000, # c
0x20000, # e
0x20000 # 10
]
blockEnds = [0x11, 0x41, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81]
firmwareStartAddress = addresses[0]
try:
with open(firmwareFile, 'rb') as f:
openFirmwareData = bytearray(f.read())
except:
print(" !!! " + "Unable to read {}. Exiting...".format(firmwareFile))
sys.exit(-1)
firmwareSize = len(openFirmwareData)
# Check firmware
filename, fileExtension = os.path.splitext(firmwareFile)
if (fileExtension != '.bin'):
print(" !!! " + "Wrong file extension: {}".format(fileExtension))
sys.exit(-3)
# Patch the firmware with the specified User language
if (langFile != None):
langMergedSuccess = False
# Ignore languages (e.g JA builds)
if (openFirmwareData.find(b'IGNRLANG') >= 0):
print(" III " + "Ignore language rule found")
langMergedSuccess = True
else:
try:
with open(langFile, 'rb') as lf:
langFileData = bytearray(lf.read())
except:
print(" !!! " + "Unable to read language file '{}'. Exiting...".format(langFile))
sys.exit(-1)
# Check if user language is valid (per header)
if (langFileData.find(b'GD77LANG', 0, 8) == 0):
langVersion = langFileData[8] << 24 | langFileData[9] << 16 | langFileData[10] << 8 | langFileData[11]
found = openFirmwareData.find(b'GD77LANG')
while (found > 0):
# Search for the first language (English)
if ((found > 0) and (openFirmwareData.find(b'English', (found + 12), ((found + 12) + 7)) == (found + 12))):
firmwareLangVersion = openFirmwareData[found + 8] << 24 | openFirmwareData[found + 9] << 16 | openFirmwareData[found + 10] << 8 | openFirmwareData[found + 11]
# The specified language file matches the firmware language version: good
if (firmwareLangVersion == langVersion):
# Looking for the language #2 tag
found = openFirmwareData.find(b'GD77LANG', found + 12)
if (found > 0):
# Copy the language data into the firmware
for i in range(0, len(langFileData)):
openFirmwareData[found + i] = langFileData[i]
print(" *** " + "{} merged".format(os.path.splitext(os.path.basename(langFile))[0]))
langMergedSuccess = True
else:
print(" !!! " + "Language version missmatch: FW:{}, File:{}".format(hex(firmwareLangVersion), hex(langVersion)))
break;
found = openFirmwareData.find(b'GD77LANG', found + 8)
else:
print(" !!! " + "Wrong Language file header: {} {} {} {} {} {} {} {}".format(hex(langFileData[0]), hex(langFileData[1]), hex(langFileData[2]), hex(langFileData[3]), hex(langFileData[4]), hex(langFileData[5]), hex(langFileData[6]), hex(langFileData[7])))
if (langMergedSuccess == False):
print(" !!! " + "Failed to merge language file {}".format(langFile))
# Flash the firmware now
print(" *** " + "Flashing Firmware: {}".format(firmwareFile))
# Check if specified FW is not the official one
for i in range(0, len(OFFICIAL_FIRMWARE_HEADER)):
if (openFirmwareData[i] != OFFICIAL_FIRMWARE_HEADER[i]):
foundOfficialFirmware = False;
break
if (foundOfficialFirmware == True):
print(" !!! " + "You can't flash the official donor firmware file. Exiting...")
sys.exit(-2)
## Get the codec source firmware
try:
officialFirmware = config['GLOBAL']['SourceSTM32Firmware']
except KeyError:
officialFirmware = ""
if ((officialFirmware != '') and (os.path.isfile(officialFirmware) == True)):
print(" *** " + "Patching for DMR" + " (using " + officialFirmware + ")")
try:
with open(officialFirmware, "rb") as f:
f.seek(0xC2C7C)
encBuf = bytearray(f.read(0x48BB0))
except:
print(" !!! " + "Patching failed. Exiting...")
sys.exit(-8)
## Decrypt the official MD9600 firmware
AMBE_SECTION_CIPHER_OFSET = (0x6937c % 1024)
for j in range(0, len(encBuf)):
encBuf[j] ^= MD9600_ENCODE_CIPHER[(j + AMBE_SECTION_CIPHER_OFSET) % 1024]
## Merge the section of the official fw into the open firmware.
if (len(openFirmwareData) >= 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] ^= DM1701_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(
"-L", "--lang", help="use <LANG>.gla file as secondary language.", dest="lang", default=None
)
#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, args.lang, 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()