Hardware components | ||||||
| × | 1 | ||||
Software apps and online services | ||||||
| ||||||
|
I've been searching AIoT - you know, AI + IoT, for Edge Intelligence - hardware platform lately, and found several alternatives. Of course, you can just use Jetson Nano or Raspberry Pi + Intel Neural Compute Stick for that, but sometime they seem overkill for certain use cases, especially when there's a need for low power. MCU-based is often needed for such use case.
So far, I've found just few alternatives:
- ESP32-based: Espressif apparently put much effort for AIoT based on its ESP32 chip lately. My company uses ESP32 a lot in our products, so it should be a great alternative. But so far I've found that the tooling support for Deep Learning on ESP32 is not yet that convenient. Espressif did make some reference implementation for face and speech recognition, but it's still a challenge if I want to use my own Machine Learning model.
- ARM-based: SparkFun Edge Development Board is a good one, already supports Tensorflow Lite out of the box. The board is kept backorder for as long as I can remember. Hugh!
- Sipeed Maix series. I just found this board series recently, and looking at the spec and the demos, it seems promising.
This post is about developing for Sipeed Maix boards using Arduino IDE and framework.
Maix Board DevelopmentI won't go detail into this as I'm still exploring the platform. To my knowledge so far, there are at least 3 alternatives to develop for Maix boards:
- "Native": Maix board uses Kendryte K210 chip which natively supports development using either Kendryte K210 Standalone SDK or FreeRTOS SDK. I haven't touch this, but definitely will.
- Python: MaixPy allows Maix board firmware development using Micropython, a lean and efficient implementation of Python 3 language. If you're familiar with Python, it's definitely way to go
- Arduino: Maixduino was recently published to support development using Arduino IDE and Arduino core and libraries. There's abundant Arduino libraries for many peripherals we can think of, so it's a good move.
Up to the time of this writing, Maixduino is already at version 0.3.7. Looking at official project repository, it seems that most of functionality is already implemented. Beside usual suspects (Wire, SPI, UART, etc), it also includes some useful libraries to make the most of the boards:
- Maix KPU: It is the important one, that allows to work with KPU, the Neural Network Processor. The library includes example to use MobileNet v1 model for image classification purpose.
- Camera: It allows to access on-board OV2640 camera sensor
- LCD: Maix Go board has on-board LCD, and other Maix board allows to add one. There's library for mere mortal like me to work with ST7789-based LCD.
- WiFi: On selected Maix boards, there's on-board WiFi chip based on ESP8285. WiFiEsp library is included to allow working with networking over WiFi.
- and more...
Official documentation at: https://maixduino.sipeed.com/en/
Maixduino on OSXIt's super exciting to be able to leverage my years of Arduino experience to develop for Maix board, on my lovely Mac that for sure uses OSX operating system. So without any hesitation, I fire up Arduino IDE and follow through the installation guideline, and soon I found myself disappointed as I'm using OSX and apparently the installation is failed miserably on OSX.
Unfortunately, up to the time of this post, apparently Maixduino does not officially support OSX. Looking at the Arduino IDE's Board Manager definition file, it only supports Windows and Linux for now. So, I guess I have to "hack" it a bit.
Looking at the installation log, the failure happens when it tries to install kflash tool, it says:
Tool kflash is not available for your operating system.
And again, looking at the Board Manager definition file, there's entry like this:
{
"name": "kflash",
"version": "1.0.0",
"systems":
[
{
"url": "http://dl.cdn.sipeed.com/win_kflash_py_v1.0.0.zip",
"checksum": "SHA-256:e2c1f217bb62bd30fd7d5a43aa7931b1144098f6d13ff9ed2a0c428130aaeec1",
"host": "i686-mingw32",
"archiveFileName": "win_kflash_py_v1.0.0.zip",
"size": "5900033"
},
{
"url": "http://dl.cdn.sipeed.com/linux_kflash_v1.0.0.zip",
"checksum": "SHA-256:d409ef1945c53b148bbf43832b25901c58dc9e1a18f431bd6992fdcc8f363630",
"host": "x86_64-linux-gnu",
"archiveFileName": "linux_kflash_v1.0.0.zip",
"size": "63387"
}
]
}
As you can see, there's no entry that says it's for OSX. Looking at another tool entry, namely "riscv64-unknown-elf-gcc", similar thing happens.
So I guess, I can just add new entry that says kflash tool for OSX. I have no idea how it should look like, so I just refer to another boards that support OSX, and finally come up with this:
{
"url": "https://github.com/kendryte/kflash.py/archive/master.zip",
"checksum": "MD5:afd678846158c4fe023dca1b8c6925fa",
"host": "i386-apple-darwin",
"archiveFileName": "kflash.py-master.zip",
"size": "51762"
}
As we can see, I use kflash tool straight from Kflash official repository. I do the same for "riscv64-unknown-elf-gcc" tool.
Board ManagerSo, for convenience, I make a new Board Manager definition file and upload it here:
https://gist.githubusercontent.com/andriyadi/a0235aadb5b5f73b1ee8cc72ba428943/raw/532ca1fe16d4a2b751d6813ec212785ecfa185f6/package_Maixduino_k210_dl_cdn_index.json
On Arduino IDE for OSX, to add support for Maix board, from the menubar, click Arduino -> Preferences..., and you'll get:
I use the URL above for Additional Board Manager URLs, instead of the one provided on the documentation.
Then, from the menubar, open Tools -> Board... -> Board Manager, now I find "Maixduino(k210) by Sipeed" entry. Yeah!
Just click "Install".
And it's successfully installed. Yuhu!
Let's start with Hello World, the Blink sketch. Open "Blink" sketch from Examples (find it yourself). Then change the board to "Sipeed Maix Go Board", as I happen to have that board.
Also change other options (Port, Baud Rate, etc) so that it looks like this:
Notes: Port is serial port detected by your OS. In OSX, it should look like "/dev/cu.usbserial-xxxxx", adjust yours.
Now, let's Verify/Compile. And thankfully, IT WORKS!
First Upload to BoardAs usual, after it compiles, it's time to upload the firmware to the board. Clicking Arrow button, and soon reality bites, it's FAILED!
It says:
It says:
java.io.IOException: Cannot run program "python3": error=2, No such file or directory
How is that possible?! I'm Python programmer, I'm well aware there's Python 3 in my system.
Let's Get DirtyFirst suspect of the issue above is that IDE cannot find python3 program from current path. Found similar issues on other board, and usually the path-related settings are in platform.txt file.
Editing platform.txt
On OSX, that file can be found at:
/Users/andri/Library/Arduino15/packages/[board]/hardware/[board]/[version]
so, for Maixduino, it's here:
/Users/andri/Library/Arduino15/packages/Maixduino/hardware/k210/0.3.6/platform.txt
Open platform.txt and find
tools.kflash.cmd=python3 {runtime.tools.kflash.path}/kflash.py
Change it to (adjust python3 path on your system):
tools.kflash.cmd=/usr/local/bin/python3 {runtime.tools.kflash.path}/kflash.py
For your convenience, I attach platform.txt in this post.
Now, it's time to hit Upload again. Aaaand boom! It's FAILED again.
It says:
[INFO] ANSI colors not used
[INFO] COM Port Selected Manually: /dev/cu.usbserial-00104314B
[INFO] Default baudrate is 115200 , later it may be changed to the value you set.
[INFO] Trying to Enter the ISP Mode...
*
[INFO] Greeting Message Detected, Start Downloading ISP
Traceback (most recent call last):
File "/Users/andri/Library/Arduino15/packages/Maixduino/tools/kflash/1.0.0/kflash.py", line 1033, in <module>
loader.install_flash_bootloader(isp_loader)
File "/Users/andri/Library/Arduino15/packages/Maixduino/tools/kflash/1.0.0/kflash.py", line 791, in install_flash_bootloader
self.flash_dataframe(data, address=0x80000000)
File "/Users/andri/Library/Arduino15/packages/Maixduino/tools/kflash/1.0.0/kflash.py", line 733, in flash_dataframe
columns, lines = os.get_terminal_size()
OSError: [Errno 25] Inappropriate ioctl for device
OSError: [Errno 25] Inappropriate ioctl for device
It seems the Python 3 path issue is solved, and IDE begins to upload as we can see from "Greeting Message Detected, Start Downloading ISP" message.
Now, there's problem is:
columns, lines = os.get_terminal_size()
Apparently that statement (line 733) in kflash.py throws exception.
Reading from this post, "os.get_terminal_size()" statement may get failed.
Editing kflash.py
So, I guess I have to edit kflash.py. It can be found at:
/Users/andri/Library/Arduino15/packages/Maixduino/tools/kflash/1.0.0/kflash.py
I won't explain what I've edited. Just download the attached modified kflash.py and replace the one on your system.
Moment of TruthWill there another issues, only one way to find out, hit Upload again. And... IT WORKS!
It's "done uploading", the green LED on board is now blinking. Yeah!
ClosingI've notified Sipeed guys of these issues. I hope they will fix it very soon. So far, they seem very cooperative. You can easily access their team via this Telegram group.
Anyway, good job on bringing Arduino support for Maix board, Sipeed. Keep it up!
{
"packages": [
{
"name": "Maixduino",
"maintainer": "Sipeed",
"websiteURL": "https://maixduino.sipeed.com",
"email": "support@sipeed.com",
"help": {
"online": "https://maixduino.sipeed.com"
},
"platforms": [
{
"category": "K210",
"name": "Maixduino(k210)",
"version": "0.3.6",
"architecture": "k210",
"url": "http://dl.cdn.sipeed.com/Maixduino_core_v0.3.6_2.zip",
"checksum": "SHA-256:111f7a76e489be11a2ed658de9d6e63a3847bc63bef67646b48df03a1550265b",
"archiveFileName": "Maixduino_core_v0.3.6_2.zip",
"size": "1063542",
"boards": [
{"name": "Sipeed \"Maix One Dock\"/\"Maix Bit\" Board"},
{"name": "Sipeed Maix Go Board"}
],
"toolsDependencies": [
{
"packager": "Maixduino",
"version": "8.2.0",
"name": "riscv64-unknown-elf-gcc"
},
{
"packager": "Maixduino",
"version": "1.0.0",
"name": "kflash"
}
]
}
],
"tools": [
{
"name": "riscv64-unknown-elf-gcc",
"version": "8.2.0",
"systems":
[
{
"url": "http://dl.cdn.sipeed.com/win_toolchain_v8.2.0_20190213.zip",
"checksum": "SHA-256:9af21c336c8ccb405a10d715c480f29c5996d3932adf6955993ca4b10502b72b",
"host": "i686-mingw32",
"archiveFileName": "win_toolchain_v8.2.0_20190213.zip",
"size": "487337534"
},
{
"url": "http://dl.cdn.sipeed.com/linux_kendryte-toolchain-ubuntu-amd64-8.2.0-20190213.tar.gz",
"checksum": "SHA-256:aa2fcc76ff61261b3667a422d4f67dec19c4547474bff4ebadaa1258b87985da",
"host": "x86_64-linux-gnu",
"archiveFileName": "linux_kendryte-toolchain-ubuntu-amd64-8.2.0-20190213.tar.gz",
"size": "360536176"
},
{
"url": "https://github.com/kendryte/kendryte-gnu-toolchain/releases/download/v8.2.0-20190213/kendryte-toolchain-osx-mojave-8.2.0-20190213.tar.gz",
"checksum": "MD5:74caa57a8d3bf2307bd23bf009fffffb",
"host": "i386-apple-darwin",
"archiveFileName": "kendryte-toolchain-osx-mojave-8.2.0-20190213.tar.gz",
"size": "50919270"
}
]
},
{
"name": "kflash",
"version": "1.0.0",
"systems":
[
{
"url": "http://dl.cdn.sipeed.com/win_kflash_py_v1.0.0.zip",
"checksum": "SHA-256:e2c1f217bb62bd30fd7d5a43aa7931b1144098f6d13ff9ed2a0c428130aaeec1",
"host": "i686-mingw32",
"archiveFileName": "win_kflash_py_v1.0.0.zip",
"size": "5900033"
},
{
"url": "http://dl.cdn.sipeed.com/linux_kflash_v1.0.0.zip",
"checksum": "SHA-256:d409ef1945c53b148bbf43832b25901c58dc9e1a18f431bd6992fdcc8f363630",
"host": "x86_64-linux-gnu",
"archiveFileName": "linux_kflash_v1.0.0.zip",
"size": "63387"
},
{
"url": "https://github.com/kendryte/kflash.py/archive/master.zip",
"checksum": "MD5:afd678846158c4fe023dca1b8c6925fa",
"host": "i386-apple-darwin",
"archiveFileName": "kflash.py-master.zip",
"size": "51762"
}
]
}
]
}
]
}
name = Maixduino
version = 0.2.3
# arch
arch = k210
arch_for_c_cpp = K210
# Compile variables
compiler.path={runtime.tools.riscv64-unknown-elf-gcc.path}/bin/
compiler.c.cmd=riscv64-unknown-elf-gcc
compiler.cpp.cmd=riscv64-unknown-elf-g++
compiler.ld.cmd=riscv64-unknown-elf-ld
compiler.ar.cmd=riscv64-unknown-elf-ar
compiler.objcopy.cmd=riscv64-unknown-elf-objcopy
compiler.elf2hex.cmd=riscv64-unknown-elf-objcopy
compiler.size.cmd=riscv64-unknown-elf-size
compiler.clib.path={runtime.tools.riscv64-unknown-elf-gcc.path}/include
compiler.sdk.path={runtime.platform.path}/cores/arduino/kendryte-standalone-sdk/lib
compiler.lib_hal_inc.path={runtime.platform.path}/cores/arduino/hal/include
compiler.cores.path={runtime.platform.path}/cores/arduino/
compiler.preproc.flags=-I{build.system.path}/include -I{compiler.cores.path} -I{compiler.lib_hal_inc.path} -I{compiler.sdk.path}/bsp/include -I{compiler.sdk.path}/drivers/include -I{compiler.sdk.path}/utils/include -I{compiler.sdk.path}/freertos/conf -I{compiler.sdk.path}/freertos/include -I{compiler.sdk.path}/freertos/portable -I{compiler.clib.path}
compiler.both.flags=-mcmodel=medany -mabi=lp64f -march=rv64imafc -fno-common -ffunction-sections -fdata-sections -fstrict-volatile-bitfields -fno-zero-initialized-in-bss -Os -ggdb -Wall -Werror=all -Wno-error=unused-function -Wno-error=unused-but-set-variable -Wno-error=unused-variable -Wno-error=deprecated-declarations -Wextra -Werror=frame-larger-than=65536 -Wno-unused-parameter -Wno-sign-compare -Wno-error=missing-braces -Wno-error=return-type -Wno-error=pointer-sign -Wno-missing-braces -Wno-strict-aliasing -Wno-implicit-fallthrough -Wno-missing-field-initializers -Wno-int-to-pointer-cast -Wno-error=comment -Wno-error=logical-not-parentheses -Wno-error=duplicate-decl-specifier -Wno-error=parentheses -lpthread
compiler.debug.flags=-DCONFIG_LOG_ENABLE -DCONFIG_LOG_LEVEL=LOG_INFO -DDEBUG=1 -D__riscv64
compiler.c.flags=-c {compiler.debug.flags} {compiler.both.flags} {compiler.preproc.flags} -std=gnu11 -Wno-pointer-to-int-cast -Wno-old-style-declaration -g -Wno-error=unused-variable -Wno-error=unused-function -Wno-error=unused-const-variable
compiler.cpp.flags=-c {compiler.debug.flags} {compiler.both.flags} -I{runtime.platform.path}/libraries/SPI/src {compiler.preproc.flags} -std=gnu++17 -g -Wno-error=unused-variable -Wno-error=unused-function -Wno-error=unused-const-variable
compiler.ld.flags=-mcmodel=medany -mabi=lp64f -march=rv64imafc -fno-common -ffunction-sections -fdata-sections -fstrict-volatile-bitfields -fno-zero-initialized-in-bss -Os -ggdb -nostartfiles -static -Wl,--gc-sections -Wl,-static -Wl,--whole-archive -Wl,--no-whole-archive -Wl,-EL -Wl,--no-relax -T {build.ldscript}
compiler.S.flags=-c {compiler.debug.flags} {compiler.both.flags} {compiler.preproc.flags} -g -x assembler-with-cpp -D __riscv64
compiler.ar.flags=rcs
compiler.objcopy.eep.flags=
compiler.elf2hex.flags=-R .rel.dyn
compiler.define=-DARDUINO=
compiler.c.extra_flags=-DF_CPU={build.f_cpu} -D{build.board} -D{arch_for_c_cpp} -DARCH={arch_for_c_cpp}
compiler.c.elf.extra_flags=
compiler.cpp.extra_flags=-DF_CPU={build.f_cpu} -D{build.board} -D{arch_for_c_cpp} -DARCH={arch_for_c_cpp}
compiler.S.extra_flags=-DF_CPU={build.f_cpu} -D{build.board}
compiler.ar.extra_flags=
compiler.elf2hex.extra_flags=
# Can be overridden in boards.txt
build.extra_flags=
# USB Flags
# ---------
build.usb_flags=
build.openocdcfg=
# Compile patterns
# ---------------------
## Compile S files
recipe.S.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.S.flags} -DARDUINO={runtime.ide.version} {compiler.S.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}"
## Compile c files
recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.c.flags} -DARDUINO={runtime.ide.version} {compiler.c.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}"
## Compile c++ files
recipe.cpp.o.pattern="{compiler.path}{compiler.cpp.cmd}" {compiler.cpp.flags} -DARDUINO={runtime.ide.version} {compiler.cpp.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}"
## Create archives
recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compiler.ar.extra_flags} "{archive_file_path}" "{object_file}"
## Link gc-sections, archives, and objects
recipe.c.combine.pattern="{compiler.path}{compiler.cpp.cmd}" {compiler.ld.flags} {build.extra_flags} {object_files} -o "{build.path}/{build.project_name}.elf" -Wl,--start-group -lgcc -lm -lc -Wl,--end-group -Wl,--start-group "{archive_file_path}" -lgcc -lm -lc -Wl,--end-group
## Create binary
recipe.objcopy.eep.pattern="{compiler.path}{compiler.objcopy.cmd}" {compiler.objcopy.eep.flags} --output-format=binary "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.bin"
## Create hex
recipe.objcopy.hex.pattern="{compiler.path}{compiler.elf2hex.cmd}" {compiler.elf2hex.flags} -O srec "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.hex"
## Compute size
recipe.size.pattern="{compiler.path}{compiler.size.cmd}" -B "{build.path}/{build.project_name}.elf"
recipe.size.regex=\s*[0-9]+\s+[0-9]+\s+[0-9]+\s+([0-9]+).*
## Save hex
recipe.output.tmp_file={build.project_name}.bin
recipe.output.save_file={build.project_name}.{build.variant}.bin
# Uploader tools
# -------------------
tools.kflash.path={runtime.tools.kflash.path}/
tools.kflash.cmd=/usr/local/bin/python3 {runtime.tools.kflash.path}/kflash.py
tools.kflash.cmd.windows={runtime.tools.kflash.path}/kflash_py
tools.kflash.program.pattern={cmd} -n -p {serial.port} -b {build.burn_baudrate} -B {build.burn_tool_firmware} {build.path}/{build.project_name}.bin
tools.kflash.program.pattern.windows="{cmd}" -n -p {serial.port} -b {build.burn_baudrate} -B {build.burn_tool_firmware} {build.path}/{build.project_name}.bin
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import time
import zlib
import copy
import struct
from enum import Enum
import binascii
import hashlib
import argparse
import math
import zipfile, tempfile
import json
import re
import os
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
timeout = 0.5
MAX_RETRY_TIMES = 10
class TimeoutError(Exception): pass
class ProgramFileFormat(Enum):
FMT_BINARY = 0
FMT_ELF = 1
FMT_KFPKG = 2
try:
import serial
import serial.tools.list_ports
except ImportError:
print(ERROR_MSG,'PySerial must be installed, run '+BASH_TIPS['GREEN']+'`pip3 install pyserial`',BASH_TIPS['DEFAULT'])
sys.exit(1)
# 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 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 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 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 ValueError('initialization vector must be 16 bytes')
else:
self._last_cipherblock = iv
def encrypt(self, plaintext):
if len(plaintext) != 16:
raise 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 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 = '789cecbd7b5c1447f6385add333dcd20283ac0a08c093202ca1a1745c5a861076118c5c4353e20bad1800d22061f08684cc2ca30d38c232a9a06677470831841d98d6b243ac6d7401449cc63cd4331d968d00106141522306078dcaaee1e5e31bbbf7bbff773ffb89f2f7e6aaa4fd5a953a74e9d5375ead1edb8638f2f7d7807031818fc97ba28282875910f0c0a14a619700098acac11a97b1707a91558983a0c9bab9e8b85abc3b1087504a6542bb1487524a652abb079ea79d87cf57c2c4a1d852d502fc05e56bf8cbda27e055ba85e88fd59fde703ab530bf1a054b720b00480e6ef605882c1188625388c61582280310c4b84308661090163189688600cc31212c6302c7182310c4bc430866189338c6158320cc6302c7181310c4b5c610cc392e1308661c90818c360380cdba4aed823c53120c14ffd2cd98c77a5fa295a24980708c546eddbe813b4206ea467cd135f9f69d44f389e8d378ea28c381e37724c8de13d240fbc3b55ae68192e38f81e4e8dc334a77040edc041d6ea71d846e0e39d0a7c82d0339622aa756e18de34aad9b3754cc7735d4535c5b5a50d279a4e359f693dd771a96beeddc8baa8c6850f17b744b7ade85cd57def6e7dddfdc6470f7f69696f7bdad9d31de083b9058c1fe91630619c5bc0a4296e013e61a302c6bf3a2a6042dca8804929a3027cd4ee01e3f7b9074c38ec1e30e923f7009f0acf80f1df78064cb8eb1930a9c513961f0dcb8f86e547c3f2a361796f58de1b96f786e5bd61f9b1b0fc58587e2c2c3f16967f1e967f1e967f1e967f3ed5cf67da133fb769ced9ee38b58c008f7d36458f2e7c1cb4774aaa9bc7b4a2d8b971f7e2f0cd459be7a6de4b15c517c74726d42788b6146f89dc5abfd5796de9daa8a4fb49ce6f97be1df5cefd7786279f485eb8fed1fae17f3df1d785db1f6d2761db743e6a37721c3652374e3d92f4c546e97cd5a3483926d1c9d512723ce6ae1baf7627fd300f9d9fda83f4c73c75fe6a4f320093ea02d4527202e6a59ba0f6222762a37513d5a3c9406c8c2e503d86fc03e6adfb83da9b9c84c97493d432f2056cacee05f5587232f69c6eb2fa39f28fd8f3ba3faa9f9760980fea2700602fd538d78e6af06c1ad3fc5cab6f877f5769cd89da530d679ace355f6afdb4e36a57d4dd85758b1ba31fae6859d5b6ba734df7fdbb8fea7e696c7ff8b4a5a7ed896f10d8dff9c4d76ddafe6ebc4854ec5c3afcc4a8539e67c69c7bee525151717169e98913a74e9d3973eedca54b738f449644fd7de13f1797459b579c5f65b977a4bee4fedf1ffdf397b276f3d3f33d164eb7e6fee4d02da457f293bec0b008005ba6a299cfffd1912ff8080778dcd79b53f74e058fe0efa869665f004c59f80e646959b1be717357df5b8da716a5ce4dbb97264a284e885c53bf46b4b5786be45bf56f3927952645adbbbfcef99dd277a2debdffeef0f527d62fdcf068c3f0ed27b68ff239e5b378dc2fe3464d3d357571f02fc19ee3cf8c8ff66bf7f39c71664674487bc89809e726ac98f874e29859e766ad98fd74f673932e4d5af542cf0bcf855e0a5df5a79e3f39a41b3652549beaaf6831e820e7ba80eb0554c516e39fd373e9354cb6e7d7cb728dff32aa24895260acbac5c6b7aaf2542164676f86b4734f5ed5f7aaf4dc5b55b716b0395fa03800c6c605db4c46f88b20e317b48a92b5034269502019c5e63cd91a11940a16b9b5590c6e28c5e7a3a2584c198665654af301901075df1b0c8897c4ef297d09900a40a64470e49f944000708d03e7e8b7061833b4dfb7faddbb69bdaaacb20d577fcee15affe1c045743455b67fd18f512b21806a7b6a204126a3d395eabfe0ebfb8acd81a128965649212027ec5f227e200516b7aa64f7e5b0915c8df4b75c29fa1a4f912d1786c1ba4122d0b44b8d0008e69dfd6cac6ab694c4b896547d6e80a99acf3efbdc755e0135dbcb0b0b4e24312cc078d95bd7de7beb32a1325695e653634fe3f0e98b2f97a8fdbc7504a05acf006f9d08a895de3484a83a9cf12071f5be037f9bbcbfa5477e8c04f20f488029b3320942a06aeb3dfb5d8645708c14051367004dc80181dd3573dcdcb270dcd0f3787ecab9f4cfca213746a73e6ee41f4831c887e769e1ae251212e0e709b362b26e081fc9fd7c10aac9fb8d5f480880d3f7f7451a633c72accf553f93b37433e24aa0242067c7be4bb0ec5a8228ce763aada0d26e016fd20951df01a9af6d001cf5c9fb0ffc6dbd39d809969ae784da5348929d166ce401d5040067b3485a296a920b5b00d7ab352df7a00e11715c9f33ba9253059437e990ad7bd8c7df2d85c1f8c5f74bd4fea8662aed26ac15b629d29b86d0863ac078c25adf1bfdb733f929835ac04a94be01681192e8e7966d29fe4ddd31a5da74a56fb3a855eede02a469501f84c2799c7e839fea6ac73a74fdd64bfc53e18f6aa51fff5c5ef391729cd210c46ae39b95289d7d8e5db74f39917f2e4cca57bec03fd7ac3da09ce2c05f7b4839dd819f288b65b535d45e54c1eaed11b1bdf030a7c14ef6a21bdcd3307b216f4f870b283194dead2fe4c524ee6823d47094e7c2e2be642f504772292573b148bd924d9d612fe0a49cf9efa24d78dabd784ca984929ec8b7075c9bcf3f29be54234eb9b67f2b4b664bffc96e44f51fc921f71fe73872b61b7f50e59bb191be0df9aa406d1070a669ae3f81a33fc1971a951aa03e95835630371ef52c365242b4005113cf4b9740d90231083dcadb96a257c95d5a802986c08ec09889a10163b78ff02556ee16b5fa364fae9d5a231f7bd841fda7a284ac35139c2667ef53f2faf31511c1e73dc5fcacc3e81ed452ae9dd84806da02d23596ba7008f526b987836ee64d4e371d54333f7368254b43345063cbbf73e8310f573a7057a614d52c6c5ddc1cddb4a261556d76ad7f575447a97ef2cea95ac619b8498601dc60823d94f3204bdc541c2b8abf9736775bd136fced7bebe6be59f426a68c8334187be788d9baa78a733a2ab51614e94c5a128c2df0d75369262011c3c1d13c0c7c4b2fa4bd4562906560a4625ccd9cd78e0f3b2f7e110b3d78b76ba5796ccae20ec941111eddbaa279758364c57d4c9a0cf5dc6557a5f8cd7deb8a1aaed52cec5ad594dd545c1ba89dac3fa53f93b3faa0f7306f5fd3ce9de3a8f7c5a03e3612fa1ba2a47bdbe6be5df436fecebd37e726634a3fc41b94a6492b86e35b08c608019eafa21e6d1568029cc13d43966eb78a93b49097b41648a45a56da3fe4ca3d5a40717614ecb5090e891f670c42c8bb5f41e881f3fa038ae5e60fff4bf9ee18b988a392af5c6f61c89382d02bdcdc97b8318f92c227814ab551af3a6fb00169368422dbdf44a36715f8219783d724133cec7b551d50f0a9bc347a847ff635dac193e2ec6933a43b2cf48a09d61c7a95a3ae4b72504f4c1a48dd73cd60ead96b1cd4116d8674f39597b60f57228a2207a755f17ba125a3674d952ede40c3b8323fee50e479a91d9cdf7f06dcc835405a9aab57e3664bab793e659fca03a3471c462d1e3e9833db1bfdd44adee0a89d7efdb7d4ea5fe7a871b4d68c9862a623b959efc8edc389db524a0d0b6b359fe880612bdbde05510d824f48804649ff264d80180bd462ca36937c4227d004ccc74cd55240e71c7f7c389ea1af6045f9bcec5e35917016a82285fe79b297bd7552902555335e5f7e6d90679600af97e9c40a926ab3012897e1b2eb882aa6646c010051e5c6a6b9494549c86a057ef38124bd6dc4ab966d31d6025b57199907f6268e4c1c4932a208e06ba046df047b1f186d871343f3e493846099c5f820cff6950559aa9baba8c9b7018ded815a9eaf97f47d3ae713e64bf34f6b4c04011c50e19f789d9c752f961bb1cc31c59e261db969f68d129c934ac02c23f4af647f6ecbcd983ed7236cdfcadd4effcacad45479004115018ac81f724d3adb2fcc7e5250f19e878a5a5bfd9c34115a1d5992cb18fcc1394341f8b614df66ffa6a80651ebc2da738653dac9da407d964e50990f7b8f06840a8d499aaaa3e05312c5a7c13df273d863549a1dec8e595628081003710e6195b4d94770dcc63298dfbe98f97550968095a44f2750c77c784c16a6cfe9ace65bf4312f8563985f46e7576694f7d5bf3555cba0562821e79590f31bb99aab0940924d834f45e8391ddc13dddea3a9ba01d4fea85d82aa3a30978ddb80993efb90a189a9abc995b9e302ba63463f66aaecbdd49e765c1f7324b375684f2a45e078ee38a5f72da431e7c966c5c3dcb22a12cb98ae0e486f2344192115668d2a0f08e66b211f65b6004c30ff0330575c96d809d33e06abc5c1499d009bc848e70b0efc0b4f2bdac4d1962e63fdb87794719c165faec9480f4e3fee98230ed3f3f9b9c1886684e9bd1ad5f7d03eaf0086106245e437b982c85a1045cf15a1a756b05ab4720fe6cfcbe9838c8b02d8720d948c2f6ca306b6fd53366e83fd812c68a48ad2da001d23b6ece06b29bc9d743a2b332e75794a766b517371d3b586af6b390e13368fd9c773f2d2e1242fa7b8344627169c3330426df7a5bc714912911e572bfd792d54c8dd9d243a3136c3c0e8220047a172a3cccacf322f727a5832b280a29512c20d372e481fe0abf35e4607cae19e15adfed90682f5b53fed4f2db7d38950b3339f94a8829e240a83f87adee4752ad8cb89e3b14c1710165a8b9ea187d47dce2011fb7487e61d4ebc4b7a399d33eca28edf3d9c08c70a1c69f6c36aee19e51484b7fcec9ba857fad2479cec5d8713e54e01dd8e3ce8093abcbe612b2d1a6837be89feb46f36f479ba8e38915dbc6d7e7138d1cbe92e79ce80a8c84f3aca143a2559a0c7ea220d477d2ebf805606d0d775a1dcea700971d295f7d7ce49887257d6af67f18e7c22214fba5abdea7af8fcb312e2ba6ba599b346516ba9168b30914240d9ccbcf622cd2dd36981a09204936b09216f499d450977e118236a0aac9d5cc3a7d9b3127c1378c9fd0a3d1a5e1f05ed88af8230cabb0ea86db4aaa062dc317921013a634269e859ab289d0de823a93dd5704eeb369ba40486783455936156b7ba1ebdaa028ef5e5ae94671b3056c039d7a5d1cc88aebb76c75847dee84229275d132c2f8507eaa894cb38b3d50e2aac17ef325b44bd2fed3cb7dfc5ea620b23adebab7b46de83ed105d56ec14a17da6fdfb13338b819c348323223390c3708480310c47843086e18800c6301cc1610cc3110cc6301c01308601ceff6d6d2398fd34b8b19bd94f60ec2f7e63f77233b33f0070756467d932fd9feecfac908ecd0ddd6f4db9dc71c8bc3ca5b8a9543b992e82be5c762bdfc7c30d31ec7ca8bc5443ab2ed5cabc8a12429bf4aad0eb17e6057611c4e48ec0d6ccb5fb6c0edbc8f47ad9c9b10e2d273501c261748efab106fa0a8c9df43960931042672e37f3ba462ac424860001f50a09c489d077f06a05a8c4b6ad70e59b2b9fd0eaec4f073915f194cb47772f1d79f778c47fa2e9f3fd62e5cb4edc734d6ff126519a3f5d1f9f80d62cdc3cfc7329bd83e2d7912da53a9a14c051717d6f305da2c8f0c8ca0c1be95b2382de96dceb34908fae04f22002c8a7c130817096cf81b10f81cbc713f8b84835b46cda991f0fee19a4acc5b7cabab8a744fb9c1afea96b4e2df7a4eb051d731a10af787cbfd6cb2f183c58cb77e7b80a7f494ab3767e9bcfbfc4c33f71b0e02a072b7f7294e7e17ff3f9571c6de0fba37bce3cf5177c8d6c0d5437b7fab075cc69728a87bee0f8d6c50ca95d229fd9ba18f9d5f220e162beec63a7b544e451de7b2e7ce4942c8efcdce14b3fa455733e4b30cbd6a96d6a15b2164d35818d4e29eab8d6fc6d43765771ebd74d376b7fac59d5b5ba634debbae60d4d8773a6ee4433e98daa82f59555a8f7342f3a03893d0050a34837d8a3515426e98529d50bb00514183641fc7045f671e52a9adbc188dd41b912fe0471de393d2c7a1795fa176cc52ea75859b2fb22aab5c43f22e27401755d1b708786232987ff9e2cb6888ea28b0db21a3ec59ff222dcceef3ca82094d401678072b99cf2f1198d9a1067acd8143ccca428d31a14f29907c1e9aee385fd38a0a13b66ca8d178e69ce8981e6633da81077df2acea6524de0c7ec39b12e6fa9fda3b3a3e82c03155de88c68951ed4ef425a259f590ab675aa55f59b22d38ad344dbea9322d715af13bd79ef9db9ef16bd8b67dc5b1f4a8ba17e860c0399ea28758464c1b0f29566548be022aa2522a2586715bbf608a0bc9c62992da40f952572a3d61f7282cff33ebf265ce4927c5e5880bbee36b912b057c782fb86f357f204545b2b20fcce0b8f6292ada462e5e562d4126ed67dcea3ee2315f241580bb291426477dd319f58100fcc8207bdd82b9205bff6babeac7f79bed92579437620bfaf539e82b8284a946cb59b2589a493dcab16c89a84d71f19ce6b3f0e93d5445cb9afbbafa552bbc17d9d536c68f64d3a942f99b9edb588a2c4b1efbf76c52bf61cec1156da07835d0ff5495bddd572d13fdbc1a34f46a96e658b4bb243fab1be9cb5dbed8857bd59964cd59500d8f7efdcc6a9d443e0bed61fd6c673b9f9f588d93a834216fbda15d4fbaffedadf8b3ea2e5df4c34dfd7514ffe8efda83b5f558dcf847d877aee7c76358e1b4ab5d6f0c2f6bad39af350fee7f500535688f3cf9b9cbd414a19b5e027807a7a15dbcf107341e18f19175767df7178056fbe1e714f7b5ea70b7bfd8a2cf61eaaf93c5a67fa77f5af33a33a16b606ee3cb19311128bb1086241f75ff44dbef46f5798687d29f8045ac8712de87e91fa5e0cb4b13f583431628c7ae757a0914ec428dd56ec1abd98ce3a387f17365e22d60fbb6b199b92dd54d4505c8bd68d8b3bd0aa7255d3ea8635d0374887f3560d4ebd6dc79956b2d78c76dc9037d86ac03f52c92a30e5b3569245c9ec4a12ae1ead8f3eee0ad4f33d19c7909b9e376c8323885efc2b55631032d5d3803a00ad23bca552309c5c6800898c88eca53cbf0760a33e801aae13cae2bd92ddd7b6988a0cc647ee0f8c89ec6ea72dae50529d07d719cd02ec8b7d2aaacee6c454c703934e2718428d207b339494f863e0159f620a161914d4e24ae7ee103a40f0b11837a4a3f153f984c23f76f38aef8ee4317ebaeac4d8dc81acb64c94c7ae97e41f57ffc2c617aa9b99444fb0ebd1aefb05f5be0637ab43ebca97c2d119bb71f11bc40b309166e2289cdf15046583aba1ea2b90339be8105c252a3cb8945698b295dc6761448a650c59435aebea5b99eaf198495745be64714fa6e689806c6ddee382fb65642d98689ea19d6e316c811c58ce34b0b1f94c03c2f135c812199b14c86c8720ade6e70567f580ba57efe9aac102bca3031444001d89a451b6ff2a2b91b27a112645e567dfbce74e49a0acdd9adc1b8035f3917735c40e0f965881c00c69ac4e14956d490407ea5b4c22017ada175077276361bf74c3a40b0db37684d282693b306ab47034e1c76c990ec29cea9e32ed21803a2492213a8cea496f67ae14c96eaafd2794221250f17026192d90414bc1041fe700e6ed2bbd94ed2008d61d5474aba8af6c819c17ac4a45e52464d5da4cca9dea8e09def517cc3f6fd64eea50bb17f457f1a2be712936a33b2630871ad1e643a5d118659a3dcef02ea4b0eb1f6982121730ee7db6ef4ed836539ec95246fab2807a8ff4d498c558749e24de1d64dab87a546f6a20b617456db10396e329f6755c4ec03a98bac92e635367d9d72e2b95441bc0be48eaee5521139d084cd9d902ef6c4fe0ede909868b16ee9710a25eb7d59457a58850996efa2bca3cab012b79543eb4fa9a53935b0368048f524e7f1349d55d0526d119a202fa7f485fae0226ba0a52ac1765a0140f2ec50e53b69029d03786fa2282fa9254d9c1440760a6ecabe472b3db3a4a5409dcd7b41cb4be59dde3b44169768f774b6e3958b663bf82facb73024e9b656b83e12abb68ffdec7b2fbac443eb15d61e38f6c57189b177079ecf24066939ff4c45e28a3be708298056b906e15d4cbd6a23223cf4fd58ebe1826757fb33bc4baa7bec6f5e2be004e37eaeed49595ea6e5b88c40c72565ef7d68c5df9a735339ce1cc40ed6f7766eb284b7c45f2eeaf40ada2442ea0425506c7ba50139110bcb304acc8cbf028dbfa0108767eaa087ebb147cdbd7af356f400aa3dbe01a9e225ca0a7e2d2eb9b8f45a15e0a2612c2ceeb5458e04156a63ef668c9ba319cad9e4a34cba72dc0acd9b3ad192a6a438c8056c9a7c1910ff67741628b29188eef549d01404dfbcc26ecb35fafb197cac89cb04fde67e94db02fd25b34e61c20d1eeec0933d23915b9f28f7f054723cb743920cf96f7c0ba55f713430fc3fae6f80be9e7e18882f1e30694336ba517daffa1be843ceac0fd947b25bb63ad5111c0badbdc4310dde6b2aaa3e0504e30590957108754d4a32a50aaa336d5396bae12802669e2486e758ff5bead078ea55bd261debe6b037398f6ada0dbcc6cd1f54ab2455d54739548534600cd291a682c0426b0d098e612810b2ed1b8a08c1068ced20286a685f2935b81e02cdd2bff3806c8cfda31f9051b263f592d907f5c25909f4dc4a15728801ea7507e81ec4d37ef8d3b4c1e26e417aa7aac0f202fb5b6f6ac4ceac95161597625a0b6dfc019657baf5ad52da59836b87608d6b503c13c125a72150834c80189dd3607ebda802612a6916d907339a80656693b6c915a556791a6b3fee7746930eb8f4a78781a070bbcc7a6143564374dd60e9e974ed1a39b22e3eb6307cf3a7e8e7d02a674e754de77887d5a94a099a1ed85232ccee8d3316698b057d279b5977a1a22403399e457712f379b250aa05f53f8146051683e5b4c0fa59d95897676ad1bce77513149d3f971698fdb9b65ba3f60c644caa91d48d26dbda1f94e09847edcc3145399b811506eed4209310ca7ae1e17fe682ad3278571fab97537d3381ace29b3a117abed8da67d77329da341596312269f33ad97d7c7c7472dd4b524ccb8b1e04de363d9833bdae945e88e00bd93da27f2d54c13f696694d0ac1392d90bc7dad37ea20954d7a95895518256b77664858e7d79d9ec6b505c91291be57b25388c91e1b1f48c84c219550e5c48836e1dfeaa8eeab40165bd0208b2f6882f3b280b25609c3848c4e887bc51aad5ef1469bd75ae383325d3c863867ad62ba3d93e33c042b88a773ca1a1331f9c7d3b068c7faadf69bd3b2b505cdb2e482568aac47a72e38a427505a649be0b3a0a0431002fb4227141abb522c45d003e17be9df4916ea6a922b6a2bcb2f2dc4509b11ff886f06f15d53e52c2137e154461580fc3540fe9a2488eb8d886b580297c5caeecae26575b2b5b246637c5976525846249153a69b86519b6e8e294b7a0010af65743cc6d47b82bc3a636205114d73de042ff3ed8e96d4542399dd3e0f25d0ec956c84ad69675b03eb11c4a1d6d04221d7220d6c5141d7ab66c74ab3f0eb57cd94629db820d9b8d6d196be363caa123eb30debabc06fdbf0adfe05cbb3ea6f7946fd77fbebbf7ad70c5bd1dcdb0b3d562a70a71527ea118e84247a3b4d770c94d106342fd2bd4ce7e7bdd70eac2f433cb2bcd5578958deb65709bd62bdee7ac57bd5b1bc5155b88484fe334960ee549ed53d31cfe6f5a6f14111ed90556c3276da6bad57a357b2d743964f12f63a49e09f233e49a140d6e29e0661817b5b85b954e7ab5b6f71f05a737ebde59a7e6c59596323db370edfeec38b681d1aa89d4c1b6c68de264f896af9b3ac89dc7c80d2a17f70aa207cdf655979512c1e8fce8351a9c95a4dbe1f06f1d951b9703f359f00afaf2d35bcdee0c042ab810c6e9700ae071e9ad1dad3b16bbbb056d4cad849b752ed64ed54fd19bdfe0b444d1f8945f21afeeee0d21a8ff118dab1fc3a3b2abb28696e923f8d7696f8fd2162103f5cf9b7ba63d03a88f26e03dd5bbb792a0ece687e1f635e43b71995e2e8584739a4718a9e91cd4ae42859e49088cff30325127078b044b091fcbef6f7feb5a5da1d946f83088ea6a32be7ccd7db381d51600229ec5d2929a86098761df8e411d34e62fa47475505e5709d2644ebb48c6568c7bc88dd33df97437f312959fd15434690141cd91d7b37e065f91ca11049473e5e0818bbb91bb544960cd76bec4a510738f800bbf72221810f94f3046a870daea51d346a36b179762920aa648be4fe4280769365dfcb9231e5c55e76c7c74e3a4dffb763dd79d4accfc154fbac5e8b5ebbd26219a4331f907b1d12ca6c1928213277b084b6a5a0f347d4f7a72b50af0f944de6e364742f82db51bdf33b7b476c6e6609e2ee9095c31998537e62aa3219f0583f62f35f29ec6f6fa1f39cb581fa03552e6ff9a3d5ff1202642ce7a93bc1d4e4be3de319cf9278cd3424f149efaa3f77a49407c31411c77bf9cf8817b917ec0ba1d099ce6177d9b8b3eba37d4f460e17dce9e751711ded8f46d1680f1eed916a0c9c75480ca4208c417ba9c84aa0cf0190b4a8f7446e44a28424337fc8352ae5c63640bd5203ce2ea7a26aa0af33f89c60667c46b5e038e4677f88405e2804d48f22a7456b517bf7e51ca892458cdd3327f9c0e539afc138dee52d75249249778c261a5acbd83650a04857510976a71052e14390c1317640e4bcf0afeead8ccebedd2bb92dd7642031b4b7ea55833403e9a0c41022a05e23910fea8ecaefb3ce6b98942c2f1c8f5dfc66a82c7d7c902c3f31bbbc8545d2b0661cd61c986d1dd5d65ea0f8e46758336d03cb55549e4d88ca8f35333611ab8119cba658349fd09875a7b859f009015c73e427a763b3c52f86a95523ad05afbe7ec5bad2e7315af95fcb5e98cdf6b2aab06fef24c3527a60b6f8b8a2c5e2d8a78e6a18aa838a2aa459d8fcd397fb75119cf91d5df461b5e0affd1a96598b3003f5bfa7b735dafe9e2f3c39b8e7fbc7c4c95a512b5cc327a175bbbe97d7af78e44f1389da4486b477076b758a16a37fde94cc0cf7a942ea0d74f2c2f34f0dd503b5b26f37a908d1283658c7b4759511990a477a6621ecb3b16d783f66ecfb0288d91d5946d629acdfb53f45be35931da9cb5bcdad7d3d76b3def687c57971664453bd8ba975075a1bbf8ecc2b088fa267431e59ffab54f71e6fdfcb0965be99ca269e43771dfc6b03b527b4a226be7dd19ad3d057c916f64a13904f4a3433f6badee0ec76e0b5463093c0687d8a891afb99f09c41df28300b7b0fecd9bbee9cc1b8c1eb91db0309e4c9588f4e06e573aa9bb813423203ae209b6cfce96b09bd8b729c4bd634fad273e3efc5a2f155faa4b717ce1c8bbceab179f97735a785bd86b5107fc784c6dd7b4e1876ada3767a883567b3a1cfee81c9cf9e0565443dd89540ebf3ebce19bcd651639d4486047647b89e929a45d2359083909bf56cfca79bb5c647c6fbc43c8e3be4a9cbac56895387846c7e9e8ab70b25d52180db33a05565862ae096f04d2e5b72467b8d89940293410af690a5062325c9267bf7425ad01b78bccf4cccfbd0e2b6c1edb1d7fd9566c94d0318a9a2e2a09f539d08bc499dc05b24e557c581fb196e556cac14d12aef980045301c9265092b730d485265b61f773dda7b3fafd1fd51e5c557a1ad57c1f268552c41bb28895540528d526c70552c41bb286c8a1da66c85ab6206ae8a256817a5b1b243521d80799355dcaa18ae52bcd6a498ac0f6c3d2e7055ccf9087d736935acf79fe424c74c5158c0cf14d5eceefba4c133c5f414ffba13ec6ceedb287a38554bfd04d75ad570be2b14013462d38f191228c61e0b729ae1587d8c2a5a63fd33d1f58beaa54422a133579c78209731c3f5f4a708535603b52a7362217c16ca6a30e5fdd8a8f8d278d7386e371c591f3a998f6ad0e74cd6eaffc59069a4ac46d644ab64d71df684ac6929088223313f4fb4172504eafd1d9677197bc8cf993edbec2d26f6f4188e592b2d065b2ff4c24bdebdd084c604a71aa726381a39b19e48a2ecfa7b2afa32f26bfac61031f639c24314a42c0504a1335bb9d7782cdff21b8916935e0e8996ef1c285195f4b7fe1a57922d574abac372dcb8f4eec07255926797e3fbcfcd514af1d741fde736b814fa379c3054a3b607f8213dcef8198d469872aa06d7a033bff98d4813617d91d26a767de732ae701f09fdcf1eeb435bcfe45a6e7ccd6ce557fcbf4aa7b323c37c7e548ee067f9051c9ee20187a718c1c38d4509d29bbdbd12913f1c13599dff847cb92fe6cf1a795a614e8959861b9640de8f05c3381aa0ce711259fe841f57e6cba8965fd17d4d4cf98d7974cab5e6af9b02f527f45225e2cd6faf41c98e077bb2bb8a3a8a5bbf6db8597b4a3b83d71790b230fb1acd61d27b78ba1b0df3d971fe6fb05d51f069e7fd3d1267375c7056cbeea0e4c50ace884194ce4474c295ab78a2630dc0e8c57885d03a767eb714969788f5e7e808aefc3ab6bc7bac60a618ad77c192b619d74d3a2198db15da31a3959799af608618e6bc78dd44b7f64e6d9adc10d8cc6b82cfeab73edd8a6f2ddab27acda70981355fef88a6f5e379fe132462311e26b4be17f1ab84d8bb84f7f93512d83106153bceee3c6710949100d714288debf759c55f69eae1d8ad0a8e6ec3a9b76e82a3f3d40f0ad68fcd35b6a0d3e06abcce5c06730410c77b870804a7b7e165d570fd230202ea27274cf0190124a24d02c95bf300e5ee849e0982fce86f474654f72458febfa9e1f3dc03ffca9b4fab46179ed7b5e3b3212debbb377b8eced3db1cb5cc7ead0ddf3b7f36a4fc69f6f9ff43da9f9b57a6ac6810cc16e18b9b5f9f2b08cc86e3ea30505433dbf9be62b6d6a610a8ae0274cf6d616b745376edaa5aa461a7b49a59d9d8c8cb19d3345542a0cfd95755302f4c45edb389b64da3962ab0d9c288b08258d33021389a73f4b2a6f20a38501145671de0b00f5d66b1dfb7091902081ce90c9149406fd00a6312a5514a0581d2bd9d85e836d28368d3d1c2712aeb3edbd38c6968d40738c2ea9e6d5db0a9d79b6ced2dd2514b8440ad9a2db42bb69921e65e5b17a2bfcce2a8256356bab9ffd9ba40f19b722916b6e6ec7518edaca97cdafbb0b320769c0a1b7f3487cab2e1cf3e6f71709231cdba20b3a7c5329996cad91dad93797198ca2fdc63def17913179a48a29be9b18f500750dbbfc34c645b27baf746c576e02632ff29454bb1bcbb5e9fc21e241fe1b44850d9defbd2fb2d6688f72bc2b3aeeee881785d772d68268abafb75d7b71d375b450f7d1b7f6c865ea37e6a2d23c67089f3759c710682a244894e0a66d56a2a4970a0919b5b9c93eab745be5dfcb6e81d57f69edf229cd2231f78460d7e206bcd0c6d5176a359a3520bf03b721c135698e42e2d7877cca5f2af1bb29a24a220dc4113dd99d85627809812b80ec0ae6e839877710959885392bae1125d008b03e7b9b9126d3608bd43fd9b04081bade62489702508b143efa03d39385a0fa2cad21859e7c2d1f052202a0ca905d4121264c49c2b0fd58a54948a0488cb4e963f862c2403b3211de7a244265b0a66b074681523da2ba286dd70a1acc56e8c2893a05c6f8ca21e143b5b87118f50cba1cfd9cc108b70e8eb1f1867a68629310a2c13583d2b7f41b4d15c39b1907b42fcb6e023cd1c24055f59d87e843de2a79aa1b5aeb7ffb2c96992e3bee3dd5f5463cdfdf9d49b76d09f5bf8f32faa4be52f59b2ca5fb030a272915a651d7ba3b5680da3f304a1906f11e4dbea5ed9cae8fc59d813e8557e10334844096f8c62440a91f5f91bb5c723cf5ca1969280509d2b17ab24c3a0740a6c4e4eb1d46b0494d5753c982854c8f116e1242f7e443f99700cd22029e71bc3a97bc55062cd220a6b73817e076995b6d538da8b5a49abc4107711491137702835987fe34e05ac0ff3d344c2de26e8a792e91ee0e1416762d96eb9cb617c74e133b8396483753bc5ce37136834c07f9f33c5872f1e73523872670fc9cdfc28c8eb5adf4a26f60c718c504df282d481536c85f9b0ea125b2f0d258a6a85352aee9a354a21d46704e307566889f3b258f47ca9e1a5f74dd924a044d580263557edbdea7aa4d74a73fd1a5a74a6e986796ccaa5da396ee856e1e4d6a91d33baced57cda70b529b4f6097af382f509c88f32a243b553757f2cb1b960012340f388f45f6f857b586915ebe707da4fb033f9b1ea13288f7d3e41fe0303bb0b0b5494b11d10016c5ab1ed1f95e63e8c0fc9520c74471391af2bd4016dbda9018a9633ba7f06b4baa8d91a1eda5f577cf30079f4f2d2ca636c7cacee18ca61eb9c4c1663a0f309c79fae2230bb68cd0e8abb0b967096effb8ba805d07206506dfb55164199da85af2b4cd9f65ed6c3ffc3cd222aea3a60792ab115b1f161b2a88fcfe3642106d01e107743d825821a6d067400cbc50bf6bfa955a7cddd3147fbdb7598fc1b0618a7eb04f7765b2ae46599051be95f8b245caa4d1d0f4eba2af9d93a1af7c0f60513f9b8593502c9797cf0b236fc490038a9ceb53e2ce9e1769ef85bda4a2e0866663bf1f37b3b3652b2b563c43983a68a04be0d53b570d5cfcffd36b836f3afd59c25c109ed9cbfce7c7bda9649c913d6066d16356519273420ba123a0270bb5ae89659a00ec9ea891f38298d841e8ca8be9ef38ceaeaf93d823b5821877742f769368bc7e627344eddc195b8cadfe2cbfc3756883cb1c9fa529af3118fda39ffebeb26e4814d6d38a39dc5fb60e5b7e6aeb9c66329dbf9f2d59c0f36bf00fa60b3900f55dafedf7d309fef87fa60f3cf421f8c2d1fd8fedf7d30c5cedff3c10a77fcbe0f56fe65bf0f36f89e8a9895eb0cdda9ecc9d97d7278d7b1131afb88f5d8b8f636fc062fdd8157f3a01f0fd40fc5f3dbd247af71003deb6fe86deca3573f80dedddfd0ebc38bad1d40efce6fe8adefa3777700bd9f34770880f612444d4c4f28d8ad9aaca5f6f6087785112aef5be9c0fbbb3a60bad5064cd54a8ce96d234d372ac16ebde4ad0ecc7b8400301d1d58e89ed95233a076fc2c38a4a41752b4059fa12ba54f6573b556a6f866f31cfdd85f6be1f7ea85deab2e2928d170748ffe4fe83c6b8fd329a3e6b41380f637ef84a18c68039a4a02783f371d1044597e2556d67016a0bbd1d6cb753d92f027bd92f4e781245502188f99c0b5913a381cd74752c3abd10937a8c6e948cab31a988c3b30d3ad1d61a7f2471f33199de0b353d80d8b7a42a5c5aa1bde73265f7ed203ebb71e51d996da3135453ce74757f1d23a2e212280e3fe355ce7f2d2da9de38a64b5df86644561126f28214e52e940d2c3492a38d50a0e4169d9b1ee1c4255664b0448560c118423c9419f4a7c0967884d38114e119706c94db9c6213750d52f379f0a2c9cb96156e82b874a4d02a570c2408905c219f99a99047a0b69ff0c50465783e09b9fa1378d322b8177b6085895f53d03c68b23fdedf57b8d6fefc181ed9d9aff8a655c85a1ea0d345ecfa1da4b70349ef453b8fa3e37a254f2eff3004351ecabcbd0da15dda3949f2b792d2b535a8921798ecfcaf4abe4c7b9578c1436f2a3446e740d3508c6eb7a350155bda55adc3d4b8aed7555aedcc58db535bd66fb53dca4239dcec7b4e121ca1168f53afe5e2cbac35f16d38acf8d0fae2ec1cbec25b8b1065316d47263231a71e98ab0919369892853a45642cdbf8ef6170aa38b622544e653c41f8ab33233a6d32aea4109cef569b0b61250c4c74000356f641d92c1541ad1a389a9e86e2d77a7aac7774d5642a3852bcfaedb42d8717db263dd06293e2c018cd40f84401f3b345f16c6eef85520ae3296416a2aea71097026cc1019aee9c3e14c9133baca184e28b7998c97399c9529e9ca6cde5ec1bb703d24542b9d69ae158a282c12dd7e9408ac47254ec04d1213ad603cfda1250480027070ffa5fc82b99a63449724bd1d33ddd461f4cef3fb8b4159bd27368e9417dee87226f3c00c83c69fec32551b303ae7bca104dcc895fbd8bb087feb18d1af3b75982a84f8c3a27191bb96309e01a074bfbcf0ccf05de144c03e58fe8c8bd5a3aaa738967b2b484236e3a53a6a5d09f4a59b47642ca3d698f12c0fa98a5de71774aacfe51bc3cca22ed0ed8928ee244b0de9b9c6cb0e4940bf0ea71e9588fa778e16d62eae4194266bbb63a8b56621436692d87c6a4f1b5eaa0d71fec322460c04de849895eeb7868270763572500728a77cb412237989d98989d6b12f760f38a76953abfaf774f175a134da89ea66ef3d3f813e8c4447e2661170f1256718e43ed0ab37e8c0b9fde24408f992403e9e04d218f4a64dd5240fab99002e12919b10c9e6c47eb497460458c754f5102a4334d4796d7d20b3bf1a8490c06586c12cf28174d8fd3611988b56d30887c9befaa244e4233c635047a27be4f28fa157e85f415a0f5eed4125a0b72f9a61581aaef63ff06977485d6fffcd782433372155578223cfcb14b30c8c21cf1976922101c0e51cd4253a12f5f5b25cf4ee65bf946189fb2500f7801ae7b24d1d9aefd2a78b6cdb491f6199ce8623af06b55242dafe586a780fd2dd0517a32cce34894125a01691ce2fa688ba7c3bfc5ba39a1736a1b24f262a8290dc2479d560465e08e9037c0d11e1828924508f97ec17e2d87b120f7fa8417973b1896a7ff7cb1232d329354415949a280c72d4cbd55932e1145ba7911257ed489c5c8b1b8a12cc905e685e44f993adc2203d92c18e27256450eadef941729ff1d8bd4d73d38ad2f06df79226d3e3606f4e867dd9a2a2ee950cc7790db49ddea69e05ed8fd69f3364e942c88845b2bf749a6eef76f9775626825e67a197fb2053be076833759b1d901f7868aaec83a683dba6cf2d0e48092e9abee9cb5b86e6b1ee657db90908fe75595f7e3a82edaff6e5b36ffb3d7eb52f3f1fc13fe7f7e51f45b025df6c16452cfa6945c65653fe69d81efbf6cedcdbbbbffc312b934b37e5570274b2fbd0ec806f806d5b2b2d0ea80ea46cfdaa2faf0dc46dfda62f8fc0c2b6fed097e781615b13faf2fcb010b2ab37a12f773a843b7ae3faf295106eee8debcb5f06e19adea37df909102eef3dda979f0ee1ccdec37df9348441efe1befc7c04f7f8f5e51f4570975f7fab105c33cec2aeff1b4a7047ff06bc3fb07f11764124a29421453491ac765d459aced21992f3e5d5db2cfd82a82125ae75b2bd228b6135a5ba828706ea0682fa750341fdba81a07edd40d020dd60a9be5cfd21d7f383a8be3e88eaeb83a8be3e882a82388d59d697cbd7d297cf6b585f3ea751af72328d1ea251371b2f72e943342a7a9046450fd2a8e8411a153d48a3a2076954f4108d8aeed3a0be7c4e83e2faf2790deacbe734e8685f3ea72187fbf2390d3ddc97cf69985f5f3eaf617df9a711dc31ae2f9fd7b03ee971f67be0a203e6ecf7c0e9b09134f426d05e3eef737f3251c99d5d148ef5a5d17b076aa5e3cd83236606ed0d594b44c867f226fd7a6faba81a1b8eceaa6c20a42a00f8493372096b88ea16c8c81d6b3d9073c8d6f7eeee378e2f18705a84f67464d55f991d10897d65e97bc66f9bd158dea8a26c25fed03642d8b16f0b671b7d23df92dd7aa46d75a6af7265df21abe0520fe9919e31f6ad5d75e6fe14b6fdd61f58f9142cced80abd58d2fe3cd2bacedcaf720bbee5ade28d8bfa0a64253f2b2d0e18e9292cdb0c6bf939892bbfeaa21ee91b8df46d045bfe4e98d99183344e0935ae8e4db9a82f58853429846ce96de37ae30df6ed1e3bd90cede667be8757a1b4b041347fba339dcd43392124d6fb134b27437a832f01fb1743564e73d67fa79beddb39e1fdd4275d7e781ae15e98fb5bea173eed3c3d90fa85b903a95f884469280fd540606c89ab29171d79036bfec7d5574fa39adf55f5d79c5c45b3b85d91bfadb9ebeabe8b036bee8a1c5873d710ea4d57c7b2d4e72c18d0ae2fc41c2751615b512aa28c6a60b9bc76fb3ccb4df8a71e155b2af6b5411ac997bf2ae3d2504d9f4a2b18ae27e6a8fa712655659c473ab74f453521cf82d3b9c4e87e9d431ac6df2af22094dc931c172b6f23cf19e773dc432a7761d0a33f36f056cbc25ae821136dce7e5fe95514687361bf39341f5372e74985d9a5fa289a7baea14be9858e771d5cd87344363d565bdaf70e44a6b254ebebc0cf2a451e21872fe6cfbadec1fc069ff69b615b05c77540abca5449b67502d3f71180696d1510625ae8bdf403f0d20ec43521447c9f3673d855e08aaafc37d870ec1642ab98e7aac294a6ea126c778e35c7d6c1c92ae0af70bd50d9f7dee103c14421269c07e63968a09a109d9776607e881621c4cc182fc7230fd991a5a9649243f2a482933c1a697c6b38f93b4e1107be0d15de452807c28246b1b292e372119d43ed21fd31e55dc73bba9db245c82744ef55efcea1038297b501cc6fbd19f51fef6777a0d617bc71316777617f6a613b976aaa5681db03b07dda1ce924d63920bdf08923bd0aacb40cc0ff854d5f62aa0e018760fdb397b5e1d307d26b76e42782ca81f41e39d20de0ae99e047d4d81f1db5e880da22e653c10f28b52b1c69f9e8638ed4c26a2e15714a07a05b04af9a1d79353750de055527dbf3c716a037c2093f93d40320adf0863a01672536c77b190dbe32f3e3792d4afbe90db45ec9303bf67cb834c4511d9fa6b8e74823b16f2c8e3d1f961bd55d075cc3c12d0ef8670e867430bdc5d1f62a40e7a8ff8529f755f16dfa09f3e368dfb538acaeb20fbb7a0876f90f1cf685f04fccbfc5b60dc1aeb9e9c0f678066dfb106cf0bd037be2006cc7d3f1b20123c7b7fc9ee9b21de1e8aee1c0f783b332b9bc80a53bc24375441d3bc22d365507c0daf6fd4baf3244c2b55851fb622e1dadc02f7ceb04a9209c108873e0f26e284104a920d4f833ad427e1f98e91e6ebad98e3dec9185a3d1f3f35c19bb8e0a1be9aa74ec9f15b6b7204e7c903ec215d54ce39f2bb6a4f36fdf2fdfbde37ffc7523649dfcce02db42ddf473064265b472376d344f391b9fecf040e6f1e39c5afc39df1bfccd4f45972f3df0fe012fcb2089fb74506428a044b57d37b6ecfcbecc1f8b0cec7bcd2c44061519507994c2ddccf4af8daa614f1cf8b7a3140a0672c9fbe22f841ae848772b07a9fe106af0a24ab5d4f3d5401a81be66337e52689e53ece84a9eebe5f22061df5db6cc76b4d7124517c5e349dcdeb66f0d47a7ca1f71eb6b900de036b689e76ffc60fe8613bcbcc6971a7650c1d30bd19bc1249c2b6364755e9fcb1eca5ad0fbad5999b204e39b0575c646e39b2eb65d0f64713b5a685276372bd33dc12b499662bcef6ecb4ba4986260b419295802cf901aad3b28a86feccaddff53b56a864e22028a5daba983474196344ea966ca742578306187d4e980bb222b53d583b4862b21b2f497d8f710ed5cdd8d443b01e70c3bac68e52f3f4b02b40fc1967cbfaa4badfaca82ce2530d51f43ea5c3080248fa48ea49f3a51d1d21df3cf9036178471fcb1c30f2d6c173f74dcce029da3ebfa6e6afd8a15ca12f2adb44a56279fe987a9231c398aa748e6fe349ec4ed997925752fb58e0ae885b2a86bb4742ffdcacc9ec1f03575ffeab049f993c16fdc821e07c5d847a32bfbde209e89be623168e679c2edb1a13b6b4fc62b5af831fca9a374e6fdfe16d434f5b7a0fc316a81fe316a8170e0bd33eea6f143af242abf1620ae6f98fb6835199642c96be34b0ae2bb7bfbe4d2b4beb3ff56f0b3b851b4f47d79e36e3f078536c84112b5bf159725744b6575d4bc66b67d6c3b83503b07f374a4b6afc65aac503a83dd27bd8e4ef0b99d56fa1b1ecf87fd4a1aabb3aa5fbb9785ea4e64ffb3b8da45edcf9e793dfd3e3cff1ead4276209f64ef4078f2d2aa0ef6c40a3d17921d48370a5494573ba0fdd9b463d5edfc599d8abd67d48a818c685af97a383aab436745a774ff0cb0f79fd585e75bd1fead7cd2e95fd838f0c62f6c693476168a7e41677559994e2aea4031a003d8d492f666351c37bb63ee5ad0d713b97328df9ad47e191ee5f7431f175012e934e81b9e338c4c447b53d8bea258f44dac5be1eaf7f0785cba8cfd225cf6cce06537f0bcf059f9122981638cf132b6ef56f86797b9514b548b6ae1a56919f056f7ab83f30acbc5757cdf5303b0eef77d8b8e1bdf6abba3670c3a0f35aa2869bdf0a7706f9dbd17ed68cb03eaefa19bc0acfc8e54df63e30fc99a3e991e256b30d05fafcf271e7503ea7ba30fef307967e87962ba851bd95169de968a90d71bd834b9616a6dea266190633e47fbeb45897313efade1b467cdc79ce6d47d6ca861d79a3e19cbf8b3c8466e76eafb169d8d1d914bd0d8a9db1a0ac74d67228fe2c7cb7b07ea4b0d193145b14656ba707c36786d84ff54dde69529d9b54535dc6d99150de81dde55b5ab6b1c6fc196df0dcce1796b94b8cf76cc0fdf16198c147fee77dd508a4eeda23613a4d11aacd303778aca13b9cb8302302305fdf93176feeb01e03623cc742913e6e3813bd50b3c5ebe99e74fd3c3f8be3bca68b5b8d598d30b71f032e1415c7a017d21aa89369c47dfa77a73e77067ff82d50ebb2c2a36ad728c1887a5a7d97393ddfefc777fca0bb914bffd8e94c2f7b91462af2325f36f8612562fe2bf85f39e0ee3203221f4a03ce86d8c9f2fafa2560aa6eb09d4322e2d914da3524bc00e2b4366c291bf0d87b10b43e841f0b05ff115f9d18e3a0ff0fc557325abaea099f793aa8579b2d8b31b35217a824ab10b45f1c5b1836fe8e0c971fc68a2b9cdfb3915a8e469bb8cfa982d67bd5bdd8ea47bdcf2dbfb3da8745626fa3a01fa3201ba5f87ce70077e9d80b7d52b6876e5e99b117dea3d720c33c68f4f4bb4dc84761ba8f77691c2f1408751a35c449c8caa5e0dd6e980177549eb451197e9044e4febf3183abf843f4fdacdd7f12da78d257f46df2ef135e4516ed45e5e8eb653ee56244956b25caf7d678840237784b94cb7142b08371c47dad5f8b2340ad270be7f5c1d80bd42adebc21917a8432e0704d2bfc3f4610fe61916b0df2ffb4719b913f89a8a4ca1359a9061a0803a6abd99d71d5350ee7b5013e20c1851267e61239a7be1934bb0f8073cf4c0c2039a19620035f44935205ee1dbfdf750d83ffb2e13afa0f3e175f4b3bf64804e8bd95a366a2eea304df16e40a5ff046ee6e595cfb730f46e30999e5ccbdfcc7e5b73c695284a505f93d0ae02fe26f7b6a204c1e9dd84e6cc3040b51603f9c9bf03872eadc95f98cfef2aa567256aceec26f6527336586dedbf10aff899f9fe426f9753d82b4876c7cdcb53ae35705ff9f9baf6db9ae8ae151dab5a5737af695ad7b0c1f11ecf51c968475f271aaf21cbe5f4bb80a80a3d40abe4419d007d41a43e3e32a93849b4eededb73df297a077ff75ef2dcf545ebd13745ae19b8bed1ee431e57b06e3ce64ec9a8d29d79ab2fa452f7ab8113e54231342de2357eaf04da79b07627cef3cb04eb0230c12ce85db27d7afc8ff49ba1bb603fe20b73a42fc05e7475ddc4e4fcfd13037c6676bf9254263a2828d36583af21a7e3aa8c8961c219795e890bf38c36ca1d6a5ea2d186567cc8eaca744ff16b79ab0f0413a57868fe8a7c5942c1c6821a79d07cac20165ab3333b83be68dfe36aa6463b8375d977e80d0e3fe158c698380b3b47f48fbe8cc38f247796428b38f005cf7f5668be57dc9ced3439a75716c78de4fde36e61aea35449362a957f999b05abfe1a9a2f8bbbb07ddf17a86cb71495bdb0fd803535561884024767650afa5a02fa76021a81b9ef279cc8490df40119cb99e54a8164f9e780e9ecdc8e05066f3b0e98156d022a538c6e456da77677e2b46ab8f88e14dd6e66f4a3cbb105ea08bf6314217672ec801466617e2692ee65c4d7cb8347778232fd7145a38522c522460ae98a9bb7bb2ea888b056925d4cf50241c6d229997095ba3d382d0d63fefdabe0932a09aa47631311aae1c255d2e5bb189db41c53319e7f003fec29d3772aba3d3f51055f7d1ba3eaab45063f7497a72e66a7e8c4fe82b9270c593af57c62c14533aa45bce090d9f5b33eff731bc755b07b2bba4b55aecf4d378bbf64a45700f53589be92b0dd55551191047d6742f5aa05c162558a994b7fd52cf1d002ea2dbd5bf76875a464298197bda6c7bb63d44c63aefabde0ad7afc2a1dbc432a403e06f52e89051a18baa3974a2c81164a620cddd01b680826c9b0e0d77478f0561d5e964d0a182989cfd5515dd5a0d4202121b60d62b755c391aba117a6e411787eeef25d6521cd60259440e79fa865cdc3255bd380eb65b10abdb94109efbbb8aaa8e1f79d292f6720d9da8a6e2208aac148a1449a06b8b77aee49bba519b0b45821216bb6bb66526fd8811e9679eab45b45899e3a5184b353c65206e631b54b41c632d40f53d471bbbf954adcdb052b7787ed3e509e5ae80e7ee746a76727d494d1a0f26274769476850e51b6034819b7bbd32aaba7bd158b208a5665d799995a2dc022902c532c44c442772cd255f5b9655ccf38e13ecbc41f1f5aa6f770b91f5a127a680ba13af480d992067c75873ee5db3aecbe136cabe0be93af0ed914b305b6564969cf406945803846a2920222875096916d0a38d20aa84fdb9dbc69d4ab40a87fbf2cfa03807a3d785a2658b68b8132dc7d59cfd1259e0a21bf239ee248860c27430d92212385fd7d8bc4111616015b83938f98daf1a0e5a2af2eeca984d48224b3af6ef9b7b72ddb9e8e137e6e8983ad70cd54abacc3c9fb12bb3ba8b0b896f375604f715807f914784450226740448c2ef48b58dfe39a89e8a95549a7fd22d29f3e3423b9201960911511872ef6431f9e7eb6b53e81d6dabd5402adf5a34cc9b6ceedeac0c65dcc7268a923c4ac3653c390a5ee11de91dede2511233bc5a23c8e555ce97b636e1e670f1268a56579503aec5b5617af30862b40c2da685894b5ead9367af80bd646b5cfb6d16b0668a1cae0abdb30c90e29a0ea6e387356aa1cd76fa518b452da2261adf4e8002bad091d6aa5eb7f63a5615171ac95cee7ad3489b7d2f903641816f52c6da684ce20d567ccefe873460cc630378538b34c8877eedab62b98c8c383b5d9d05acfe065743ece40bb2fca3f9147b56901a1c2a5e7f6672cc5f606dbb5206e372e0d3448dc0d40bd1769d94ae64c5e586eb130b8b616b0d6b882746262dc01b103da32d9f1276a51b3b3c396d13719a8dcfb22a8dffafb22caf85f6c9974e26dd9bac8fe0bb4383dabc1eaa7422ad359c8d9b264dd0a20362fccf6e76c92b08f42d66e1f05b57894bd038b7891b5c9d01d872caffc324eb8cdf2ea9d87966dbf7016f88d39a167fd100b6439cc612d50c35ae081ff772c90a54bb3fce7420b343edb0211166b814ee43dc9ba40e077fabf58a090ac916c1dc35b205b879ab5401d6b81d9032d10d153ab7e38cf59202781bb65fd5a947efe4900b4b218490cb4328d64ab7d3b23aaf953636ec63eea6a3d201e1f7a805643631b616a1875ad1ea723b1806f3d254450f88ddcd187bcc9eaf2e01b25009dbe617efa7fd1aad44229dab15161cadb960ad543183296b5991d6b5ad7af24ee7045bab7efdb9cc3616f86ebdf979041e1b309bb22d8fd032069b36fef8ef94893b16bb147d8eebe779823d4aadbe6fe5d9b2504518338379399c07b793ad62d6dcc8563c408758069691b26214603491e946c7add086cefc84a09219eb945a856a90ba951621c5ad6977dfb092213b98cb7c5bdb366d325701e1f571e422a40672ea15ce881ded4da229cbfeb3d5242346f97a4b76d974895828c65c8ea7adc97ef56ee3e7019b599db05e1f6da10269a5f3305c1316d50d7ed23465bef4adb7e95404d199b5b06d3a47eac3f7faf943daf984d9428b2324b69f4a561c779847c38e7edd83ad03e59d848b572e00e51acd410c0fa538f765073d6efbb9c65e8db59f1c7467279558f7c1ba4e3d13ee1078fc6ee42f7c25cb597b4236bfbf76b32dc19e934500c7dab2c439db9afdc7d47b9f1f75139b43fe9aa554784eaa9fd5a9cc3d1f118da86b1bbf8bcf7b58364c018a6816b90322dcc32dc350f847e60df730dd4f2ebeff6c94d531b66d48a5af97dd3efb81a126b8c54d19ab96b3421bae7ef25e86d0c49fac8e7d89f33aa3294d41b75807febea09bf1ab895b10c9d38a1b326d7cf241e2120345f3eb3d24d708c9c4244cef6ac07c16b3f037208c967def0d14752d2ef81a6583565d91e79695590492405627238995775cec0ef273ddc65e5bf9ef01da65c09d7f2d14da53bf9fb970fd00cb5a2c1315fadaa9dbc935f7f5471bc977caff98404c402cdc7fae70fd5fc7625cadf47b4c5f16f7a5abfcc8cf016b7f60e17fae759e78bbb28b133d084687de0221fcb7831cebdcd5c10e11edf1d42190204f20fc6636d370525c229b33dff8069feae0529b95880f871d6fe8208ab716ba7c6ac7fde9a6de87ad52c9f26043f583425115334d3b56e2db9ea00b52d9aceda7fd73219fe7e6ee12cb2ffff1d619cf60ad09e4aa0965a4180d9c24c85639d0d2a32967a13c2be2f140c2c739d4065a85504e0dec713d5f697547c0f2ab042ac10bda9227a7842ebdbb893ee2f098867d506be41b5f9d33b1d7b95eea8e6feb72247a74c02d9fcd7436b9e76c750e2363055e9bdec8682d0d76f12b33715eb1c6de36baa7966bb62afa19a06eee70f2cd3fccc323e5583cb70361af0859162ef846b42778c7de0f8bf5c10955857440572296b737cb1f632fa5e1ad4d6584779f92725570533d1b7f5b1c86042803938e0cace65acee095d2cdee9922b73e9bbe6c17c02e1b3f82cbc3098cf8238b95b1b2848908f84bff35faff869be1cbd0dff8adca30dfcf48adc133ea7c8a5f0779bacf142dc9cc6829573ea2e24c81e5e582f6bbb902eebceca1cfe416fef4218de85e17b18fc8ff6f66e80e1ed92dedebfc270108645305c3ad6db3b09c661c949891b12e27d12366fdeb8799c33f84b8472ee32d54a9fa40d3e91c971a96b7d5ed9189f007975a4b389286d1644494af3498fdb9c069fd21236a76f4a7b365ef2c68d9b8033e0fed237ac8ddb109f0c6b640bf1a5a0666edc9ce0139fbe7ed32c1fbf545767101e979e9ae013f4965fd09419c96f4df2512e0aef836036d89c90f817bfa0a9f12b27f8a54ef4796900e6efe5b8b22cacf9bdec097e6b264ef2f98fd98842de649089e80c8d43173e3bdd115f7de53fe7cf50fce7fc451bb8f8353e5ecbc79bf838938f77f0b1918f0bf9f8241f9fe5e3cff8f83a1f370c819b87c01d7c0c3672b10f1f4fe0e3997cace0e3b543e04d7cfc161f1b87c08543e06343e09343e0b343e0723efe8c8f6f0d816b86c00d43e0e62170c710186c1a0c3b0d81dd86c05e43609f4d83e53961081c34049e39042e4fe6e9f1f1ad2170cd10b86108dc3c04ee180283f58361a721b01b0f7bf171d01078261f2bf8387608bc968f37f1f18e21f0de21b071085c38043e36043ec9c767f9f8fa10f8d610b86608dc30046e1e02770c81c186c1b0d310d86d08ecc5c33e8efe1d022b86c0f386c06bd2827c501cb9c8272d61fda68d9be3362725a4a2f4293e5cfe543e0ee6e3697c3c9d8f67f0710817a7f6d14b8ddb02876338f025a5c21139956d1dcce7e9c6f5e1c56d4e4c5f9fb0212df58f9b13d2d2376ff0d912979c9ee0c08f73e04ffd0dfe40c435713c7f713c7f713c7f713c7f710efefae82c79367f3c9d549e4e2a4f2795a793eaa033938f5fe4e329417c3c8593c74c473d4bfbe5ea131e979c9cb099cde7cba5f1e5d2b8726f276cde8832e6c56d8e7f616bd266c8214a029be37c7c50fa624e4471f1f19b135221cfa99bb8f4256971d49b3e9b36b2b31f8413f97455f2c6d571c9fd19697cfad2b59b13e2e207a407f1e9cf6216fe417560f3a13ab0716ad01fd76c42f50e12e21fd76c8e5b9f30800d90ca971b8c07401c5f5f64fa062a2d69e386ffac05109fa71337f5f7ca0d2e00d581c39fc6c7d3f978061f87f0ed98fa2cfe52412a5f3e952f9fca974fe5cba73acacfe4e317f9784a101f4fe1e4c5d349e3e9a4f174d2783ae35fe4ecf0653e4ee3e3023e769fc5c5577878ee6c2e6ee2e1b743fe73bc1a76f39b6c7f70725913979e9ce6b326218d5a3b004ede18173f303f350dba4d2c9c045520112a50d286d4b4cde9acc8d9f4f549a971bc8bd74f0cfe45fb4cf04b4e9f089dad593e0370500550bad4c62d0948a5e3d27cfc826626bf35d9273961c32ce4174d421a3d8b4d9c047b814b8b8f4b8b9bc5fb5fa92ca1597e53e227ad81d4d2d093f75f82d6f30ee090ba7e8f0fb661ff33469e55fba0fafb8507fed7bffa5fff6ab0bff3bffed560f8ffeffe151c2466074f59af1c301cfd393dcd67e31a9ff509eb376edee6eacc0d234b962f090f7bf9650e3f787dcc00fcd46da96f24bc05d7c4149a8fe37d566ff36157b57ec9f13e5b93d2d6a2b5245a860e1a8f9e51efb20da9e99be0e49e86c6a86da9881aa231cb272ee8253fb4c28d9bc2c753513c6e08c5ff877f222e120e4926060238fe3fac64c01ff97f0759f07f8cd9cacfa79bf9f84b3e9e31878b47bdc4c54d3cbc85cfcfe46168290083018741f03c94c79f4e020206110c240c4e308861708661180c2e10c71586e1308c80c10dec158c84f128585e026377187bc0e0099fa530783dbf178c86610c0cde30c860180bc373303c0f830f0ce360f085410ec37818fc60f08721008609304c842110863fc0300986ffab1dbb8f6da28c0338dea1c3461343fc431b63642c688a113a60624144c4811d0e99ac607913b6b5d8b1b2ce76b06e22022214182f226237df1011ab2822225644458451e6861315abc2c0b7597120be6145e4fcde7a63eb2f26868821c63dcba7dfbbe76eb75bbbb477eb8e1e30210d3dd10bbd918e6bd007d7c28cbee887ebd01fd763006ec040cef546ce7310bd8966d0c17408bfebcdb0201343710bb2300cb7b2cf7064e3368ce079c8a156b68de418a3703beb368cc618d6c7621cc6e30ee62660227291877cd8e160fb24dc09270a989b8c42b83005457077563f4696eaee82075e94303715d3500a1faf7019ca7137a6e31ecce854a9bb1733310bb3711fe6f03df7632ee6713c3fe6630116a2028bb0184ba07e3d80656995ba07b19ce587b0020f23804a8e558547f0281ec3e378022bf12456b1df53588da7b106cf301fc4b3780e6bf13c5ec03abc88f5ecf71236e0656cc42bcc6fc2ab08e1356cc6ebd88237f026dec256bc8d6d7807dbb103d5d8893076a106efa21675d88df7508ff7b1071fe0437cc439ecc5c788e0139ea14f3b57ea3e63791ff6a381d7e0003dc8fe9fe30b7c89aff0351af10da2f81687f01d9a701847f03d8ee207fc889ff0337ec131fc8a187ec371fc8e13f80327a1746e7e0318a1bd95e4c4d3355b5bb7aa0f9bb62bca76ecc1411cc1099cbf43512ec555e887a11883c9988d65588975d8821a4410450cfa6a4531c008332cb0c18954f487152e94c38f15588d0dd8867a34a009c7d5e3ed5494496145990623cb7a9861c1441463062a50852036e162f6afa67bd18863b892b93ee888211885d5d88c5a342006fd2e45b91c660cc53814a302abb019bb7108db6af81968840e97211d16d8518e6558833d588f504dfcfbaa915aab286918880c78e0473d8e425fc773ae6d4b6139a42dabdb57b2be116174dacdef46b3e0c43e34e1445d7cdffa36c7543b80fd5da84239fb2caf6bdd1e66ce501b97a151e7baa436dff9abb733dc9894a674498dff5199467ab98d354d73977a4b723da66c8f7bb2239f9beb5e693dfb9a0a1d45764f5989a37ba9db53e82dcecd77b44eb17b913dd7e52e62d15e687215e47153ef70784adc5e939b0b91a2124f598f7cdd10e6465887e7b47c3efec5f58691872becea729beb089f35d75bf86f9d5f09c7f672728ca93eabbb38db53e0f61494a8efcbc6141777f6599939d609993913060fcbb68e36a614fb321caedc32875d3da92cb68f9deacbf67a32ede353baa5746b3e5fe33e5e6374d59ab5bf75fd6a8d3aa7aeebdb48c7a40371d391a7b136b4aeabcb79daf6896d541de4ef36a943877318e76a23598c8e7f33cefb87437f96877f56cb75932e7e719874ea1ad1ac3ecc498e6f1f947c6abfe6b175ae767f394fbbdff1276effaf8f24ad17f6ae5b9b7ed194c6a68664dd0567f58cda47fb681feda37dfc9f4654fb5c6d694c543f3fb10651a3a859d4226a13758afa44fda201d1a06848342c1a118d8ac644f50b126b10358a9a452da23651a7a84fd42f1a100d8a8644c3a211d1a8684c54bf30b10651a3a859d4226a13758afa44fda201d1a06848342c1a118d8ac644f5158935881a45cda216519ba853d427ea170d88064543a261d18868543426aa5f945883a851d42c6a11b5893a457da27ed180685034241a168d88464563a2fac58935881a45cda216519ba853d427ea170d88064543a261d18868543426aa5f925883a851d42c6a11b5893a457da27ed180685034241a168d88464563a2faa58935881a45cda216519ba853d427ea170d88064543a261d18868cb5018ba3338cef4f1ced468f97f84a28d967f4b6c3d9934b3659f9987075c72bac7fd13bd7c3541'
ISP_PROG = binascii.unhexlify(ISP_PROG)
ISP_PROG = zlib.decompress(ISP_PROG)
def printProgressBar (iteration, total, prefix = '', suffix = '', 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)
print('\r%s |%s| %s%% %s' % (prefix, bar, percent, suffix), end = '\r')
# Print New Line on Complete
if iteration == total:
print()
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("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('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('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
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):
op = data[0]
reason = data[1]
text = ''
try:
if ISPResponse.ISPOperation(op) == ISPResponse.ISPOperation.ISP_DEBUG_INFO:
text = data[2:].decode()
except ValueError:
print('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
@staticmethod
def parse(data):
op = data[0]
reason = data[1]
text = ''
if FlashModeResponse.Operation(op) == FlashModeResponse.Operation.ISP_DEBUG_INFO:
text = data[2:].decode()
return (op, reason, text)
def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in range(0, len(l), n):
yield l[i:i + n]
def get_terminal_size(fallback=(100, 24)):
for i in range(0,3):
try:
columns, rows = os.get_terminal_size(i)
except OSError:
continue
break
else: # set default if the loop completes which means all failed
columns, rows = fallback
return columns, rows
class MAIXLoader:
def change_baudrate(self, baudrate):
print(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
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
)
print(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)
""" 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'
#print('[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)
# print(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_init = time.time()
data = b''
# find start boarder
#sys.stdout.write('[RECV one return] raw data: ')
while 1:
if time.time() - timeout_init > timeout:
# print()
# print(ERROR_MSG,'Response timeout',BASH_TIPS['DEFAULT'])
raise 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:
raise 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('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)
#print('-- RESET to LOW, IO16 to HIGH --')
# Pull reset down and keep 10ms
self._port.setDTR (True)
self._port.setRTS (False)
time.sleep(0.1)
#print('-- 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)
#print('-- RESET to LOW --')
# Pull reset down and keep 10ms
self._port.setDTR (True)
self._port.setRTS (False)
time.sleep(0.1)
#print('-- 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)
#print('-- RESET to LOW, IO16 to HIGH --')
# Pull reset down and keep 10ms
self._port.setDTR (False)
self._port.setRTS (True)
time.sleep(0.1)
#print('-- 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)
#print('-- RESET to LOW --')
# Pull reset down and keep 10ms
self._port.setDTR (False)
self._port.setRTS (True)
time.sleep(0.1)
#print('-- RESET to HIGH, BOOT --')
# Pull IO16 to low and release reset
self._port.setRTS (False)
self._port.setDTR (False)
time.sleep(0.1)
# maix go for old cmsis-dap firmware
def reset_to_isp_goD(self):
self._port.setDTR (True) ## output 0
self._port.setRTS (True)
time.sleep(0.01)
#print('-- RESET to LOW --')
# Pull reset down and keep 10ms
self._port.setRTS (False)
self._port.setDTR (True)
time.sleep(0.01)
#print('-- RESET to HIGH, BOOT --')
# Pull IO16 to low and release reset
self._port.setRTS (False)
self._port.setDTR (True)
time.sleep(0.01)
def reset_to_boot_goD(self):
self._port.setDTR (False)
self._port.setRTS (False)
time.sleep(0.01)
#print('-- RESET to LOW --')
# Pull reset down and keep 10ms
self._port.setRTS (False)
self._port.setDTR (True)
time.sleep(0.01)
#print('-- RESET to HIGH, BOOT --')
# Pull IO16 to low and release reset
self._port.setRTS (True)
self._port.setDTR (True)
time.sleep(0.01)
# maix go for openec or new cmsis-dap firmware
def reset_to_boot_maixgo(self):
self._port.setDTR (False)
self._port.setRTS (False)
time.sleep(0.01)
#print('-- RESET to LOW --')
# Pull reset down and keep 10ms
self._port.setRTS (False)
self._port.setDTR (True)
time.sleep(0.01)
#print('-- RESET to HIGH, BOOT --')
# Pull IO16 to low and release reset
self._port.setRTS (False)
self._port.setDTR (False)
time.sleep(0.01)
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())
#print('MAIX return op:', ISPResponse.ISPOperation(op).name, 'reason:', ISPResponse.ErrorCode(reason).name)
def flash_greeting(self):
retry_count = 0
while 1:
self._port.write(b'\xc0\xd2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0')
retry_count = retry_count + 1
try:
op, reason, text = FlashModeResponse.parse(self.recv_one_return())
except IndexError:
if retry_count > MAX_RETRY_TIMES:
print(ERROR_MSG,"Failed to Connect to K210's Stub",BASH_TIPS['DEFAULT'])
sys.exit(1)
print(WARN_MSG,"Index Error, retrying...",BASH_TIPS['DEFAULT'])
time.sleep(0.1)
continue
except TimeoutError:
if retry_count > MAX_RETRY_TIMES:
print(ERROR_MSG,"Failed to Connect to K210's Stub",BASH_TIPS['DEFAULT'])
sys.exit(1)
print(WARN_MSG,"Timeout Error, retrying...",BASH_TIPS['DEFAULT'])
time.sleep(0.1)
continue
except:
if retry_count > MAX_RETRY_TIMES:
print(ERROR_MSG,"Failed to Connect to K210's Stub",BASH_TIPS['DEFAULT'])
sys.exit(1)
print(WARN_MSG,"Unexcepted Error, retrying...",BASH_TIPS['DEFAULT'])
time.sleep(0.1)
continue
# print('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:
print(INFO_MSG,"Boot to Flashmode Successfully",BASH_TIPS['DEFAULT'])
self._port.flushInput()
self._port.flushOutput()
break
else:
if retry_count > MAX_RETRY_TIMES:
print(ERROR_MSG,"Failed to Connect to K210's Stub",BASH_TIPS['DEFAULT'])
sys.exit(1)
print(WARN_MSG,"Unexcepted Return recevied, retrying...",BASH_TIPS['DEFAULT'])
time.sleep(0.1)
continue
def boot(self, address=0x80000000):
print(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())
#print('[RECV] op:', ISPResponse.ISPOperation(op).name, 'reason:', ISPResponse.ErrorCode(reason).name)
if text:
print('-' * 30)
print(text)
print('-' * 30)
if ISPResponse.ErrorCode(reason) not in (ISPResponse.ErrorCode.ISP_RET_DEFAULT, ISPResponse.ErrorCode.ISP_RET_OK):
print('Failed, retry, errcode=', hex(reason))
return False
return True
def flash_recv_debug(self):
op, reason, text = FlashModeResponse.parse(self.recv_one_return())
#print('[Flash-RECV] op:', FlashModeResponse.Operation(op).name, 'reason:',
# FlashModeResponse.ErrorCode(reason).name)
if text:
print('-' * 30)
print(text)
print('-' * 30)
if FlashModeResponse.ErrorCode(reason) not in (FlashModeResponse.ErrorCode.ISP_RET_OK, FlashModeResponse.ErrorCode.ISP_RET_OK):
print('Failed, retry')
return False
return True
def init_flash(self, chip_type):
chip_type = int(chip_type)
print(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:
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:
print(ERROR_MSG,"Failed to initialize flash",BASH_TIPS['DEFAULT'])
sys.exit(1)
print(WARN_MSG,"Index Error, retrying...",BASH_TIPS['DEFAULT'])
time.sleep(0.1)
continue
except TimeoutError:
if retry_count > MAX_RETRY_TIMES:
print(ERROR_MSG,"Failed to initialize flash",BASH_TIPS['DEFAULT'])
sys.exit(1)
print(WARN_MSG,"Timeout Error, retrying...",BASH_TIPS['DEFAULT'])
time.sleep(0.1)
continue
except:
if retry_count > MAX_RETRY_TIMES:
print(ERROR_MSG,"Failed to initialize flash",BASH_TIPS['DEFAULT'])
sys.exit(1)
print(WARN_MSG,"Unexcepted Error, retrying...",BASH_TIPS['DEFAULT'])
time.sleep(0.1)
continue
# print('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:
print(INFO_MSG,"Initialization flash Successfully",BASH_TIPS['DEFAULT'])
break
else:
if retry_count > MAX_RETRY_TIMES:
print(ERROR_MSG,"Failed to initialize flash",BASH_TIPS['DEFAULT'])
sys.exit(1)
print(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)
#print('[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):
while 1:
#print('[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)
#print('[INFO]', 'sent', sent, 'bytes', 'checksum', binascii.hexlify(crc32_checksum).decode())
address += len(chunk)
if self.recv_debug():
break
columns, lines = get_terminal_size()
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):
'''
typedef struct __attribute__((packed)) {
uint8_t op;
int32_t checksum; // checksum
uint32_t address;
uint32_t data_len;
uint8_t data_buf[1024];
} isp_request_t;
'''
DATAFRAME_SIZE = 4096
data_chunks = chunks(data, DATAFRAME_SIZE)
#print('[DEBUG] flash dataframe | data length:', len(data))
for n, chunk in enumerate(data_chunks):
#print('[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
#print("[$$$$]", binascii.hexlify(out[:32]).decode())
retry_count = 0
while True:
try:
sent = self.write(out)
#print('[INFO]', 'sent', sent, 'bytes', 'checksum', crc32_checksum)
self.flash_recv_debug()
except:
retry_count = retry_count + 1
if retry_count > MAX_RETRY_TIMES:
print(ERROR_MSG,"Error Count Exceeded, Stop Trying",BASH_TIPS['DEFAULT'])
sys.exit(1)
continue
break
address += len(chunk)
def flash_erase(self):
#print('[DEBUG] erasing spi flash.')
self._port.write(b'\xc0\xd3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0')
op, reason, text = FlashModeResponse.parse(self.recv_one_return())
#print('MAIX return op:', FlashModeResponse.Operation(op).name, 'reason:',
# FlashModeResponse.ErrorCode(reason).name)
def install_flash_bootloader(self, data):
# 1. flash bootloader
self.flash_dataframe(data, address=0x80000000)
def load_elf_to_sram(self, f):
from elftools.elf.elffile import ELFFile
from elftools.elf.descriptions import describe_p_type
elffile = ELFFile(f)
if elffile['e_entry'] != 0x80000000:
print(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'])
print(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:
print("Skipped")
continue
self.flash_dataframe(segment.data(), segment['p_vaddr'])
def flash_firmware(self, firmware_bin: bytes, aes_key: bytes = None, address_offset = 0, sha256Prefix = True):
#print('[DEBUG] flash_firmware DEBUG: aeskey=', aes_key)
if sha256Prefix == True:
#
# : SHA256(after)(32bytes) + AES_CIPHER_FLAG (1byte) + firmware_size(4bytes) + firmware_data
aes_cipher_flag = b'\x01' if aes_key else b'\x00'
#
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_chunk = math.ceil(len(firmware_with_header)/4096)
# 3.
data_chunks = chunks(firmware_with_header, 4096) # 4kb for a sector
else:
total_chunk = math.ceil(len(firmware_bin)/4096)
data_chunks = chunks(firmware_bin, 4096)
time_start = time.time()
for n, chunk in enumerate(data_chunks):
chunk = chunk.ljust(4096, b'\x00') # align by 4kb
# 3.1 dataframe
#print('[INFO]', 'Write firmware data piece')
self.dump_to_flash(chunk, address= n * 4096 + address_offset)
columns, lines = get_terminal_size()
time_delta = time.time() - time_start
speed = ''
if (time_delta > 1):
speed = str(int((n + 1) * 4096 / 1024.0 / time_delta)) + 'kiB/s'
printProgressBar(n+1, total_chunk, prefix = 'Programming BIN:', suffix = speed, length = columns - 35)
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 = ['kflash.py', _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)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("-p", "--port", help="COM Port", default="DEFAULT")
parser.add_argument("-c", "--chip", help="SPI Flash type, 0 for in-chip, 1 for on-board", 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", "--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("firmware", help="firmware bin path")
args = parser.parse_args()
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']
print(INFO_MSG,'ANSI colors not used',BASH_TIPS['DEFAULT'])
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):
print(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'])
sys.exit(1)
list_port_info.sort()
_port = list_port_info[1].device
print(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):
print(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'])
sys.exit(1)
list_port_info.sort()
_port = list_port_info[0].device
print(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
print(INFO_MSG,"COM Port Auto Detected, Selected ", _port, BASH_TIPS['DEFAULT'])
except StopIteration:
print(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'])
sys.exit(1)
else:
_port = args.port
print(INFO_MSG,"COM Port Selected Manually: ", _port, BASH_TIPS['DEFAULT'])
loader = MAIXLoader(port=_port, baudrate=115200)
file_format = ProgramFileFormat.FMT_BINARY
# 0. Check firmware
try:
firmware_bin = open(args.firmware, 'rb')
except FileNotFoundError:
print(ERROR_MSG,'Unable to find the firmware at ', args.firmware, BASH_TIPS['DEFAULT'])
sys.exit(1)
with open(args.firmware, 'rb') as f:
file_header = f.read(4)
if file_header.startswith(bytes([0x50, 0x4B])):
if ".kfpkg" != os.path.splitext(args.firmware)[1]:
print(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])):
file_format = ProgramFileFormat.FMT_ELF
if args.sram:
print(INFO_MSG, 'Find an ELF file:', args.firmware, BASH_TIPS['DEFAULT'])
else:
print(ERROR_MSG, 'This is an ELF file and cannot be programmed directly:', args.firmware, BASH_TIPS['DEFAULT'])
print(ERROR_MSG, 'Please retry:', args.firmware + '.bin', BASH_TIPS['DEFAULT'])
sys.exit(1)
# 1. Greeting.
print(INFO_MSG,"Trying to Enter the ISP Mode...",BASH_TIPS['DEFAULT'])
retry_count = 0
while 1:
retry_count = retry_count + 1
if retry_count > 15:
print("\n" + 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'])
sys.exit(1)
if args.Board == "dan" or args.Board == "bit" or args.Board == "trainer":
try:
print('.', end='')
loader.reset_to_isp_dan()
loader.greeting()
break
except TimeoutError:
pass
elif args.Board == "kd233":
try:
print('_', end='')
loader.reset_to_isp_kd233()
loader.greeting()
break
except TimeoutError:
pass
elif args.Board == "goE":
try:
print('*', end='')
loader.reset_to_isp_kd233()
loader.greeting()
break
except TimeoutError:
pass
elif args.Board == "goD":
try:
print('#', end='')
loader.reset_to_isp_goD()
loader.greeting()
break
except TimeoutError:
pass
else:
try:
...
This file has been truncated, please download it to see its full contents.
Comments