#!/usr/bin/env python3 # -*- coding: utf-8 -*- from __future__ import (division, print_function) import sys import time import zlib import copy import struct import binascii import hashlib import argparse import math import zipfile, tempfile import json import re import os class KFlash: print_callback = None def __init__(self, print_callback = None): self.killProcess = False self.loader = None KFlash.print_callback = print_callback @staticmethod def log(*args, **kwargs): if KFlash.print_callback: KFlash.print_callback(*args, **kwargs) else: print(*args, **kwargs) def process(self, terminal=True, dev="", baudrate=1500000, board=None, sram = False, file="", callback=None, noansi=False, terminal_auto_size=False, terminal_size=(50, 1), slow_mode = False, addr=None, length=None): self.killProcess = False BASH_TIPS = dict(NORMAL='\033[0m',BOLD='\033[1m',DIM='\033[2m',UNDERLINE='\033[4m', DEFAULT='\033[0m', RED='\033[31m', YELLOW='\033[33m', GREEN='\033[32m', BG_DEFAULT='\033[49m', BG_WHITE='\033[107m') ERROR_MSG = BASH_TIPS['RED']+BASH_TIPS['BOLD']+'[ERROR]'+BASH_TIPS['NORMAL'] WARN_MSG = BASH_TIPS['YELLOW']+BASH_TIPS['BOLD']+'[WARN]'+BASH_TIPS['NORMAL'] INFO_MSG = BASH_TIPS['GREEN']+BASH_TIPS['BOLD']+'[INFO]'+BASH_TIPS['NORMAL'] VID_LIST_FOR_AUTO_LOOKUP = "(1A86)|(0403)|(067B)|(10C4)|(C251)|(0403)" # WCH FTDI PL CL DAP OPENEC ISP_RECEIVE_TIMEOUT = 0.5 MAX_RETRY_TIMES = 10 ISP_FLASH_SECTOR_SIZE = 4096 ISP_FLASH_DATA_FRAME_SIZE = ISP_FLASH_SECTOR_SIZE * 16 def tuple2str(t): ret = "" for i in t: ret += i+" " return ret def raise_exception(exception): if self.loader: try: self.loader._port.close() except Exception: pass raise exception try: from enum import Enum except ImportError: err = (ERROR_MSG,'enum34 must be installed, run '+BASH_TIPS['GREEN']+'`' + ('pip', 'pip3')[sys.version_info > (3, 0)] + ' install enum34`',BASH_TIPS['DEFAULT']) err = tuple2str(err) raise Exception(err) try: import serial import serial.tools.list_ports except ImportError: err = (ERROR_MSG,'PySerial must be installed, run '+BASH_TIPS['GREEN']+'`' + ('pip', 'pip3')[sys.version_info > (3, 0)] + ' install pyserial`',BASH_TIPS['DEFAULT']) err = tuple2str(err) raise Exception(err) class TimeoutError(Exception): pass class ProgramFileFormat(Enum): FMT_BINARY = 0 FMT_ELF = 1 FMT_KFPKG = 2 # AES is from: https://github.com/ricmoo/pyaes, Copyright by Richard Moore class AES: '''Encapsulates the AES block cipher. You generally should not need this. Use the AESModeOfOperation classes below instead.''' @staticmethod def _compact_word(word): return (word[0] << 24) | (word[1] << 16) | (word[2] << 8) | word[3] # Number of rounds by keysize number_of_rounds = {16: 10, 24: 12, 32: 14} # Round constant words rcon = [ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ] # S-box and Inverse S-box (S is for Substitution) S = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ] Si =[ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d ] # Transformations for encryption T1 = [ 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a ] T2 = [ 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616 ] T3 = [ 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16 ] T4 = [ 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c ] # Transformations for decryption T5 = [ 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742 ] T6 = [ 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857 ] T7 = [ 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8 ] T8 = [ 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0 ] # Transformations for decryption key expansion U1 = [ 0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3 ] U2 = [ 0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697 ] U3 = [ 0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46 ] U4 = [ 0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d ] def __init__(self, key): if len(key) not in (16, 24, 32): raise_exception( ValueError('Invalid key size') ) rounds = self.number_of_rounds[len(key)] # Encryption round keys self._Ke = [[0] * 4 for i in range(rounds + 1)] # Decryption round keys self._Kd = [[0] * 4 for i in range(rounds + 1)] round_key_count = (rounds + 1) * 4 KC = len(key) // 4 # Convert the key into ints tk = [ struct.unpack('>i', key[i:i + 4])[0] for i in range(0, len(key), 4) ] # Copy values into round key arrays for i in range(0, KC): self._Ke[i // 4][i % 4] = tk[i] self._Kd[rounds - (i // 4)][i % 4] = tk[i] # Key expansion (fips-197 section 5.2) rconpointer = 0 t = KC while t < round_key_count: tt = tk[KC - 1] tk[0] ^= ((self.S[(tt >> 16) & 0xFF] << 24) ^ (self.S[(tt >> 8) & 0xFF] << 16) ^ (self.S[ tt & 0xFF] << 8) ^ self.S[(tt >> 24) & 0xFF] ^ (self.rcon[rconpointer] << 24)) rconpointer += 1 if KC != 8: for i in range(1, KC): tk[i] ^= tk[i - 1] # Key expansion for 256-bit keys is "slightly different" (fips-197) else: for i in range(1, KC // 2): tk[i] ^= tk[i - 1] tt = tk[KC // 2 - 1] tk[KC // 2] ^= (self.S[ tt & 0xFF] ^ (self.S[(tt >> 8) & 0xFF] << 8) ^ (self.S[(tt >> 16) & 0xFF] << 16) ^ (self.S[(tt >> 24) & 0xFF] << 24)) for i in range(KC // 2 + 1, KC): tk[i] ^= tk[i - 1] # Copy values into round key arrays j = 0 while j < KC and t < round_key_count: self._Ke[t // 4][t % 4] = tk[j] self._Kd[rounds - (t // 4)][t % 4] = tk[j] j += 1 t += 1 # Inverse-Cipher-ify the decryption round key (fips-197 section 5.3) for r in range(1, rounds): for j in range(0, 4): tt = self._Kd[r][j] self._Kd[r][j] = (self.U1[(tt >> 24) & 0xFF] ^ self.U2[(tt >> 16) & 0xFF] ^ self.U3[(tt >> 8) & 0xFF] ^ self.U4[ tt & 0xFF]) def encrypt(self, plaintext): 'Encrypt a block of plain text using the AES block cipher.' if len(plaintext) != 16: raise_exception( ValueError('wrong block length') ) rounds = len(self._Ke) - 1 (s1, s2, s3) = [1, 2, 3] a = [0, 0, 0, 0] # Convert plaintext to (ints ^ key) t = [(AES._compact_word(plaintext[4 * i:4 * i + 4]) ^ self._Ke[0][i]) for i in range(0, 4)] # Apply round transforms for r in range(1, rounds): for i in range(0, 4): a[i] = (self.T1[(t[ i ] >> 24) & 0xFF] ^ self.T2[(t[(i + s1) % 4] >> 16) & 0xFF] ^ self.T3[(t[(i + s2) % 4] >> 8) & 0xFF] ^ self.T4[ t[(i + s3) % 4] & 0xFF] ^ self._Ke[r][i]) t = copy.copy(a) # The last round is special result = [ ] for i in range(0, 4): tt = self._Ke[rounds][i] result.append((self.S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) result.append((self.S[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) result.append((self.S[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) result.append((self.S[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) return result def decrypt(self, ciphertext): 'Decrypt a block of cipher text using the AES block cipher.' if len(ciphertext) != 16: raise_exception( ValueError('wrong block length') ) rounds = len(self._Kd) - 1 (s1, s2, s3) = [3, 2, 1] a = [0, 0, 0, 0] # Convert ciphertext to (ints ^ key) t = [(AES._compact_word(ciphertext[4 * i:4 * i + 4]) ^ self._Kd[0][i]) for i in range(0, 4)] # Apply round transforms for r in range(1, rounds): for i in range(0, 4): a[i] = (self.T5[(t[ i ] >> 24) & 0xFF] ^ self.T6[(t[(i + s1) % 4] >> 16) & 0xFF] ^ self.T7[(t[(i + s2) % 4] >> 8) & 0xFF] ^ self.T8[ t[(i + s3) % 4] & 0xFF] ^ self._Kd[r][i]) t = copy.copy(a) # The last round is special result = [ ] for i in range(0, 4): tt = self._Kd[rounds][i] result.append((self.Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) result.append((self.Si[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) result.append((self.Si[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) result.append((self.Si[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) return result class AES_128_CBC: def __init__(self, key, iv = None): self._aes = AES(key) if iv is None: self._last_cipherblock = [ 0 ] * 16 elif len(iv) != 16: raise_exception( ValueError('initialization vector must be 16 bytes') ) else: self._last_cipherblock = iv def encrypt(self, plaintext): if len(plaintext) != 16: raise_exception( ValueError('plaintext block must be 16 bytes') ) precipherblock = [ (p ^ l) for (p, l) in zip(plaintext, self._last_cipherblock) ] self._last_cipherblock = self._aes.encrypt(precipherblock) return b''.join(map(lambda x: x.to_bytes(1, 'little'), self._last_cipherblock)) def decrypt(self, ciphertext): if len(ciphertext) != 16: raise_exception( ValueError('ciphertext block must be 16 bytes') ) cipherblock = ciphertext plaintext = [ (p ^ l) for (p, l) in zip(self._aes.decrypt(cipherblock), self._last_cipherblock) ] self._last_cipherblock = cipherblock return b''.join(map(lambda x: x.to_bytes(1, 'little'), plaintext)) ISP_PROG = '789cedbd0d5453c7d63f3c27c9c94910141b3058628b4440b92dd78a8aadd60b4a8862ebb57e807a6b0b3d2062d14ad552dbd21292438c68911e2028d82256b0dc5b6f5bd458d12255c4da8fdb2fc57e68d10041c50f2a105030ef9e734e02a4dee73efff5bc6bfdd7bbde275d3ff799af3d7b66f69ed933670e1dbbefe6671f5e24108186fed62f983871fd82004024c664b30821363b7bc4faed0b27ea2289285d14314b378b98ad9b4d44eba2098d4e43c4e86208ad4e4bccd1cd21e6eae612b1ba58629e6e1ef194ee29e269ddd3c47cdd7ce2afbabfee78617d9968e27aef89681142b77e002c228002168980021689810216498002169140018ba440018b28a0804532a0804572a080451e40018b8601052cf2040a58e40514b0683850c0a2114001661db4495757a1bc2c430ad1273f285e16f5ad0f8aec5010be6826f140fe4b010bfe4aa44b9b3dda86b73f706b54e7833d0ff59537553457b5ed6fffe4d6a1ce233d9ff5cdba14d3127b65fef5851d715dcb7b9febbf7ca9b5e5ea951bd77fefe8eebad37baf3f2480f00e1937d23b64fc58ef90471ef30e09887a2064dc330f848c4f7c20e491f4074202743e21e3f27d42c6eff60979e4639f9080ba5121e3be1b1532fed2a890473a4641f9d1507e34941f0de54743797f28ef0fe5fda1bc3f941f03e5c740f931507e0c947f18ca3f0ce51f86f20faf0f0a987c3bc87bb2478e8f885e42a29b0113178f2ebb39b169ee7a6fdfc9e509b3122f278a5e2e7f79d6facbeba549154931c9adc9d2572a5e89c968cdf05855b52a36f56aaac76b55afc5be7ef5f5e169fbd3e6afb9b166f89bfbdf9cffd68db728689b3140e74d8d25461ac7ea465281c403c640dd03949a5018d53a05358ef0318ed3f9504184af3148e74b0513a38cc1ba515408a13486e894d478c2cf385ee7474d20461b27e84653a1c483c650dd83d49f087fe39f74fed42384caf8884e453d4a8c313eaa1b4385110f19c3740f517f261e36fe59f7b0822002d6a38089bcc6468d9436af0f89ec60b4b4ca8e488d19e26d594d336e67444f5c8f167877d59abdb998b3e50984268ac8ce526e4448416a3ccd19a007c68661b4a91229c5284b2116ff8b168b91489f9d85d3f40d94077313f337238e439f994259acb1f22bd397ca64cca3de834b019427305a2594514bed525c07d4f22a42e2393de418ed742545f07551941962f5a76594d79c527aba9f1f119e42114448f1097f63b7e3fc09525bdc5055488f392882a72fbf5aa40bf2379288ee3c84fc8d52a4d3f83310a25b44ac2f25d2e5ef7837aca8e39e7a1f85d4ef5388d0646791a458dbe538fc4366ad781f250d270f2186542392b864e1a5f1ebe7a561e608f2dce3e365f7409a62994b1af5fb4a02e4187550b275918242a21ad21219667493236d400e521b5654fca5824422e66a7e4c71bc6faef5a1c6fb4ab6d182a5126b48906cdf0fc9b55b17618ed3650723e90de7913f25c3dc3703f7556d88e71e56b4e3dd359670d9212c276e4f1945f5d612237768c723987962188db45d2de940c218755c86512613f911648db69ba5b43fe5ec5b9fa8033f2c06147ff9e3225d30ae99de700e6a8536c5f833105adb82d85150eb3ba3df3d54983ea4055c8f32671123c53dfa45eda6f4e0f6fef82ac3464de02d69a7daa703299340272427779b23b12468774bf3180dff9cb0eb49e1a9ec3d9d2648783ebef763cd58415f0326d6e378ee39e1cff99a09c2735958a1e651a74e3fba43f39833ffa3bb34539cf91f512570ba37cdde54c769e11eb1fde26e5e1f45f6a6b3fc1369bfc8f749e5c5525a0ebd77fe4b75052572b611741bd2523cb9bc11f69f75317c4cc83c22c6a4e16227da7fe67b39eb97f275a20d9793088d067a7a82d01e943757788accd76149f9b69b55695ce9c7ede770fd7b18eac70f788924f6733f690b2dc4c8c0b6426da86122f260187e3c91733cd1577aad0ee13155a34e342b098f2c315241762069bb204b9f58d3013948134edb946ed2aa3d3b50493c49ec01cac63388b5db4704922bb6493b036f85354f6a528fd9ede4fe6b7972f6caf1b2b09c7c8da03f5f93d142da1d22c83a8cb9875bcab79318c9822d605de3b84bdcb8b7ab7d9d7cb3cef1bae9e49a75daa995984700f9dfe111f9fd501e912707f3c8eaffeff040df0ce5818e0fe681a4832d27f24ba73d09e163cebc2bd2cb9be6772ebc15d7bebcedb9e69ce6e0bed89e2a53d8964906d603792b862191f975d094dcbf5be4ed1509d2a4cb1b666d2adf247aedf2ea592f96bf48681281076bef1d31dd7827f288915edf8cca8d25060a8d290d36d11b4a90420e53ae6518fa9e99cff84be528dbcc2ae5221d5b63181755237f9c98b9f352df0acb98f4853d8a9d52515ce7f25b2fb429965f2594cf82bd793ed5217f317f7579db99a6f97dcfb5e7b45734871ac24c9f980ee5beb0d37f987f60c9962d63e9f7e4a8352106d63969eae54db35e2b7f4df4fae51767a5119a202c1b8c6a89410ef36c04c14a90a8504bdfc810eb433cd06573b6719b96ef6989d0d306a4501ab8defe294feddb812a7262417bc63b7b7c376b9680ec41a53377d49876442eb37cf81fcaf7c7aba53c9742cd9a5a96fa483cf3246fa721ef17d0d84ec45aea7d93b6c66c43ca57201473a80ccfe20de8a73c3e1cbc9b14c281a77421a59fababe24604e79c619c321dffe7410bf01d36f36409d43cf314cf5dfbae937bc8bb83b99fdb39947b4c89933be6cd52de81eaaaeee11acc51ea94d4b8633bcc28fc9aa9dd6186f5555fbfa468574c8dd28e6a8a0ea1b37966e0a53f9553345dd928c8a9fa5c1d1a3762376ef1f0a19255160c704b29e0b96dccff23b78a7c9e1bcf6be588c72c4c0cace32805edb910613c7cbd3f5e415193742d8a7abb233b4b1f43a28379bb63fccf2911935b43dd8aeccdfb38af7faa2ea4bf9b24fba75caa7d3c3df656845c86027b823bc325414495818c6161a6916be6b74bfbf65004d23f6e40c23cde00fe86779704cf3d2cf9055151288cff4b2514ac5e5f5222d553fe4625ca56ea58bfafbe31abb32a91df5375297514dd6543d08bc355df5e5e376b43f906d1a6cba984662c9ebbe2ad66db3d9f240b2541f994c2188d2accf4ce56e4d3a66ab6c0caf85981fa1f1274c5a26af36baeafc516eced256d0f6cc36b4fa84198699e35b97431212190e17d9f04a684241184f839ee39412f965d4ee067d42a063cf898b131728d7236eebfd99feb1b7c91b88144e5d44f792546dbef6c1125ae7bc7574baf6c9ca98c07bba3b435ac39181d3197ce5e965e7eaba2fd4c5b4ee737cddf37c5f52def79aef3855b2bdb57b7ad6d3e629e640a335419b28de2fa42184f06915a3c4be91bf6a2cf294c0fa2cbd4173086f4063bda16cf941547ca7347de5474d94708d29a88a0fcf8b12da591ac2d04951ed7c54fd9a78a32e576fcb25ba3200dceb65e126b1723bd361a114181d48a3cbd3609f307ba01f8631da10d952253fc1ed48b2ad64937b426c5a456a44a575f7e6dd6ebe5af8bdeb89c366b4df99a64e80bbdf647a48f3e092dafcfd34737a3cf259876a2cb920b5b75e33e8da777750a7506fc43a8db4c0465f6eeb5e8b505604d062889eb7d1f89a30f800c3fe5bd2059b19518c7a484a714a07e6566de685b6646d07b966e07cc38ad88893f68897a71f78bbab90acfbb0e76eb5d07b966e616269659539d6120989b35c6c951f49514b173f4b2ca88a0d17585f1636a85701e5ea79638ea526a4c11c493a5d6961d7d1dc7aa330a886d36aeece58c81b2c544d0a60e4153b7f2e576a75cb25467441397ec38b7f55265bf3377423e1114d52fb4d5c4e7ae313d494c376c43a9b5780cac9ba9667dc359243e538ff0589453d5362d213ed382667954a7dc81f82ef48247f8ea3b88082d21b708fd5676ea3f8d95b5b4b219f7a63eda8098d5fc48bc8ffa1f542ec15e7750213f26078431a9b62f263ebc56bd693941bf23978e4d8b92d4bda8f0ea73306bc8794c9ab567ee85ba947e654bde87d76aa827a2e8d60c2a5868e1f15c68953f79d57ccbe1004d7b0db731d9c116c2ea931fb5b2bcd0992f723bdfbbc74a9faee56568d926dd50b1eedf69113f0bcd3e556d7f9fd86de56abd6e143bb90564413dde64335f6bd6fac1b58e1d546bd3e6dd29a916dcba4207e661bd66b90d695ca9b20c28f51079910f45be34984730430217a1af0d35a619448da114b51c13c348e91bea11d7e70d2d7c9f3774097d3e526b2db19ddb3c5768716de2a7d95951235932523252033b20ced33cce9427b0e402892feca394e1dc4c6bd8948efdaae0f6d836a175fd82579a2b78b04972a7bf9bd791cccf332c4306c805ef35eb75a586db4b6d12b4eef10829eccba539229d86df65ed7d5c4879d89cc2f9f4d3f8127b5f55a6c890fa31fb346134a7e8347297d7092bc84ac89d736a8a528a30bf72932b1599790e648620f3246512d4299184b392807e652aa4c9e74ef25cc5b4a8165cff4db5a0ff173cabe2f55b2dc947665c6b39354990caeb52ad1e66ca586622c22bfd1e09e19a1fb8b918e7aea6c2040bf4781a66ee3dd46e84571612665cf063f97e4a061f16e61fd84579f2fa63edc77b4fd87f79d2de2d2205f9919750639f823cee559e0063c0e5a3ef2aa88fbcac7e2df784f43b0af25baf7acba6743c2ed2ce2a03115d026b0a6db320de6fc7de6cb5d180c4f5140a6b2625826cbde5c997403a697b687358931067cf4e0e4c16f8dec512f2758abbb15ca551b47f0bd2d9186d69ddd87dea3212f5c6cf6460b7a7a58d36648aa1df6e04ffa6df52a224092c6349231565f56eb967d2d6c1ba7fdc8b1ed5858aebc0fff2bc6261a5df7af5c75b479eedc3311f7925d73e393bd448a79f10b1197654673d76897d45ea7872cb91224faba72d8ab2ae69bc37f232b4437a22728b149f531515a56455203565417ba416d88903258102f6488002f6888102f6888002f61040017b105000f8825d5d23d822069dddc6169104f7afe8ecb66516b62804f175e464dbb282ef1465d529c7e4cd2cb2a69fe8d96559965ed15e650863ca617f91d3298cfe70b396f3b3167ed6c4683f6b56f99527cf6c3769677e7b744e681f4986f5847666adcab739ad3dcbef2919a93107703d7d481f2219c6e4ea6eeac16f64ed54c00e9b829478f0a9c7bfd12b2584c21c22a69fa6903c05fc48bf4e844b6ccac854f6e6a9c7777a04331365e5ce596e74ffe291973e88feaf7806fcb050f3948c7fceda8f57c860a63509af8782cdddae6236d3fcd8eb3baa8c0c256eb03bd638c299cac84c5f3c53043649c1f356fb1d44ead1f5b05724917a32603ce9a19e01348014a9c791a2b1313a58b5190f6196b96c56721e5fa7aa8f7f4ab1cf68129efa6634f34f4607ea99d186651525715aafe6b4fe88d9175299201f61b67d523893e910d28f0ae15b7c585ccf8735b79ce585f04d21fd84b30dc278f4cf98a3fb52a891b7b37e7e476ceb99d12e4b827dc1b8ce852c6558a49ed6b910efb1d413250b85b23765abc898bdc24eaaec862c4d1ef385103a7e9dd1ce389d6c51add6d9745a6c2dfa4692189d5ede73e6d6f76d397d159ddfb49f6bfeb9e9b9be177a5676aebeb5b67d77eea42d618650d3d986d235f50d78f4f48f7b20853d04d10f50de30a2b17416e5476874f38879341a365e7e7d79ce079ae79cbedf66da8b0c26c91a8f8d51715be9f57f23966f9525a8d27c16d09d95c1d1d1074be96f0d211799e01c21ff3baa8472703f2accaa26212698f623bd6bb6ec8c2435f40e0f8453f994e3e332afe8233c888a92f0612591d50673a47ada4e74b0ef83b2813ca8ad3ffeb1b38feed31f9123fd0113aa93f79fafc8a1d797a09f73662478beaa0b8ecb8965b2cd745c9907e655b5d3b4156b957a5a15dad4abd3b6ae8bd950b141baa935356675c56ae98b975f9ff546f91ba2cccb6b663272d0cf8861284b17ab8b56cc1b767c8505d7223e866b898eae305ae55ef7c4d05fb204f6152a80ce967ad36b76c9e079ce1767240b3cd36a24a522af6d255e248cea1874d55c73b2404c77752232a846b297506450912b4e54e096f01ed343be2d1f6bd501bd88b3201b25c176d71fff692d96819d77cd413cad9877d7e1f594e9a9b916cfb4b539a1c2c9e1f1742c45798a22c36e51a45032b55f3352b54bbebd61ae311c885235459fbc6abc6aa0d7f7a3ab4659c2cc9c73cc4ca164d6a6a5d1e52963de5b7ad22fe1088c08d7db3bc3bd76b97a5b07de5e708e53c680cc2ae38a0ecf3467ef2704f2d66eb763594d16551add528960ec5fbf20a2d7ef42570dc1509b20e5cbcf464f379a2355094b4fe2d17fe6eec0280648977d37c172d548dffe3bf1b3b1a6a151340dc60e8f5c4d4ea34864ae3258679775b71cd4d740ffd79810a1a99317d69478f8a3f46a7adeaf088ff473dc3843ce79653f671e7b21e7a2d3bb7af1d9e8cb861aa331ead993aa84cbb8e61a7ce610dc3770e610db33bf3374cbfe2dac845c484493f3faff666a0f64fe78da80cf1ac49f82857c6040fd8fd33fca9121e1a75a7dbc9ca05fbf8bf4ca09046dcc20ce300b99ec9d73c12b57c84dc32ed58e49cf692f6fab68c667080b7bf009c373ed2fb4ad6c668d1b61dd6a12d1afd9456c27e5b0e0f35f98f7e84eb3e863adaa8ed0dcef54a13c8d3b55902091f5c681be50933092892cb5ee61f33298414cf287e826b3846d9c8c74217897e8af54a2e1d47c334a61a594831ef523422f9942e8e146892ac92fcd67554749b9b9f886cfb5e214458a1215db12cb148d05b08bbc2526beccd7d22d3619db98844a8c46b11b379272646a68f901e497945e122e3547d20beb3dfa239810f101b9c8bc04cf9f9a51b4e880b75f527f8c90e3d75332d6e68354cdd5d2026486bd9efa68a30f470f34fab029a3d0d61b5baf96b6069abdad4ead3bbe186667e2ecb1efb02ca884b2907b617d8f24691bec751b4f826436e92e0bc4f8f2319d109341e5d7b2d2c8252cd544595b5a3bd9c6714489b1817ab2d6278d9e2345aa5505374baf5653cd688265aa614aad390e24b01cf2e468ed210f9c27d0ac4a616d4aa4b2ed025eb71e161f3621fa72eb282f3d11e21f171249863031b837aa8b4e713d52dd2a2594b87cf839a90fad80bef66ef76943d6ac1bfe8d907b76b8c28ac416e0f1428ab4fa9514b4a3b5a3442ac64ff9212d1733e70ff46e9472bef989cd3319f1e4cd043d5a329a0c625f9982a2642d77d8ee0844ef92aa301f567bdbd19b87f7cbea27ecf7708c544c27c14a325aac024b21c4077211fbda49076ddb89c28d3b23fbb5f4d7b6507ec5d51af97d76c31b59b40fdd1f1fbef56f4470c1135be85ddd7ee0a98aca5df35242667f7c682e3da22b80dec01074c9f4b1e6341962b7fe63b3b8d2138d7d8ff340f7db72e851694a56f994987e871aa5b7c889b80245920fcab209fbf9b7f490db8fa65fb1234ee2c7ec6ff229216f42ec3abb4a68c71b4baa147166941f435f3a2561e35250494e8ed83f6714f21f350a0d97ce2f52905287f70bb45fbd94d4969c0b8eac1ed588b89ec7e5a736de90b57bb7a12be846fac1ef62e89653a8447a88ac03ff0febcb29c4c63500c75669268ef1e563ec10f30a955eab20415fa4a02fa9f53d6c5c085192738a5a66f15e4d4beb91cfca8e9dd6171befc9d66a2c3e49de691d3bab371745d27f7b48cc6bb36a55385580ca8bb6df545de5b4f9635b1b473fb5b5b2363fe479d3f39acaa6fe6814f16835fda50c7296aec4ba55daaa5a85cb8cac9964187d2c4ae9f3627f84f5edd626af63f921bc6eb45c6ca9ae325ea8255332a9270afa3332b7161ed44ff58095812eeaf6e0eaa84e4954bc7117e9b4b4d413d569ab61ae9b594226876fa944cb0b327dab33de47e11e7722c35fab42dfbbc6b5e979e030ba0b053234e9099e8aa723b09088c5a3144e2647d518b544e84e653c8c6d807db562f583bcad1e49f9493d791e61cd996ecdd4d26be3c58c563d19663e18efd2948e927098dfe91633024d3b6d93b8ecd76fcc67d5546ed4a7ef71fcc6db934cb57a4b2e5218b6dc8b2a6672ebf2d407eea2bd31d5c65c54602bb866cd30feca32c308d71a7f74630dcc2884306f403f73567aa0fb5bdd67d8a30e2da27deab9b7287a2d89acdb2cf748b2df52ddb017edca0da7ea6107b14b4bdf684055467a5d8b87fe1489188a21f7e435deb35eb5dd83b9f4958d90967f66700adb9d81fa2dec2b46872247da47df6a90eaab49a4ff8441fa5a9210d73284fe335224fe8c1189ab49b1fe3023661946a2fe2803890f330ef58178a43e6c27d4476d84faa346b1fa4083587d3845a43eaa1483c729511fa51c1b2ddb137753bb49f5d1867bd66b204bb3ad3b3b8bbebd57529d538fe8b7ce8a584db743a7ed57d26c17ec1dc28ddd483c87024b6e40a16635a2880b96706317d2c7401cd50592ab5123b22abba1453a6d8b7092a099ab5470dee548213c870f8b1f1c935ede96d31e6618ba2e7dc28c6e8f496a4d18baea0409fbb2136cd59649cdce7d6079b27eaac10133ac88356d24d8611287a2f79483be1321c62b99e2aedcc1af662962f06bcaee202216af670b1977deb02b1d06abd9da9a3e3a3e758a302f7decfd62b5f14f44710a2deb468a8d36c7cc425932691a7b3dbda45a7e05d1dedd1205394c449ffa40f27349b529358ad7cf8c7fb25746836e4c072fd6e0886302b7b0bda351f59554423d63b243d0c79b7b6be933a944f14ba52f16df545dbb6898528eef18305be87c69a07eb2c4516d2889141f3120c56b671cb13be91ccaaf5aae256855b7074b419ddff48e2a5e559aa6909a1c8a2d124275b3f89a82ca92d0c90d3256ba4ef4bd91ee3f855409a56daaa4d2765897c5b4b5411225618d12915f42b1d52fa9d8e6b7aaf85ab53189c0927333d793f6725ef208a23489c9adbe9242a80f4c26e29cfbb7e6ef0eaa5695de52a59576d2542b3e4d10013fb1a656b50e9ec5a53de208180ba34452dc975e5b0e1e88304abfa4d6d2a752bd705b3979190981db8ce5c772b358eea6060f05b54e4467362090af0de46b5760a95fc2524309912a41754995a46a51ad525d294eaace498dca8c2173ab8d93097addb907ab53af212c6b359344b0ada350414b714a1d19c7f0de84d0e76f395bd2d488fbec420df4c02dbfb462684d37d71aa8479c885bc348247c8bf4d0a2d2be672cce9d66d937cf58e8c8d5f2d2b4e255ceb6b8da70a34172df36ac69407f6cc3f7a6476bef577fc77deabf3450ffa94b9638e1842c800edd621591ad388f82221dbd2517cd74b10de91f671c6cef178e333bd654631939d95a1ba49c6c6f3548fc12fc2ef925f9b570b2d10d220505fe3345123e7481d527a5c0e6f762f1b572c6d9570969c441bf557e57fcd2fcae73725230ea1429fa02cb4949c4aa0e9f0d1016fb74d559aa8c81c635b54e599b6ad6d49e318da9aebe72851b1ba76ff7e131bc0f0d358431e606ee9ca9dc79fad614ceaf07389e35369495cece3fa13a5e9e204ac2370e70a93083be308880fcdcac5c5644cf25d1b3abaacccfb63973e1dd40267f4a00fb81eb16bcf7e4cf8962dbe6374b3b593be55d6508334c321d3299bec4dc4c31448ca0e16f0c2dadf71d47883694affb262736a73c75566a30834f9684f32172883c7cf957fbe3f13e88f6ef42fd19fd0217a7648c708e31a7addf824b0967600f387be313666a0ed7231f50db9d3d824207f74865ded01e2146e219873586b40537571936d3816d52984d47d7cf986bb209e7a6845809a3aba4c4752cdb6d449fde60bb29c27463afb6f438ecd324789f96b904bf2529e7de93e4e7325f3e92a6fb9aa5a2291a6676e7d90d7a4a3d4322c1bda31e2741acddd28f5ba24a83fd1ab75334223ebc833b7b51502800fa793cbdd9067b69d769ef3a2ecdae4464836a813a5882f03b2ad58faa344273ccc19df8d829d9945f9cfbcebd16532ea1cdb7fa2d587ab2a3d6a53316e8a17dd49bce1e4ae8137ac8827bc89839b48706ce6e0fd6e1511fdc375937d3f06d1a7e17b9eddf9c1df1a7007bb174bbac7c9e21291f4ed2a42161076922e63e5d36d0de328f19ab424d3b1a3c5f0dc6bbff4524ca5c267097416c9aebec7deafd7abc6932eef147ded07de13acb0f8718292ffbf1dfb02c6a3f180b89c483c9e54ed9789b78dff9d454c4e74517391979ae6fe2f3d158d0e359a9f88c546fe6ad4361a6c4512c3e4bc556023e07c2bd45bf23f52653141495f5535eb1465ddc85e8a79bd0e165746c13f83afc49ebacd4728ed3b4a4cc46f107204f5184585d2641f4cf52d98255b8bdf9b93b1a54d163de9e91b6e3c48ca540933c5fd5c5e03ee98fd7c781b58ce942a5911bb574b25d16414506905478bc1d91b98ffeab3f8335dadff24bebca2b3153043e5bf56bc29a817550618e10d34b29ec83fae0f2f9d6396d8fa4a9cbc611c7be73efcb8000dc979f5a3c5f256218a859043587e6581fe8ea2e8dfcf437a899b1a1655abac026c1e5c758589b94d3c0cc258fd5ea3f6508eb16f92df1a724f2ca557f3485982e7f3c4aa71d692d7de6d993d6150137f1ceff4ccefc1c6e94b565aeb393ccdaaa1dd3e51f4476d43acfa963dbdc7530b2016b1631f7e0094e1779bd49fa37bac85b62e680866535e39ca1a67fa7b74dfa81918f7c7ee8c80fcc8961066927ece1e7e27dbb2950d0af24ec4f9329861496b2f7871b8c911dc5c1058f6565fa4c92d0cf37a358610d8aa4ddf540a7719d2695631e1566eb835d7dd56456a4333eab0cc66c4c97682067c27b62c8d91f534db5445a7fe8be837d6b3627a6a4e0057eefebbbcd1c0756ff61c5be440be6a9dbca36fb2083cd1c8f6d5ebbaf74762c331d64c4617595b142b0ef65a4a6d042e7900fe1fb37c1cda186fd0669bbd0be38fd41f05572240ee13d8982b5b738c273ba91df4af13492604ce925f498d3922366d315b145e2d8f1f6f6d547ccc56bfd6e785f53804cc5ad4a2d784c931bbd795f91ca871d64bb0df1f371e58eadb4eb5df495406656d2e5043cbf2a6f3b1cb0722cf06b25e6145ed21f9438cc7320ffe6f19edbdede6fdeba9adee22bd71fce019fdd97501f3e8caac956b435993115b61c31fbada6c7c8a4660d77222ca79516a9320624f8cb39194723cec98a6f145f25e7f0d2614f5d65b52a643d0aead6c374925da2688c40fc9901a3ad363720efe4eff2b89233bb45259412959895e86daaca5c4c2b7228c776e005dec0cd7c0b39e7c35aefb5de37fdaeaeb028ce99d1482d9d087e4e630af2a78c627fa952d8158716b1fcaeb8b85eca68fde34322c3cd36a44a5e9187df12a86b6c7d5b6f6cbf5a70c5e746fdb167c0d61ba03cde152bf0294a4a035234e2181bec8a15f814858bb1434c06ec8a59d8152bf029ca95fa1e456308e14f35f0bb62d8a5f8ad4c2fb15eb3ddf3845d31ef23b8d652bc52eca154ce9522ab72f04a617b70e84a31253db8653fb79a075e915e9f64a07f85bd5623ac77655284676ce6264ba1c831fb26caa63a771f0f94afb4fe95ecfb5dfb640a99dc9b274fd991c75a603ffd39cea96a02adca9a5006cf125513a1b99a109b5495e495c89f8663eb937662fb33e586194cff62a90d94aa49d5ce6855df3aed095bd36234116662c182bbcb93434dcef7a3912788ebc29a19b0c9de5182ef0ee0396b45add9e6002fbcf28da3ed784e9035c9da613692719e488aeadb77b4cc09ecd7b8e61039f105ce873928390e38149eda0bcfe388c2da3ff468152575f66859e1e01e6d20ffe8aff125b972fb2991eb2dab717039ade8fee584f143ce5201a621e3776f6829febfe1a4b911b79e1a8b3539f3373c1f119a497a911ebff50b6cd35c0f33f06fad52962b818f82b2dd195b964f8174f7acd76df7f8b7c1c29e7f2fff265858dddf970befaecaaa8595b64019c4bd7f7946786bf5144f23170b6f9c0cc22cbc4f08670bf362b7f29cc3a190b6ce2a67088d590bad3c403de7a27c9e2e81d75c594ab6f96c6d68b3530ac15fc974beb38cec506a713b5296abe88ebbf83e30a1f9ce824f22a2462aa770f2153bfdd3b2503e666fb1109ea020b72f129effac80447304e74f161f318bab2924d29b63a0af732a96e8b4538d0a298adcfa02bd732fca56266a746cb5b152144eda1178b62197a456b6e11eae952f91b370a044fe757c47e8524cb5b15174c4bcd90aad44eac314c26f86b892ef35f4e9b45fd78e40b74610da3f47b47812c03333fe88f19f115d9e38b6ff6e76162bfb96e46fe3af0779b1ef8bc733b839b669fd84c88efe7867de6356e7fbadb2ce31979cb330b2cbafbb66e45ea1e7eeea23b29068bb2ab9d00a76d7a29e1644e8a25d3749ecd82b065f2195f78cfd52fb175b1f08713094aae54a6dffe2af2dc23bb38ea16fd59a3a9d1ccada47d7bbde124ec3b79306bf6bd377e05549da8ed7a5dbe3229d77563a9ca58fb70d481f706d407a7443987d6e3aa537ddc4d24b06af33bc64d7fc52e9c26684253e6b7195bf6a8e861132244d2b4dea77b85a7b6d4defc02e801819dc8c7b37b6a9ca807b57e82fd79b5cf45ba861e41597a72a685764ab6a15adec15a992fa95aa667ace2d68b5045a0f982841583611eca006c977c9e5d90aefe2032e5fa9e5d76a693bf46734c272068b351234b26540bad20452926dbe6e714a09328e431f7909761a394fe44be4879385228b7604be013e2e7c4997e87608fa489767bd5e798fe7c3dffbbc1d12d94194c17fa0b7c4d7e6c6e7f1bcf22bdd5e89b835ebe34a9fec2ce54139580c53939df5cc12c885ef494fcb919943b875fe0d62a422a367c411b3be8142816d930ce0950a3664db72c41cdcac073ddf6f98f1e6b4d726bff248daf855135f96b667178f6fc352289868c4b728730aa3a57b2b450a32eb0e6e9fe94b713d8926352b4cbe28db1cb8f20c93e933f2784b97736598c40c7870d8ab9c24f4d5fb8a2bb513ae8f49777fcf83dffdac6cc6774675d1ca5898013cb678f72f57ce83a7612f7a1f9c37e1291adf0e55c845f8742c9891a65624fdf17d0f2f9d3ec2036d6b004f975c22566ce81aa14626a48f188658cfd18891ec3811085eb0d56bc35deb30c3dd95a6fed12b2cb8445d6d503ddf2f2992629a18f9710a3f7633cde27146873ea4c1516510f9642b89ed5e9a155b85f935cc62bf232a3152b29af82e51846604f6d3c65d4ec0b349757ca76856527863a5a8da5e292a86f5b5b459d05d1821a62e6a6418a390664975f8664f13779fe84a79026e015e2bf896f0eb0c3f873331ce3932e1fc503ba51b611ee5e7f965ce3cc7fb78cd71a6ec5de44c09b83b34855ce23a1be81d9a92fc57d75717763e05fc02e4a5dd964b97da907e1f8914f61e8a16db45bbb4e46c1ad9452e39163a4b46760a1a3cbba4710ee17fde12494b65f8e6ed5ff0a9a7fff9d34891310d9173a6fbb521ffc61e94993b9c9a64f6039f7da659a70d1f5e89c4f387a39287a620920c7fe822516d6b20fc8793c83a7bb8c3bff155c8af28f4130fa7e879a4881e2d169962e811e7f17b11e2bc8889a1479d07afd108fb3263d41345aacb63f73977695fd7ea42fa2d56a9f8de91a2d2cb0a4683b8d58893be25cad5d36d6eed7ed7868aa314e4447c1f86c2b71915e43a11b43dabd2d57632dad59fcdceb693c9fe4bb544497c06227cc33792447f2ebe6da9386b4125321992d7bbfa63a91d295eed2186cb26158ba7c09ad9839fabccf430b1e8f3a2cd2ff8c7372026664cee74732bfe5aa2682a0adfdc88c2e3eab92f224e23ff1c29b26a5aeff1ed9955247ba1d7e2948b99e2940bfdc2cbc5cd20072a7f861591f37f2bcf83dee115969b398c139c2b2c58fecd4ae4415a20003ecb6c989972473714cf26359b4a8a4fc0dcb064f029c67a582960469ec85259b2db16edc4db2992890a2a4b144e358bcc959c5ff4eb678699669f974ca764ebc39ab38bca936f6748269a52c08e36dfaea426aef79e8b4b8baaa94e5185b9f8452f6b69ca8e7f45689168b8e48902fe1dbc739e09e3e619f060b9ba0666dcfd062c07fefe08d7ef5ab16e3f2051ee95617bf83eb05095b8e3842c39db3cb4f6db29d113f97595b7d615e91b3539ce9e7b03fc05894ee3c1f0169bb59f88c1375714e23d4f2864c85b111f17c98e0a46ac6f082a453b8b3e2b2c9d0576d2a7d8d84d949c3312cc969aa20a54dd3a8a184ba9cbcef679500568aa591f4cf595349a0926b7c65c89cee6a903ec7d64b0f541e9dd2d46421b41fe69c1d898ad8bd85121a8aa485d7668f8d6d96408f88365873cadbe0df72a12f8dbfdb08b125519e9d5a093d4ad11994be8951651b6af32847b4f30b15777a4b038ca22ed43fda330c72db08fda98577c02cf4578fc58284ddfa8940e78fdf39b1736614e6106f06957592430221431977ebb4b546588f0f8d302568ec4fea41cb1a05edf9b4b67e3dbf4f44e23a265858825b328a757434eb08e79bc7fd0195b974e3bb01f17ad9ec98d603f77670deba5c248892c52e419484d35ab0328a4301bd19122790a8402c12b1b4721bcaf156ba9bdbe560b893c15526f09ee9bfd45781f4486581f6cb8476acd78df98d35ace1635a2080a794e355ba401c087db2b49d12cece9992bb01f28fd54210d901c32eb62d41f51d8bf454c701d65dd79ea1e2ec1520ba453cd8b67eb82777cde1fd1e260b09e64ddaed44ec47de62da15b2a45f8cbb792f825e841ea88790b1511823c611f3e9b89c163bd240f7fcb35d0cb50e26a2512f9822d796ed2cd2cf4ac73a6716da70224d5469b08fb07ca4aec3387fcb3cafc0ef0dd6add6ee3f24c5698b5627a01e5f178bab40fdf928fbd35bf1d97bd0d3687fb4d51d088a61644500128d01c3d5b3c8142ba718a2289887847e11b0c1a54308b98a00bf6390136295b1fa19db89eb34fbe5ebe4e5be5275c9dc5b4bc61734a18986d79b205f8cd2c883eee66a9dbe74e54078c23066ed18731f8167d188c658796be5c395c24686065ec26dd1385aa28c674c49c6d8ca0a217a8fed65b72619be72fd95938f42c177aca152a29f4455d25fd166728085d2fa97785a6a00b255fd43a431a74ace43b57da12bcbaf42f71a526e3f0dd25aef48d386c7fc695ce7db573f319577a210eff56e84adf8bc3b585168b347ac1afcb33334a0a0f427bec6ff5e65dd8f615cc9d7c7c49613dc2a7f2d72dcef059b429a3bed6196a41e9195fbbd2ba5062c677ae349288caf8c995e64b1019c9aeb4202282ea7324bb52a740b8c791e84ad740f89623d195be04c24d8ebdaef464081f77ec75a56f84709663b72b9d813072ec76a517e2f0bd2057fa5e1cee0b1a68150e378dad05bb10d16d9522e7f8dac2078f2fce5d1a8339652a314fdc575b4f614de7f8b8a57c75ea02c7bf34d6adc4995e6e5454f19ca634d609a1c1ba814303ba814303ba814303ba8143437483e3fa54e387fcc80fe1faec10aecf0ee1faec10ae38c46bcc1257aa508b2b5dd030573aaf51cff07d1ae7a651e7ae1ce3e3dd342a6e8846c50dd1a8b8211a153744a3e28668549c9b46c5b934c895ce6b50a22b5dd020573aaf417b5de9bc86ec76a5f31abadb95ce6b58902b5dd03057fa411cee19eb4a1734ccd57bbcfdee38e60cf3f6bbe360d448063c67ec1f0bbedaa71334c2db8409bc7facd3383de43d16962a13d1d64a29becded4f05392e68e9269b089f33da504443080a5266e691d608ed79949937c6ba237797cdf90ddef1ef9cdf2cf35a84df24a91abfb6384314f175adeb5974c182e7f22b5ada5619ec9afb86f1b6e19af9166d33616d6b29f93a4ff503b60a3e769709eb196bcfe86bb10cc470edb7fec4f54fe9c2cc8c5d26d09587b1d6f5e67d9d57fabd6015cf1f33d5612bf94d53eb0c633d85b2b7a096df52f9f2cf1d33617d63b0be8de0ca5f8cb23853b0c66940e35ab89863a6d2e7b02645501d8e2e7e349ee76e66dba95b6037bf0923fc1c8e8b1ac2f3d78b53b8349c1241118e5f393e99cab34209185f025b39c35bffc57e6e6c67cc1ee0fec889eb0771dea3b3fec8fde8e7bd0707733f3a6b30f7a331380ea7e11a48822b712afd98336d70cdff38f5cc415cf31bda819ad31a182e6f5fcc1f6bee3b957f6c70cd7d31836bee73e3de7e6a0cc77dc6bc41edfa52ce4b121b95816331675c0327e5990b359c34b33ff7ad7ba52ebf0b78a49df8ba9a8fc3357daeac63f99198a11dc8f34843660dd6b97c2d3e1570ea5cc8b5019dc31a26bc11f62535fc935a24d75ca8855da64848f189a8df4ac0ee65df605f7e7e33f8fe649747d0d7262d8dba3cb9bf37319710ce31b25095c9750bd951c5cc77de44cae1ce80f9b38e7b55aefbab09b55506e7d770c7fbaa18671ea4174e0ebb88a0a16f6a2cd056f1074664d06669159b7a51c98fd188edec14937246e2bff87df4e4662c3529c1721fb4f0b91bd049edf13fe486b95b025631c74b4b684a1a2b896db9d65c5b8fb07691b0d3a9a79cef1faf89274808c91c34c7c903d784f93cb99908c2bc48096121847edc739d9b59da2b1f71f67cc38f7ccfe39926b089ef7fe709f0e05dfbec3e5233382cbe22d7d4f3522e6072e9b7a9604273a9d1b933572dc03e6149a3126dcb6542c297742122688d058f9fe067f7e0d6973e7f2c775bd9406c59371f5bd2a8451706e50ee872c65344efa0f8b2dbcef806b4a27650fedfb9f845258d116817d43f7d499768ca607eb79ce929a87e30bf1bce7833ba6471be3d4cf8d9598b11e96ae5ceefa27fc2b17db3b1968fdee78c2d6be463b1a44c087e03f48cc599d67416a71dd5f67223bf6f9e6263d70832a844e98bb056f8834ec0aac4a5f82f61d0d716613e6fc671bf3e8ff72b99425c96958fc312b5087191979d7114f19df09d1f34024ba3bde40c37f1e10e67f8373e0c7c0853adb3edb067cfd5fd8bd0e437086dfa9508e2795faa755a5dbd2b77a35beee33ff1b98fcefed4f2c7dc36b7dc4de79cb97defc3dbee961bfde8cc3d61506ee7d307d583668eef07763e78479d9d259c217eb579f64c23d9c2cd6a0b4b1a43a086fc7f99b4e6a9b0ffdad77d868fc7ef6d8e7e2f9b9d9d85f344409e1d27b641afe1901642577e6360143147bd4d7b9a3ba1e4ce201aaaf02e2473c9b3f340ee06d96c3cc2d959fa1452c47d95e5caa7dd77c48cff9e42a9168f2adb6d47747eab8897903a513a5b17626a50a5c26e7f4db6d9b99aefca1da9b5eeb4dd5325f637ca925b84da538e0f70b595e3da9f9d0773b7e8ba8308929d000bf655c49f258473a2329df68942bab001f93314cacef2493c1f4be66efca56016be71c8df34fcdac297c0dcd5e5d411bea466172f9bf6d3270a8b671321f85de3e6d944507616438a1bba1ce26924d207cf21d86ea9a8e51e11a20e6843c5b0ebe77af59fad073a2c9bd263dbfce5871c78b71e6a086ec73bf16d5afa6e2512cefb8a856f140b89b2422dfd50378219dcdc2d12eadf21bc6366893298e14bbad1d0f8c87788b2a9cd614d50e6aacd43a414faa39a60679a476bf4d1ceb9b2ec6276b2c86763d6f4250745e52b4b0a26a3f275fe71f588cde81ee1af3c8522ba47a0f43c6530fef64ffaf6a402ef84e9544ba47fbc163d1523daa00b625b48d953a79efd9ccdb08f507dee0f1bfbea886f11ce53025556db420805a4a8035a3debf3d4c152a40b79f6d4b331787dd085602f1042b3e0c946c98007b55dac3a85570de1bceb6de11d3b834f34b19f981c24757eeff9bc3fe94308b32ff7c65bff37fe6d0dfec21c5a7da5923b2357ffbdf25d46bb87ea40cac9f8ef8a18de95453adfccc822077f4929d8938dd722aa94d32225778ef626d64df57e4aa6d776707f7380695092dcf9ea778234a7f19b073e8e3cef7c07af3f25e86f59296dbac6a76abee14b240827d50def96d21d3f38bf2017f9b258377db9ef73336616aa12d5fb2d32eeef4c2849116e1d3347c72a8b618f2fdbf728bdcb97620b414a3feefdfe86fdc5e26a19d28f9f8d74e3d9b6f1481da820585996a87ab352fc79b1faf069a43e7a1ec611628c0d227fc94174840997f620d9f781cc24a15f137e0f5c092b3c896f0090a7842f7a7fe7dffab8ee165c37cdb1eef2bd3b50f7aab4a175fbb9d7fd11d47de03cccfc2bd2f17728f8ab14fc2d0aff65cafedcf5a101287319bb4c23562cfb02b1bdbd6f11a1e19b3e40ecf22e319d2517b1d4adb7e86dbde0ed0f975f54e2f7c6ac69f471629e2e3a681f4dca65aebbde19445009c53858f9b7c7c347f7a26ad30791576a694a2e6595c0577eeb2daf7975d1d67aaa8f6d9c27ce5cfc58168ce65be11b3610ec2f77c59f3628703d7a9b94d40e973ca75cb695352a8f135a76d49fd04f6f579b7a23fb477daa0d3ff51a41b7364af9efe6ea93b748f71795ceda0f5e826e2e39ef9805d7229fb7cbe275daf5de6b2d2f55b84f2762a96f8f9bf2365ae45fb1ca9388fe86c2df9fbce5a5ad8b4eadd56949ed33b5382cd7a65bf8f8672c0a5f03a25f3579f78fd6c5281693a2eaa526517fbc8ebd92a77b273cc3243ac584431f8b944bf2e8372822d4cc323d0e3aa512d11b298265da1ca1e6708a8a0a5f6a1485671845d53994985552a25946baaf11559915b0d3839d0ea2bb1ac1f36973404c01292acc5bb6b53ae2165a013dd0fb177ac9ade18a8c0dc8eb845c8befc4d092ab9e5e5a7af8550fdacf0329323af169bdb8118d9428941b107f5feab2b25f9909a5e5910aaae92daf2cfa793bb6cae1776430c749efc868d24396b9988534b67931ca5c82c7e1315de2b6ef950a9f6ef18a6d51db761c5f5fe683eeff6d123baa17346534aa3f1697136b586ec49ced78f614d97d18ad7594bd938826cb9fcb69b1b0cd064444e3be4caf25a3e7fb10315eda2f6ac7de1b2bc9af9df0f3f5da29f7f8d40f6b93ef31b5a476d735f6950d28d0b8eb73a1adc3aecaa0ade2abb240233dda03b1af406b35b4e110f456344a64155a252273494d35d515c94ab3c4f4e7dd32bc96e01b19a6f7aae3de4778d4c32767a1255b59e8c36d274c3c5ff28e04e41d714784fb90e5fb508ffb9055c2789fa7443817110dad115137d8e671a8e358a031ea8e8232a0544ba071d9f7176a37dd190b5e7122b4c22b4ba7b50ea7ae2aec3ea8aed6ebb85007714704755077906f342df54064f4e8b2a0e835f7bcb2303f9d36f56050f4c63bd72db85f701f103175d1bb8e0d843e3c787f6bbd0dd6dabf5801d6fa711678d86fe942af6c659781a58e9073da4c0fc396fab6e4a2f2c256851cdb2911ebbbafeea4eb36c893bc3d28c04aab0ba077b8fb6bc74eb2e69348c1d96854acb5e1fe36bafb4bce460df7b7d13366b0504df8a94d8462b312d12d673d84af5bff3c60a504582953abe0ac74ef202b0d98ec6ea56bfe60a551b1899c95ce15ac3455b0d2b983fa302af67eda4c4b3cd0fa8007ff8d3e67c6132c7b4e2262974844bd5b376d0d270b44e1861cb0d643a26aa650c482dd9717ee2fa0bb0c88d48a94478a321713dbc3ed0694b84da40c352b7ccc48b71d6bd90af65041545e8524bcb91971d6b89c92b1f13e88dc0cb64cf5fc855e70cbc369cbf86b173aefaa14f4db74554a17ff075ba664822d5b17d87f078b33711aacbb23a1b33c24bc2d2b562f4772cbfc9c60de2649fb03d8daed0f80163f60ef21a21fe76c72e6e65db54fff3e56b2a9f6998bd76b37fdce5be07796e47b6bdc2c90933097b3403d67813bfedfb1408e2fc3c99f0716587c7f0bc4b9380b94519715ab4351d0c1ff608112aa4991f1a060815c1d3ace028d9c05e60cb640cc4fa7fda986b740be072e550f68d1c69adb216065f18a78b0323df8506fb1d2a6bf5cc9cbcca74fb522f2e6ae6bd8b31f730562a3e833ad22268608f97e94829c38fb6cdee85dfe54e3f1f0b395089f8d1141a67f31daf5654a7c8706f6ce176aebb4d701994bbab8bb6c788febf5b5c2871411db5d7fb3440ca339dbf49e829a387b3a698f0cf7791f29baec6ff5c77faccfdcbad0376a9beb7678b44e7b01ac204b1c1edf85b0af37da7a49d9755701bd3f26af1ae2786fa6fe4e15b7439f4e5646e2bf9483ff46967307ae1ece7ba79468338d25d26906df7549500a5fb74b37d333d6e49fc836bbeebe041323f9349b34b04de9c3fddd38e998adf8fda197e133c3c8e681fb1c993e2c388015e0c7659b5b2cce7229625739112e17db844beaa2679ae8228388cf1342f039de4763b60a69ef1910cf59948a79b3e6c9e80c706624d9e64b96c1a19fb85bb9aef7ff28ac7d52dbd46669a7705be0145f03d55b4c97af9cb5521f617cf872b2c9c65254807a86fda1626da6867ebec5b91fe8e34b357d95b9049fb1603fd9ebb4c23702cd2c544fabf716efa31e2363a68f6a45e1ab4ee3dbeb8fa9a79d0d30c5d0ca1f91be42fbd892b7d5550d134ba44a24a78653050d47cc3cdf20fb562bcf39b281d0ac000f2daebd6a8b707fa00bcffacbdb9c6bc073cd615b04298ef0b26bafeb3fa510394f7fc0f4f0ae26695245c2d0d94dd867dd4a4c545a39dfb12e2bda5fdee9182e092eb0ce95f7d1720fa48f3004d0a09a998f27fa74594aa37d92fa2368738858fdfe38a2eb9cb852f2d8f4517f22f47f37c04e840891dfcc2e2a8db61667f4ea2da687ad39e6be672cf8fecf4fb5facae8c7f4530cde1d79ba109d2d8ec92eba541b06ff7e51cb6bf9c05f596565dbc5f88657a8815e4ea2e992ac48e7373ba82e73b13f29717d4f31b8ccb7242e433f47227e37226d1e2819f923aac3b77cf00d4fe9f5fd86c02b5b98819288bc5f6de83b5c5b30b3c5f96edb07d73c70877374fa2328a74f90ec4e7f3c2def429334fe4bce4692a6d67572ee1e4a8bb36d424d4df76d57c2195c53203370fb7170995bf72d13d030b48cb09fffb198c6a543f533378fb9e6fccbb5984b8217e60252aaba9c7fc5ed04feba1bb435c1595e7da8f25bf134fcd729899870524c3825e0cbce62ad3ec97d5cbe4f2abf99c55cb20c951349ee2767d9d1a1729626aabdbb5069b27a24fc3bf7d9ba5fe7aaf1ddfda7d5be5de8d7a7d5a3e0395dad847f37a9ae1c4d9c71a574c58c96a3c9aaeb47d7a8ba8e6e54f5676721b4f06387e31dc07180df270ec76ac0278033071c8e6f000b0f391c154025871d8e6ca05169a9296b939302925f7ef9a597c77aa0a792d7a66c58e50aa2bf456b662dd1ae08d0acdd90fc72404c5ae2fa55014fbf94948c849f339d4bc1094f04a4ae4ddd10b031f1e50df004855edeb86ec37df3a5bdf4d23ae421f049dab866ddf3f44b2f273fe1c5456d5cbb2a716d521a48368809fc447f4659f7a38b23ee1fefa45f4dfdafd3273df15fa7c74ee6699240df1468b1403f11e8f6293cad12e84aa1dee593fe6bfac2cbc9892fae7b091acbf7c7cac48d691b0256266fa0570d0aa7bd949834387dfd06e8312e9c9a96969c929806bdb57ec3cb1be90da92fade5e2d7a4ae4f14867880d9d0788ee9a0f000d3ffc98fe2fe855961e84f3a382012fd8fab71fd64ff2799c5ffed9ca1c2f83c22d071029d28d07f08b447a07de13c8d13c2db85303c21022002881f4648f2978f1009900228800c2007780086013c218f1760386004c01b6d178f04fa00945700f501ea0b1805cf4a80dfc3dbd168c083007f800a3006f010e0614000602c2010a0068c03040182012180f180098050c09f008f001e058401fe0c9808780c3009100e980c9802980a88004c033c0e7802301d3003f0246026e02f804890350ae49c057436d068a01aa031d0562d600e602e2016300ff014e069c07cc8f357c002c0338085d00f8b802e86b425c0230e100fe1a5806580e510fe1be059c00ac07310f73c20019008780140039200c990be12900258054885b8d580170169803580b5809700eb203d1df032603d6003c46d04bc02c800bc0a23bc09f01ae075c01b804cc09bde3bd05b802c800e900dd0030c508601e4008cc06f33c004d802c8056c056c03bc0dc803e0fff201ef4cdc815840013c17028a006640316007f0da09280194027601de05bc072803ec069443be3d80f7017b0115804a88df07f8005005f83be01f800f01fb01ff047c04f93e067c02a8061c001c84f843000be030e053c011400de028e018e033402de038a00ef039e004e024a01e700ad000380df8027006f025e02bc0d7806f00ff027c0bf80ef03de007c08f80b320c3394023e03ce027e8a19f1fde817e81e75f01170017610c7e03da04f92f012e03ac8066400ba0156003b401ae00ae02ae01da01d70137003701b7001d80df01b7019d802e4037c00ee801f402ee00ee02fa00fd807b00c7c3dc04b050985216f164dc0221bc18ffb3fa1787e355400ea000500ef808f019e01bc045c00dc03dc0f05f1d8e870053013180c5801700eb006f02b6024a01ff001c057c05f815d00ee803785e7038548073f07c0b2083e700401860062016b014b012f00ac00078075006d80ff80af0e06f0e4728e057783e0a6807f4017c2e3a1ce30093009180f9806701ab016700af01b6007602aa003f03ae028e00ee003c80e752c05a4016e01dc03f004701df03da01f700dee0cb8f034402e2006b01d9807701af5c823a003b010720fc2da00dd007f083b830400c60392007900448bbc4977b0d70eeb2c3d10ce804f40082c1ff9f013000de071c052cb6f2693f024d139e71fae266876315e00dc049c0558843405580ed8032c047cd7c5ec3209e98fe0ef1012dd0678030789ed33c90fe06c47d7599478f001c87d058418f0205aae66852e286c4e7335293c0979bf964c09480e0e0804151339e0c98c6e786c57edd0be0af8d7d326049d4c2c5cf2f5afcd705cf4ffa37f18f3d3fc5b92e8e0d9cbbf615f0189202d625be9cba6153a0101f7bcde198058811e8daf681f07c01380e874307210eb0f9068f77010601af5c1f08e36783909e3d08476f423f102291187e12e147bafda4ffe147fd0f7fb2ffcbbf1edae96720dea92204df8ae07d9d00b12b9dfb4d4ce5c36d022d5b3d34fdffeb3fe7ffd76478f8d77f9ffcc09ad6f68b241af67f55a2fffdfdefef7f7ffffbfbdfdfff9f7e4b5fe4d755275de5465f75a39bdd68b11bdde7460fbbd1d36ef4bc1b6d73a33d6e54963694fab9d1f16e749a1b9de34697bad1556ef45537bad98d16bbd17d6ef4b01b3ded46cfbbd13637dae346656b86523f373ade8d4e73a373dce85237baca8dbeea4637bbd16237bacf8d1e76a3a7dde87937dae6467bdca86ced50eae746c7bbd1696e748e1b5dea4657b9d157dde866375aec46f7b9d1c36ef4b41b3def46dbdc688f1b95bd3494fab9d1f16e749a1b9de34697bad1556ef45537bad98d16bbd17d6ef4b01b3ded46cfbbd13637dae34665eb86523f373ade8d4e73a373dce85237baca8dbeea4637bbd16237bacf8d1e76a3a7dde87937dae6467bdca82c7d28f573a3e3dde834373ac78d2e75a3abdce8ab6e74b31b2d76a3fbdce861377ada8d9e77a36d6eb4c78dca5e1e4afddce878373acd8dce71a34bdde82a37faaa1bddec468bdde83e377ad88d9e76a3e7dd689b1bed71a3b2f543a99f1b1def46a7b9d1396ed4b92f77083fe7f61c5d946721b7dfffc9db90ff07cff9cdf8' ISP_PROG = binascii.unhexlify(ISP_PROG) ISP_PROG = zlib.decompress(ISP_PROG) def printProgressBar (iteration, total, prefix = '', suffix = '', filename = '', decimals = 1, length = 100, fill = '='): """ Call in a loop to create terminal progress bar @params: iteration - Required : current iteration (Int) total - Required : total iterations (Int) prefix - Optional : prefix string (Str) suffix - Optional : suffix string (Str) decimals - Optional : positive number of decimals in percent complete (Int) length - Optional : character length of bar (Int) fill - Optional : bar fill character (Str) """ percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total))) filledLength = int(length * iteration // total) bar = fill * filledLength + '-' * (length - filledLength) KFlash.log('\r%s |%s| %s%% %s' % (prefix, bar, percent, suffix), end = '\r') # Print New Line on Complete if iteration == total: KFlash.log() if callback: fileTypeStr = filename if prefix == "Downloading ISP:": fileTypeStr = "ISP" elif prefix == "Programming BIN:" and fileTypeStr == "": fileTypeStr = "BIN" callback(fileTypeStr, iteration, total, suffix) def slip_reader(port): partial_packet = None in_escape = False while True: waiting = port.inWaiting() read_bytes = port.read(1 if waiting == 0 else waiting) if read_bytes == b'': raise_exception( Exception("Timed out waiting for packet %s" % ("header" if partial_packet is None else "content")) ) for b in read_bytes: if type(b) is int: b = bytes([b]) # python 2/3 compat if partial_packet is None: # waiting for packet header if b == b'\xc0': partial_packet = b"" else: raise_exception( Exception('Invalid head of packet (%r)' % b) ) elif in_escape: # part-way through escape sequence in_escape = False if b == b'\xdc': partial_packet += b'\xc0' elif b == b'\xdd': partial_packet += b'\xdb' else: raise_exception( Exception('Invalid SLIP escape (%r%r)' % (b'\xdb', b)) ) elif b == b'\xdb': # start of escape sequence in_escape = True elif b == b'\xc0': # end of packet yield partial_packet partial_packet = None else: # normal byte in packet partial_packet += b class ISPResponse: class ISPOperation(Enum): ISP_ECHO = 0xC1 ISP_NOP = 0xC2 ISP_MEMORY_WRITE = 0xC3 ISP_MEMORY_READ = 0xC4 ISP_MEMORY_BOOT = 0xC5 ISP_DEBUG_INFO = 0xD1 ISP_CHANGE_BAUDRATE = 0xc6 class ErrorCode(Enum): ISP_RET_DEFAULT = 0 ISP_RET_OK = 0xE0 ISP_RET_BAD_DATA_LEN = 0xE1 ISP_RET_BAD_DATA_CHECKSUM = 0xE2 ISP_RET_INVALID_COMMAND = 0xE3 @staticmethod def parse(data): # type: (bytes) -> (int, int, str) op = 0 reason = 0 text = '' if (sys.version_info > (3, 0)): op = int(data[0]) reason = int(data[1]) else: op = ord(data[0]) reason = ord(data[1]) try: if ISPResponse.ISPOperation(op) == ISPResponse.ISPOperation.ISP_DEBUG_INFO: text = data[2:].decode() except ValueError: KFlash.log('Warning: recv unknown op', op) return (op, reason, text) class FlashModeResponse: class Operation(Enum): ISP_DEBUG_INFO = 0xD1 ISP_NOP = 0xD2 ISP_FLASH_ERASE = 0xD3 ISP_FLASH_WRITE = 0xD4 ISP_REBOOT = 0xD5 ISP_UARTHS_BAUDRATE_SET = 0xD6 FLASHMODE_FLASH_INIT = 0xD7 class ErrorCode(Enum): ISP_RET_DEFAULT = 0 ISP_RET_OK = 0xE0 ISP_RET_BAD_DATA_LEN = 0xE1 ISP_RET_BAD_DATA_CHECKSUM = 0xE2 ISP_RET_INVALID_COMMAND = 0xE3 ISP_RET_BAD_INITIALIZATION = 0xE4 ISP_RET_BAD_EXEC = 0xE5 @staticmethod def parse(data): # type: (bytes) -> (int, int, str) op = 0 reason = 0 text = '' if (sys.version_info > (3, 0)): op = int(data[0]) reason = int(data[1]) else: op = ord(data[0]) reason = ord(data[1]) if FlashModeResponse.Operation(op) == FlashModeResponse.Operation.ISP_DEBUG_INFO: text = data[2:].decode() reason_enum = FlashModeResponse.ErrorCode(reason) if (not text) or (text.strip() == ""): if reason_enum == FlashModeResponse.ErrorCode.ISP_RET_OK: text = "ok" elif reason_enum == FlashModeResponse.ErrorCode.ISP_RET_BAD_DATA_LEN: text = "bad data len" elif reason_enum == FlashModeResponse.ErrorCode.ISP_RET_BAD_DATA_CHECKSUM: print("aaa") text = "bad data checksum" elif reason_enum == FlashModeResponse.ErrorCode.ISP_RET_BAD_INITIALIZATION: text = "bad initialization" elif reason_enum == FlashModeResponse.ErrorCode.ISP_RET_INVALID_COMMAND: text = "invalid command" elif reason_enum == FlashModeResponse.ErrorCode.ISP_RET_BAD_EXEC: text = "execute cmd error" else: text = "unknown error" return (op, reason, text) def chunks(l, n, address=None): """Yield successive n-sized chunks from l.""" if address != None and (address % n != 0): start_pos = n - (address - address // n * n) if start_pos % ISP_FLASH_SECTOR_SIZE != 0: raise_exception(Exception("data should 4KiB align")) count_4k_blocks = start_pos // ISP_FLASH_SECTOR_SIZE count = math.ceil((len(l) - start_pos)/n) + count_4k_blocks for i in range(count): if i < count_4k_blocks: yield l[ISP_FLASH_SECTOR_SIZE*i:ISP_FLASH_SECTOR_SIZE*(i+1)] else: start = start_pos+(i-count_4k_blocks)*n yield l[start:start+n] else: for i in range(0, len(l), n): yield l[i:i + n] class TerminalSize: @staticmethod def getTerminalSize(): import platform current_os = platform.system() tuple_xy=None if current_os == 'Windows': tuple_xy = TerminalSize._getTerminalSize_windows() if tuple_xy is None: tuple_xy = TerminalSize._getTerminalSize_tput() # needed for window's python in cygwin's xterm! if current_os == 'Linux' or current_os == 'Darwin' or current_os.startswith('CYGWIN'): tuple_xy = TerminalSize._getTerminalSize_linux() if tuple_xy is None: # Use default value tuple_xy = (80, 25) # default value return tuple_xy @staticmethod def _getTerminalSize_windows(): res=None try: from ctypes import windll, create_string_buffer # stdin handle is -10 # stdout handle is -11 # stderr handle is -12 h = windll.kernel32.GetStdHandle(-12) csbi = create_string_buffer(22) res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi) except: return None if res: import struct (bufx, bufy, curx, cury, wattr, left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw) sizex = right - left + 1 sizey = bottom - top + 1 return sizex, sizey else: return None @staticmethod def _getTerminalSize_tput(): # get terminal width # src: http://stackoverflow.com/questions/263890/how-do-i-find-the-width-height-of-a-terminal-window try: import subprocess proc=subprocess.Popen(["tput", "cols"],stdin=subprocess.PIPE,stdout=subprocess.PIPE) output=proc.communicate(input=None) cols=int(output[0]) proc=subprocess.Popen(["tput", "lines"],stdin=subprocess.PIPE,stdout=subprocess.PIPE) output=proc.communicate(input=None) rows=int(output[0]) return (cols,rows) except: return None @staticmethod def _getTerminalSize_linux(): def ioctl_GWINSZ(fd): try: import fcntl, termios, struct, os cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ,'1234')) except: return None return cr cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) if not cr: try: fd = os.open(os.ctermid(), os.O_RDONLY) cr = ioctl_GWINSZ(fd) os.close(fd) except: pass if not cr: try: cr = (os.env['LINES'], os.env['COLUMNS']) except: return None return int(cr[1]), int(cr[0]) @staticmethod def get_terminal_size(fallback=(100, 24), terminal = False): try: columns, rows = TerminalSize.getTerminalSize() if not terminal: if not terminal_auto_size: columns, rows = terminal_size except: columns, rows = fallback return columns, rows class MAIXLoader: def change_baudrate(self, baudrate): KFlash.log(INFO_MSG,"Selected Baudrate: ", baudrate, BASH_TIPS['DEFAULT']) out = struct.pack('III', 0, 4, baudrate) crc32_checksum = struct.pack('I', binascii.crc32(out) & 0xFFFFFFFF) out = struct.pack('HH', 0xd6, 0x00) + crc32_checksum + out self.write(out) time.sleep(0.05) self._port.baudrate = baudrate if args.Board == "goE": if baudrate >= 4500000: # OPENEC super baudrate KFlash.log(INFO_MSG, "Enable OPENEC super baudrate!!!", BASH_TIPS['DEFAULT']) if baudrate == 4500000: self._port.baudrate = 300 if baudrate == 6000000: self._port.baudrate = 250 if baudrate == 7500000: self._port.baudrate = 350 def change_baudrate_stage0(self, baudrate): # Dangerous, here are dinosaur infested!!!!! # Don't touch this code unless you know what you are doing # Stage0 baudrate is fixed # Contributor: [@rgwan](https://github.com/rgwan) # rgwan baudrate = 1500000 if args.Board == "goE" or args.Board == "trainer": KFlash.log(INFO_MSG,"Selected Stage0 Baudrate: ", baudrate, BASH_TIPS['DEFAULT']) # This is for openec, contained ft2232, goE and trainer KFlash.log(INFO_MSG,"FT2232 mode", BASH_TIPS['DEFAULT']) baudrate_stage0 = int(baudrate * 38.6 / 38) out = struct.pack('III', 0, 4, baudrate_stage0) crc32_checksum = struct.pack('I', binascii.crc32(out) & 0xFFFFFFFF) out = struct.pack('HH', 0xc6, 0x00) + crc32_checksum + out self.write(out) time.sleep(0.05) self._port.baudrate = baudrate retry_count = 0 while 1: self.checkKillExit() retry_count = retry_count + 1 if retry_count > 3: err = (ERROR_MSG,'Fast mode failed, please use slow mode by add parameter ' + BASH_TIPS['GREEN'] + '--Slow', BASH_TIPS['DEFAULT']) err = tuple2str(err) raise_exception( Exception(err) ) try: self.greeting() break except TimeoutError: pass elif args.Board == "dan" or args.Board == "bit" or args.Board == "kd233": KFlash.log(INFO_MSG,"CH340 mode", BASH_TIPS['DEFAULT']) # This is for CH340, contained dan, bit and kd233 baudrate_stage0 = int(baudrate * 38.4 / 38) # CH340 can not use this method, test failed, take risks at your own risk else: # This is for unknown board KFlash.log(WARN_MSG,"Unknown mode", BASH_TIPS['DEFAULT']) def __init__(self, port='/dev/ttyUSB1', baudrate=115200): # configure the serial connections (the parameters differs on the device you are connecting to) self._port = serial.Serial( port=port, baudrate=baudrate, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS, timeout=0.1 ) KFlash.log(INFO_MSG, "Default baudrate is", baudrate, ", later it may be changed to the value you set.", BASH_TIPS['DEFAULT']) self._port.isOpen() self._slip_reader = slip_reader(self._port) self._kill_process = False """ Read a SLIP packet from the serial port """ def read(self): return next(self._slip_reader) """ Write bytes to the serial port while performing SLIP escaping """ def write(self, packet): buf = b'\xc0' \ + (packet.replace(b'\xdb', b'\xdb\xdd').replace(b'\xc0', b'\xdb\xdc')) \ + b'\xc0' #KFlash.log('[WRITE]', binascii.hexlify(buf)) return self._port.write(buf) def read_loop(self): #out = b'' # while self._port.inWaiting() > 0: # out += self._port.read(1) # KFlash.log(out) while 1: sys.stdout.write('[RECV] raw data: ') sys.stdout.write(binascii.hexlify(self._port.read(1)).decode()) sys.stdout.flush() def recv_one_return(self, timeout_s = None): timeout_init = time.time() data = b'' if timeout_s == None: timeout_s = ISP_RECEIVE_TIMEOUT # find start boarder #sys.stdout.write('[RECV one return] raw data: ') while 1: if time.time() - timeout_init > timeout_s: raise_exception( TimeoutError ) c = self._port.read(1) #sys.stdout.write(binascii.hexlify(c).decode()) sys.stdout.flush() if c == b'\xc0': break in_escape = False while 1: if time.time() - timeout_init > timeout_s: raise_exception( TimeoutError ) c = self._port.read(1) #sys.stdout.write(binascii.hexlify(c).decode()) sys.stdout.flush() if c == b'\xc0': break elif in_escape: # part-way through escape sequence in_escape = False if c == b'\xdc': data += b'\xc0' elif c == b'\xdd': data += b'\xdb' else: raise_exception( Exception('Invalid SLIP escape (%r%r)' % (b'\xdb', c)) ) elif c == b'\xdb': # start of escape sequence in_escape = True data += c #sys.stdout.write('\n') return data # kd233 or open-ec or new cmsis-dap def reset_to_isp_kd233(self): self._port.setDTR (False) self._port.setRTS (False) time.sleep(0.1) #KFlash.log('-- RESET to LOW, IO16 to HIGH --') # Pull reset down and keep 10ms self._port.setDTR (True) self._port.setRTS (False) time.sleep(0.1) #KFlash.log('-- IO16 to LOW, RESET to HIGH --') # Pull IO16 to low and release reset self._port.setRTS (True) self._port.setDTR (False) time.sleep(0.1) def reset_to_boot_kd233(self): self._port.setDTR (False) self._port.setRTS (False) time.sleep(0.1) #KFlash.log('-- RESET to LOW --') # Pull reset down and keep 10ms self._port.setDTR (True) self._port.setRTS (False) time.sleep(0.1) #KFlash.log('-- RESET to HIGH, BOOT --') # Pull IO16 to low and release reset self._port.setRTS (False) self._port.setDTR (False) time.sleep(0.1) #dan dock def reset_to_isp_dan(self): self._port.setDTR (False) self._port.setRTS (False) time.sleep(0.1) #KFlash.log('-- RESET to LOW, IO16 to HIGH --') # Pull reset down and keep 10ms self._port.setDTR (False) self._port.setRTS (True) time.sleep(0.1) #KFlash.log('-- IO16 to LOW, RESET to HIGH --') # Pull IO16 to low and release reset self._port.setRTS (False) self._port.setDTR (True) time.sleep(0.1) def reset_to_boot_dan(self): self._port.setDTR (False) self._port.setRTS (False) time.sleep(0.1) #KFlash.log('-- RESET to LOW --') # Pull reset down and keep 10ms self._port.setDTR (False) self._port.setRTS (True) time.sleep(0.1) #KFlash.log('-- RESET to HIGH, BOOT --') # Pull IO16 to low and release reset self._port.setRTS (False) self._port.setDTR (False) time.sleep(0.1) # maix goD for old cmsis-dap firmware def reset_to_isp_goD(self): self._port.setDTR (True) ## output 0 self._port.setRTS (True) time.sleep(0.1) #KFlash.log('-- RESET to LOW --') # Pull reset down and keep 10ms self._port.setRTS (False) self._port.setDTR (True) time.sleep(0.1) #KFlash.log('-- RESET to HIGH, BOOT --') # Pull IO16 to low and release reset self._port.setRTS (False) self._port.setDTR (True) time.sleep(0.1) def reset_to_boot_goD(self): self._port.setDTR (False) self._port.setRTS (False) time.sleep(0.1) #KFlash.log('-- RESET to LOW --') # Pull reset down and keep 10ms self._port.setRTS (False) self._port.setDTR (True) time.sleep(0.1) #KFlash.log('-- RESET to HIGH, BOOT --') # Pull IO16 to low and release reset self._port.setRTS (True) self._port.setDTR (True) time.sleep(0.1) # maix goE for openec or new cmsis-dap firmware def reset_to_boot_maixgo(self): self._port.setDTR (False) self._port.setRTS (False) time.sleep(0.1) #KFlash.log('-- RESET to LOW --') # Pull reset down and keep 10ms self._port.setRTS (False) self._port.setDTR (True) time.sleep(0.1) #KFlash.log('-- RESET to HIGH, BOOT --') # Pull IO16 to low and release reset self._port.setRTS (False) self._port.setDTR (False) time.sleep(0.1) def greeting(self): self._port.write(b'\xc0\xc2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0') op, reason, text = ISPResponse.parse(self.recv_one_return()) #KFlash.log('MAIX return op:', ISPResponse.ISPOperation(op).name, 'reason:', ISPResponse.ErrorCode(reason).name) def flash_greeting(self): retry_count = 0 while 1: self.checkKillExit() try: self._port.write(b'\xc0\xd2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0') except Exception: raise_exception( Exception("Connection disconnected, try again or maybe need use Slow mode, or decrease baudrate") ) retry_count = retry_count + 1 try: op, reason, text = FlashModeResponse.parse(self.recv_one_return()) except IndexError: if retry_count > MAX_RETRY_TIMES: err = (ERROR_MSG,"Failed to Connect to K210's Stub",BASH_TIPS['DEFAULT']) err = tuple2str(err) raise_exception( Exception(err) ) KFlash.log(WARN_MSG,"Index Error, retrying...",BASH_TIPS['DEFAULT']) time.sleep(0.1) continue except TimeoutError: if retry_count > MAX_RETRY_TIMES: err = (ERROR_MSG,"Failed to Connect to K210's Stub",BASH_TIPS['DEFAULT']) err = tuple2str(err) raise_exception( Exception(err) ) KFlash.log(WARN_MSG,"Timeout Error, retrying...",BASH_TIPS['DEFAULT']) time.sleep(0.1) continue except: if retry_count > MAX_RETRY_TIMES: err = (ERROR_MSG,"Failed to Connect to K210's Stub",BASH_TIPS['DEFAULT']) err = tuple2str(err) raise_exception( Exception(err) ) KFlash.log(WARN_MSG,"Unexcepted Error, retrying...",BASH_TIPS['DEFAULT']) time.sleep(0.1) continue # KFlash.log('MAIX return op:', FlashModeResponse.Operation(op).name, 'reason:', # FlashModeResponse.ErrorCode(reason).name) if FlashModeResponse.Operation(op) == FlashModeResponse.Operation.ISP_NOP and FlashModeResponse.ErrorCode(reason) == FlashModeResponse.ErrorCode.ISP_RET_OK: KFlash.log(INFO_MSG,"Boot to Flashmode Successfully",BASH_TIPS['DEFAULT']) self._port.flushInput() self._port.flushOutput() break else: if retry_count > MAX_RETRY_TIMES: err = (ERROR_MSG,"Failed to Connect to K210's Stub",BASH_TIPS['DEFAULT']) err = tuple2str(err) raise_exception( Exception(err) ) KFlash.log(WARN_MSG,"Unexcepted Return recevied, retrying...",BASH_TIPS['DEFAULT']) time.sleep(0.1) continue def boot(self, address=0x80000000): KFlash.log(INFO_MSG,"Booting From " + hex(address),BASH_TIPS['DEFAULT']) out = struct.pack('II', address, 0) crc32_checksum = struct.pack('I', binascii.crc32(out) & 0xFFFFFFFF) out = struct.pack('HH', 0xc5, 0x00) + crc32_checksum + out # op: ISP_MEMORY_WRITE: 0xc3 self.write(out) def recv_debug(self): op, reason, text = ISPResponse.parse(self.recv_one_return()) #KFlash.log('[RECV] op:', ISPResponse.ISPOperation(op).name, 'reason:', ISPResponse.ErrorCode(reason).name) if text: KFlash.log('-' * 30) KFlash.log(text) KFlash.log('-' * 30) if ISPResponse.ErrorCode(reason) not in (ISPResponse.ErrorCode.ISP_RET_DEFAULT, ISPResponse.ErrorCode.ISP_RET_OK): KFlash.log('Failed, retry, errcode=', hex(reason)) return False return True def flash_recv_debug(self): op, reason, text = FlashModeResponse.parse(self.recv_one_return()) #KFlash.log('[Flash-RECV] op:', FlashModeResponse.Operation(op).name, 'reason:', # FlashModeResponse.ErrorCode(reason).name) if text: KFlash.log('-' * 30) KFlash.log(text) KFlash.log('-' * 30) if FlashModeResponse.ErrorCode(reason) not in (FlashModeResponse.ErrorCode.ISP_RET_OK, FlashModeResponse.ErrorCode.ISP_RET_OK): KFlash.log('Failed, retry') return False return True def init_flash(self, chip_type): chip_type = int(chip_type) KFlash.log(INFO_MSG,"Selected Flash: ",("In-Chip", "On-Board")[chip_type],BASH_TIPS['DEFAULT']) out = struct.pack('II', chip_type, 0) crc32_checksum = struct.pack('I', binascii.crc32(out) & 0xFFFFFFFF) out = struct.pack('HH', 0xd7, 0x00) + crc32_checksum + out '''Retry when it have error''' retry_count = 0 while 1: self.checkKillExit() sent = self.write(out) retry_count = retry_count + 1 try: op, reason, text = FlashModeResponse.parse(self.recv_one_return()) except IndexError: if retry_count > MAX_RETRY_TIMES: err = (ERROR_MSG,"Failed to initialize flash",BASH_TIPS['DEFAULT']) err = tuple2str(err) raise_exception( Exception(err) ) KFlash.log(WARN_MSG,"Index Error, retrying...",BASH_TIPS['DEFAULT']) time.sleep(0.1) continue except TimeoutError: if retry_count > MAX_RETRY_TIMES: err = (ERROR_MSG,"Failed to initialize flash",BASH_TIPS['DEFAULT']) err = tuple2str(err) raise_exception( Exception(err) ) KFlash.log(WARN_MSG,"Timeout Error, retrying...",BASH_TIPS['DEFAULT']) time.sleep(0.1) continue except: if retry_count > MAX_RETRY_TIMES: err = (ERROR_MSG,"Failed to initialize flash",BASH_TIPS['DEFAULT']) err = tuple2str(err) raise_exception( Exception(err) ) KFlash.log(WARN_MSG,"Unexcepted Error, retrying...",BASH_TIPS['DEFAULT']) time.sleep(0.1) continue # KFlash.log('MAIX return op:', FlashModeResponse.Operation(op).name, 'reason:', # FlashModeResponse.ErrorCode(reason).name) if FlashModeResponse.Operation(op) == FlashModeResponse.Operation.FLASHMODE_FLASH_INIT and FlashModeResponse.ErrorCode(reason) == FlashModeResponse.ErrorCode.ISP_RET_OK: KFlash.log(INFO_MSG,"Initialization flash Successfully",BASH_TIPS['DEFAULT']) break else: if retry_count > MAX_RETRY_TIMES: err = (ERROR_MSG,"Failed to initialize flash",BASH_TIPS['DEFAULT']) err = tuple2str(err) raise_exception( Exception(err) ) KFlash.log(WARN_MSG,"Unexcepted Return recevied, retrying...",BASH_TIPS['DEFAULT']) time.sleep(0.1) continue def flash_dataframe(self, data, address=0x80000000): DATAFRAME_SIZE = 1024 data_chunks = chunks(data, DATAFRAME_SIZE) #KFlash.log('[DEBUG] flash dataframe | data length:', len(data)) total_chunk = math.ceil(len(data)/DATAFRAME_SIZE) time_start = time.time() for n, chunk in enumerate(data_chunks): self.checkKillExit() while 1: self.checkKillExit() #KFlash.log('[INFO] sending chunk', i, '@address', hex(address), 'chunklen', len(chunk)) out = struct.pack('II', address, len(chunk)) crc32_checksum = struct.pack('I', binascii.crc32(out + chunk) & 0xFFFFFFFF) out = struct.pack('HH', 0xc3, 0x00) + crc32_checksum + out + chunk # op: ISP_MEMORY_WRITE: 0xc3 sent = self.write(out) #KFlash.log('[INFO]', 'sent', sent, 'bytes', 'checksum', binascii.hexlify(crc32_checksum).decode()) address += len(chunk) if self.recv_debug(): break columns, lines = TerminalSize.get_terminal_size((100, 24), terminal) time_delta = time.time() - time_start speed = '' if (time_delta > 1): speed = str(int((n + 1) * DATAFRAME_SIZE / 1024.0 / time_delta)) + 'kiB/s' printProgressBar(n+1, total_chunk, prefix = 'Downloading ISP:', suffix = speed, length = columns - 35) def dump_to_flash(self, data, address=0, size=None): ''' typedef struct __attribute__((packed)) { uint8_t op; int32_t checksum; /* All the fields below are involved in the calculation of checksum */ uint32_t address; uint32_t data_len; uint8_t data_buf[1024]; } isp_request_t; ''' if size == None: DATAFRAME_SIZE = ISP_FLASH_DATA_FRAME_SIZE size = DATAFRAME_SIZE data_chunks = chunks(data, size) #KFlash.log('[DEBUG] flash dataframe | data length:', len(data)) for n, chunk in enumerate(data_chunks): #KFlash.log('[INFO] sending chunk', i, '@address', hex(address)) out = struct.pack('II', address, len(chunk)) crc32_checksum = struct.pack('I', binascii.crc32(out + chunk) & 0xFFFFFFFF) out = struct.pack('HH', 0xd4, 0x00) + crc32_checksum + out + chunk #KFlash.log("[$$$$]", binascii.hexlify(out[:32]).decode()) retry_count = 0 while True: try: sent = self.write(out) #KFlash.log('[INFO]', 'sent', sent, 'bytes', 'checksum', crc32_checksum) self.flash_recv_debug() except: retry_count = retry_count + 1 if retry_count > MAX_RETRY_TIMES: err = (ERROR_MSG,"Error Count Exceeded, Stop Trying",BASH_TIPS['DEFAULT']) err = tuple2str(err) raise_exception( Exception(err) ) continue break address += len(chunk) def flash_erase(self, erase_addr = 0, erase_len = 0): #KFlash.log('[DEBUG] erasing spi flash.') cmd0 = b'\xd3\x00\x00\x00' cmd = struct.pack("I", erase_addr) cmd += struct.pack("I", erase_len) cmd = cmd0 + struct.pack('I', binascii.crc32(cmd) & 0xFFFFFFFF) + cmd self.write(cmd) t = time.time() op, reason, text = FlashModeResponse.parse(self.recv_one_return(timeout_s=90)) if FlashModeResponse.ErrorCode(reason) != FlashModeResponse.ErrorCode.ISP_RET_OK: err = (ERROR_MSG,"erase error, error code: 0x{:02X}: {}".format(reason, text)) err = tuple2str(err) raise_exception( Exception(err) ) else: KFlash.log(INFO_MSG,"erase ok") #KFlash.log('MAIX return op:', FlashModeResponse.Operation(op).name, 'reason:', # FlashModeResponse.ErrorCode(reason).name) def install_flash_bootloader(self, data): # Download flash bootloader self.flash_dataframe(data, address=0x80000000) def load_elf_to_sram(self, f): try: from elftools.elf.elffile import ELFFile from elftools.elf.descriptions import describe_p_type except ImportError: err = (ERROR_MSG,'pyelftools must be installed, run '+BASH_TIPS['GREEN']+'`' + ('pip', 'pip3')[sys.version_info > (3, 0)] + ' install pyelftools`',BASH_TIPS['DEFAULT']) err = tuple2str(err) raise_exception( Exception(err) ) elffile = ELFFile(f) if elffile['e_entry'] != 0x80000000: KFlash.log(WARN_MSG,"ELF entry is 0x%x instead of 0x80000000" % (elffile['e_entry']), BASH_TIPS['DEFAULT']) for segment in elffile.iter_segments(): t = describe_p_type(segment['p_type']) KFlash.log(INFO_MSG, ("Program Header: Size: %d, Virtual Address: 0x%x, Type: %s" % (segment['p_filesz'], segment['p_vaddr'], t)), BASH_TIPS['DEFAULT']) if not (segment['p_vaddr'] & 0x80000000): continue if segment['p_filesz']==0 or segment['p_vaddr']==0: KFlash.log("Skipped") continue self.flash_dataframe(segment.data(), segment['p_vaddr']) def flash_firmware(self, firmware_bin, aes_key = None, address_offset = 0, sha256Prefix = True, filename = ""): # type: (bytes, bytes, int, bool) -> None # Don't remove above code! #KFlash.log('[DEBUG] flash_firmware DEBUG: aeskey=', aes_key) if sha256Prefix == True: # Add header to the firmware # Format: SHA256(after)(32bytes) + AES_CIPHER_FLAG (1byte) + firmware_size(4bytes) + firmware_data aes_cipher_flag = b'\x01' if aes_key else b'\x00' # Encryption if aes_key: enc = AES_128_CBC(aes_key, iv=b'\x00'*16).encrypt padded = firmware_bin + b'\x00'*15 # zero pad firmware_bin = b''.join([enc(padded[i*16:i*16+16]) for i in range(len(padded)//16)]) firmware_len = len(firmware_bin) data = aes_cipher_flag + struct.pack('I', firmware_len) + firmware_bin sha256_hash = hashlib.sha256(data).digest() firmware_with_header = data + sha256_hash total_len = len(firmware_with_header) # Slice download firmware data_chunks = chunks(firmware_with_header, ISP_FLASH_DATA_FRAME_SIZE) # 4kiB for a sector, 16kiB for dataframe else: total_len = len(firmware_bin) data_chunks = chunks(firmware_bin, ISP_FLASH_DATA_FRAME_SIZE, address = address_offset) time_start = time.time() write_len = 0 for n, chunk in enumerate(data_chunks): self.checkKillExit() # 4K align aligned_chunk = len(chunk) aligned_chunk = (ISP_FLASH_SECTOR_SIZE - (aligned_chunk % ISP_FLASH_SECTOR_SIZE))%ISP_FLASH_SECTOR_SIZE + aligned_chunk chunk = chunk.ljust(aligned_chunk, b'\x00') # align by size of dataframe # Download a dataframe #KFlash.log('[INFO]', 'Write firmware data piece') chunk_len = len(chunk) self.dump_to_flash(chunk, address= write_len + address_offset, size=chunk_len) write_len += chunk_len columns, lines = TerminalSize.get_terminal_size((100, 24), terminal) time_delta = time.time() - time_start speed = '' if (time_delta > 1): speed = str(int(write_len / 1024.0 / time_delta)) + 'kiB/s' printProgressBar(write_len, total_len, prefix = 'Programming BIN:', filename=filename, suffix = speed, length = columns - 35) def kill(self): self._kill_process = True def checkKillExit(self): if self._kill_process: self._port.close() self._kill_process = False raise Exception("Cancel") def open_terminal(reset): control_signal = '0' if reset else '1' control_signal_b = not reset import serial.tools.miniterm # For using the terminal with MaixPy the 'filter' option must be set to 'direct' # because some control characters are emited sys.argv = [sys.argv[0], _port, '115200', '--dtr='+control_signal, '--rts='+control_signal, '--filter=direct'] serial.tools.miniterm.main(default_port=_port, default_baudrate=115200, default_dtr=control_signal_b, default_rts=control_signal_b) sys.exit(0) boards_choices = ["kd233", "dan", "bit", "bit_mic", "goE", "goD", "maixduino", "trainer"] if terminal: parser = argparse.ArgumentParser() parser.add_argument("-p", "--port", help="COM Port", default="DEFAULT") parser.add_argument("-f", "--flash", help="SPI Flash type, 0 for SPI3, 1 for SPI0", default=1) parser.add_argument("-b", "--baudrate", type=int, help="UART baudrate for uploading firmware", default=115200) parser.add_argument("-l", "--bootloader", help="Bootloader bin path", required=False, default=None) parser.add_argument("-k", "--key", help="AES key in hex, if you need encrypt your firmware.", required=False, default=None) parser.add_argument("-v", "--version", help="Print version.", action='version', version='0.8.3') parser.add_argument("--verbose", help="Increase output verbosity", default=False, action="store_true") parser.add_argument("-t", "--terminal", help="Start a terminal after finish (Python miniterm)", default=False, action="store_true") parser.add_argument("-n", "--noansi", help="Do not use ANSI colors, recommended in Windows CMD", default=False, action="store_true") parser.add_argument("-s", "--sram", help="Download firmware to SRAM and boot", default=False, action="store_true") parser.add_argument("-B", "--Board",required=False, type=str, help="Select dev board, e.g. kd233, dan, bit, goD, goE or trainer") parser.add_argument("-S", "--Slow",required=False, help="Slow download mode", default=False) parser.add_argument("-A", "--addr",required=False, help="flash addr", type=str, default="-1") parser.add_argument("-L", "--length",required=False, help="flash addr", type=str, default="-1") parser.add_argument("firmware", help="firmware bin path") args = parser.parse_args() else: args = argparse.Namespace() setattr(args, "port", "DEFAULT") setattr(args, "flash", 1) setattr(args, "baudrate", 115200) setattr(args, "bootloader", None) setattr(args, "key", None) setattr(args, "verbose", False) setattr(args, "terminal", False) setattr(args, "noansi", False) setattr(args, "sram", False) setattr(args, "Board", None) setattr(args, "Slow", False) setattr(args, "addr", -1) setattr(args, "length", -1) # udpate args for none terminal call if not terminal: args.port = dev args.baudrate = baudrate args.noansi = noansi args.sram = sram args.Board = board args.firmware = file args.Slow = slow_mode args.addr = addr args.length = length if args.Board == "maixduino" or args.Board == "bit_mic": args.Board = "goE" if (args.noansi == True): BASH_TIPS = dict(NORMAL='',BOLD='',DIM='',UNDERLINE='', DEFAULT='', RED='', YELLOW='', GREEN='', BG_DEFAULT='', BG_WHITE='') ERROR_MSG = BASH_TIPS['RED']+BASH_TIPS['BOLD']+'[ERROR]'+BASH_TIPS['NORMAL'] WARN_MSG = BASH_TIPS['YELLOW']+BASH_TIPS['BOLD']+'[WARN]'+BASH_TIPS['NORMAL'] INFO_MSG = BASH_TIPS['GREEN']+BASH_TIPS['BOLD']+'[INFO]'+BASH_TIPS['NORMAL'] KFlash.log(INFO_MSG,'ANSI colors not used',BASH_TIPS['DEFAULT']) manually_set_the_board = False if args.Board: manually_set_the_board = True if args.port == "DEFAULT": if args.Board == "goE": list_port_info = list(serial.tools.list_ports.grep("0403")) #Take the second one if len(list_port_info) == 0: err = (ERROR_MSG,"No vaild COM Port found in Auto Detect, Check Your Connection or Specify One by"+BASH_TIPS['GREEN']+'`--port/-p`',BASH_TIPS['DEFAULT']) err = tuple2str(err) raise_exception( Exception(err) ) list_port_info.sort() if len(list_port_info) == 1: _port = list_port_info[0].device elif len(list_port_info) > 1: _port = list_port_info[1].device KFlash.log(INFO_MSG,"COM Port Auto Detected, Selected ", _port, BASH_TIPS['DEFAULT']) elif args.Board == "trainer": list_port_info = list(serial.tools.list_ports.grep("0403")) #Take the first one if(len(list_port_info)==0): err = (ERROR_MSG,"No vaild COM Port found in Auto Detect, Check Your Connection or Specify One by"+BASH_TIPS['GREEN']+'`--port/-p`',BASH_TIPS['DEFAULT']) err = tuple2str(err) raise_exception( Exception(err) ) list_port_info.sort() _port = list_port_info[0].device KFlash.log(INFO_MSG,"COM Port Auto Detected, Selected ", _port, BASH_TIPS['DEFAULT']) else: try: list_port_info = next(serial.tools.list_ports.grep(VID_LIST_FOR_AUTO_LOOKUP)) #Take the first one within the list _port = list_port_info.device KFlash.log(INFO_MSG,"COM Port Auto Detected, Selected ", _port, BASH_TIPS['DEFAULT']) except StopIteration: err = (ERROR_MSG,"No vaild COM Port found in Auto Detect, Check Your Connection or Specify One by"+BASH_TIPS['GREEN']+'`--port/-p`',BASH_TIPS['DEFAULT']) err = tuple2str(err) raise_exception( Exception(err) ) else: _port = args.port KFlash.log(INFO_MSG,"COM Port Selected Manually: ", _port, BASH_TIPS['DEFAULT']) self.loader = MAIXLoader(port=_port, baudrate=115200) file_format = ProgramFileFormat.FMT_BINARY # 0. Check firmware or cmd cmds = ['erase'] if not args.firmware in cmds: if not os.path.exists(args.firmware): err = (ERROR_MSG,'Unable to find the firmware at ', args.firmware, BASH_TIPS['DEFAULT']) err = tuple2str(err) raise_exception( Exception(err) ) with open(args.firmware, 'rb') as f: file_header = f.read(4) #if file_header.startswith(bytes([0x50, 0x4B])): if file_header.startswith(b'\x50\x4B'): if ".kfpkg" != os.path.splitext(args.firmware)[1]: KFlash.log(INFO_MSG, 'Find a zip file, but not with ext .kfpkg:', args.firmware, BASH_TIPS['DEFAULT']) else: file_format = ProgramFileFormat.FMT_KFPKG #if file_header.startswith(bytes([0x7F, 0x45, 0x4C, 0x46])): if file_header.startswith(b'\x7f\x45\x4c\x46'): file_format = ProgramFileFormat.FMT_ELF if args.sram: KFlash.log(INFO_MSG, 'Find an ELF file:', args.firmware, BASH_TIPS['DEFAULT']) else: err = (ERROR_MSG, 'This is an ELF file and cannot be programmed to flash directly:', args.firmware, BASH_TIPS['DEFAULT'] , '\r\nPlease retry:', args.firmware + '.bin', BASH_TIPS['DEFAULT']) err = tuple2str(err) raise_exception( Exception(err) ) # 1. Greeting. KFlash.log(INFO_MSG,"Trying to Enter the ISP Mode...",BASH_TIPS['DEFAULT']) retry_count = 0 while 1: self.checkKillExit() if not self.loader._port.isOpen(): self.loader._port.open() try: retry_count = retry_count + 1 if retry_count > 15: err = (ERROR_MSG,"No vaild Kendryte K210 found in Auto Detect, Check Your Connection or Specify One by"+BASH_TIPS['GREEN']+'`-p '+('/dev/ttyUSB0', 'COM3')[sys.platform == 'win32']+'`',BASH_TIPS['DEFAULT']) err = tuple2str(err) raise_exception( Exception(err) ) if args.Board == "dan" or args.Board == "bit" or args.Board == "trainer": try: KFlash.log('.', end='') self.loader.reset_to_isp_dan() self.loader.greeting() break except TimeoutError: pass elif args.Board == "kd233": try: KFlash.log('_', end='') self.loader.reset_to_isp_kd233() self.loader.greeting() break except TimeoutError: pass elif args.Board == "goE": try: KFlash.log('*', end='') self.loader.reset_to_isp_kd233() self.loader.greeting() break except TimeoutError: pass elif args.Board == "goD": try: KFlash.log('#', end='') self.loader.reset_to_isp_goD() self.loader.greeting() break except TimeoutError: pass else: try: KFlash.log('.', end='') self.loader.reset_to_isp_dan() self.loader.greeting() args.Board = "dan" KFlash.log() KFlash.log(INFO_MSG,"Automatically detected dan/bit/trainer",BASH_TIPS['DEFAULT']) break except TimeoutError: if not self.loader._port.isOpen(): self.loader._port.open() pass try: KFlash.log('_', end='') self.loader.reset_to_isp_kd233() self.loader.greeting() args.Board = "kd233" KFlash.log() KFlash.log(INFO_MSG,"Automatically detected goE/kd233",BASH_TIPS['DEFAULT']) break except TimeoutError: if not self.loader._port.isOpen(): self.loader._port.open() pass try: KFlash.log('.', end='') self.loader.reset_to_isp_goD() self.loader.greeting() args.Board = "goD" KFlash.log() KFlash.log(INFO_MSG,"Automatically detected goD",BASH_TIPS['DEFAULT']) break except TimeoutError: if not self.loader._port.isOpen(): self.loader._port.open() pass try: # Magic, just repeat, don't remove, it may unstable, don't know why. KFlash.log('_', end='') self.loader.reset_to_isp_kd233() self.loader.greeting() args.Board = "kd233" KFlash.log() KFlash.log(INFO_MSG,"Automatically detected goE/kd233",BASH_TIPS['DEFAULT']) break except TimeoutError: if not self.loader._port.isOpen(): self.loader._port.open() pass except Exception as e: KFlash.log() raise_exception( Exception("Greeting fail, check serial port ("+str(e)+")" ) ) # Don't remove this line # Dangerous, here are dinosaur infested!!!!! ISP_RECEIVE_TIMEOUT = 3 KFlash.log() KFlash.log(INFO_MSG,"Greeting Message Detected, Start Downloading ISP",BASH_TIPS['DEFAULT']) if manually_set_the_board and (not args.Slow): if (args.baudrate >= 1500000) or args.sram: self.loader.change_baudrate_stage0(args.baudrate) # 2. download bootloader and firmware if args.sram: with open(args.firmware, 'rb') as firmware_bin: if file_format == ProgramFileFormat.FMT_KFPKG: err = (ERROR_MSG, "Unable to load kfpkg to SRAM") err = tuple2str(err) raise_exception( Exception(err) ) elif file_format == ProgramFileFormat.FMT_ELF: self.loader.load_elf_to_sram(firmware_bin) else: self.loader.install_flash_bootloader(firmware_bin.read()) else: # install bootloader at 0x80000000 if args.bootloader: with open(args.bootloader, 'rb') as f: isp_loader = f.read() else: isp_loader = ISP_PROG self.loader.install_flash_bootloader(isp_loader) # Boot the code from SRAM self.loader.boot() if args.sram: # Dangerous, here are dinosaur infested!!!!! # Don't touch this code unless you know what you are doing self.loader._port.baudrate = args.baudrate KFlash.log(INFO_MSG,"Boot user code from SRAM", BASH_TIPS['DEFAULT']) if(args.terminal == True): open_terminal(False) msg = "Burn SRAM OK" raise_exception( Exception(msg) ) # Dangerous, here are dinosaur infested!!!!! # Don't touch this code unless you know what you are doing self.loader._port.baudrate = 115200 KFlash.log(INFO_MSG,"Wait For 0.1 second for ISP to Boot", BASH_TIPS['DEFAULT']) time.sleep(0.1) self.loader.flash_greeting() if args.baudrate != 115200: self.loader.change_baudrate(args.baudrate) KFlash.log(INFO_MSG,"Baudrate changed, greeting with ISP again ... ", BASH_TIPS['DEFAULT']) self.loader.flash_greeting() self.loader.init_flash(args.flash) if file_format == ProgramFileFormat.FMT_KFPKG: KFlash.log(INFO_MSG,"Extracting KFPKG ... ", BASH_TIPS['DEFAULT']) with tempfile.TemporaryDirectory() as tmpdir: try: with zipfile.ZipFile(args.firmware) as zf: zf.extractall(tmpdir) except zipfile.BadZipFile: err = (ERROR_MSG,'Unable to Decompress the kfpkg, your file might be corrupted.',BASH_TIPS['DEFAULT']) err = tuple2str(err) raise_exception( Exception(err) ) fFlashList = open(os.path.join(tmpdir, 'flash-list.json'), "r") sFlashList = re.sub(r'"address": (.*),', r'"address": "\1",', fFlashList.read()) #Pack the Hex Number in json into str fFlashList.close() jsonFlashList = json.loads(sFlashList) for lBinFiles in jsonFlashList['files']: self.checkKillExit() KFlash.log(INFO_MSG,"Writing",lBinFiles['bin'],"into","0x%08x"%int(lBinFiles['address'], 0),BASH_TIPS['DEFAULT']) with open(os.path.join(tmpdir, lBinFiles["bin"]), "rb") as firmware_bin: self.loader.flash_firmware(firmware_bin.read(), None, int(lBinFiles['address'], 0), lBinFiles['sha256Prefix'], filename=lBinFiles['bin']) else: if args.firmware == "erase": if args.addr.lower().startswith("0x"): addr = int(args.addr, base=16) else: addr = int(args.addr) if args.length.lower() == "all": addr = 0 length = 0xFFFFFFEE KFlash.log(INFO_MSG,"erase all") else: if args.length.lower().startswith("0x"): length = int(args.length, base=16) else: length = int(args.length) KFlash.log(INFO_MSG,"erase '0x{:x}' - '0x{:x}' ({}B, {:.02}KiB, {:.02}MiB)".format(addr, addr+length, length, length/1024.0, length/1024.0/1024.0)) if ((addr % 4096) != 0) or ( length != 0xFFFFFFEE and (length % 4096) != 0) or addr < 0 or addr > 0x01000000 or length < 0 or ( length > 0x01000000 and length != 0xFFFFFFEE): err = (ERROR_MSG,"erase flash addr or length error, addr should >= 0x00000000, and length should >= 4096 or 'all'") err = tuple2str(err) raise_exception( Exception(err) ) self.loader.flash_erase(addr, length) else: with open(args.firmware, 'rb') as firmware_bin: if args.key: aes_key = binascii.a2b_hex(args.key) if len(aes_key) != 16: raise_exception( ValueError('AES key must by 16 bytes') ) self.loader.flash_firmware(firmware_bin.read(), aes_key=aes_key) else: self.loader.flash_firmware(firmware_bin.read()) # 3. boot if args.Board == "dan" or args.Board == "bit" or args.Board == "trainer": self.loader.reset_to_boot_dan() elif args.Board == "kd233": self.loader.reset_to_boot_kd233() elif args.Board == "goE": self.loader.reset_to_boot_maixgo() elif args.Board == "goD": self.loader.reset_to_boot_goD() else: KFlash.log(WARN_MSG,"Board unknown !! please press reset to boot!!") KFlash.log(INFO_MSG,"Rebooting...", BASH_TIPS['DEFAULT']) try: self.loader._port.close() except Exception: pass if(args.terminal == True): open_terminal(True) def kill(self): if self.loader: self.loader.kill() self.killProcess = True def checkKillExit(self): if self.killProcess: if self.loader: self.loader._port.close() raise Exception("Cancel") def main(): kflash = KFlash() try: kflash.process() except Exception as e: if str(e) == "Burn SRAM OK": sys.exit(0) kflash.log(str(e)) sys.exit(1) if __name__ == '__main__': main()