From: Mark Qvist Date: Fri, 28 Jan 2022 15:30:10 +0000 (+0100) Subject: Renamed release folder X-Git-Url: https://git.tnoah.ca/?a=commitdiff_plain;h=61444f25318bb2176ea132a501da1d2641403dae;p=lora-radio.git Renamed release folder --- diff --git a/Makefile b/Makefile index 25ccacc..7e3e416 100644 --- a/Makefile +++ b/Makefile @@ -65,7 +65,7 @@ release-all: release-rnode release-tbeam release-lora32_v20 release-lora32_v21 r release-rnode: arduino-cli compile --fqbn unsignedio:avr:rnode -e - cp build/unsignedio.avr.rnode/RNode_Firmware.ino.hex Precompiled/rnode_firmware_latest.hex + cp build/unsignedio.avr.rnode/RNode_Firmware.ino.hex Release/rnode_firmware_latest.hex rm -r build release-tbeam: @@ -74,7 +74,7 @@ release-tbeam: cp build/esp32.esp32.t-beam/RNode_Firmware.ino.bin build/rnode_firmware_latest_tbeam.bin cp build/esp32.esp32.t-beam/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_latest_tbeam.bootloader cp build/esp32.esp32.t-beam/RNode_Firmware.ino.partitions.bin build/rnode_firmware_latest_tbeam.partitions - zip --junk-paths ./Precompiled/rnode_firmware_latest_tbeam.zip ./Precompiled/esptool/esptool.py build/rnode_firmware_latest_tbeam.boot_app0 build/rnode_firmware_latest_tbeam.bin build/rnode_firmware_latest_tbeam.bootloader build/rnode_firmware_latest_tbeam.partitions + zip --junk-paths ./Release/rnode_firmware_latest_tbeam.zip ./Release/esptool/esptool.py build/rnode_firmware_latest_tbeam.boot_app0 build/rnode_firmware_latest_tbeam.bin build/rnode_firmware_latest_tbeam.bootloader build/rnode_firmware_latest_tbeam.partitions rm -r build release-lora32_v20: @@ -83,7 +83,7 @@ release-lora32_v20: cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bin build/rnode_firmware_latest_lora32v20.bin cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_latest_lora32v20.bootloader cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.partitions.bin build/rnode_firmware_latest_lora32v20.partitions - zip --junk-paths ./Precompiled/rnode_firmware_latest_lora32v20.zip ./Precompiled/esptool/esptool.py build/rnode_firmware_latest_lora32v20.boot_app0 build/rnode_firmware_latest_lora32v20.bin build/rnode_firmware_latest_lora32v20.bootloader build/rnode_firmware_latest_lora32v20.partitions + zip --junk-paths ./Release/rnode_firmware_latest_lora32v20.zip ./Release/esptool/esptool.py build/rnode_firmware_latest_lora32v20.boot_app0 build/rnode_firmware_latest_lora32v20.bin build/rnode_firmware_latest_lora32v20.bootloader build/rnode_firmware_latest_lora32v20.partitions rm -r build release-lora32_v21: @@ -92,7 +92,7 @@ release-lora32_v21: cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bin build/rnode_firmware_latest_lora32v21.bin cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_latest_lora32v21.bootloader cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.partitions.bin build/rnode_firmware_latest_lora32v21.partitions - zip --junk-paths ./Precompiled/rnode_firmware_latest_lora32v21.zip ./Precompiled/esptool/esptool.py build/rnode_firmware_latest_lora32v21.boot_app0 build/rnode_firmware_latest_lora32v21.bin build/rnode_firmware_latest_lora32v21.bootloader build/rnode_firmware_latest_lora32v21.partitions + zip --junk-paths ./Release/rnode_firmware_latest_lora32v21.zip ./Release/esptool/esptool.py build/rnode_firmware_latest_lora32v21.boot_app0 build/rnode_firmware_latest_lora32v21.bin build/rnode_firmware_latest_lora32v21.bootloader build/rnode_firmware_latest_lora32v21.partitions rm -r build release-lora32_v20_extled: @@ -101,7 +101,7 @@ release-lora32_v20_extled: cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bin build/rnode_firmware_latest_lora32v20.bin cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_latest_lora32v20.bootloader cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.partitions.bin build/rnode_firmware_latest_lora32v20.partitions - zip --junk-paths ./Precompiled/rnode_firmware_latest_lora32v20_extled.zip ./Precompiled/esptool/esptool.py build/rnode_firmware_latest_lora32v20.boot_app0 build/rnode_firmware_latest_lora32v20.bin build/rnode_firmware_latest_lora32v20.bootloader build/rnode_firmware_latest_lora32v20.partitions + zip --junk-paths ./Release/rnode_firmware_latest_lora32v20_extled.zip ./Release/esptool/esptool.py build/rnode_firmware_latest_lora32v20.boot_app0 build/rnode_firmware_latest_lora32v20.bin build/rnode_firmware_latest_lora32v20.bootloader build/rnode_firmware_latest_lora32v20.partitions rm -r build release-lora32_v21_extled: @@ -110,7 +110,7 @@ release-lora32_v21_extled: cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bin build/rnode_firmware_latest_lora32v21.bin cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_latest_lora32v21.bootloader cp build/esp32.esp32.ttgo-lora32/RNode_Firmware.ino.partitions.bin build/rnode_firmware_latest_lora32v21.partitions - zip --junk-paths ./Precompiled/rnode_firmware_latest_lora32v21_extled.zip ./Precompiled/esptool/esptool.py build/rnode_firmware_latest_lora32v21.boot_app0 build/rnode_firmware_latest_lora32v21.bin build/rnode_firmware_latest_lora32v21.bootloader build/rnode_firmware_latest_lora32v21.partitions + zip --junk-paths ./Release/rnode_firmware_latest_lora32v21_extled.zip ./Release/esptool/esptool.py build/rnode_firmware_latest_lora32v21.boot_app0 build/rnode_firmware_latest_lora32v21.bin build/rnode_firmware_latest_lora32v21.bootloader build/rnode_firmware_latest_lora32v21.partitions rm -r build release-featheresp32: @@ -119,7 +119,7 @@ release-featheresp32: cp build/esp32.esp32.featheresp32/RNode_Firmware.ino.bin build/rnode_firmware_latest_featheresp32.bin cp build/esp32.esp32.featheresp32/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_latest_featheresp32.bootloader cp build/esp32.esp32.featheresp32/RNode_Firmware.ino.partitions.bin build/rnode_firmware_latest_featheresp32.partitions - zip --junk-paths ./Precompiled/rnode_firmware_latest_featheresp32.zip ./Precompiled/esptool/esptool.py build/rnode_firmware_latest_featheresp32.boot_app0 build/rnode_firmware_latest_featheresp32.bin build/rnode_firmware_latest_featheresp32.bootloader build/rnode_firmware_latest_featheresp32.partitions + zip --junk-paths ./Release/rnode_firmware_latest_featheresp32.zip ./Release/esptool/esptool.py build/rnode_firmware_latest_featheresp32.boot_app0 build/rnode_firmware_latest_featheresp32.bin build/rnode_firmware_latest_featheresp32.bootloader build/rnode_firmware_latest_featheresp32.partitions rm -r build release-genericesp32: @@ -128,10 +128,10 @@ release-genericesp32: cp build/esp32.esp32.esp32/RNode_Firmware.ino.bin build/rnode_firmware_latest_esp32_generic.bin cp build/esp32.esp32.esp32/RNode_Firmware.ino.bootloader.bin build/rnode_firmware_latest_esp32_generic.bootloader cp build/esp32.esp32.esp32/RNode_Firmware.ino.partitions.bin build/rnode_firmware_latest_esp32_generic.partitions - zip --junk-paths ./Precompiled/rnode_firmware_latest_esp32_generic.zip ./Precompiled/esptool/esptool.py build/rnode_firmware_latest_esp32_generic.boot_app0 build/rnode_firmware_latest_esp32_generic.bin build/rnode_firmware_latest_esp32_generic.bootloader build/rnode_firmware_latest_esp32_generic.partitions + zip --junk-paths ./Release/rnode_firmware_latest_esp32_generic.zip ./Release/esptool/esptool.py build/rnode_firmware_latest_esp32_generic.boot_app0 build/rnode_firmware_latest_esp32_generic.bin build/rnode_firmware_latest_esp32_generic.bootloader build/rnode_firmware_latest_esp32_generic.partitions rm -r build release-mega2560: arduino-cli compile --fqbn arduino:avr:mega -e - cp build/arduino.avr.mega/RNode_Firmware.ino.hex Precompiled/rnode_firmware_latest_m2560.hex + cp build/arduino.avr.mega/RNode_Firmware.ino.hex Release/rnode_firmware_latest_m2560.hex rm -r build diff --git a/Precompiled/README.md b/Precompiled/README.md deleted file mode 100644 index 37384d1..0000000 --- a/Precompiled/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# Precompiled Firmware -This directory contains the latest version of the precompiled firmware. You can download and flash the firmware to supported boards using the [RNode Config Utility](https://github.com/markqvist/rnodeconfigutil). diff --git a/Precompiled/esptool/esptool.py b/Precompiled/esptool/esptool.py deleted file mode 100755 index 01176af..0000000 --- a/Precompiled/esptool/esptool.py +++ /dev/null @@ -1,4582 +0,0 @@ -#!/usr/bin/env python -# -# ESP8266 & ESP32 family ROM Bootloader Utility -# Copyright (C) 2014-2021 Fredrik Ahlberg, Angus Gratton, Espressif Systems (Shanghai) CO LTD, other contributors as noted. -# https://github.com/espressif/esptool -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin -# Street, Fifth Floor, Boston, MA 02110-1301 USA. - -from __future__ import division, print_function - -import argparse -import base64 -import binascii -import copy -import hashlib -import inspect -import io -import itertools -import os -import shlex -import string -import struct -import sys -import time -import zlib - -try: - import serial -except ImportError: - print("Pyserial is not installed for %s. Check the README for installation instructions." % (sys.executable)) - raise - -# check 'serial' is 'pyserial' and not 'serial' https://github.com/espressif/esptool/issues/269 -try: - if "serialization" in serial.__doc__ and "deserialization" in serial.__doc__: - raise ImportError(""" -esptool.py depends on pyserial, but there is a conflict with a currently installed package named 'serial'. - -You may be able to work around this by 'pip uninstall serial; pip install pyserial' \ -but this may break other installed Python software that depends on 'serial'. - -There is no good fix for this right now, apart from configuring virtualenvs. \ -See https://github.com/espressif/esptool/issues/269#issuecomment-385298196 for discussion of the underlying issue(s).""") -except TypeError: - pass # __doc__ returns None for pyserial - -try: - import serial.tools.list_ports as list_ports -except ImportError: - print("The installed version (%s) of pyserial appears to be too old for esptool.py (Python interpreter %s). " - "Check the README for installation instructions." % (sys.VERSION, sys.executable)) - raise -except Exception: - if sys.platform == "darwin": - # swallow the exception, this is a known issue in pyserial+macOS Big Sur preview ref https://github.com/espressif/esptool/issues/540 - list_ports = None - else: - raise - - -__version__ = "3.1" - -MAX_UINT32 = 0xffffffff -MAX_UINT24 = 0xffffff - -DEFAULT_TIMEOUT = 3 # timeout for most flash operations -START_FLASH_TIMEOUT = 20 # timeout for starting flash (may perform erase) -CHIP_ERASE_TIMEOUT = 120 # timeout for full chip erase -MAX_TIMEOUT = CHIP_ERASE_TIMEOUT * 2 # longest any command can run -SYNC_TIMEOUT = 0.1 # timeout for syncing with bootloader -MD5_TIMEOUT_PER_MB = 8 # timeout (per megabyte) for calculating md5sum -ERASE_REGION_TIMEOUT_PER_MB = 30 # timeout (per megabyte) for erasing a region -ERASE_WRITE_TIMEOUT_PER_MB = 40 # timeout (per megabyte) for erasing and writing data -MEM_END_ROM_TIMEOUT = 0.05 # special short timeout for ESP_MEM_END, as it may never respond -DEFAULT_SERIAL_WRITE_TIMEOUT = 10 # timeout for serial port write -DEFAULT_CONNECT_ATTEMPTS = 7 # default number of times to try connection - - -def timeout_per_mb(seconds_per_mb, size_bytes): - """ Scales timeouts which are size-specific """ - result = seconds_per_mb * (size_bytes / 1e6) - if result < DEFAULT_TIMEOUT: - return DEFAULT_TIMEOUT - return result - - -def _chip_to_rom_loader(chip): - return { - 'esp8266': ESP8266ROM, - 'esp32': ESP32ROM, - 'esp32s2': ESP32S2ROM, - 'esp32s3beta2': ESP32S3BETA2ROM, - 'esp32s3beta3': ESP32S3BETA3ROM, - 'esp32c3': ESP32C3ROM, - 'esp32c6beta': ESP32C6BETAROM, - }[chip] - - -def get_default_connected_device(serial_list, port, connect_attempts, initial_baud, chip='auto', trace=False, - before='default_reset'): - _esp = None - for each_port in reversed(serial_list): - print("Serial port %s" % each_port) - try: - if chip == 'auto': - _esp = ESPLoader.detect_chip(each_port, initial_baud, before, trace, - connect_attempts) - else: - chip_class = _chip_to_rom_loader(chip) - _esp = chip_class(each_port, initial_baud, trace) - _esp.connect(before, connect_attempts) - break - except (FatalError, OSError) as err: - if port is not None: - raise - print("%s failed to connect: %s" % (each_port, err)) - _esp = None - return _esp - - -DETECTED_FLASH_SIZES = {0x12: '256KB', 0x13: '512KB', 0x14: '1MB', - 0x15: '2MB', 0x16: '4MB', 0x17: '8MB', - 0x18: '16MB', 0x19: '32MB', 0x1a: '64MB'} - - -def check_supported_function(func, check_func): - """ - Decorator implementation that wraps a check around an ESPLoader - bootloader function to check if it's supported. - - This is used to capture the multidimensional differences in - functionality between the ESP8266 & ESP32/32S2/32S3/32C3 ROM loaders, and the - software stub that runs on both. Not possible to do this cleanly - via inheritance alone. - """ - def inner(*args, **kwargs): - obj = args[0] - if check_func(obj): - return func(*args, **kwargs) - else: - raise NotImplementedInROMError(obj, func) - return inner - - -def stub_function_only(func): - """ Attribute for a function only supported in the software stub loader """ - return check_supported_function(func, lambda o: o.IS_STUB) - - -def stub_and_esp32_function_only(func): - """ Attribute for a function only supported by software stubs or ESP32/32S2/32S3/32C3 ROM """ - return check_supported_function(func, lambda o: o.IS_STUB or isinstance(o, ESP32ROM)) - - -PYTHON2 = sys.version_info[0] < 3 # True if on pre-Python 3 - -# Function to return nth byte of a bitstring -# Different behaviour on Python 2 vs 3 -if PYTHON2: - def byte(bitstr, index): - return ord(bitstr[index]) -else: - def byte(bitstr, index): - return bitstr[index] - -# Provide a 'basestring' class on Python 3 -try: - basestring -except NameError: - basestring = str - - -def print_overwrite(message, last_line=False): - """ Print a message, overwriting the currently printed line. - - If last_line is False, don't append a newline at the end (expecting another subsequent call will overwrite this one.) - - After a sequence of calls with last_line=False, call once with last_line=True. - - If output is not a TTY (for example redirected a pipe), no overwriting happens and this function is the same as print(). - """ - if sys.stdout.isatty(): - print("\r%s" % message, end='\n' if last_line else '') - else: - print(message) - - -def _mask_to_shift(mask): - """ Return the index of the least significant bit in the mask """ - shift = 0 - while mask & 0x1 == 0: - shift += 1 - mask >>= 1 - return shift - - -def esp8266_function_only(func): - """ Attribute for a function only supported on ESP8266 """ - return check_supported_function(func, lambda o: o.CHIP_NAME == "ESP8266") - - -class ESPLoader(object): - """ Base class providing access to ESP ROM & software stub bootloaders. - Subclasses provide ESP8266 & ESP32 specific functionality. - - Don't instantiate this base class directly, either instantiate a subclass or - call ESPLoader.detect_chip() which will interrogate the chip and return the - appropriate subclass instance. - - """ - CHIP_NAME = "Espressif device" - IS_STUB = False - - DEFAULT_PORT = "/dev/ttyUSB0" - - # Commands supported by ESP8266 ROM bootloader - ESP_FLASH_BEGIN = 0x02 - ESP_FLASH_DATA = 0x03 - ESP_FLASH_END = 0x04 - ESP_MEM_BEGIN = 0x05 - ESP_MEM_END = 0x06 - ESP_MEM_DATA = 0x07 - ESP_SYNC = 0x08 - ESP_WRITE_REG = 0x09 - ESP_READ_REG = 0x0a - - # Some comands supported by ESP32 ROM bootloader (or -8266 w/ stub) - ESP_SPI_SET_PARAMS = 0x0B - ESP_SPI_ATTACH = 0x0D - ESP_READ_FLASH_SLOW = 0x0e # ROM only, much slower than the stub flash read - ESP_CHANGE_BAUDRATE = 0x0F - ESP_FLASH_DEFL_BEGIN = 0x10 - ESP_FLASH_DEFL_DATA = 0x11 - ESP_FLASH_DEFL_END = 0x12 - ESP_SPI_FLASH_MD5 = 0x13 - - # Commands supported by ESP32-S2/S3/C3/C6 ROM bootloader only - ESP_GET_SECURITY_INFO = 0x14 - - # Some commands supported by stub only - ESP_ERASE_FLASH = 0xD0 - ESP_ERASE_REGION = 0xD1 - ESP_READ_FLASH = 0xD2 - ESP_RUN_USER_CODE = 0xD3 - - # Flash encryption encrypted data command - ESP_FLASH_ENCRYPT_DATA = 0xD4 - - # Response code(s) sent by ROM - ROM_INVALID_RECV_MSG = 0x05 # response if an invalid message is received - - # Maximum block sized for RAM and Flash writes, respectively. - ESP_RAM_BLOCK = 0x1800 - - FLASH_WRITE_SIZE = 0x400 - - # Default baudrate. The ROM auto-bauds, so we can use more or less whatever we want. - ESP_ROM_BAUD = 115200 - - # First byte of the application image - ESP_IMAGE_MAGIC = 0xe9 - - # Initial state for the checksum routine - ESP_CHECKSUM_MAGIC = 0xef - - # Flash sector size, minimum unit of erase. - FLASH_SECTOR_SIZE = 0x1000 - - UART_DATE_REG_ADDR = 0x60000078 - - CHIP_DETECT_MAGIC_REG_ADDR = 0x40001000 # This ROM address has a different value on each chip model - - UART_CLKDIV_MASK = 0xFFFFF - - # Memory addresses - IROM_MAP_START = 0x40200000 - IROM_MAP_END = 0x40300000 - - # The number of bytes in the UART response that signify command status - STATUS_BYTES_LENGTH = 2 - - # Response to ESP_SYNC might indicate that flasher stub is running instead of the ROM bootloader - sync_stub_detected = False - - # Device PIDs - USB_JTAG_SERIAL_PID = 0x1001 - - def __init__(self, port=DEFAULT_PORT, baud=ESP_ROM_BAUD, trace_enabled=False): - """Base constructor for ESPLoader bootloader interaction - - Don't call this constructor, either instantiate ESP8266ROM - or ESP32ROM, or use ESPLoader.detect_chip(). - - This base class has all of the instance methods for bootloader - functionality supported across various chips & stub - loaders. Subclasses replace the functions they don't support - with ones which throw NotImplementedInROMError(). - - """ - self.secure_download_mode = False # flag is set to True if esptool detects the ROM is in Secure Download Mode - - if isinstance(port, basestring): - self._port = serial.serial_for_url(port) - else: - self._port = port - self._slip_reader = slip_reader(self._port, self.trace) - # setting baud rate in a separate step is a workaround for - # CH341 driver on some Linux versions (this opens at 9600 then - # sets), shouldn't matter for other platforms/drivers. See - # https://github.com/espressif/esptool/issues/44#issuecomment-107094446 - self._set_port_baudrate(baud) - self._trace_enabled = trace_enabled - # set write timeout, to prevent esptool blocked at write forever. - try: - self._port.write_timeout = DEFAULT_SERIAL_WRITE_TIMEOUT - except NotImplementedError: - # no write timeout for RFC2217 ports - # need to set the property back to None or it will continue to fail - self._port.write_timeout = None - - @property - def serial_port(self): - return self._port.port - - def _set_port_baudrate(self, baud): - try: - self._port.baudrate = baud - except IOError: - raise FatalError("Failed to set baud rate %d. The driver may not support this rate." % baud) - - @staticmethod - def detect_chip(port=DEFAULT_PORT, baud=ESP_ROM_BAUD, connect_mode='default_reset', trace_enabled=False, - connect_attempts=DEFAULT_CONNECT_ATTEMPTS): - """ Use serial access to detect the chip type. - - We use the UART's datecode register for this, it's mapped at - the same address on ESP8266 & ESP32 so we can use one - memory read and compare to the datecode register for each chip - type. - - This routine automatically performs ESPLoader.connect() (passing - connect_mode parameter) as part of querying the chip. - """ - detect_port = ESPLoader(port, baud, trace_enabled=trace_enabled) - detect_port.connect(connect_mode, connect_attempts, detecting=True) - try: - print('Detecting chip type...', end='') - sys.stdout.flush() - chip_magic_value = detect_port.read_reg(ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR) - - for cls in [ESP8266ROM, ESP32ROM, ESP32S2ROM, ESP32S3BETA2ROM, ESP32S3BETA3ROM, ESP32C3ROM, ESP32C6BETAROM]: - if chip_magic_value in cls.CHIP_DETECT_MAGIC_VALUE: - # don't connect a second time - inst = cls(detect_port._port, baud, trace_enabled=trace_enabled) - inst._post_connect() - print(' %s' % inst.CHIP_NAME, end='') - if detect_port.sync_stub_detected: - inst = inst.STUB_CLASS(inst) - inst.sync_stub_detected = True - return inst - except UnsupportedCommandError: - raise FatalError("Unsupported Command Error received. Probably this means Secure Download Mode is enabled, " - "autodetection will not work. Need to manually specify the chip.") - finally: - print('') # end line - raise FatalError("Unexpected CHIP magic value 0x%08x. Failed to autodetect chip type." % (chip_magic_value)) - - """ 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' - self.trace("Write %d bytes: %s", len(buf), HexFormatter(buf)) - self._port.write(buf) - - def trace(self, message, *format_args): - if self._trace_enabled: - now = time.time() - try: - - delta = now - self._last_trace - except AttributeError: - delta = 0.0 - self._last_trace = now - prefix = "TRACE +%.3f " % delta - print(prefix + (message % format_args)) - - """ Calculate checksum of a blob, as it is defined by the ROM """ - @staticmethod - def checksum(data, state=ESP_CHECKSUM_MAGIC): - for b in data: - if type(b) is int: # python 2/3 compat - state ^= b - else: - state ^= ord(b) - - return state - - """ Send a request and read the response """ - def command(self, op=None, data=b"", chk=0, wait_response=True, timeout=DEFAULT_TIMEOUT): - saved_timeout = self._port.timeout - new_timeout = min(timeout, MAX_TIMEOUT) - if new_timeout != saved_timeout: - self._port.timeout = new_timeout - - try: - if op is not None: - self.trace("command op=0x%02x data len=%s wait_response=%d timeout=%.3f data=%s", - op, len(data), 1 if wait_response else 0, timeout, HexFormatter(data)) - pkt = struct.pack(b' self.STATUS_BYTES_LENGTH: - return data[:-self.STATUS_BYTES_LENGTH] - else: # otherwise, just return the 'val' field which comes from the reply header (this is used by read_reg) - return val - - def flush_input(self): - self._port.flushInput() - self._slip_reader = slip_reader(self._port, self.trace) - - def sync(self): - val, _ = self.command(self.ESP_SYNC, b'\x07\x07\x12\x20' + 32 * b'\x55', - timeout=SYNC_TIMEOUT) - - # ROM bootloaders send some non-zero "val" response. The flasher stub sends 0. If we receive 0 then it - # probably indicates that the chip wasn't or couldn't be reseted properly and esptool is talking to the - # flasher stub. - self.sync_stub_detected = val == 0 - - for _ in range(7): - val, _ = self.command() - self.sync_stub_detected &= val == 0 - - def _setDTR(self, state): - self._port.setDTR(state) - - def _setRTS(self, state): - self._port.setRTS(state) - # Work-around for adapters on Windows using the usbser.sys driver: - # generate a dummy change to DTR so that the set-control-line-state - # request is sent with the updated RTS state and the same DTR state - self._port.setDTR(self._port.dtr) - - def _get_pid(self): - if list_ports is None: - print("\nListing all serial ports is currently not available. Can't get device PID.") - return - active_port = self._port.port - - # Pyserial only identifies regular ports, URL handlers are not supported - if not active_port.startswith(("COM", "/dev/")): - print("\nDevice PID identification is only supported on COM and /dev/ serial ports.") - return - # Return the real path if the active port is a symlink - if active_port.startswith("/dev/") and os.path.islink(active_port): - active_port = os.path.realpath(active_port) - - ports = list_ports.comports() - for p in ports: - if p.device == active_port: - return p.pid - print("\nFailed to get PID of a device on {}, using standard reset sequence.".format(active_port)) - - def bootloader_reset(self, esp32r0_delay=False, usb_jtag_serial=False): - """ Issue a reset-to-bootloader, with esp32r0 workaround options - and USB-JTAG-Serial custom reset sequence option - """ - # RTS = either CH_PD/EN or nRESET (both active low = chip in reset) - # DTR = GPIO0 (active low = boot to flasher) - # - # DTR & RTS are active low signals, - # ie True = pin @ 0V, False = pin @ VCC. - if usb_jtag_serial: - # Custom reset sequence, which is required when the device - # is connecting via its USB-JTAG-Serial peripheral - self._setRTS(False) - self._setDTR(False) # Idle - time.sleep(0.1) - self._setDTR(True) # Set IO0 - self._setRTS(False) - time.sleep(0.1) - self._setRTS(True) # Reset. Note dtr/rts calls inverted so we go through (1,1) instead of (0,0) - self._setDTR(False) - self._setRTS(True) # Extra RTS set for RTS as Windows only propagates DTR on RTS setting - time.sleep(0.1) - self._setDTR(False) - self._setRTS(False) - else: - self._setDTR(False) # IO0=HIGH - self._setRTS(True) # EN=LOW, chip in reset - time.sleep(0.1) - if esp32r0_delay: - # Some chips are more likely to trigger the esp32r0 - # watchdog reset silicon bug if they're held with EN=LOW - # for a longer period - time.sleep(1.2) - self._setDTR(True) # IO0=LOW - self._setRTS(False) # EN=HIGH, chip out of reset - if esp32r0_delay: - # Sleep longer after reset. - # This workaround only works on revision 0 ESP32 chips, - # it exploits a silicon bug spurious watchdog reset. - time.sleep(0.4) # allow watchdog reset to occur - time.sleep(0.05) - self._setDTR(False) # IO0=HIGH, done - - def _connect_attempt(self, mode='default_reset', esp32r0_delay=False, usb_jtag_serial=False): - """ A single connection attempt, with esp32r0 workaround options """ - # esp32r0_delay is a workaround for bugs with the most common auto reset - # circuit and Windows, if the EN pin on the dev board does not have - # enough capacitance. - # - # Newer dev boards shouldn't have this problem (higher value capacitor - # on the EN pin), and ESP32 revision 1 can't use this workaround as it - # relies on a silicon bug. - # - # Details: https://github.com/espressif/esptool/issues/136 - last_error = None - - # If we're doing no_sync, we're likely communicating as a pass through - # with an intermediate device to the ESP32 - if mode == "no_reset_no_sync": - return last_error - - if mode != 'no_reset': - self.bootloader_reset(esp32r0_delay, usb_jtag_serial) - - for _ in range(5): - try: - self.flush_input() - self._port.flushOutput() - self.sync() - return None - except FatalError as e: - if esp32r0_delay: - print('_', end='') - else: - print('.', end='') - sys.stdout.flush() - time.sleep(0.05) - last_error = e - return last_error - - def get_memory_region(self, name): - """ Returns a tuple of (start, end) for the memory map entry with the given name, or None if it doesn't exist - """ - try: - return [(start, end) for (start, end, n) in self.MEMORY_MAP if n == name][0] - except IndexError: - return None - - def connect(self, mode='default_reset', attempts=DEFAULT_CONNECT_ATTEMPTS, detecting=False): - """ Try connecting repeatedly until successful, or giving up """ - if mode in ['no_reset', 'no_reset_no_sync']: - print('WARNING: Pre-connection option "{}" was selected.'.format(mode), - 'Connection may fail if the chip is not in bootloader or flasher stub mode.') - print('Connecting...', end='') - sys.stdout.flush() - last_error = None - - usb_jtag_serial = (mode == 'usb_reset') or (self._get_pid() == self.USB_JTAG_SERIAL_PID) - - try: - for _ in range(attempts) if attempts > 0 else itertools.count(): - last_error = self._connect_attempt(mode=mode, esp32r0_delay=False, usb_jtag_serial=usb_jtag_serial) - if last_error is None: - break - last_error = self._connect_attempt(mode=mode, esp32r0_delay=True, usb_jtag_serial=usb_jtag_serial) - if last_error is None: - break - finally: - print('') # end 'Connecting...' line - - if last_error is not None: - raise FatalError('Failed to connect to %s: %s' % (self.CHIP_NAME, last_error)) - - if not detecting: - try: - # check the date code registers match what we expect to see - chip_magic_value = self.read_reg(ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR) - if chip_magic_value not in self.CHIP_DETECT_MAGIC_VALUE: - actually = None - for cls in [ESP8266ROM, ESP32ROM, ESP32S2ROM, ESP32S3BETA2ROM, ESP32S3BETA3ROM, ESP32C3ROM]: - if chip_magic_value in cls.CHIP_DETECT_MAGIC_VALUE: - actually = cls - break - if actually is None: - print(("WARNING: This chip doesn't appear to be a %s (chip magic value 0x%08x). " - "Probably it is unsupported by this version of esptool.") % (self.CHIP_NAME, chip_magic_value)) - else: - raise FatalError("This chip is %s not %s. Wrong --chip argument?" % (actually.CHIP_NAME, self.CHIP_NAME)) - except UnsupportedCommandError: - self.secure_download_mode = True - self._post_connect() - - def _post_connect(self): - """ - Additional initialization hook, may be overridden by the chip-specific class. - Gets called after connect, and after auto-detection. - """ - pass - - def read_reg(self, addr, timeout=DEFAULT_TIMEOUT): - """ Read memory address in target """ - # we don't call check_command here because read_reg() function is called - # when detecting chip type, and the way we check for success (STATUS_BYTES_LENGTH) is different - # for different chip types (!) - val, data = self.command(self.ESP_READ_REG, struct.pack(' 0: - # add a dummy write to a date register as an excuse to have a delay - command += struct.pack(' start: - raise FatalError(("Software loader is resident at 0x%08x-0x%08x. " - "Can't load binary at overlapping address range 0x%08x-0x%08x. " - "Either change binary loading address, or use the --no-stub " - "option to disable the software loader.") % (start, end, load_start, load_end)) - - return self.check_command("enter RAM download mode", self.ESP_MEM_BEGIN, - struct.pack(' length: - raise FatalError('Read more than expected') - - digest_frame = self.read() - if len(digest_frame) != 16: - raise FatalError('Expected digest, got: %s' % hexify(digest_frame)) - expected_digest = hexify(digest_frame).upper() - digest = hashlib.md5(data).hexdigest().upper() - if digest != expected_digest: - raise FatalError('Digest mismatch: expected %s, got %s' % (expected_digest, digest)) - return data - - def flash_spi_attach(self, hspi_arg): - """Send SPI attach command to enable the SPI flash pins - - ESP8266 ROM does this when you send flash_begin, ESP32 ROM - has it as a SPI command. - """ - # last 3 bytes in ESP_SPI_ATTACH argument are reserved values - arg = struct.pack(' 0: - self.write_reg(SPI_MOSI_DLEN_REG, mosi_bits - 1) - if miso_bits > 0: - self.write_reg(SPI_MISO_DLEN_REG, miso_bits - 1) - else: - - def set_data_lengths(mosi_bits, miso_bits): - SPI_DATA_LEN_REG = SPI_USR1_REG - SPI_MOSI_BITLEN_S = 17 - SPI_MISO_BITLEN_S = 8 - mosi_mask = 0 if (mosi_bits == 0) else (mosi_bits - 1) - miso_mask = 0 if (miso_bits == 0) else (miso_bits - 1) - self.write_reg(SPI_DATA_LEN_REG, - (miso_mask << SPI_MISO_BITLEN_S) | ( - mosi_mask << SPI_MOSI_BITLEN_S)) - - # SPI peripheral "command" bitmasks for SPI_CMD_REG - SPI_CMD_USR = (1 << 18) - - # shift values - SPI_USR2_COMMAND_LEN_SHIFT = 28 - - if read_bits > 32: - raise FatalError("Reading more than 32 bits back from a SPI flash operation is unsupported") - if len(data) > 64: - raise FatalError("Writing more than 64 bytes of data with one SPI command is unsupported") - - data_bits = len(data) * 8 - old_spi_usr = self.read_reg(SPI_USR_REG) - old_spi_usr2 = self.read_reg(SPI_USR2_REG) - flags = SPI_USR_COMMAND - if read_bits > 0: - flags |= SPI_USR_MISO - if data_bits > 0: - flags |= SPI_USR_MOSI - set_data_lengths(data_bits, read_bits) - self.write_reg(SPI_USR_REG, flags) - self.write_reg(SPI_USR2_REG, - (7 << SPI_USR2_COMMAND_LEN_SHIFT) | spiflash_command) - if data_bits == 0: - self.write_reg(SPI_W0_REG, 0) # clear data register before we read it - else: - data = pad_to(data, 4, b'\00') # pad to 32-bit multiple - words = struct.unpack("I" * (len(data) // 4), data) - next_reg = SPI_W0_REG - for word in words: - self.write_reg(next_reg, word) - next_reg += 4 - self.write_reg(SPI_CMD_REG, SPI_CMD_USR) - - def wait_done(): - for _ in range(10): - if (self.read_reg(SPI_CMD_REG) & SPI_CMD_USR) == 0: - return - raise FatalError("SPI command did not complete in time") - wait_done() - - status = self.read_reg(SPI_W0_REG) - # restore some SPI controller registers - self.write_reg(SPI_USR_REG, old_spi_usr) - self.write_reg(SPI_USR2_REG, old_spi_usr2) - return status - - def read_status(self, num_bytes=2): - """Read up to 24 bits (num_bytes) of SPI flash status register contents - via RDSR, RDSR2, RDSR3 commands - - Not all SPI flash supports all three commands. The upper 1 or 2 - bytes may be 0xFF. - """ - SPIFLASH_RDSR = 0x05 - SPIFLASH_RDSR2 = 0x35 - SPIFLASH_RDSR3 = 0x15 - - status = 0 - shift = 0 - for cmd in [SPIFLASH_RDSR, SPIFLASH_RDSR2, SPIFLASH_RDSR3][0:num_bytes]: - status += self.run_spiflash_command(cmd, read_bits=8) << shift - shift += 8 - return status - - def write_status(self, new_status, num_bytes=2, set_non_volatile=False): - """Write up to 24 bits (num_bytes) of new status register - - num_bytes can be 1, 2 or 3. - - Not all flash supports the additional commands to write the - second and third byte of the status register. When writing 2 - bytes, esptool also sends a 16-byte WRSR command (as some - flash types use this instead of WRSR2.) - - If the set_non_volatile flag is set, non-volatile bits will - be set as well as volatile ones (WREN used instead of WEVSR). - - """ - SPIFLASH_WRSR = 0x01 - SPIFLASH_WRSR2 = 0x31 - SPIFLASH_WRSR3 = 0x11 - SPIFLASH_WEVSR = 0x50 - SPIFLASH_WREN = 0x06 - SPIFLASH_WRDI = 0x04 - - enable_cmd = SPIFLASH_WREN if set_non_volatile else SPIFLASH_WEVSR - - # try using a 16-bit WRSR (not supported by all chips) - # this may be redundant, but shouldn't hurt - if num_bytes == 2: - self.run_spiflash_command(enable_cmd) - self.run_spiflash_command(SPIFLASH_WRSR, struct.pack(">= 8 - - self.run_spiflash_command(SPIFLASH_WRDI) - - def get_crystal_freq(self): - # Figure out the crystal frequency from the UART clock divider - # Returns a normalized value in integer MHz (40 or 26 are the only supported values) - # - # The logic here is: - # - We know that our baud rate and the ESP UART baud rate are roughly the same, or we couldn't communicate - # - We can read the UART clock divider register to know how the ESP derives this from the APB bus frequency - # - Multiplying these two together gives us the bus frequency which is either the crystal frequency (ESP32) - # or double the crystal frequency (ESP8266). See the self.XTAL_CLK_DIVIDER parameter for this factor. - uart_div = self.read_reg(self.UART_CLKDIV_REG) & self.UART_CLKDIV_MASK - est_xtal = (self._port.baudrate * uart_div) / 1e6 / self.XTAL_CLK_DIVIDER - norm_xtal = 40 if est_xtal > 33 else 26 - if abs(norm_xtal - est_xtal) > 1: - print("WARNING: Detected crystal freq %.2fMHz is quite different to normalized freq %dMHz. Unsupported crystal in use?" % (est_xtal, norm_xtal)) - return norm_xtal - - def hard_reset(self): - print('Hard resetting via RTS pin...') - self._setRTS(True) # EN->LOW - time.sleep(0.1) - self._setRTS(False) - - def soft_reset(self, stay_in_bootloader): - if not self.IS_STUB: - if stay_in_bootloader: - return # ROM bootloader is already in bootloader! - else: - # 'run user code' is as close to a soft reset as we can do - self.flash_begin(0, 0) - self.flash_finish(False) - else: - if stay_in_bootloader: - # soft resetting from the stub loader - # will re-load the ROM bootloader - self.flash_begin(0, 0) - self.flash_finish(True) - elif self.CHIP_NAME != "ESP8266": - raise FatalError("Soft resetting is currently only supported on ESP8266") - else: - # running user code from stub loader requires some hacks - # in the stub loader - self.command(self.ESP_RUN_USER_CODE, wait_response=False) - - -class ESP8266ROM(ESPLoader): - """ Access class for ESP8266 ROM bootloader - """ - CHIP_NAME = "ESP8266" - IS_STUB = False - - CHIP_DETECT_MAGIC_VALUE = [0xfff0c101] - - # OTP ROM addresses - ESP_OTP_MAC0 = 0x3ff00050 - ESP_OTP_MAC1 = 0x3ff00054 - ESP_OTP_MAC3 = 0x3ff0005c - - SPI_REG_BASE = 0x60000200 - SPI_USR_OFFS = 0x1c - SPI_USR1_OFFS = 0x20 - SPI_USR2_OFFS = 0x24 - SPI_MOSI_DLEN_OFFS = None - SPI_MISO_DLEN_OFFS = None - SPI_W0_OFFS = 0x40 - - UART_CLKDIV_REG = 0x60000014 - - XTAL_CLK_DIVIDER = 2 - - FLASH_SIZES = { - '512KB': 0x00, - '256KB': 0x10, - '1MB': 0x20, - '2MB': 0x30, - '4MB': 0x40, - '2MB-c1': 0x50, - '4MB-c1': 0x60, - '8MB': 0x80, - '16MB': 0x90, - } - - BOOTLOADER_FLASH_OFFSET = 0 - - MEMORY_MAP = [[0x3FF00000, 0x3FF00010, "DPORT"], - [0x3FFE8000, 0x40000000, "DRAM"], - [0x40100000, 0x40108000, "IRAM"], - [0x40201010, 0x402E1010, "IROM"]] - - def get_efuses(self): - # Return the 128 bits of ESP8266 efuse as a single Python integer - result = self.read_reg(0x3ff0005c) << 96 - result |= self.read_reg(0x3ff00058) << 64 - result |= self.read_reg(0x3ff00054) << 32 - result |= self.read_reg(0x3ff00050) - return result - - def _get_flash_size(self, efuses): - # rX_Y = EFUSE_DATA_OUTX[Y] - r0_4 = (efuses & (1 << 4)) != 0 - r3_25 = (efuses & (1 << 121)) != 0 - r3_26 = (efuses & (1 << 122)) != 0 - r3_27 = (efuses & (1 << 123)) != 0 - - if r0_4 and not r3_25: - if not r3_27 and not r3_26: - return 1 - elif not r3_27 and r3_26: - return 2 - if not r0_4 and r3_25: - if not r3_27 and not r3_26: - return 2 - elif not r3_27 and r3_26: - return 4 - return -1 - - def get_chip_description(self): - efuses = self.get_efuses() - is_8285 = (efuses & ((1 << 4) | 1 << 80)) != 0 # One or the other efuse bit is set for ESP8285 - if is_8285: - flash_size = self._get_flash_size(efuses) - max_temp = (efuses & (1 << 5)) != 0 # This efuse bit identifies the max flash temperature - chip_name = { - 1: "ESP8285H08" if max_temp else "ESP8285N08", - 2: "ESP8285H16" if max_temp else "ESP8285N16" - }.get(flash_size, "ESP8285") - return chip_name - return "ESP8266EX" - - def get_chip_features(self): - features = ["WiFi"] - if "ESP8285" in self.get_chip_description(): - features += ["Embedded Flash"] - return features - - def flash_spi_attach(self, hspi_arg): - if self.IS_STUB: - super(ESP8266ROM, self).flash_spi_attach(hspi_arg) - else: - # ESP8266 ROM has no flash_spi_attach command in serial protocol, - # but flash_begin will do it - self.flash_begin(0, 0) - - def flash_set_parameters(self, size): - # not implemented in ROM, but OK to silently skip for ROM - if self.IS_STUB: - super(ESP8266ROM, self).flash_set_parameters(size) - - def chip_id(self): - """ Read Chip ID from efuse - the equivalent of the SDK system_get_chip_id() function """ - id0 = self.read_reg(self.ESP_OTP_MAC0) - id1 = self.read_reg(self.ESP_OTP_MAC1) - return (id0 >> 24) | ((id1 & MAX_UINT24) << 8) - - def read_mac(self): - """ Read MAC from OTP ROM """ - mac0 = self.read_reg(self.ESP_OTP_MAC0) - mac1 = self.read_reg(self.ESP_OTP_MAC1) - mac3 = self.read_reg(self.ESP_OTP_MAC3) - if (mac3 != 0): - oui = ((mac3 >> 16) & 0xff, (mac3 >> 8) & 0xff, mac3 & 0xff) - elif ((mac1 >> 16) & 0xff) == 0: - oui = (0x18, 0xfe, 0x34) - elif ((mac1 >> 16) & 0xff) == 1: - oui = (0xac, 0xd0, 0x74) - else: - raise FatalError("Unknown OUI") - return oui + ((mac1 >> 8) & 0xff, mac1 & 0xff, (mac0 >> 24) & 0xff) - - def get_erase_size(self, offset, size): - """ Calculate an erase size given a specific size in bytes. - - Provides a workaround for the bootloader erase bug.""" - - sectors_per_block = 16 - sector_size = self.FLASH_SECTOR_SIZE - num_sectors = (size + sector_size - 1) // sector_size - start_sector = offset // sector_size - - head_sectors = sectors_per_block - (start_sector % sectors_per_block) - if num_sectors < head_sectors: - head_sectors = num_sectors - - if num_sectors < 2 * head_sectors: - return (num_sectors + 1) // 2 * sector_size - else: - return (num_sectors - head_sectors) * sector_size - - def override_vddsdio(self, new_voltage): - raise NotImplementedInROMError("Overriding VDDSDIO setting only applies to ESP32") - - -class ESP8266StubLoader(ESP8266ROM): - """ Access class for ESP8266 stub loader, runs on top of ROM. - """ - FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c - IS_STUB = True - - def __init__(self, rom_loader): - self.secure_download_mode = rom_loader.secure_download_mode - self._port = rom_loader._port - self._trace_enabled = rom_loader._trace_enabled - self.flush_input() # resets _slip_reader - - def get_erase_size(self, offset, size): - return size # stub doesn't have same size bug as ROM loader - - -ESP8266ROM.STUB_CLASS = ESP8266StubLoader - - -class ESP32ROM(ESPLoader): - """Access class for ESP32 ROM bootloader - - """ - CHIP_NAME = "ESP32" - IMAGE_CHIP_ID = 0 - IS_STUB = False - - CHIP_DETECT_MAGIC_VALUE = [0x00f01d83] - - IROM_MAP_START = 0x400d0000 - IROM_MAP_END = 0x40400000 - - DROM_MAP_START = 0x3F400000 - DROM_MAP_END = 0x3F800000 - - # ESP32 uses a 4 byte status reply - STATUS_BYTES_LENGTH = 4 - - SPI_REG_BASE = 0x3ff42000 - SPI_USR_OFFS = 0x1c - SPI_USR1_OFFS = 0x20 - SPI_USR2_OFFS = 0x24 - SPI_MOSI_DLEN_OFFS = 0x28 - SPI_MISO_DLEN_OFFS = 0x2c - EFUSE_RD_REG_BASE = 0x3ff5a000 - - EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE + 0x18 - EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = (1 << 7) # EFUSE_RD_DISABLE_DL_ENCRYPT - - DR_REG_SYSCON_BASE = 0x3ff66000 - - SPI_W0_OFFS = 0x80 - - UART_CLKDIV_REG = 0x3ff40014 - - XTAL_CLK_DIVIDER = 1 - - FLASH_SIZES = { - '1MB': 0x00, - '2MB': 0x10, - '4MB': 0x20, - '8MB': 0x30, - '16MB': 0x40 - } - - BOOTLOADER_FLASH_OFFSET = 0x1000 - - OVERRIDE_VDDSDIO_CHOICES = ["1.8V", "1.9V", "OFF"] - - MEMORY_MAP = [[0x00000000, 0x00010000, "PADDING"], - [0x3F400000, 0x3F800000, "DROM"], - [0x3F800000, 0x3FC00000, "EXTRAM_DATA"], - [0x3FF80000, 0x3FF82000, "RTC_DRAM"], - [0x3FF90000, 0x40000000, "BYTE_ACCESSIBLE"], - [0x3FFAE000, 0x40000000, "DRAM"], - [0x3FFE0000, 0x3FFFFFFC, "DIRAM_DRAM"], - [0x40000000, 0x40070000, "IROM"], - [0x40070000, 0x40078000, "CACHE_PRO"], - [0x40078000, 0x40080000, "CACHE_APP"], - [0x40080000, 0x400A0000, "IRAM"], - [0x400A0000, 0x400BFFFC, "DIRAM_IRAM"], - [0x400C0000, 0x400C2000, "RTC_IRAM"], - [0x400D0000, 0x40400000, "IROM"], - [0x50000000, 0x50002000, "RTC_DATA"]] - - FLASH_ENCRYPTED_WRITE_ALIGN = 32 - - """ Try to read the BLOCK1 (encryption key) and check if it is valid """ - - def is_flash_encryption_key_valid(self): - - """ Bit 0 of efuse_rd_disable[3:0] is mapped to BLOCK1 - this bit is at position 16 in EFUSE_BLK0_RDATA0_REG """ - word0 = self.read_efuse(0) - rd_disable = (word0 >> 16) & 0x1 - - # reading of BLOCK1 is NOT ALLOWED so we assume valid key is programmed - if rd_disable: - return True - else: - # reading of BLOCK1 is ALLOWED so we will read and verify for non-zero. - # When ESP32 has not generated AES/encryption key in BLOCK1, the contents will be readable and 0. - # If the flash encryption is enabled it is expected to have a valid non-zero key. We break out on - # first occurance of non-zero value - key_word = [0] * 7 - for i in range(len(key_word)): - key_word[i] = self.read_efuse(14 + i) - # key is non-zero so break & return - if key_word[i] != 0: - return True - return False - - def get_flash_crypt_config(self): - """ For flash encryption related commands we need to make sure - user has programmed all the relevant efuse correctly so before - writing encrypted write_flash_encrypt esptool will verify the values - of flash_crypt_config to be non zero if they are not read - protected. If the values are zero a warning will be printed - - bit 3 in efuse_rd_disable[3:0] is mapped to flash_crypt_config - this bit is at position 19 in EFUSE_BLK0_RDATA0_REG """ - word0 = self.read_efuse(0) - rd_disable = (word0 >> 19) & 0x1 - - if rd_disable == 0: - """ we can read the flash_crypt_config efuse value - so go & read it (EFUSE_BLK0_RDATA5_REG[31:28]) """ - word5 = self.read_efuse(5) - word5 = (word5 >> 28) & 0xF - return word5 - else: - # if read of the efuse is disabled we assume it is set correctly - return 0xF - - def get_encrypted_download_disabled(self): - if self.read_reg(self.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG) & self.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT: - return True - else: - return False - - def get_pkg_version(self): - word3 = self.read_efuse(3) - pkg_version = (word3 >> 9) & 0x07 - pkg_version += ((word3 >> 2) & 0x1) << 3 - return pkg_version - - def get_chip_revision(self): - word3 = self.read_efuse(3) - word5 = self.read_efuse(5) - apb_ctl_date = self.read_reg(self.DR_REG_SYSCON_BASE + 0x7C) - - rev_bit0 = (word3 >> 15) & 0x1 - rev_bit1 = (word5 >> 20) & 0x1 - rev_bit2 = (apb_ctl_date >> 31) & 0x1 - if rev_bit0: - if rev_bit1: - if rev_bit2: - return 3 - else: - return 2 - else: - return 1 - return 0 - - def get_chip_description(self): - pkg_version = self.get_pkg_version() - chip_revision = self.get_chip_revision() - rev3 = (chip_revision == 3) - single_core = self.read_efuse(3) & (1 << 0) # CHIP_VER DIS_APP_CPU - - chip_name = { - 0: "ESP32-S0WDQ6" if single_core else "ESP32-D0WDQ6", - 1: "ESP32-S0WD" if single_core else "ESP32-D0WD", - 2: "ESP32-D2WD", - 4: "ESP32-U4WDH", - 5: "ESP32-PICO-V3" if rev3 else "ESP32-PICO-D4", - 6: "ESP32-PICO-V3-02", - }.get(pkg_version, "unknown ESP32") - - # ESP32-D0WD-V3, ESP32-D0WDQ6-V3 - if chip_name.startswith("ESP32-D0WD") and rev3: - chip_name += "-V3" - - return "%s (revision %d)" % (chip_name, chip_revision) - - def get_chip_features(self): - features = ["WiFi"] - word3 = self.read_efuse(3) - - # names of variables in this section are lowercase - # versions of EFUSE names as documented in TRM and - # ESP-IDF efuse_reg.h - - chip_ver_dis_bt = word3 & (1 << 1) - if chip_ver_dis_bt == 0: - features += ["BT"] - - chip_ver_dis_app_cpu = word3 & (1 << 0) - if chip_ver_dis_app_cpu: - features += ["Single Core"] - else: - features += ["Dual Core"] - - chip_cpu_freq_rated = word3 & (1 << 13) - if chip_cpu_freq_rated: - chip_cpu_freq_low = word3 & (1 << 12) - if chip_cpu_freq_low: - features += ["160MHz"] - else: - features += ["240MHz"] - - pkg_version = self.get_pkg_version() - if pkg_version in [2, 4, 5, 6]: - features += ["Embedded Flash"] - - if pkg_version == 6: - features += ["Embedded PSRAM"] - - word4 = self.read_efuse(4) - adc_vref = (word4 >> 8) & 0x1F - if adc_vref: - features += ["VRef calibration in efuse"] - - blk3_part_res = word3 >> 14 & 0x1 - if blk3_part_res: - features += ["BLK3 partially reserved"] - - word6 = self.read_efuse(6) - coding_scheme = word6 & 0x3 - features += ["Coding Scheme %s" % { - 0: "None", - 1: "3/4", - 2: "Repeat (UNSUPPORTED)", - 3: "Invalid"}[coding_scheme]] - - return features - - def read_efuse(self, n): - """ Read the nth word of the ESP3x EFUSE region. """ - return self.read_reg(self.EFUSE_RD_REG_BASE + (4 * n)) - - def chip_id(self): - raise NotSupportedError(self, "chip_id") - - def read_mac(self): - """ Read MAC from EFUSE region """ - words = [self.read_efuse(2), self.read_efuse(1)] - bitstring = struct.pack(">II", *words) - bitstring = bitstring[2:8] # trim the 2 byte CRC - try: - return tuple(ord(b) for b in bitstring) - except TypeError: # Python 3, bitstring elements are already bytes - return tuple(bitstring) - - def get_erase_size(self, offset, size): - return size - - def override_vddsdio(self, new_voltage): - new_voltage = new_voltage.upper() - if new_voltage not in self.OVERRIDE_VDDSDIO_CHOICES: - raise FatalError("The only accepted VDDSDIO overrides are '1.8V', '1.9V' and 'OFF'") - RTC_CNTL_SDIO_CONF_REG = 0x3ff48074 - RTC_CNTL_XPD_SDIO_REG = (1 << 31) - RTC_CNTL_DREFH_SDIO_M = (3 << 29) - RTC_CNTL_DREFM_SDIO_M = (3 << 27) - RTC_CNTL_DREFL_SDIO_M = (3 << 25) - # RTC_CNTL_SDIO_TIEH = (1 << 23) # not used here, setting TIEH=1 would set 3.3V output, not safe for esptool.py to do - RTC_CNTL_SDIO_FORCE = (1 << 22) - RTC_CNTL_SDIO_PD_EN = (1 << 21) - - reg_val = RTC_CNTL_SDIO_FORCE # override efuse setting - reg_val |= RTC_CNTL_SDIO_PD_EN - if new_voltage != "OFF": - reg_val |= RTC_CNTL_XPD_SDIO_REG # enable internal LDO - if new_voltage == "1.9V": - reg_val |= (RTC_CNTL_DREFH_SDIO_M | RTC_CNTL_DREFM_SDIO_M | RTC_CNTL_DREFL_SDIO_M) # boost voltage - self.write_reg(RTC_CNTL_SDIO_CONF_REG, reg_val) - print("VDDSDIO regulator set to %s" % new_voltage) - - def read_flash_slow(self, offset, length, progress_fn): - BLOCK_LEN = 64 # ROM read limit per command (this limit is why it's so slow) - - data = b'' - while len(data) < length: - block_len = min(BLOCK_LEN, length - len(data)) - r = self.check_command("read flash block", self.ESP_READ_FLASH_SLOW, - struct.pack('> 21) & 0x0F - return pkg_version - - def get_chip_description(self): - chip_name = { - 0: "ESP32-S2", - 1: "ESP32-S2FH16", - 2: "ESP32-S2FH32", - }.get(self.get_pkg_version(), "unknown ESP32-S2") - - return "%s" % (chip_name) - - def get_chip_features(self): - features = ["WiFi"] - - if self.secure_download_mode: - features += ["Secure Download Mode Enabled"] - - pkg_version = self.get_pkg_version() - - if pkg_version in [1, 2]: - if pkg_version == 1: - features += ["Embedded 2MB Flash"] - elif pkg_version == 2: - features += ["Embedded 4MB Flash"] - features += ["105C temp rating"] - - num_word = 4 - block2_addr = self.EFUSE_BASE + 0x05C - word4 = self.read_reg(block2_addr + (4 * num_word)) - block2_version = (word4 >> 4) & 0x07 - - if block2_version == 1: - features += ["ADC and temperature sensor calibration in BLK2 of efuse"] - return features - - def get_crystal_freq(self): - # ESP32-S2 XTAL is fixed to 40MHz - return 40 - - def override_vddsdio(self, new_voltage): - raise NotImplementedInROMError("VDD_SDIO overrides are not supported for ESP32-S2") - - def read_mac(self): - mac0 = self.read_reg(self.MAC_EFUSE_REG) - mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC - bitstring = struct.pack(">II", mac1, mac0)[2:] - try: - return tuple(ord(b) for b in bitstring) - except TypeError: # Python 3, bitstring elements are already bytes - return tuple(bitstring) - - def get_flash_crypt_config(self): - return None # doesn't exist on ESP32-S2 - - def get_key_block_purpose(self, key_block): - if key_block < 0 or key_block > 5: - raise FatalError("Valid key block numbers must be in range 0-5") - - reg, shift = [(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT), - (self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT), - (self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT), - (self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT), - (self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT), - (self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT)][key_block] - return (self.read_reg(reg) >> shift) & 0xF - - def is_flash_encryption_key_valid(self): - # Need to see either an AES-128 key or two AES-256 keys - purposes = [self.get_key_block_purpose(b) for b in range(6)] - - if any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes): - return True - - return any(p == self.PURPOSE_VAL_XTS_AES256_KEY_1 for p in purposes) \ - and any(p == self.PURPOSE_VAL_XTS_AES256_KEY_2 for p in purposes) - - def uses_usb(self, _cache=[]): - if self.secure_download_mode: - return False # can't detect native USB in secure download mode - if not _cache: - buf_no = self.read_reg(self.UARTDEV_BUF_NO) & 0xff - _cache.append(buf_no == self.UARTDEV_BUF_NO_USB) - return _cache[0] - - def _post_connect(self): - if self.uses_usb(): - self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK - - def _check_if_can_reset(self): - """ - Check the strapping register to see if we can reset out of download mode. - """ - if os.getenv("ESPTOOL_TESTING") is not None: - print("ESPTOOL_TESTING is set, ignoring strapping mode check") - # Esptool tests over USB CDC run with GPIO0 strapped low, don't complain in this case. - return - strap_reg = self.read_reg(self.GPIO_STRAP_REG) - force_dl_reg = self.read_reg(self.RTC_CNTL_OPTION1_REG) - if strap_reg & self.GPIO_STRAP_SPI_BOOT_MASK == 0 and force_dl_reg & self.RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK == 0: - print("ERROR: {} chip was placed into download mode using GPIO0.\n" - "esptool.py can not exit the download mode over USB. " - "To run the app, reset the chip manually.\n" - "To suppress this error, set --after option to 'no_reset'.".format(self.get_chip_description())) - raise SystemExit(1) - - def hard_reset(self): - if self.uses_usb(): - self._check_if_can_reset() - - self._setRTS(True) # EN->LOW - if self.uses_usb(): - # Give the chip some time to come out of reset, to be able to handle further DTR/RTS transitions - time.sleep(0.2) - self._setRTS(False) - time.sleep(0.2) - else: - self._setRTS(False) - - -class ESP32S3ROM(ESP32ROM): - CHIP_NAME = "ESP32-S3" - - IROM_MAP_START = 0x42000000 - IROM_MAP_END = 0x44000000 - DROM_MAP_START = 0x3c000000 - DROM_MAP_END = 0x3e000000 - - UART_DATE_REG_ADDR = 0x60000080 - - SPI_REG_BASE = 0x60002000 - SPI_USR_OFFS = 0x18 - SPI_USR1_OFFS = 0x1c - SPI_USR2_OFFS = 0x20 - SPI_MOSI_DLEN_OFFS = 0x24 - SPI_MISO_DLEN_OFFS = 0x28 - SPI_W0_OFFS = 0x58 - - FLASH_ENCRYPTED_WRITE_ALIGN = 16 - - # todo: use espefuse APIs to get this info - EFUSE_BASE = 0x6001A000 # BLOCK0 read base address - MAC_EFUSE_REG = EFUSE_BASE + 0x044 - - EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030 # BLOCK0 read base address - - EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34 - EFUSE_PURPOSE_KEY0_SHIFT = 24 - EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34 - EFUSE_PURPOSE_KEY1_SHIFT = 28 - EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY2_SHIFT = 0 - EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY3_SHIFT = 4 - EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY4_SHIFT = 8 - EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY5_SHIFT = 12 - - EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE - EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20 - - PURPOSE_VAL_XTS_AES256_KEY_1 = 2 - PURPOSE_VAL_XTS_AES256_KEY_2 = 3 - PURPOSE_VAL_XTS_AES128_KEY = 4 - - UART_CLKDIV_REG = 0x60000014 - - GPIO_STRAP_REG = 0x60004038 - - MEMORY_MAP = [[0x00000000, 0x00010000, "PADDING"], - [0x3C000000, 0x3D000000, "DROM"], - [0x3D000000, 0x3E000000, "EXTRAM_DATA"], - [0x600FE000, 0x60100000, "RTC_DRAM"], - [0x3FC88000, 0x3FD00000, "BYTE_ACCESSIBLE"], - [0x3FC88000, 0x403E2000, "MEM_INTERNAL"], - [0x3FC88000, 0x3FD00000, "DRAM"], - [0x40000000, 0x4001A100, "IROM_MASK"], - [0x40370000, 0x403E0000, "IRAM"], - [0x600FE000, 0x60100000, "RTC_IRAM"], - [0x42000000, 0x42800000, "IROM"], - [0x50000000, 0x50002000, "RTC_DATA"]] - - def get_chip_description(self): - return "ESP32-S3" - - def get_chip_features(self): - return ["WiFi", "BLE"] - - def get_crystal_freq(self): - # ESP32S3 XTAL is fixed to 40MHz - return 40 - - def get_flash_crypt_config(self): - return None # doesn't exist on ESP32-S3 - - def get_key_block_purpose(self, key_block): - if key_block < 0 or key_block > 5: - raise FatalError("Valid key block numbers must be in range 0-5") - - reg, shift = [(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT), - (self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT), - (self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT), - (self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT), - (self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT), - (self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT)][key_block] - return (self.read_reg(reg) >> shift) & 0xF - - def is_flash_encryption_key_valid(self): - # Need to see either an AES-128 key or two AES-256 keys - purposes = [self.get_key_block_purpose(b) for b in range(6)] - - if any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes): - return True - - return any(p == self.PURPOSE_VAL_XTS_AES256_KEY_1 for p in purposes) \ - and any(p == self.PURPOSE_VAL_XTS_AES256_KEY_2 for p in purposes) - - def override_vddsdio(self, new_voltage): - raise NotImplementedInROMError("VDD_SDIO overrides are not supported for ESP32-S3") - - def read_mac(self): - mac0 = self.read_reg(self.MAC_EFUSE_REG) - mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC - bitstring = struct.pack(">II", mac1, mac0)[2:] - try: - return tuple(ord(b) for b in bitstring) - except TypeError: # Python 3, bitstring elements are already bytes - return tuple(bitstring) - - -class ESP32S3BETA2ROM(ESP32S3ROM): - CHIP_NAME = "ESP32-S3(beta2)" - IMAGE_CHIP_ID = 4 - - CHIP_DETECT_MAGIC_VALUE = [0xeb004136] - - def get_chip_description(self): - return "ESP32-S3(beta2)" - - -class ESP32S3BETA3ROM(ESP32S3ROM): - CHIP_NAME = "ESP32-S3(beta3)" - IMAGE_CHIP_ID = 6 - - CHIP_DETECT_MAGIC_VALUE = [0x9] - - def get_chip_description(self): - return "ESP32-S3(beta3)" - - -class ESP32C3ROM(ESP32ROM): - CHIP_NAME = "ESP32-C3" - IMAGE_CHIP_ID = 5 - - IROM_MAP_START = 0x42000000 - IROM_MAP_END = 0x42800000 - DROM_MAP_START = 0x3c000000 - DROM_MAP_END = 0x3c800000 - - SPI_REG_BASE = 0x60002000 - SPI_USR_OFFS = 0x18 - SPI_USR1_OFFS = 0x1C - SPI_USR2_OFFS = 0x20 - SPI_MOSI_DLEN_OFFS = 0x24 - SPI_MISO_DLEN_OFFS = 0x28 - SPI_W0_OFFS = 0x58 - - BOOTLOADER_FLASH_OFFSET = 0x0 - - # Magic value for ESP32C3 eco 1+2 and ESP32C3 eco3 respectivly - CHIP_DETECT_MAGIC_VALUE = [0x6921506f, 0x1b31506f] - - UART_DATE_REG_ADDR = 0x60000000 + 0x7c - - EFUSE_BASE = 0x60008800 - MAC_EFUSE_REG = EFUSE_BASE + 0x044 - - EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030 # BLOCK0 read base address - - EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34 - EFUSE_PURPOSE_KEY0_SHIFT = 24 - EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34 - EFUSE_PURPOSE_KEY1_SHIFT = 28 - EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY2_SHIFT = 0 - EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY3_SHIFT = 4 - EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY4_SHIFT = 8 - EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY5_SHIFT = 12 - - EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE - EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20 - - PURPOSE_VAL_XTS_AES128_KEY = 4 - - GPIO_STRAP_REG = 0x3f404038 - - FLASH_ENCRYPTED_WRITE_ALIGN = 16 - - MEMORY_MAP = [[0x00000000, 0x00010000, "PADDING"], - [0x3C000000, 0x3C800000, "DROM"], - [0x3FC80000, 0x3FCE0000, "DRAM"], - [0x3FC88000, 0x3FD00000, "BYTE_ACCESSIBLE"], - [0x3FF00000, 0x3FF20000, "DROM_MASK"], - [0x40000000, 0x40060000, "IROM_MASK"], - [0x42000000, 0x42800000, "IROM"], - [0x4037C000, 0x403E0000, "IRAM"], - [0x50000000, 0x50002000, "RTC_IRAM"], - [0x50000000, 0x50002000, "RTC_DRAM"], - [0x600FE000, 0x60100000, "MEM_INTERNAL2"]] - - def get_pkg_version(self): - num_word = 3 - block1_addr = self.EFUSE_BASE + 0x044 - word3 = self.read_reg(block1_addr + (4 * num_word)) - pkg_version = (word3 >> 21) & 0x0F - return pkg_version - - def get_chip_revision(self): - # reads WAFER_VERSION field from EFUSE_RD_MAC_SPI_SYS_3_REG - block1_addr = self.EFUSE_BASE + 0x044 - num_word = 3 - pos = 18 - return (self.read_reg(block1_addr + (4 * num_word)) & (0x7 << pos)) >> pos - - def get_chip_description(self): - chip_name = { - 0: "ESP32-C3", - }.get(self.get_pkg_version(), "unknown ESP32-C3") - chip_revision = self.get_chip_revision() - - return "%s (revision %d)" % (chip_name, chip_revision) - - def get_chip_features(self): - return ["Wi-Fi"] - - def get_crystal_freq(self): - # ESP32C3 XTAL is fixed to 40MHz - return 40 - - def override_vddsdio(self, new_voltage): - raise NotImplementedInROMError("VDD_SDIO overrides are not supported for ESP32-C3") - - def read_mac(self): - mac0 = self.read_reg(self.MAC_EFUSE_REG) - mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC - bitstring = struct.pack(">II", mac1, mac0)[2:] - try: - return tuple(ord(b) for b in bitstring) - except TypeError: # Python 3, bitstring elements are already bytes - return tuple(bitstring) - - def get_flash_crypt_config(self): - return None # doesn't exist on ESP32-C3 - - def get_key_block_purpose(self, key_block): - if key_block < 0 or key_block > 5: - raise FatalError("Valid key block numbers must be in range 0-5") - - reg, shift = [(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT), - (self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT), - (self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT), - (self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT), - (self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT), - (self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT)][key_block] - return (self.read_reg(reg) >> shift) & 0xF - - def is_flash_encryption_key_valid(self): - # Need to see an AES-128 key - purposes = [self.get_key_block_purpose(b) for b in range(6)] - - return any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes) - - -class ESP32C6BETAROM(ESP32C3ROM): - CHIP_NAME = "ESP32-C6 BETA" - IMAGE_CHIP_ID = 7 - - CHIP_DETECT_MAGIC_VALUE = [0x0da1806f] - - UART_DATE_REG_ADDR = 0x00000500 - - def get_chip_description(self): - chip_name = { - 0: "ESP32-C6", - }.get(self.get_pkg_version(), "unknown ESP32-C6") - chip_revision = self.get_chip_revision() - - return "%s (revision %d)" % (chip_name, chip_revision) - - -class ESP32StubLoader(ESP32ROM): - """ Access class for ESP32 stub loader, runs on top of ROM. - """ - FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c - STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM - IS_STUB = True - - def __init__(self, rom_loader): - self.secure_download_mode = rom_loader.secure_download_mode - self._port = rom_loader._port - self._trace_enabled = rom_loader._trace_enabled - self.flush_input() # resets _slip_reader - - -ESP32ROM.STUB_CLASS = ESP32StubLoader - - -class ESP32S2StubLoader(ESP32S2ROM): - """ Access class for ESP32-S2 stub loader, runs on top of ROM. - - (Basically the same as ESP32StubLoader, but different base class. - Can possibly be made into a mixin.) - """ - FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c - STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM - IS_STUB = True - - def __init__(self, rom_loader): - self.secure_download_mode = rom_loader.secure_download_mode - self._port = rom_loader._port - self._trace_enabled = rom_loader._trace_enabled - self.flush_input() # resets _slip_reader - - if rom_loader.uses_usb(): - self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK - self.FLASH_WRITE_SIZE = self.USB_RAM_BLOCK - - -ESP32S2ROM.STUB_CLASS = ESP32S2StubLoader - - -class ESP32S3BETA2StubLoader(ESP32S3BETA2ROM): - """ Access class for ESP32S3 stub loader, runs on top of ROM. - - (Basically the same as ESP32StubLoader, but different base class. - Can possibly be made into a mixin.) - """ - FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c - STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM - IS_STUB = True - - def __init__(self, rom_loader): - self.secure_download_mode = rom_loader.secure_download_mode - self._port = rom_loader._port - self._trace_enabled = rom_loader._trace_enabled - self.flush_input() # resets _slip_reader - - -ESP32S3BETA2ROM.STUB_CLASS = ESP32S3BETA2StubLoader - - -class ESP32S3BETA3StubLoader(ESP32S3BETA3ROM): - """ Access class for ESP32S3 stub loader, runs on top of ROM. - - (Basically the same as ESP32StubLoader, but different base class. - Can possibly be made into a mixin.) - """ - FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c - STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM - IS_STUB = True - - def __init__(self, rom_loader): - self.secure_download_mode = rom_loader.secure_download_mode - self._port = rom_loader._port - self._trace_enabled = rom_loader._trace_enabled - self.flush_input() # resets _slip_reader - - -ESP32S3BETA3ROM.STUB_CLASS = ESP32S3BETA3StubLoader - - -class ESP32C3StubLoader(ESP32C3ROM): - """ Access class for ESP32C3 stub loader, runs on top of ROM. - - (Basically the same as ESP32StubLoader, but different base class. - Can possibly be made into a mixin.) - """ - FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c - STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM - IS_STUB = True - - def __init__(self, rom_loader): - self.secure_download_mode = rom_loader.secure_download_mode - self._port = rom_loader._port - self._trace_enabled = rom_loader._trace_enabled - self.flush_input() # resets _slip_reader - - -ESP32C3ROM.STUB_CLASS = ESP32C3StubLoader - - -class ESPBOOTLOADER(object): - """ These are constants related to software ESP8266 bootloader, working with 'v2' image files """ - - # First byte of the "v2" application image - IMAGE_V2_MAGIC = 0xea - - # First 'segment' value in a "v2" application image, appears to be a constant version value? - IMAGE_V2_SEGMENT = 4 - - -def LoadFirmwareImage(chip, filename): - """ Load a firmware image. Can be for any supported SoC. - - ESP8266 images will be examined to determine if they are original ROM firmware images (ESP8266ROMFirmwareImage) - or "v2" OTA bootloader images. - - Returns a BaseFirmwareImage subclass, either ESP8266ROMFirmwareImage (v1) or ESP8266V2FirmwareImage (v2). - """ - chip = chip.lower().replace("-", "") - with open(filename, 'rb') as f: - if chip == 'esp32': - return ESP32FirmwareImage(f) - elif chip == "esp32s2": - return ESP32S2FirmwareImage(f) - elif chip == "esp32s3beta2": - return ESP32S3BETA2FirmwareImage(f) - elif chip == "esp32s3beta3": - return ESP32S3BETA3FirmwareImage(f) - elif chip == 'esp32c3': - return ESP32C3FirmwareImage(f) - elif chip == 'esp32c6beta': - return ESP32C6BETAFirmwareImage(f) - else: # Otherwise, ESP8266 so look at magic to determine the image type - magic = ord(f.read(1)) - f.seek(0) - if magic == ESPLoader.ESP_IMAGE_MAGIC: - return ESP8266ROMFirmwareImage(f) - elif magic == ESPBOOTLOADER.IMAGE_V2_MAGIC: - return ESP8266V2FirmwareImage(f) - else: - raise FatalError("Invalid image magic number: %d" % magic) - - -class ImageSegment(object): - """ Wrapper class for a segment in an ESP image - (very similar to a section in an ELFImage also) """ - def __init__(self, addr, data, file_offs=None): - self.addr = addr - self.data = data - self.file_offs = file_offs - self.include_in_checksum = True - if self.addr != 0: - self.pad_to_alignment(4) # pad all "real" ImageSegments 4 byte aligned length - - def copy_with_new_addr(self, new_addr): - """ Return a new ImageSegment with same data, but mapped at - a new address. """ - return ImageSegment(new_addr, self.data, 0) - - def split_image(self, split_len): - """ Return a new ImageSegment which splits "split_len" bytes - from the beginning of the data. Remaining bytes are kept in - this segment object (and the start address is adjusted to match.) """ - result = copy.copy(self) - result.data = self.data[:split_len] - self.data = self.data[split_len:] - self.addr += split_len - self.file_offs = None - result.file_offs = None - return result - - def __repr__(self): - r = "len 0x%05x load 0x%08x" % (len(self.data), self.addr) - if self.file_offs is not None: - r += " file_offs 0x%08x" % (self.file_offs) - return r - - def get_memory_type(self, image): - """ - Return a list describing the memory type(s) that is covered by this - segment's start address. - """ - return [map_range[2] for map_range in image.ROM_LOADER.MEMORY_MAP if map_range[0] <= self.addr < map_range[1]] - - def pad_to_alignment(self, alignment): - self.data = pad_to(self.data, alignment, b'\x00') - - -class ELFSection(ImageSegment): - """ Wrapper class for a section in an ELF image, has a section - name as well as the common properties of an ImageSegment. """ - def __init__(self, name, addr, data): - super(ELFSection, self).__init__(addr, data) - self.name = name.decode("utf-8") - - def __repr__(self): - return "%s %s" % (self.name, super(ELFSection, self).__repr__()) - - -class BaseFirmwareImage(object): - SEG_HEADER_LEN = 8 - SHA256_DIGEST_LEN = 32 - - """ Base class with common firmware image functions """ - def __init__(self): - self.segments = [] - self.entrypoint = 0 - self.elf_sha256 = None - self.elf_sha256_offset = 0 - - def load_common_header(self, load_file, expected_magic): - (magic, segments, self.flash_mode, self.flash_size_freq, self.entrypoint) = struct.unpack(' 16: - raise FatalError('Invalid segment count %d (max 16). Usually this indicates a linker script problem.' % len(self.segments)) - - def load_segment(self, f, is_irom_segment=False): - """ Load the next segment from the image file """ - file_offs = f.tell() - (offset, size) = struct.unpack(' 0x40200000 or offset < 0x3ffe0000 or size > 65536: - print('WARNING: Suspicious segment 0x%x, length %d' % (offset, size)) - - def maybe_patch_segment_data(self, f, segment_data): - """If SHA256 digest of the ELF file needs to be inserted into this segment, do so. Returns segment data.""" - segment_len = len(segment_data) - file_pos = f.tell() # file_pos is position in the .bin file - if self.elf_sha256_offset >= file_pos and self.elf_sha256_offset < file_pos + segment_len: - # SHA256 digest needs to be patched into this binary segment, - # calculate offset of the digest inside the binary segment. - patch_offset = self.elf_sha256_offset - file_pos - # Sanity checks - if patch_offset < self.SEG_HEADER_LEN or patch_offset + self.SHA256_DIGEST_LEN > segment_len: - raise FatalError('Cannot place SHA256 digest on segment boundary' - '(elf_sha256_offset=%d, file_pos=%d, segment_size=%d)' % - (self.elf_sha256_offset, file_pos, segment_len)) - # offset relative to the data part - patch_offset -= self.SEG_HEADER_LEN - if segment_data[patch_offset:patch_offset + self.SHA256_DIGEST_LEN] != b'\x00' * self.SHA256_DIGEST_LEN: - raise FatalError('Contents of segment at SHA256 digest offset 0x%x are not all zero. Refusing to overwrite.' % - self.elf_sha256_offset) - assert(len(self.elf_sha256) == self.SHA256_DIGEST_LEN) - segment_data = segment_data[0:patch_offset] + self.elf_sha256 + \ - segment_data[patch_offset + self.SHA256_DIGEST_LEN:] - return segment_data - - def save_segment(self, f, segment, checksum=None): - """ Save the next segment to the image file, return next checksum value if provided """ - segment_data = self.maybe_patch_segment_data(f, segment.data) - f.write(struct.pack(' 0: - if len(irom_segments) != 1: - raise FatalError('Found %d segments that could be irom0. Bad ELF file?' % len(irom_segments)) - return irom_segments[0] - return None - - def get_non_irom_segments(self): - irom_segment = self.get_irom_segment() - return [s for s in self.segments if s != irom_segment] - - def merge_adjacent_segments(self): - if not self.segments: - return # nothing to merge - - segments = [] - # The easiest way to merge the sections is the browse them backward. - for i in range(len(self.segments) - 1, 0, -1): - # elem is the previous section, the one `next_elem` may need to be - # merged in - elem = self.segments[i - 1] - next_elem = self.segments[i] - if all((elem.get_memory_type(self) == next_elem.get_memory_type(self), - elem.include_in_checksum == next_elem.include_in_checksum, - next_elem.addr == elem.addr + len(elem.data))): - # Merge any segment that ends where the next one starts, without spanning memory types - # - # (don't 'pad' any gaps here as they may be excluded from the image due to 'noinit' - # or other reasons.) - elem.data += next_elem.data - else: - # The section next_elem cannot be merged into the previous one, - # which means it needs to be part of the final segments. - # As we are browsing the list backward, the elements need to be - # inserted at the beginning of the final list. - segments.insert(0, next_elem) - - # The first segment will always be here as it cannot be merged into any - # "previous" section. - segments.insert(0, self.segments[0]) - - # note: we could sort segments here as well, but the ordering of segments is sometimes - # important for other reasons (like embedded ELF SHA-256), so we assume that the linker - # script will have produced any adjacent sections in linear order in the ELF, anyhow. - self.segments = segments - - -class ESP8266ROMFirmwareImage(BaseFirmwareImage): - """ 'Version 1' firmware image, segments loaded directly by the ROM bootloader. """ - - ROM_LOADER = ESP8266ROM - - def __init__(self, load_file=None): - super(ESP8266ROMFirmwareImage, self).__init__() - self.flash_mode = 0 - self.flash_size_freq = 0 - self.version = 1 - - if load_file is not None: - segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC) - - for _ in range(segments): - self.load_segment(load_file) - self.checksum = self.read_checksum(load_file) - - self.verify() - - def default_output_name(self, input_file): - """ Derive a default output name from the ELF name. """ - return input_file + '-' - - def save(self, basename): - """ Save a set of V1 images for flashing. Parameter is a base filename. """ - # IROM data goes in its own plain binary file - irom_segment = self.get_irom_segment() - if irom_segment is not None: - with open("%s0x%05x.bin" % (basename, irom_segment.addr - ESP8266ROM.IROM_MAP_START), "wb") as f: - f.write(irom_segment.data) - - # everything but IROM goes at 0x00000 in an image file - normal_segments = self.get_non_irom_segments() - with open("%s0x00000.bin" % basename, 'wb') as f: - self.write_common_header(f, normal_segments) - checksum = ESPLoader.ESP_CHECKSUM_MAGIC - for segment in normal_segments: - checksum = self.save_segment(f, segment, checksum) - self.append_checksum(f, checksum) - - -ESP8266ROM.BOOTLOADER_IMAGE = ESP8266ROMFirmwareImage - - -class ESP8266V2FirmwareImage(BaseFirmwareImage): - """ 'Version 2' firmware image, segments loaded by software bootloader stub - (ie Espressif bootloader or rboot) - """ - - ROM_LOADER = ESP8266ROM - - def __init__(self, load_file=None): - super(ESP8266V2FirmwareImage, self).__init__() - self.version = 2 - if load_file is not None: - segments = self.load_common_header(load_file, ESPBOOTLOADER.IMAGE_V2_MAGIC) - if segments != ESPBOOTLOADER.IMAGE_V2_SEGMENT: - # segment count is not really segment count here, but we expect to see '4' - print('Warning: V2 header has unexpected "segment" count %d (usually 4)' % segments) - - # irom segment comes before the second header - # - # the file is saved in the image with a zero load address - # in the header, so we need to calculate a load address - irom_segment = self.load_segment(load_file, True) - irom_segment.addr = 0 # for actual mapped addr, add ESP8266ROM.IROM_MAP_START + flashing_addr + 8 - irom_segment.include_in_checksum = False - - first_flash_mode = self.flash_mode - first_flash_size_freq = self.flash_size_freq - first_entrypoint = self.entrypoint - # load the second header - - segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC) - - if first_flash_mode != self.flash_mode: - print('WARNING: Flash mode value in first header (0x%02x) disagrees with second (0x%02x). Using second value.' - % (first_flash_mode, self.flash_mode)) - if first_flash_size_freq != self.flash_size_freq: - print('WARNING: Flash size/freq value in first header (0x%02x) disagrees with second (0x%02x). Using second value.' - % (first_flash_size_freq, self.flash_size_freq)) - if first_entrypoint != self.entrypoint: - print('WARNING: Entrypoint address in first header (0x%08x) disagrees with second header (0x%08x). Using second value.' - % (first_entrypoint, self.entrypoint)) - - # load all the usual segments - for _ in range(segments): - self.load_segment(load_file) - self.checksum = self.read_checksum(load_file) - - self.verify() - - def default_output_name(self, input_file): - """ Derive a default output name from the ELF name. """ - irom_segment = self.get_irom_segment() - if irom_segment is not None: - irom_offs = irom_segment.addr - ESP8266ROM.IROM_MAP_START - else: - irom_offs = 0 - return "%s-0x%05x.bin" % (os.path.splitext(input_file)[0], - irom_offs & ~(ESPLoader.FLASH_SECTOR_SIZE - 1)) - - def save(self, filename): - with open(filename, 'wb') as f: - # Save first header for irom0 segment - f.write(struct.pack(b' 0: - last_addr = flash_segments[0].addr - for segment in flash_segments[1:]: - if segment.addr // self.IROM_ALIGN == last_addr // self.IROM_ALIGN: - raise FatalError(("Segment loaded at 0x%08x lands in same 64KB flash mapping as segment loaded at 0x%08x. " - "Can't generate binary. Suggest changing linker script or ELF to merge sections.") % - (segment.addr, last_addr)) - last_addr = segment.addr - - def get_alignment_data_needed(segment): - # Actual alignment (in data bytes) required for a segment header: positioned so that - # after we write the next 8 byte header, file_offs % IROM_ALIGN == segment.addr % IROM_ALIGN - # - # (this is because the segment's vaddr may not be IROM_ALIGNed, more likely is aligned - # IROM_ALIGN+0x18 to account for the binary file header - align_past = (segment.addr % self.IROM_ALIGN) - self.SEG_HEADER_LEN - pad_len = (self.IROM_ALIGN - (f.tell() % self.IROM_ALIGN)) + align_past - if pad_len == 0 or pad_len == self.IROM_ALIGN: - return 0 # already aligned - - # subtract SEG_HEADER_LEN a second time, as the padding block has a header as well - pad_len -= self.SEG_HEADER_LEN - if pad_len < 0: - pad_len += self.IROM_ALIGN - return pad_len - - # try to fit each flash segment on a 64kB aligned boundary - # by padding with parts of the non-flash segments... - while len(flash_segments) > 0: - segment = flash_segments[0] - pad_len = get_alignment_data_needed(segment) - if pad_len > 0: # need to pad - if len(ram_segments) > 0 and pad_len > self.SEG_HEADER_LEN: - pad_segment = ram_segments[0].split_image(pad_len) - if len(ram_segments[0].data) == 0: - ram_segments.pop(0) - else: - pad_segment = ImageSegment(0, b'\x00' * pad_len, f.tell()) - checksum = self.save_segment(f, pad_segment, checksum) - total_segments += 1 - else: - # write the flash segment - assert (f.tell() + 8) % self.IROM_ALIGN == segment.addr % self.IROM_ALIGN - checksum = self.save_flash_segment(f, segment, checksum) - flash_segments.pop(0) - total_segments += 1 - - # flash segments all written, so write any remaining RAM segments - for segment in ram_segments: - checksum = self.save_segment(f, segment, checksum) - total_segments += 1 - - if self.secure_pad: - # pad the image so that after signing it will end on a a 64KB boundary. - # This ensures all mapped flash content will be verified. - if not self.append_digest: - raise FatalError("secure_pad only applies if a SHA-256 digest is also appended to the image") - align_past = (f.tell() + self.SEG_HEADER_LEN) % self.IROM_ALIGN - # 16 byte aligned checksum (force the alignment to simplify calculations) - checksum_space = 16 - if self.secure_pad == '1': - # after checksum: SHA-256 digest + (to be added by signing process) version, signature + 12 trailing bytes due to alignment - space_after_checksum = 32 + 4 + 64 + 12 - elif self.secure_pad == '2': # Secure Boot V2 - # after checksum: SHA-256 digest + signature sector, but we place signature sector after the 64KB boundary - space_after_checksum = 32 - pad_len = (self.IROM_ALIGN - align_past - checksum_space - space_after_checksum) % self.IROM_ALIGN - pad_segment = ImageSegment(0, b'\x00' * pad_len, f.tell()) - - checksum = self.save_segment(f, pad_segment, checksum) - total_segments += 1 - - # done writing segments - self.append_checksum(f, checksum) - image_length = f.tell() - - if self.secure_pad: - assert ((image_length + space_after_checksum) % self.IROM_ALIGN) == 0 - - # kinda hacky: go back to the initial header and write the new segment count - # that includes padding segments. This header is not checksummed - f.seek(1) - try: - f.write(chr(total_segments)) - except TypeError: # Python 3 - f.write(bytes([total_segments])) - - if self.append_digest: - # calculate the SHA256 of the whole file and append it - f.seek(0) - digest = hashlib.sha256() - digest.update(f.read(image_length)) - f.write(digest.digest()) - - with open(filename, 'wb') as real_file: - real_file.write(f.getvalue()) - - def save_flash_segment(self, f, segment, checksum=None): - """ Save the next segment to the image file, return next checksum value if provided """ - segment_end_pos = f.tell() + len(segment.data) + self.SEG_HEADER_LEN - segment_len_remainder = segment_end_pos % self.IROM_ALIGN - if segment_len_remainder < 0x24: - # Work around a bug in ESP-IDF 2nd stage bootloader, that it didn't map the - # last MMU page, if an IROM/DROM segment was < 0x24 bytes over the page boundary. - segment.data += b'\x00' * (0x24 - segment_len_remainder) - return self.save_segment(f, segment, checksum) - - def load_extended_header(self, load_file): - def split_byte(n): - return (n & 0x0F, (n >> 4) & 0x0F) - - fields = list(struct.unpack(self.EXTENDED_HEADER_STRUCT_FMT, load_file.read(16))) - - self.wp_pin = fields[0] - - # SPI pin drive stengths are two per byte - self.clk_drv, self.q_drv = split_byte(fields[1]) - self.d_drv, self.cs_drv = split_byte(fields[2]) - self.hd_drv, self.wp_drv = split_byte(fields[3]) - - chip_id = fields[4] - if chip_id != self.ROM_LOADER.IMAGE_CHIP_ID: - print(("Unexpected chip id in image. Expected %d but value was %d. " - "Is this image for a different chip model?") % (self.ROM_LOADER.IMAGE_CHIP_ID, chip_id)) - - # reserved fields in the middle should all be zero - if any(f for f in fields[6:-1] if f != 0): - print("Warning: some reserved header fields have non-zero values. This image may be from a newer esptool.py?") - - append_digest = fields[-1] # last byte is append_digest - if append_digest in [0, 1]: - self.append_digest = (append_digest == 1) - else: - raise RuntimeError("Invalid value for append_digest field (0x%02x). Should be 0 or 1.", append_digest) - - def save_extended_header(self, save_file): - def join_byte(ln, hn): - return (ln & 0x0F) + ((hn & 0x0F) << 4) - - append_digest = 1 if self.append_digest else 0 - - fields = [self.wp_pin, - join_byte(self.clk_drv, self.q_drv), - join_byte(self.d_drv, self.cs_drv), - join_byte(self.hd_drv, self.wp_drv), - self.ROM_LOADER.IMAGE_CHIP_ID, - self.min_rev] - fields += [0] * 8 # padding - fields += [append_digest] - - packed = struct.pack(self.EXTENDED_HEADER_STRUCT_FMT, *fields) - save_file.write(packed) - - -ESP32ROM.BOOTLOADER_IMAGE = ESP32FirmwareImage - - -class ESP32S2FirmwareImage(ESP32FirmwareImage): - """ ESP32S2 Firmware Image almost exactly the same as ESP32FirmwareImage """ - ROM_LOADER = ESP32S2ROM - - -ESP32S2ROM.BOOTLOADER_IMAGE = ESP32S2FirmwareImage - - -class ESP32S3BETA2FirmwareImage(ESP32FirmwareImage): - """ ESP32S3 Firmware Image almost exactly the same as ESP32FirmwareImage """ - ROM_LOADER = ESP32S3BETA2ROM - - -ESP32S3BETA2ROM.BOOTLOADER_IMAGE = ESP32S3BETA2FirmwareImage - - -class ESP32S3BETA3FirmwareImage(ESP32FirmwareImage): - """ ESP32S3 Firmware Image almost exactly the same as ESP32FirmwareImage """ - ROM_LOADER = ESP32S3BETA3ROM - - -ESP32S3BETA3ROM.BOOTLOADER_IMAGE = ESP32S3BETA3FirmwareImage - - -class ESP32C3FirmwareImage(ESP32FirmwareImage): - """ ESP32C3 Firmware Image almost exactly the same as ESP32FirmwareImage """ - ROM_LOADER = ESP32C3ROM - - -ESP32C3ROM.BOOTLOADER_IMAGE = ESP32C3FirmwareImage - - -class ESP32C6BETAFirmwareImage(ESP32FirmwareImage): - """ ESP32C6 Firmware Image almost exactly the same as ESP32FirmwareImage """ - ROM_LOADER = ESP32C6BETAROM - - -ESP32C6BETAROM.BOOTLOADER_IMAGE = ESP32C6BETAFirmwareImage - - -class ELFFile(object): - SEC_TYPE_PROGBITS = 0x01 - SEC_TYPE_STRTAB = 0x03 - - LEN_SEC_HEADER = 0x28 - - SEG_TYPE_LOAD = 0x01 - LEN_SEG_HEADER = 0x20 - - def __init__(self, name): - # Load sections from the ELF file - self.name = name - with open(self.name, 'rb') as f: - self._read_elf_file(f) - - def get_section(self, section_name): - for s in self.sections: - if s.name == section_name: - return s - raise ValueError("No section %s in ELF file" % section_name) - - def _read_elf_file(self, f): - # read the ELF file header - LEN_FILE_HEADER = 0x34 - try: - (ident, _type, machine, _version, - self.entrypoint, _phoff, shoff, _flags, - _ehsize, _phentsize, _phnum, shentsize, - shnum, shstrndx) = struct.unpack("<16sHHLLLLLHHHHHH", f.read(LEN_FILE_HEADER)) - except struct.error as e: - raise FatalError("Failed to read a valid ELF header from %s: %s" % (self.name, e)) - - if byte(ident, 0) != 0x7f or ident[1:4] != b'ELF': - raise FatalError("%s has invalid ELF magic header" % self.name) - if machine not in [0x5e, 0xf3]: - raise FatalError("%s does not appear to be an Xtensa or an RISCV ELF file. e_machine=%04x" % (self.name, machine)) - if shentsize != self.LEN_SEC_HEADER: - raise FatalError("%s has unexpected section header entry size 0x%x (not 0x%x)" % (self.name, shentsize, self.LEN_SEC_HEADER)) - if shnum == 0: - raise FatalError("%s has 0 section headers" % (self.name)) - self._read_sections(f, shoff, shnum, shstrndx) - self._read_segments(f, _phoff, _phnum, shstrndx) - - def _read_sections(self, f, section_header_offs, section_header_count, shstrndx): - f.seek(section_header_offs) - len_bytes = section_header_count * self.LEN_SEC_HEADER - section_header = f.read(len_bytes) - if len(section_header) == 0: - raise FatalError("No section header found at offset %04x in ELF file." % section_header_offs) - if len(section_header) != (len_bytes): - raise FatalError("Only read 0x%x bytes from section header (expected 0x%x.) Truncated ELF file?" % (len(section_header), len_bytes)) - - # walk through the section header and extract all sections - section_header_offsets = range(0, len(section_header), self.LEN_SEC_HEADER) - - def read_section_header(offs): - name_offs, sec_type, _flags, lma, sec_offs, size = struct.unpack_from(" 0] - self.sections = prog_sections - - def _read_segments(self, f, segment_header_offs, segment_header_count, shstrndx): - f.seek(segment_header_offs) - len_bytes = segment_header_count * self.LEN_SEG_HEADER - segment_header = f.read(len_bytes) - if len(segment_header) == 0: - raise FatalError("No segment header found at offset %04x in ELF file." % segment_header_offs) - if len(segment_header) != (len_bytes): - raise FatalError("Only read 0x%x bytes from segment header (expected 0x%x.) Truncated ELF file?" % (len(segment_header), len_bytes)) - - # walk through the segment header and extract all segments - segment_header_offsets = range(0, len(segment_header), self.LEN_SEG_HEADER) - - def read_segment_header(offs): - seg_type, seg_offs, _vaddr, lma, size, _memsize, _flags, _align = struct.unpack_from(" 0] - self.segments = prog_segments - - def sha256(self): - # return SHA256 hash of the input ELF file - sha256 = hashlib.sha256() - with open(self.name, 'rb') as f: - sha256.update(f.read()) - return sha256.digest() - - -def slip_reader(port, trace_function): - """Generator to read SLIP packets from a serial port. - Yields one full SLIP packet at a time, raises exception on timeout or invalid data. - - Designed to avoid too many calls to serial.read(1), which can bog - down on slow systems. - """ - 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'': - waiting_for = "header" if partial_packet is None else "content" - trace_function("Timed out waiting for packet %s", waiting_for) - raise FatalError("Timed out waiting for packet %s" % waiting_for) - trace_function("Read %d bytes: %s", len(read_bytes), HexFormatter(read_bytes)) - 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: - trace_function("Read invalid data: %s", HexFormatter(read_bytes)) - trace_function("Remaining data in serial buffer: %s", HexFormatter(port.read(port.inWaiting()))) - raise FatalError('Invalid head of packet (0x%s)' % hexify(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: - trace_function("Read invalid data: %s", HexFormatter(read_bytes)) - trace_function("Remaining data in serial buffer: %s", HexFormatter(port.read(port.inWaiting()))) - raise FatalError('Invalid SLIP escape (0xdb, 0x%s)' % (hexify(b))) - elif b == b'\xdb': # start of escape sequence - in_escape = True - elif b == b'\xc0': # end of packet - trace_function("Received full packet: %s", HexFormatter(partial_packet)) - yield partial_packet - partial_packet = None - else: # normal byte in packet - partial_packet += b - - -def arg_auto_int(x): - return int(x, 0) - - -def div_roundup(a, b): - """ Return a/b rounded up to nearest integer, - equivalent result to int(math.ceil(float(int(a)) / float(int(b))), only - without possible floating point accuracy errors. - """ - return (int(a) + int(b) - 1) // int(b) - - -def align_file_position(f, size): - """ Align the position in the file to the next block of specified size """ - align = (size - 1) - (f.tell() % size) - f.seek(align, 1) - - -def flash_size_bytes(size): - """ Given a flash size of the type passed in args.flash_size - (ie 512KB or 1MB) then return the size in bytes. - """ - if "MB" in size: - return int(size[:size.index("MB")]) * 1024 * 1024 - elif "KB" in size: - return int(size[:size.index("KB")]) * 1024 - else: - raise FatalError("Unknown size %s" % size) - - -def hexify(s, uppercase=True): - format_str = '%02X' if uppercase else '%02x' - if not PYTHON2: - return ''.join(format_str % c for c in s) - else: - return ''.join(format_str % ord(c) for c in s) - - -class HexFormatter(object): - """ - Wrapper class which takes binary data in its constructor - and returns a hex string as it's __str__ method. - - This is intended for "lazy formatting" of trace() output - in hex format. Avoids overhead (significant on slow computers) - of generating long hex strings even if tracing is disabled. - - Note that this doesn't save any overhead if passed as an - argument to "%", only when passed to trace() - - If auto_split is set (default), any long line (> 16 bytes) will be - printed as separately indented lines, with ASCII decoding at the end - of each line. - """ - def __init__(self, binary_string, auto_split=True): - self._s = binary_string - self._auto_split = auto_split - - def __str__(self): - if self._auto_split and len(self._s) > 16: - result = "" - s = self._s - while len(s) > 0: - line = s[:16] - ascii_line = "".join(c if (c == ' ' or (c in string.printable and c not in string.whitespace)) - else '.' for c in line.decode('ascii', 'replace')) - s = s[16:] - result += "\n %-16s %-16s | %s" % (hexify(line[:8], False), hexify(line[8:], False), ascii_line) - return result - else: - return hexify(self._s, False) - - -def pad_to(data, alignment, pad_character=b'\xFF'): - """ Pad to the next alignment boundary """ - pad_mod = len(data) % alignment - if pad_mod != 0: - data += pad_character * (alignment - pad_mod) - return data - - -class FatalError(RuntimeError): - """ - Wrapper class for runtime errors that aren't caused by internal bugs, but by - ESP8266 responses or input content. - """ - def __init__(self, message): - RuntimeError.__init__(self, message) - - @staticmethod - def WithResult(message, result): - """ - Return a fatal error object that appends the hex values of - 'result' as a string formatted argument. - """ - message += " (result was %s)" % hexify(result) - return FatalError(message) - - -class NotImplementedInROMError(FatalError): - """ - Wrapper class for the error thrown when a particular ESP bootloader function - is not implemented in the ROM bootloader. - """ - def __init__(self, bootloader, func): - FatalError.__init__(self, "%s ROM does not support function %s." % (bootloader.CHIP_NAME, func.__name__)) - - -class NotSupportedError(FatalError): - def __init__(self, esp, function_name): - FatalError.__init__(self, "Function %s is not supported for %s." % (function_name, esp.CHIP_NAME)) - -# "Operation" commands, executable at command line. One function each -# -# Each function takes either two args (, ) or a single -# argument. - - -class UnsupportedCommandError(RuntimeError): - """ - Wrapper class for when ROM loader returns an invalid command response. - - Usually this indicates the loader is running in Secure Download Mode. - """ - def __init__(self, esp, op): - if esp.secure_download_mode: - msg = "This command (0x%x) is not supported in Secure Download Mode" % op - else: - msg = "Invalid (unsupported) command 0x%x" % op - RuntimeError.__init__(self, msg) - - -def load_ram(esp, args): - image = LoadFirmwareImage(esp.CHIP_NAME, args.filename) - - print('RAM boot...') - for seg in image.segments: - size = len(seg.data) - print('Downloading %d bytes at %08x...' % (size, seg.addr), end=' ') - sys.stdout.flush() - esp.mem_begin(size, div_roundup(size, esp.ESP_RAM_BLOCK), esp.ESP_RAM_BLOCK, seg.addr) - - seq = 0 - while len(seg.data) > 0: - esp.mem_block(seg.data[0:esp.ESP_RAM_BLOCK], seq) - seg.data = seg.data[esp.ESP_RAM_BLOCK:] - seq += 1 - print('done!') - - print('All segments done, executing at %08x' % image.entrypoint) - esp.mem_finish(image.entrypoint) - - -def read_mem(esp, args): - print('0x%08x = 0x%08x' % (args.address, esp.read_reg(args.address))) - - -def write_mem(esp, args): - esp.write_reg(args.address, args.value, args.mask, 0) - print('Wrote %08x, mask %08x to %08x' % (args.value, args.mask, args.address)) - - -def dump_mem(esp, args): - with open(args.filename, 'wb') as f: - for i in range(args.size // 4): - d = esp.read_reg(args.address + (i * 4)) - f.write(struct.pack(b'> 16 - args.flash_size = DETECTED_FLASH_SIZES.get(size_id) - if args.flash_size is None: - print('Warning: Could not auto-detect Flash size (FlashID=0x%x, SizeID=0x%x), defaulting to 4MB' % (flash_id, size_id)) - args.flash_size = '4MB' - else: - print('Auto-detected Flash size:', args.flash_size) - - -def _update_image_flash_params(esp, address, args, image): - """ Modify the flash mode & size bytes if this looks like an executable bootloader image """ - if len(image) < 8: - return image # not long enough to be a bootloader image - - # unpack the (potential) image header - magic, _, flash_mode, flash_size_freq = struct.unpack("BBBB", image[:4]) - if address != esp.BOOTLOADER_FLASH_OFFSET: - return image # not flashing bootloader offset, so don't modify this - - if (args.flash_mode, args.flash_freq, args.flash_size) == ('keep',) * 3: - return image # all settings are 'keep', not modifying anything - - # easy check if this is an image: does it start with a magic byte? - if magic != esp.ESP_IMAGE_MAGIC: - print("Warning: Image file at 0x%x doesn't look like an image file, so not changing any flash settings." % address) - return image - - # make sure this really is an image, and not just data that - # starts with esp.ESP_IMAGE_MAGIC (mostly a problem for encrypted - # images that happen to start with a magic byte - try: - test_image = esp.BOOTLOADER_IMAGE(io.BytesIO(image)) - test_image.verify() - except Exception: - print("Warning: Image file at 0x%x is not a valid %s image, so not changing any flash settings." % (address, esp.CHIP_NAME)) - return image - - if args.flash_mode != 'keep': - flash_mode = {'qio': 0, 'qout': 1, 'dio': 2, 'dout': 3}[args.flash_mode] - - flash_freq = flash_size_freq & 0x0F - if args.flash_freq != 'keep': - flash_freq = {'40m': 0, '26m': 1, '20m': 2, '80m': 0xf}[args.flash_freq] - - flash_size = flash_size_freq & 0xF0 - if args.flash_size != 'keep': - flash_size = esp.parse_flash_size_arg(args.flash_size) - - flash_params = struct.pack(b'BB', flash_mode, flash_size + flash_freq) - if flash_params != image[2:4]: - print('Flash params set to 0x%04x' % struct.unpack(">H", flash_params)) - image = image[0:2] + flash_params + image[4:] - return image - - -def write_flash(esp, args): - # set args.compress based on default behaviour: - # -> if either --compress or --no-compress is set, honour that - # -> otherwise, set --compress unless --no-stub is set - if args.compress is None and not args.no_compress: - args.compress = not args.no_stub - - # In case we have encrypted files to write, we first do few sanity checks before actual flash - if args.encrypt or args.encrypt_files is not None: - do_write = True - - if not esp.secure_download_mode: - if esp.get_encrypted_download_disabled(): - raise FatalError("This chip has encrypt functionality in UART download mode disabled. " - "This is the Flash Encryption configuration for Production mode instead of Development mode.") - - crypt_cfg_efuse = esp.get_flash_crypt_config() - - if crypt_cfg_efuse is not None and crypt_cfg_efuse != 0xF: - print('Unexpected FLASH_CRYPT_CONFIG value: 0x%x' % (crypt_cfg_efuse)) - do_write = False - - enc_key_valid = esp.is_flash_encryption_key_valid() - - if not enc_key_valid: - print('Flash encryption key is not programmed') - do_write = False - - # Determine which files list contain the ones to encrypt - files_to_encrypt = args.addr_filename if args.encrypt else args.encrypt_files - - for address, argfile in files_to_encrypt: - if address % esp.FLASH_ENCRYPTED_WRITE_ALIGN: - print("File %s address 0x%x is not %d byte aligned, can't flash encrypted" % - (argfile.name, address, esp.FLASH_ENCRYPTED_WRITE_ALIGN)) - do_write = False - - if not do_write and not args.ignore_flash_encryption_efuse_setting: - raise FatalError("Can't perform encrypted flash write, consult Flash Encryption documentation for more information") - - # verify file sizes fit in flash - if args.flash_size != 'keep': # TODO: check this even with 'keep' - flash_end = flash_size_bytes(args.flash_size) - for address, argfile in args.addr_filename: - argfile.seek(0, os.SEEK_END) - if address + argfile.tell() > flash_end: - raise FatalError(("File %s (length %d) at offset %d will not fit in %d bytes of flash. " - "Use --flash-size argument, or change flashing address.") - % (argfile.name, argfile.tell(), address, flash_end)) - argfile.seek(0) - - if args.erase_all: - erase_flash(esp, args) - else: - for address, argfile in args.addr_filename: - argfile.seek(0, os.SEEK_END) - write_end = address + argfile.tell() - argfile.seek(0) - bytes_over = address % esp.FLASH_SECTOR_SIZE - if bytes_over != 0: - print("WARNING: Flash address {:#010x} is not aligned to a {:#x} byte flash sector. " - "{:#x} bytes before this address will be erased." - .format(address, esp.FLASH_SECTOR_SIZE, bytes_over)) - # Print the address range of to-be-erased flash memory region - print("Flash will be erased from {:#010x} to {:#010x}..." - .format(address - bytes_over, div_roundup(write_end, esp.FLASH_SECTOR_SIZE) * esp.FLASH_SECTOR_SIZE - 1)) - - """ Create a list describing all the files we have to flash. Each entry holds an "encrypt" flag - marking whether the file needs encryption or not. This list needs to be sorted. - - First, append to each entry of our addr_filename list the flag args.encrypt - For example, if addr_filename is [(0x1000, "partition.bin"), (0x8000, "bootloader")], - all_files will be [(0x1000, "partition.bin", args.encrypt), (0x8000, "bootloader", args.encrypt)], - where, of course, args.encrypt is either True or False - """ - all_files = [(offs, filename, args.encrypt) for (offs, filename) in args.addr_filename] - - """Now do the same with encrypt_files list, if defined. - In this case, the flag is True - """ - if args.encrypt_files is not None: - encrypted_files_flag = [(offs, filename, True) for (offs, filename) in args.encrypt_files] - - # Concatenate both lists and sort them. - # As both list are already sorted, we could simply do a merge instead, - # but for the sake of simplicity and because the lists are very small, - # let's use sorted. - all_files = sorted(all_files + encrypted_files_flag, key=lambda x: x[0]) - - for address, argfile, encrypted in all_files: - compress = args.compress - - # Check whether we can compress the current file before flashing - if compress and encrypted: - print('\nWARNING: - compress and encrypt options are mutually exclusive ') - print('Will flash %s uncompressed' % argfile.name) - compress = False - - if args.no_stub: - print('Erasing flash...') - image = pad_to(argfile.read(), esp.FLASH_ENCRYPTED_WRITE_ALIGN if encrypted else 4) - if len(image) == 0: - print('WARNING: File %s is empty' % argfile.name) - continue - image = _update_image_flash_params(esp, address, args, image) - calcmd5 = hashlib.md5(image).hexdigest() - uncsize = len(image) - if compress: - uncimage = image - image = zlib.compress(uncimage, 9) - # Decompress the compressed binary a block at a time, to dynamically calculate the - # timeout based on the real write size - decompress = zlib.decompressobj() - blocks = esp.flash_defl_begin(uncsize, len(image), address) - else: - blocks = esp.flash_begin(uncsize, address, begin_rom_encrypted=encrypted) - argfile.seek(0) # in case we need it again - seq = 0 - bytes_sent = 0 # bytes sent on wire - bytes_written = 0 # bytes written to flash - t = time.time() - - timeout = DEFAULT_TIMEOUT - - while len(image) > 0: - print_overwrite('Writing at 0x%08x... (%d %%)' % (address + bytes_written, 100 * (seq + 1) // blocks)) - sys.stdout.flush() - block = image[0:esp.FLASH_WRITE_SIZE] - if compress: - # feeding each compressed block into the decompressor lets us see block-by-block how much will be written - block_uncompressed = len(decompress.decompress(block)) - bytes_written += block_uncompressed - block_timeout = max(DEFAULT_TIMEOUT, timeout_per_mb(ERASE_WRITE_TIMEOUT_PER_MB, block_uncompressed)) - if not esp.IS_STUB: - timeout = block_timeout # ROM code writes block to flash before ACKing - esp.flash_defl_block(block, seq, timeout=timeout) - if esp.IS_STUB: - timeout = block_timeout # Stub ACKs when block is received, then writes to flash while receiving the block after it - else: - # Pad the last block - block = block + b'\xff' * (esp.FLASH_WRITE_SIZE - len(block)) - if encrypted: - esp.flash_encrypt_block(block, seq) - else: - esp.flash_block(block, seq) - bytes_written += len(block) - bytes_sent += len(block) - image = image[esp.FLASH_WRITE_SIZE:] - seq += 1 - - if esp.IS_STUB: - # Stub only writes each block to flash after 'ack'ing the receive, so do a final dummy operation which will - # not be 'ack'ed until the last block has actually been written out to flash - esp.read_reg(ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR, timeout=timeout) - - t = time.time() - t - speed_msg = "" - if compress: - if t > 0.0: - speed_msg = " (effective %.1f kbit/s)" % (uncsize / t * 8 / 1000) - print_overwrite('Wrote %d bytes (%d compressed) at 0x%08x in %.1f seconds%s...' % (uncsize, - bytes_sent, - address, t, speed_msg), last_line=True) - else: - if t > 0.0: - speed_msg = " (%.1f kbit/s)" % (bytes_written / t * 8 / 1000) - print_overwrite('Wrote %d bytes at 0x%08x in %.1f seconds%s...' % (bytes_written, address, t, speed_msg), last_line=True) - - if not encrypted and not esp.secure_download_mode: - try: - res = esp.flash_md5sum(address, uncsize) - if res != calcmd5: - print('File md5: %s' % calcmd5) - print('Flash md5: %s' % res) - print('MD5 of 0xFF is %s' % (hashlib.md5(b'\xFF' * uncsize).hexdigest())) - raise FatalError("MD5 of file does not match data in flash!") - else: - print('Hash of data verified.') - except NotImplementedInROMError: - pass - - print('\nLeaving...') - - if esp.IS_STUB: - # skip sending flash_finish to ROM loader here, - # as it causes the loader to exit and run user code - esp.flash_begin(0, 0) - - # Get the "encrypted" flag for the last file flashed - # Note: all_files list contains triplets like: - # (address: Integer, filename: String, encrypted: Boolean) - last_file_encrypted = all_files[-1][2] - - # Check whether the last file flashed was compressed or not - if args.compress and not last_file_encrypted: - esp.flash_defl_finish(False) - else: - esp.flash_finish(False) - - if args.verify: - print('Verifying just-written flash...') - print('(This option is deprecated, flash contents are now always read back after flashing.)') - # If some encrypted files have been flashed print a warning saying that we won't check them - if args.encrypt or args.encrypt_files is not None: - print('WARNING: - cannot verify encrypted files, they will be ignored') - # Call verify_flash function only if there at least one non-encrypted file flashed - if not args.encrypt: - verify_flash(esp, args) - - -def image_info(args): - image = LoadFirmwareImage(args.chip, args.filename) - print('Image version: %d' % image.version) - print('Entry point: %08x' % image.entrypoint if image.entrypoint != 0 else 'Entry point not set') - print('%d segments' % len(image.segments)) - print() - idx = 0 - for seg in image.segments: - idx += 1 - segs = seg.get_memory_type(image) - seg_name = ",".join(segs) - print('Segment %d: %r [%s]' % (idx, seg, seg_name)) - calc_checksum = image.calculate_checksum() - print('Checksum: %02x (%s)' % (image.checksum, - 'valid' if image.checksum == calc_checksum else 'invalid - calculated %02x' % calc_checksum)) - try: - digest_msg = 'Not appended' - if image.append_digest: - is_valid = image.stored_digest == image.calc_digest - digest_msg = "%s (%s)" % (hexify(image.calc_digest).lower(), - "valid" if is_valid else "invalid") - print('Validation Hash: %s' % digest_msg) - except AttributeError: - pass # ESP8266 image has no append_digest field - - -def make_image(args): - image = ESP8266ROMFirmwareImage() - if len(args.segfile) == 0: - raise FatalError('No segments specified') - if len(args.segfile) != len(args.segaddr): - raise FatalError('Number of specified files does not match number of specified addresses') - for (seg, addr) in zip(args.segfile, args.segaddr): - with open(seg, 'rb') as f: - data = f.read() - image.segments.append(ImageSegment(addr, data)) - image.entrypoint = args.entrypoint - image.save(args.output) - - -def elf2image(args): - e = ELFFile(args.input) - if args.chip == 'auto': # Default to ESP8266 for backwards compatibility - print("Creating image for ESP8266...") - args.chip = 'esp8266' - - if args.chip == 'esp32': - image = ESP32FirmwareImage() - if args.secure_pad: - image.secure_pad = '1' - elif args.secure_pad_v2: - image.secure_pad = '2' - elif args.chip == 'esp32s2': - image = ESP32S2FirmwareImage() - if args.secure_pad_v2: - image.secure_pad = '2' - elif args.chip == 'esp32s3beta2': - image = ESP32S3BETA2FirmwareImage() - if args.secure_pad_v2: - image.secure_pad = '2' - elif args.chip == 'esp32s3beta3': - image = ESP32S3BETA3FirmwareImage() - if args.secure_pad_v2: - image.secure_pad = '2' - elif args.chip == 'esp32c3': - image = ESP32C3FirmwareImage() - if args.secure_pad_v2: - image.secure_pad = '2' - elif args.chip == 'esp32c6beta': - image = ESP32C6BETAFirmwareImage() - if args.secure_pad_v2: - image.secure_pad = '2' - elif args.version == '1': # ESP8266 - image = ESP8266ROMFirmwareImage() - else: - image = ESP8266V2FirmwareImage() - image.entrypoint = e.entrypoint - image.flash_mode = {'qio': 0, 'qout': 1, 'dio': 2, 'dout': 3}[args.flash_mode] - - if args.chip != 'esp8266': - image.min_rev = int(args.min_rev) - - # ELFSection is a subclass of ImageSegment, so can use interchangeably - image.segments = e.segments if args.use_segments else e.sections - - image.flash_size_freq = image.ROM_LOADER.FLASH_SIZES[args.flash_size] - image.flash_size_freq += {'40m': 0, '26m': 1, '20m': 2, '80m': 0xf}[args.flash_freq] - - if args.elf_sha256_offset: - image.elf_sha256 = e.sha256() - image.elf_sha256_offset = args.elf_sha256_offset - - before = len(image.segments) - image.merge_adjacent_segments() - if len(image.segments) != before: - delta = before - len(image.segments) - print("Merged %d ELF section%s" % (delta, "s" if delta > 1 else "")) - - image.verify() - - if args.output is None: - args.output = image.default_output_name(args.input) - image.save(args.output) - - -def read_mac(esp, args): - mac = esp.read_mac() - - def print_mac(label, mac): - print('%s: %s' % (label, ':'.join(map(lambda x: '%02x' % x, mac)))) - print_mac("MAC", mac) - - -def chip_id(esp, args): - try: - chipid = esp.chip_id() - print('Chip ID: 0x%08x' % chipid) - except NotSupportedError: - print('Warning: %s has no Chip ID. Reading MAC instead.' % esp.CHIP_NAME) - read_mac(esp, args) - - -def erase_flash(esp, args): - print('Erasing flash (this may take a while)...') - t = time.time() - esp.erase_flash() - print('Chip erase completed successfully in %.1fs' % (time.time() - t)) - - -def erase_region(esp, args): - print('Erasing region (may be slow depending on size)...') - t = time.time() - esp.erase_region(args.address, args.size) - print('Erase completed successfully in %.1f seconds.' % (time.time() - t)) - - -def run(esp, args): - esp.run() - - -def flash_id(esp, args): - flash_id = esp.flash_id() - print('Manufacturer: %02x' % (flash_id & 0xff)) - flid_lowbyte = (flash_id >> 16) & 0xFF - print('Device: %02x%02x' % ((flash_id >> 8) & 0xff, flid_lowbyte)) - print('Detected flash size: %s' % (DETECTED_FLASH_SIZES.get(flid_lowbyte, "Unknown"))) - - -def read_flash(esp, args): - if args.no_progress: - flash_progress = None - else: - def flash_progress(progress, length): - msg = '%d (%d %%)' % (progress, progress * 100.0 / length) - padding = '\b' * len(msg) - if progress == length: - padding = '\n' - sys.stdout.write(msg + padding) - sys.stdout.flush() - t = time.time() - data = esp.read_flash(args.address, args.size, flash_progress) - t = time.time() - t - print_overwrite('Read %d bytes at 0x%x in %.1f seconds (%.1f kbit/s)...' - % (len(data), args.address, t, len(data) / t * 8 / 1000), last_line=True) - with open(args.filename, 'wb') as f: - f.write(data) - - -def verify_flash(esp, args): - differences = False - - for address, argfile in args.addr_filename: - image = pad_to(argfile.read(), 4) - argfile.seek(0) # rewind in case we need it again - - image = _update_image_flash_params(esp, address, args, image) - - image_size = len(image) - print('Verifying 0x%x (%d) bytes @ 0x%08x in flash against %s...' % (image_size, image_size, address, argfile.name)) - # Try digest first, only read if there are differences. - digest = esp.flash_md5sum(address, image_size) - expected_digest = hashlib.md5(image).hexdigest() - if digest == expected_digest: - print('-- verify OK (digest matched)') - continue - else: - differences = True - if getattr(args, 'diff', 'no') != 'yes': - print('-- verify FAILED (digest mismatch)') - continue - - flash = esp.read_flash(address, image_size) - assert flash != image - diff = [i for i in range(image_size) if flash[i] != image[i]] - print('-- verify FAILED: %d differences, first @ 0x%08x' % (len(diff), address + diff[0])) - for d in diff: - flash_byte = flash[d] - image_byte = image[d] - if PYTHON2: - flash_byte = ord(flash_byte) - image_byte = ord(image_byte) - print(' %08x %02x %02x' % (address + d, flash_byte, image_byte)) - if differences: - raise FatalError("Verify failed.") - - -def read_flash_status(esp, args): - print('Status value: 0x%04x' % esp.read_status(args.bytes)) - - -def write_flash_status(esp, args): - fmt = "0x%%0%dx" % (args.bytes * 2) - args.value = args.value & ((1 << (args.bytes * 8)) - 1) - print(('Initial flash status: ' + fmt) % esp.read_status(args.bytes)) - print(('Setting flash status: ' + fmt) % args.value) - esp.write_status(args.value, args.bytes, args.non_volatile) - print(('After flash status: ' + fmt) % esp.read_status(args.bytes)) - - -def get_security_info(esp, args): - (flags, flash_crypt_cnt, key_purposes) = esp.get_security_info() - # TODO: better display - print('Flags: 0x%08x (%s)' % (flags, bin(flags))) - print('Flash_Crypt_Cnt: 0x%x' % flash_crypt_cnt) - print('Key_Purposes: %s' % (key_purposes,)) - - -def merge_bin(args): - chip_class = _chip_to_rom_loader(args.chip) - - # sort the files by offset. The AddrFilenamePairAction has already checked for overlap - input_files = sorted(args.addr_filename, key=lambda x: x[0]) - if not input_files: - raise FatalError("No input files specified") - first_addr = input_files[0][0] - if first_addr < args.target_offset: - raise FatalError("Output file target offset is 0x%x. Input file offset 0x%x is before this." % (args.target_offset, first_addr)) - - if args.format != 'raw': - raise FatalError("This version of esptool only supports the 'raw' output format") - - with open(args.output, 'wb') as of: - def pad_to(flash_offs): - # account for output file offset if there is any - of.write(b'\xFF' * (flash_offs - args.target_offset - of.tell())) - for addr, argfile in input_files: - pad_to(addr) - image = argfile.read() - image = _update_image_flash_params(chip_class, addr, args, image) - of.write(image) - if args.fill_flash_size: - pad_to(flash_size_bytes(args.fill_flash_size)) - print("Wrote 0x%x bytes to file %s, ready to flash to offset 0x%x" % (of.tell(), args.output, args.target_offset)) - - -def version(args): - print(__version__) - -# -# End of operations functions -# - - -def main(argv=None, esp=None): - """ - Main function for esptool - - argv - Optional override for default arguments parsing (that uses sys.argv), can be a list of custom arguments - as strings. Arguments and their values need to be added as individual items to the list e.g. "-b 115200" thus - becomes ['-b', '115200']. - - esp - Optional override of the connected device previously returned by get_default_connected_device() - """ - - external_esp = esp is not None - - parser = argparse.ArgumentParser(description='esptool.py v%s - ESP8266 ROM Bootloader Utility' % __version__, prog='esptool') - - parser.add_argument('--chip', '-c', - help='Target chip type', - type=lambda c: c.lower().replace('-', ''), # support ESP32-S2, etc. - choices=['auto', 'esp8266', 'esp32', 'esp32s2', 'esp32s3beta2', 'esp32s3beta3', 'esp32c3', 'esp32c6beta'], - default=os.environ.get('ESPTOOL_CHIP', 'auto')) - - parser.add_argument( - '--port', '-p', - help='Serial port device', - default=os.environ.get('ESPTOOL_PORT', None)) - - parser.add_argument( - '--baud', '-b', - help='Serial port baud rate used when flashing/reading', - type=arg_auto_int, - default=os.environ.get('ESPTOOL_BAUD', ESPLoader.ESP_ROM_BAUD)) - - parser.add_argument( - '--before', - help='What to do before connecting to the chip', - choices=['default_reset', 'usb_reset', 'no_reset', 'no_reset_no_sync'], - default=os.environ.get('ESPTOOL_BEFORE', 'default_reset')) - - parser.add_argument( - '--after', '-a', - help='What to do after esptool.py is finished', - choices=['hard_reset', 'soft_reset', 'no_reset', 'no_reset_stub'], - default=os.environ.get('ESPTOOL_AFTER', 'hard_reset')) - - parser.add_argument( - '--no-stub', - help="Disable launching the flasher stub, only talk to ROM bootloader. Some features will not be available.", - action='store_true') - - parser.add_argument( - '--trace', '-t', - help="Enable trace-level output of esptool.py interactions.", - action='store_true') - - parser.add_argument( - '--override-vddsdio', - help="Override ESP32 VDDSDIO internal voltage regulator (use with care)", - choices=ESP32ROM.OVERRIDE_VDDSDIO_CHOICES, - nargs='?') - - parser.add_argument( - '--connect-attempts', - help=('Number of attempts to connect, negative or 0 for infinite. ' - 'Default: %d.' % DEFAULT_CONNECT_ATTEMPTS), - type=int, - default=os.environ.get('ESPTOOL_CONNECT_ATTEMPTS', DEFAULT_CONNECT_ATTEMPTS)) - - subparsers = parser.add_subparsers( - dest='operation', - help='Run esptool {command} -h for additional help') - - def add_spi_connection_arg(parent): - parent.add_argument('--spi-connection', '-sc', help='ESP32-only argument. Override default SPI Flash connection. ' - 'Value can be SPI, HSPI or a comma-separated list of 5 I/O numbers to use for SPI flash (CLK,Q,D,HD,CS).', - action=SpiConnectionAction) - - parser_load_ram = subparsers.add_parser( - 'load_ram', - help='Download an image to RAM and execute') - parser_load_ram.add_argument('filename', help='Firmware image') - - parser_dump_mem = subparsers.add_parser( - 'dump_mem', - help='Dump arbitrary memory to disk') - parser_dump_mem.add_argument('address', help='Base address', type=arg_auto_int) - parser_dump_mem.add_argument('size', help='Size of region to dump', type=arg_auto_int) - parser_dump_mem.add_argument('filename', help='Name of binary dump') - - parser_read_mem = subparsers.add_parser( - 'read_mem', - help='Read arbitrary memory location') - parser_read_mem.add_argument('address', help='Address to read', type=arg_auto_int) - - parser_write_mem = subparsers.add_parser( - 'write_mem', - help='Read-modify-write to arbitrary memory location') - parser_write_mem.add_argument('address', help='Address to write', type=arg_auto_int) - parser_write_mem.add_argument('value', help='Value', type=arg_auto_int) - parser_write_mem.add_argument('mask', help='Mask of bits to write', type=arg_auto_int, nargs='?', default='0xFFFFFFFF') - - def add_spi_flash_subparsers(parent, allow_keep, auto_detect): - """ Add common parser arguments for SPI flash properties """ - extra_keep_args = ['keep'] if allow_keep else [] - - if auto_detect and allow_keep: - extra_fs_message = ", detect, or keep" - elif auto_detect: - extra_fs_message = ", or detect" - elif allow_keep: - extra_fs_message = ", or keep" - else: - extra_fs_message = "" - - parent.add_argument('--flash_freq', '-ff', help='SPI Flash frequency', - choices=extra_keep_args + ['40m', '26m', '20m', '80m'], - default=os.environ.get('ESPTOOL_FF', 'keep' if allow_keep else '40m')) - parent.add_argument('--flash_mode', '-fm', help='SPI Flash mode', - choices=extra_keep_args + ['qio', 'qout', 'dio', 'dout'], - default=os.environ.get('ESPTOOL_FM', 'keep' if allow_keep else 'qio')) - parent.add_argument('--flash_size', '-fs', help='SPI Flash size in MegaBytes (1MB, 2MB, 4MB, 8MB, 16M)' - ' plus ESP8266-only (256KB, 512KB, 2MB-c1, 4MB-c1)' + extra_fs_message, - action=FlashSizeAction, auto_detect=auto_detect, - default=os.environ.get('ESPTOOL_FS', 'keep' if allow_keep else '1MB')) - add_spi_connection_arg(parent) - - parser_write_flash = subparsers.add_parser( - 'write_flash', - help='Write a binary blob to flash') - - parser_write_flash.add_argument('addr_filename', metavar='
', help='Address followed by binary filename, separated by space', - action=AddrFilenamePairAction) - parser_write_flash.add_argument('--erase-all', '-e', - help='Erase all regions of flash (not just write areas) before programming', - action="store_true") - - add_spi_flash_subparsers(parser_write_flash, allow_keep=True, auto_detect=True) - parser_write_flash.add_argument('--no-progress', '-p', help='Suppress progress output', action="store_true") - parser_write_flash.add_argument('--verify', help='Verify just-written data on flash ' - '(mostly superfluous, data is read back during flashing)', action='store_true') - parser_write_flash.add_argument('--encrypt', help='Apply flash encryption when writing data (required correct efuse settings)', - action='store_true') - # In order to not break backward compatibility, our list of encrypted files to flash is a new parameter - parser_write_flash.add_argument('--encrypt-files', metavar='
', - help='Files to be encrypted on the flash. Address followed by binary filename, separated by space.', - action=AddrFilenamePairAction) - parser_write_flash.add_argument('--ignore-flash-encryption-efuse-setting', help='Ignore flash encryption efuse settings ', - action='store_true') - - compress_args = parser_write_flash.add_mutually_exclusive_group(required=False) - compress_args.add_argument('--compress', '-z', help='Compress data in transfer (default unless --no-stub is specified)', - action="store_true", default=None) - compress_args.add_argument('--no-compress', '-u', help='Disable data compression during transfer (default if --no-stub is specified)', - action="store_true") - - subparsers.add_parser( - 'run', - help='Run application code in flash') - - parser_image_info = subparsers.add_parser( - 'image_info', - help='Dump headers from an application image') - parser_image_info.add_argument('filename', help='Image file to parse') - - parser_make_image = subparsers.add_parser( - 'make_image', - help='Create an application image from binary files') - parser_make_image.add_argument('output', help='Output image file') - parser_make_image.add_argument('--segfile', '-f', action='append', help='Segment input file') - parser_make_image.add_argument('--segaddr', '-a', action='append', help='Segment base address', type=arg_auto_int) - parser_make_image.add_argument('--entrypoint', '-e', help='Address of entry point', type=arg_auto_int, default=0) - - parser_elf2image = subparsers.add_parser( - 'elf2image', - help='Create an application image from ELF file') - parser_elf2image.add_argument('input', help='Input ELF file') - parser_elf2image.add_argument('--output', '-o', help='Output filename prefix (for version 1 image), or filename (for version 2 single image)', type=str) - parser_elf2image.add_argument('--version', '-e', help='Output image version', choices=['1', '2'], default='1') - parser_elf2image.add_argument('--min-rev', '-r', help='Minimum chip revision', choices=['0', '1', '2', '3'], default='0') - parser_elf2image.add_argument('--secure-pad', action='store_true', - help='Pad image so once signed it will end on a 64KB boundary. For Secure Boot v1 images only.') - parser_elf2image.add_argument('--secure-pad-v2', action='store_true', - help='Pad image to 64KB, so once signed its signature sector will start at the next 64K block. ' - 'For Secure Boot v2 images only.') - parser_elf2image.add_argument('--elf-sha256-offset', help='If set, insert SHA256 hash (32 bytes) of the input ELF file at specified offset in the binary.', - type=arg_auto_int, default=None) - parser_elf2image.add_argument('--use_segments', help='If set, ELF segments will be used instead of ELF sections to genereate the image.', - action='store_true') - - add_spi_flash_subparsers(parser_elf2image, allow_keep=False, auto_detect=False) - - subparsers.add_parser( - 'read_mac', - help='Read MAC address from OTP ROM') - - subparsers.add_parser( - 'chip_id', - help='Read Chip ID from OTP ROM') - - parser_flash_id = subparsers.add_parser( - 'flash_id', - help='Read SPI flash manufacturer and device ID') - add_spi_connection_arg(parser_flash_id) - - parser_read_status = subparsers.add_parser( - 'read_flash_status', - help='Read SPI flash status register') - - add_spi_connection_arg(parser_read_status) - parser_read_status.add_argument('--bytes', help='Number of bytes to read (1-3)', type=int, choices=[1, 2, 3], default=2) - - parser_write_status = subparsers.add_parser( - 'write_flash_status', - help='Write SPI flash status register') - - add_spi_connection_arg(parser_write_status) - parser_write_status.add_argument('--non-volatile', help='Write non-volatile bits (use with caution)', action='store_true') - parser_write_status.add_argument('--bytes', help='Number of status bytes to write (1-3)', type=int, choices=[1, 2, 3], default=2) - parser_write_status.add_argument('value', help='New value', type=arg_auto_int) - - parser_read_flash = subparsers.add_parser( - 'read_flash', - help='Read SPI flash content') - add_spi_connection_arg(parser_read_flash) - parser_read_flash.add_argument('address', help='Start address', type=arg_auto_int) - parser_read_flash.add_argument('size', help='Size of region to dump', type=arg_auto_int) - parser_read_flash.add_argument('filename', help='Name of binary dump') - parser_read_flash.add_argument('--no-progress', '-p', help='Suppress progress output', action="store_true") - - parser_verify_flash = subparsers.add_parser( - 'verify_flash', - help='Verify a binary blob against flash') - parser_verify_flash.add_argument('addr_filename', help='Address and binary file to verify there, separated by space', - action=AddrFilenamePairAction) - parser_verify_flash.add_argument('--diff', '-d', help='Show differences', - choices=['no', 'yes'], default='no') - add_spi_flash_subparsers(parser_verify_flash, allow_keep=True, auto_detect=True) - - parser_erase_flash = subparsers.add_parser( - 'erase_flash', - help='Perform Chip Erase on SPI flash') - add_spi_connection_arg(parser_erase_flash) - - parser_erase_region = subparsers.add_parser( - 'erase_region', - help='Erase a region of the flash') - add_spi_connection_arg(parser_erase_region) - parser_erase_region.add_argument('address', help='Start address (must be multiple of 4096)', type=arg_auto_int) - parser_erase_region.add_argument('size', help='Size of region to erase (must be multiple of 4096)', type=arg_auto_int) - - parser_merge_bin = subparsers.add_parser( - 'merge_bin', - help='Merge multiple raw binary files into a single file for later flashing') - - parser_merge_bin.add_argument('--output', '-o', help='Output filename', type=str, required=True) - parser_merge_bin.add_argument('--format', '-f', help='Format of the output file', choices='raw', default='raw') # for future expansion - add_spi_flash_subparsers(parser_merge_bin, allow_keep=True, auto_detect=False) - - parser_merge_bin.add_argument('--target-offset', '-t', help='Target offset where the output file will be flashed', - type=arg_auto_int, default=0) - parser_merge_bin.add_argument('--fill-flash-size', help='If set, the final binary file will be padded with FF ' - 'bytes up to this flash size.', action=FlashSizeAction) - parser_merge_bin.add_argument('addr_filename', metavar='
', - help='Address followed by binary filename, separated by space', - action=AddrFilenamePairAction) - - subparsers.add_parser( - 'version', help='Print esptool version') - - subparsers.add_parser('get_security_info', help='Get some security-related data') - - # internal sanity check - every operation matches a module function of the same name - for operation in subparsers.choices.keys(): - assert operation in globals(), "%s should be a module function" % operation - - argv = expand_file_arguments(argv or sys.argv[1:]) - - args = parser.parse_args(argv) - print('esptool.py v%s' % __version__) - - # operation function can take 1 arg (args), 2 args (esp, arg) - # or be a member function of the ESPLoader class. - - if args.operation is None: - parser.print_help() - sys.exit(1) - - # Forbid the usage of both --encrypt, which means encrypt all the given files, - # and --encrypt-files, which represents the list of files to encrypt. - # The reason is that allowing both at the same time increases the chances of - # having contradictory lists (e.g. one file not available in one of list). - if args.operation == "write_flash" and args.encrypt and args.encrypt_files is not None: - raise FatalError("Options --encrypt and --encrypt-files must not be specified at the same time.") - - operation_func = globals()[args.operation] - - if PYTHON2: - # This function is depreciated in Python3 - operation_args = inspect.getargspec(operation_func).args - else: - operation_args = inspect.getfullargspec(operation_func).args - - if operation_args[0] == 'esp': # operation function takes an ESPLoader connection object - if args.before != "no_reset_no_sync": - initial_baud = min(ESPLoader.ESP_ROM_BAUD, args.baud) # don't sync faster than the default baud rate - else: - initial_baud = args.baud - - if args.port is None: - ser_list = get_port_list() - print("Found %d serial ports" % len(ser_list)) - else: - ser_list = [args.port] - esp = esp or get_default_connected_device(ser_list, port=args.port, connect_attempts=args.connect_attempts, - initial_baud=initial_baud, chip=args.chip, trace=args.trace, - before=args.before) - if esp is None: - raise FatalError("Could not connect to an Espressif device on any of the %d available serial ports." % len(ser_list)) - - if esp.secure_download_mode: - print("Chip is %s in Secure Download Mode" % esp.CHIP_NAME) - else: - print("Chip is %s" % (esp.get_chip_description())) - print("Features: %s" % ", ".join(esp.get_chip_features())) - print("Crystal is %dMHz" % esp.get_crystal_freq()) - read_mac(esp, args) - - if not args.no_stub: - if esp.secure_download_mode: - print("WARNING: Stub loader is not supported in Secure Download Mode, setting --no-stub") - args.no_stub = True - else: - esp = esp.run_stub() - - if args.override_vddsdio: - esp.override_vddsdio(args.override_vddsdio) - - if args.baud > initial_baud: - try: - esp.change_baud(args.baud) - except NotImplementedInROMError: - print("WARNING: ROM doesn't support changing baud rate. Keeping initial baud rate %d" % initial_baud) - - # override common SPI flash parameter stuff if configured to do so - if hasattr(args, "spi_connection") and args.spi_connection is not None: - if esp.CHIP_NAME != "ESP32": - raise FatalError("Chip %s does not support --spi-connection option." % esp.CHIP_NAME) - print("Configuring SPI flash mode...") - esp.flash_spi_attach(args.spi_connection) - elif args.no_stub: - print("Enabling default SPI flash mode...") - # ROM loader doesn't enable flash unless we explicitly do it - esp.flash_spi_attach(0) - - if hasattr(args, "flash_size"): - print("Configuring flash size...") - detect_flash_size(esp, args) - if args.flash_size != 'keep': # TODO: should set this even with 'keep' - esp.flash_set_parameters(flash_size_bytes(args.flash_size)) - - try: - operation_func(esp, args) - finally: - try: # Clean up AddrFilenamePairAction files - for address, argfile in args.addr_filename: - argfile.close() - except AttributeError: - pass - - # Handle post-operation behaviour (reset or other) - if operation_func == load_ram: - # the ESP is now running the loaded image, so let it run - print('Exiting immediately.') - elif args.after == 'hard_reset': - esp.hard_reset() - elif args.after == 'soft_reset': - print('Soft resetting...') - # flash_finish will trigger a soft reset - esp.soft_reset(False) - elif args.after == 'no_reset_stub': - print('Staying in flasher stub.') - else: # args.after == 'no_reset' - print('Staying in bootloader.') - if esp.IS_STUB: - esp.soft_reset(True) # exit stub back to ROM loader - - if not external_esp: - esp._port.close() - - else: - operation_func(args) - - -def get_port_list(): - if list_ports is None: - raise FatalError("Listing all serial ports is currently not available. Please try to specify the port when " - "running esptool.py or update the pyserial package to the latest version") - return sorted(ports.device for ports in list_ports.comports()) - - -def expand_file_arguments(argv): - """ Any argument starting with "@" gets replaced with all values read from a text file. - Text file arguments can be split by newline or by space. - Values are added "as-is", as if they were specified in this order on the command line. - """ - new_args = [] - expanded = False - for arg in argv: - if arg.startswith("@"): - expanded = True - with open(arg[1:], "r") as f: - for line in f.readlines(): - new_args += shlex.split(line) - else: - new_args.append(arg) - if expanded: - print("esptool.py %s" % (" ".join(new_args[1:]))) - return new_args - return argv - - -class FlashSizeAction(argparse.Action): - """ Custom flash size parser class to support backwards compatibility with megabit size arguments. - - (At next major relase, remove deprecated sizes and this can become a 'normal' choices= argument again.) - """ - def __init__(self, option_strings, dest, nargs=1, auto_detect=False, **kwargs): - super(FlashSizeAction, self).__init__(option_strings, dest, nargs, **kwargs) - self._auto_detect = auto_detect - - def __call__(self, parser, namespace, values, option_string=None): - try: - value = { - '2m': '256KB', - '4m': '512KB', - '8m': '1MB', - '16m': '2MB', - '32m': '4MB', - '16m-c1': '2MB-c1', - '32m-c1': '4MB-c1', - }[values[0]] - print("WARNING: Flash size arguments in megabits like '%s' are deprecated." % (values[0])) - print("Please use the equivalent size '%s'." % (value)) - print("Megabit arguments may be removed in a future release.") - except KeyError: - value = values[0] - - known_sizes = dict(ESP8266ROM.FLASH_SIZES) - known_sizes.update(ESP32ROM.FLASH_SIZES) - if self._auto_detect: - known_sizes['detect'] = 'detect' - known_sizes['keep'] = 'keep' - if value not in known_sizes: - raise argparse.ArgumentError(self, '%s is not a known flash size. Known sizes: %s' % (value, ", ".join(known_sizes.keys()))) - setattr(namespace, self.dest, value) - - -class SpiConnectionAction(argparse.Action): - """ Custom action to parse 'spi connection' override. Values are SPI, HSPI, or a sequence of 5 pin numbers separated by commas. - """ - def __call__(self, parser, namespace, value, option_string=None): - if value.upper() == "SPI": - value = 0 - elif value.upper() == "HSPI": - value = 1 - elif "," in value: - values = value.split(",") - if len(values) != 5: - raise argparse.ArgumentError(self, '%s is not a valid list of comma-separate pin numbers. Must be 5 numbers - CLK,Q,D,HD,CS.' % value) - try: - values = tuple(int(v, 0) for v in values) - except ValueError: - raise argparse.ArgumentError(self, '%s is not a valid argument. All pins must be numeric values' % values) - if any([v for v in values if v > 33 or v < 0]): - raise argparse.ArgumentError(self, 'Pin numbers must be in the range 0-33.') - # encode the pin numbers as a 32-bit integer with packed 6-bit values, the same way ESP32 ROM takes them - # TODO: make this less ESP32 ROM specific somehow... - clk, q, d, hd, cs = values - value = (hd << 24) | (cs << 18) | (d << 12) | (q << 6) | clk - else: - raise argparse.ArgumentError(self, '%s is not a valid spi-connection value. ' - 'Values are SPI, HSPI, or a sequence of 5 pin numbers CLK,Q,D,HD,CS).' % value) - setattr(namespace, self.dest, value) - - -class AddrFilenamePairAction(argparse.Action): - """ Custom parser class for the address/filename pairs passed as arguments """ - def __init__(self, option_strings, dest, nargs='+', **kwargs): - super(AddrFilenamePairAction, self).__init__(option_strings, dest, nargs, **kwargs) - - def __call__(self, parser, namespace, values, option_string=None): - # validate pair arguments - pairs = [] - for i in range(0, len(values), 2): - try: - address = int(values[i], 0) - except ValueError: - raise argparse.ArgumentError(self, 'Address "%s" must be a number' % values[i]) - try: - argfile = open(values[i + 1], 'rb') - except IOError as e: - raise argparse.ArgumentError(self, e) - except IndexError: - raise argparse.ArgumentError(self, 'Must be pairs of an address and the binary filename to write there') - pairs.append((address, argfile)) - - # Sort the addresses and check for overlapping - end = 0 - for address, argfile in sorted(pairs, key=lambda x: x[0]): - argfile.seek(0, 2) # seek to end - size = argfile.tell() - argfile.seek(0) - sector_start = address & ~(ESPLoader.FLASH_SECTOR_SIZE - 1) - sector_end = ((address + size + ESPLoader.FLASH_SECTOR_SIZE - 1) & ~(ESPLoader.FLASH_SECTOR_SIZE - 1)) - 1 - if sector_start < end: - message = 'Detected overlap at address: 0x%x for file: %s' % (address, argfile.name) - raise argparse.ArgumentError(self, message) - end = sector_end - setattr(namespace, self.dest, pairs) - - -# Binary stub code (see flasher_stub dir for source & details) -ESP8266ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" -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\ -"""))) -ESP32ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" -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\ -"""))) -ESP32S2ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" -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\ -"""))) -ESP32S3BETA2ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" -eNqVW+t23LYRfpXVyrrZyjkAuUsCalyv4nQt59JItizLjnJqEiRjt64ry5tqrSh99mJuAMhdNemPlUkQBGYGM9/c6F93Fu1ysXMwqnculsr4n4Lfm4uldskNXfBNNXlwsXT1Iz8nDheH8M/GxbKr/K/zE9QIRmDJ\ -zD/rbG941/+ZjPylnfif36rN/Ejhf9N0N3hxSi8a7f8teot4UmB5v4IxRH0FY2rhl1MJO/W4Ayr8aOmnwhoTWAeI1b0FLU3TjR8NNJziozenKZ+eZnizGZDjyfB7G7hS986O6CnOrP7IzP6+8NsfhQMYrRwFcCjk\ -tCAlJ1zVtJ5yJIS4KzOIJNWJXO2APJu9pYs4ghI++7zKh1/x1o9mwMpYwZnCiazyAr8Z0dsKsX6ePw5bRVLaJpGaG5JlBwz1qRruaflf1FV4D+Ttqkci+DjTTNL72UyujmAvfgc0lVcTqddApIHNR6J3/hyajrf3\ -rLUVqXSqvTa5DgprmRuTCr3O9hJty/l1FntvpgW1NZFt+IGWa3/KzjIXPBZectk5v+GptHVqV9nT4fknG+A5VZEa2UykitclHNecJ08i5y3bcwXXhscrYj5ShuZRJ6/iel4fNKwJOqzULS0AT7RfoNVzeClVuFTx\ -P6RytReLsEx//EP/rQUR3SSEoso7eJIIhwXZ18B9Ftl0tghXBE16ijcFiQdnu2/JYIEVk9hnRDK/nCoJJfWdb5nhW5enjyMS4FT/fgNET2kdsDiQhLGH8EfDuW1YeoaAoWFjhhPTgGfY550reLV4V7074XPuhrv3\ -JvY1J056nb8eH39NCtoCd/7M65KpXsHj/yW0Nl8lAB1ZwT/X82Uroqn6oiGObU9IfkplT0BIKK4NEhLMbobCco5JNPy0TxqxDMxHtl1zF9sJWK0ZU5rBUOCu6+IPJ0zEgVhm1Ovf23BFiBY81XiTURzIykDXqymR\ -LphdVwyphmUIPkQoB5Os4N4Dvck22ekwbmlGk4rJNsqDnMlHo0VLYQCiA+AoW0BtNhk6kB9vQqZm9SzBZh8QT7BmV/ubGpHTr5EDRd+Jqsz4CeN2T23qO9QmmxMTQcy4XPqmWafxILQ5QIlgd4OUn42TkTaBQT30\ -qohDRnf/p/9DVMLtzYj00uZfhHMakfwdbvAYbjbv7+5tRkVrJQ6bzIqKw68GaTufHOcMHRakWp+Dur5+fnxxcUghFUloS2Krt163q4LeUJO3936mI4UoAV3kZOj9l7Cgv3Il3KmXoBf+T138kw9TkUamwrcr1n55\ -vGDlQoQ4fgfs+r3rnAUH5uWvazZnxCXGVLRaMBBXQBBTZ6NLP6EcPfPkss+yaOkHL4gJ0tXj88MERJIIFvTdWRB6VsXjA7VFW+IzR28CXropyBzgWty7wcgAbEb/HWh5Q7LwkjHANdCaA4db0TYHkPjrqnqaQZR9\ -l0wQtCar74eQxbiDMd26yd7pLhzgwXgP/tmdwEJOZbx0H8kuWXQVRmX9uBYjL69cDanhMlyRQm7B6QBJSdCq0aBHQFU+Yi+44mK+R2tEwyQ9r8WTa1aYLJ6aoBR5vxbivyRyy6LhyvUgrK/warQSSdS8X8Viuctl\ -VuoeIOKqB8AD7oIjPIzhmBENTYDVZkcwAZhyhxLTZ2lCgiN6z2DsA4lYNb6fqy9FobO9c3vUSlDxBWISapYc+3RI4oghaTILxDFBgDpVE90DxpOa5A7PAezXhz3JHDdnEBh6xHQRXhxBL4m01y7Y8Jw1ak6MERsH\ -Gemd0vF9CR51PeagFinn3bvJ3f47Zs3n6Q2Q9G9QjBlFDhDLeEGC6Gh4do3/FBsbRAFAs1Z3aTwbenBeJ2kC8hqR67E7INkhBEwYAjTkMtlIvCuBATDoxB0lkggnoGMyvRYsOnd/uM8qF207XOGb8QwR7myT0kF0\ -lyXHBcnbgHaV+GS7ho4klUDPXsd38PAKUnG0S7XCICq8W3ekoudVoudAhCvvUil58316+G/Tm8v0ZpneLNKboyNWEEgtMDSYzE6P2Po2KkHIfbBbDbBh1CtgrhaxPWOsUM+6JINYIfyjAOATDsuxVPOI+U4ie7dG\ -8BHjPhLwdrKXuSEoCkFN8QX67E8MFnobJl8uekpaRuVB9awuFqKuqKIMyHiCWWJZgjya3r9TTa17Hb25yTBE+Y3ubBKpoW9sIjPAg6lIzSrEem98V2lUhPh8CQIcMTngWCQXwtiI1VP4ZL8GBbeC/VqthqTfjG9e\ -gBymhMZIF8rgapso0+znqEJ2+QtraFkSlgNGyKkE34EB+vw9seya6GaRTVT2vXi6bkWeZ54IYKylSNsybiBqFu+Jpromrg3HRqB2IA0NMV3xJGKCm6Y6I4zckKMJdQpdX88FgW4JAmxJsaVToLjKkERK0eFrkppt\ -6dQsa07N+pwCip0OOWyj1OBl065DljbqC0Z8dbBGPEvzewCBdacfjg6fQp1TSq5Y7dTljNJFo+AqZrVvzIzDqV7uN0gUV8YmrAw9JzVbnRgXXfMQi2TinfHGH+FOsuIkqSF3qk9iwqBJVqjTCl5nvkdwM4Rx7w8Y\ -/rD+qfHmkjJpHAa45+FlyGuIQr45oqdv6YTxKURBHUUwFxfkVnEcqgI8vn+Kbvg4hDqgCz8S7HpKNdfBmyoUYT+MMVn+KpoXoqIbKsCGZBqa0oGAZLjmVzX8/XZcZmwt05hFUpEFQFKPtiRs35Lnhz+CMW7gX7Ao\ -HLx/L8SnTwkc62DMnhBAJzBg1C38vbYRQ5sVmw/w/4yTL92r9ede74/HMSTekwj4l/UuoynWCMdlKz2EjGIliObB+FwOrlrDH5wsdR1IDKAcYHLmhqNNtyb6BunXHIU2+kveMhmsk+s25Jp00rxstYb62vLRYsLb\ -3v8a/HD+IFZNko4Jll4+9bhdJvlv3pPjBPorGt1dycridnEMoCk7uVjsnWxz4g5+xUnRNKdjRhVDKmWJnDyuYmdvMIfDUk0+6h7DtQHLTgjO7cm8Dy7abcLWXMpRsSMBxRtSVQL5diog/0e2Z0Rm/YsV55IQVWf9\ -UAwXV9H/wDhEmVUyjnNwwblUamJaEUVKJTz0KoYJK4hynFvas+6tBEdsO7BBsRx1PB8CZ1vIOKCY6XZlqVsWXQEjqjvi6lOF/rE7jytjmVHN78Vikx3QBfugHyO6MPpbxkcNLSnNG9hcyu4F+aAI652sOudyJm9Z\ -97ack0CJfqjRiwoMCFNFG6i633FCjFRdhaLbgmkW4OnsT5y29iT/KqFYxYqdXtnxJqhjbzh2hzBNqmOXAxPDbCjRFz1xJjMVzdTmx2RQy+A7TpcGSWuYmA/5+ldSklCE5P0J/4AVWCQtzyLNRc3aPxo8o3CPrD2o\ -UI7YcSIBXykxNJwiNrU6GtmSWwjl2t5FUpYzfYNWdd5Sya3rzrhv1cz/NIgY4LCmZ88l0FuX9qvngNjzPw9jjW0GcoFDxjByvecXO6bEGv4odqfqyRlX4NstaGAjaTdkpS45f3I/e/DA+/fKHb5kWKG848NKBXiX\ -1lhJu3OGU5B0p7iEqNVcHsG7P6wJAIsYOmNo3vSjOCfoDxkXjh9L++3sFWXILmlzQPyurXAD06YvyXZBZsSQS4qrVVqW18T2kL2hOludSW8MlBn6H3T3gudmwu+j9fzahN/GhfxqB6spkBdOD6bzxEQ1UJ2ej+WO\ -xLrzATjpcxCOIBwK05kPKxp6Pb1tFfXGten5lBRIOnx4yYFpMEPRWnYcpkkMGQ+M1zQ16zhaEKxXOKaXzqbjenaNJrhDpFd0lbFvo97yM9oSm1dTztMnif21vIvggsliBKbQEZonQzjJyojbpvgPN07NS/wYY4e8\ -qTXR3YOmVSwIb8w3a2DAIEf75glprEL61+5rhdZfpP9R9ja3CBkZlW5bvUX+lgLqhj7YAKfX6j2Hxvng1SaXa7NEVIbz1BCMAjuNuKEsqkMgDjsdT7kPbc5xl52MyvOdfCfhEuB3HLhgoppLQ1nqmFAwMYMSBjYe\ -SzzoD1TyAcwE10/lZlXuUUHaStQylQtuzVP7AS9G8qgQBZQYCxEzD89LDr/QJ/nwK7AifGjmIyc+THEq2YBKhIaKla8T2hEnsih8eWLO8BMAhDqTPeb3zHMg+Ps0xIQXExMCXUt9nSueyrs/QzZwDWf5EtaGUhZ2\ -KVFtjJR22jWdvPYnEHhUYVj2qzVa3G4/ll0fha+TdmJfJWjScJOCXZjiar1RkxUffsbRWqtODwkibPkQ1rgl1E/LTUnZC1uAlo4JgrGGgR3d0oAO/ABDk+BxntmjD6zAgzShsYEjAE/+vaukxdHiLQbkGHinfRR4\ -rUr7IZWFyRqwB9TVYVD1MqfNVWgKdo8jXhKmrAgmjYN7eOaKDdZErJq1sMJLMkyuOeZQrOvY+VhSUOnzqyQShgioqmM6DLaHdNbBwikjURmL0eZs4RlbhVjfMHBzxRkTMz1h4G6TpyR4/xBfKx9+x8LUk358h1R1\ -VMRpDPuzQfEHCm0USb77lo7GYpcZCedUCrh3PLdy4HxhRHNI33Dh01oiEzv8NWEYFXA/p7DfcQ+rlApaS+1yTO/YWKPFc0GAPLfmJhVraIV2txdO6INIMXzthx1W6JlMb7GzgePtbczvjJSmM9AKd0Y1uzofZHhd\ -SI0PgXs9iRr4K6ZDbugst/OYWLkiRga6E4za5Z5UmIi+q5TSBCtUOkUVD2Mo6VRMJnu9rkxI/SEZnMjgJx/n1K7/KjkiQD19DU/OEfssYR92EVdgQXP8ZLFQ0/ZzfnGmaoHZdPAgcKEwqTCSy1MB/pjM0yEa3qxC\ -UMVfbeBnXUTjKlYGU2SOGop8qPq/ww2mGBehvWOlOeNEqUrS8zrUHHoFByrwoLOs6xFNczVZOqY6JZ1aO30Vy3QYYLd/tIjxbsjdCeZJKaRAU9dLkz+8QG4YCOH7MfxoLmeP7eQzDB0/NXC6/Y6DcYb2KtviQJ8r\ -3HhttjiysGTv7XR/9dtMehWD5VwEwkIw2E5sh5HyN3BxJR8IfH4BjazpwfVn+Gc5Bbk4ZbFncbomRoc4VQ7JJDK1g3NCH6Olbw8fawSyMoLpTgASusdklvohLP6RWgHYVhR9th+B4Gug7XO13BZAuo7Ne4X9KDaa\ -CsW89ReWEYr5Y5HAo46YV2kGwboJ6ko5s6PADa8NRar0AcZsTfqFWU5ttmG7j2KBJOW6Jrio7bZEhTwgJEBSVhlubcB0wO/KsWd3Iw6xcx4AYU0v48KQfoJCgJZirz0J54BVKIZA28MwxEP5PHxOwiJzHquvQMme\ -cnkzhBvw/MrJd2ggRbfkhSoJnzuOLC1/09oUe9xxwq5wQWWQyknHG78iFidT2hfzBAndl/ThGKpiwUqIlsaJXKjShxWMPZ+fiGKecKs5PLV+/b/KU487ZxwUp91H7dbAno41Q5wzWVNVCKl8z1DmYTvQ+iInx9nb\ -cPJ7OMuubzAHryUnhogWpdP/ElO+hW9ilE8hyS6bU8nRDZyzy+IXOIalLJGbwoZifpv8P4COs2CNBr89ZqNel6mnX38o+KCN9gUi3FgW7uQbjnKlsY91h53YiSYq8UX5ei7ZNmzXyGfvwmXRe3UcqSKx7eyP8H9b\ -/O3TorqC/3OhVVnmpVVl5p+0HxZXn2XQKDtRfrCpFtXgP2e46tEOP0kX8uGhndrst/8CeNOGkA==\ -"""))) -ESP32S3BETA3ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" -eNqVW3l33LYR/yqrlXXZznsAuUsCalJLcbqWczSSD1l21FeDIBm7dV1Z3jzLitLPXswFgNxVk/6xMgnimBnM/OYA/OvOsrta7uxPmp3zK2XCT8Hv9fmV9tkLPfCLm907v/LNg9AnNVeH8M/G+VXvwq8PHdQEWmDK\ -Inzr7aB5N/yZTcKjnYVfWKorQksVfvN8NRg4p4FGh3+rwSSBFJg+zGAMUe+gTS3DdCpjp5n2QEVorUNXmGMG8wCxejChpW66Da2Rhmf46fWznM9AM4xsR+QEMsLaBp7UndMj+oo93R/pOVwXfvcncQMmK1sBHAo5\ -HUjJC1cNzac8CSGtygwiSU0mVzsizxZv6CG1oIRPP6/yEWa8Ca0FsDJVsKewI6u8wO+A6O2E2NAvbId1iZSuzaTmx2TZEUNDqsZrWv4XdRXGgby9eyCCTz3NLH8/OJCnI1iLx4Cm8mwi9QaINLD4RPQu7EPb8/KB\ -tc6RSufaa7PnqLCWuTG50JtiL9O2koez2Ac9LaitSWzDD7Rch132lrngtjjIF2c8IlBpm9yuisfj/c8WwH1yiRpZTKSKzzVs14I7zxLnHduzg2fD7Y6YT5SheTTZUJwv6IOGOUGHlbqhCeCLDhN0egGDcoXLFf99\ -Lld7vozTDNvfD0ctieg2IxRV3sOXTDgsyLEGdkZg6OAdERdUq0mNFckHu/vvyGKBF5MZaIKy++FPTTCpbx1lxqMunj1MUIBdw/gWqJ7TPGByIApjD+GPho3bsPQNEUPDwownpgXXcJ9XdjC0euvenvBG9+PVBx2H\ -qpM6vSpfTY+/IQ3tgLuw6U3NVK8A8v8SWleuEoCerOKfHzizFdG4oWiIYzsQUuji7AkICcW1QUKC3u1YWN4ziYa/DkkjloH5xLZvb2M7Q6s1bUozGgre9X36YYeZeBBtoiqSHkdcjp5quskoDlQV0MfNiXLB7MYx\ -pBoWIfgQIRxM0sF7AHpTbLLTYdzSjCaOqTYqgJwpJ5NlR2EAogPgKBtAYzYZOpCdQLZpWDtrsNl7hAwwZ9+ElwaRM8xRAkXfi6Yc8BfG7YHWNLdoTbEgJqKUcbp8pFmn8CC0BUCJYHeLlJ9Os5Yug0E99qqIQ0b3\ -/6f/o92E5c2E1NKWX8R9mpD8PS7wEF427+7ubSY9a0UxKsfBV4uUnc2OS8YNCzJtzkBXXz09Pj8/pICK5LMlkdWboNiuohFq9ubOz7ShECOgg5yNff8VTBiefA1v6gVoRfjTVP/irVSkj7no7YqpXxwvWbUQHo7f\ -ArNh7aZksYFtheeGbRlBiQEVTRasw1cQwjTF5CJ0qCdPArnssSya+f5zYoI09fjsMEOQLH4FbfcWRF64tHmgtGhJvOPoS8BHtxUZAzyLczcYF4DF6H8ALa9JFkEyBrgGWkvgcCtZ5ggPf11VTjOKsW+TCSLWbHV8\ -DFiM35/Sq5/tPduFDdyf7sE/uzOYyKuCpx7C2AWLzmFMNoxqMe56Q4ZzsOR/SRm3YGeAnCxc1WjKE6ConLD7W/EtP+A0aJKh2RNykdxZWYq0Y4JP5PY6iPyymK1IJivPo4De4dNkJYZoeD3HIrnNVzp1B7BwFfpx\ -c/voAQ9TIGZEOzNItcURdACm/KFE80WeimCL3jMY9UAK5qZ3S/WlKHOxd2aPOokmvkA0Qq2SLZ+PSTxi9RdXAorPfRXzzY4BI0lNcofvAPPr452sj18wAIxdYT4JT45wl8XYaydsuc8aFSfGiI39gvRO6TRewkbd\ -TDmcRcp59X52u+NO+fJZ/gImRCaLJhBCXvHPEIvE9tbHl2pjg2gBgNbqNt1nc48O7CRPQl4hfj30+yRFBIIZA4GGfKaYiIclSABWvbikTCZxL3RKqNdCRu/vjtdZ5aLrxjN8Oz1AnDvdpJQQXWbNsUE2GmTjxC/b\ -NXRk6QR69yaNwW2sSNnRQtUKg6j6ft3misa7TOOBCF/fplwy8l2uBm/yl4v85Sp/WQ5Vp8OkCLXidbRBslxurg43nIDmfTBlDUhi1Evo3oj8njB8qCd9lk2scPBBMPERh+hYt3nAAsiifL9mBxLsfSDCe1nLXBM6\ -xQin+gJd+EfGD70NnS+WA22tkxahnrrzpegt6ipjNMqkSBKLYKRp/K36av2r5NxNgRHLb/Rms7ANXWWbmAEejCN9cwj/wQov8yAJIfsCBDhhcsDXSF6EoRLrqfDJrg6qbxW7ukaNSb+eXj8HOcwJoJEulMHlNlGm\ -2fVRueziF1bVuiZ4B7CQXYnuBKP1xTti2bfJ8yKbqPV7aXf9ijxPAxHAWEdht2UAQSCt3hFNTUNcGw6VQO1AGhpCvOpRAgc/z3VGGLmmMCkWLXTzaSFQdENYYGsKNb0CxVWGJFKLDn8iqdmOds2y5jSszzmy2PmY\ -wy5JDQabbh3EdElfMABsojXiXprfQwosQv14dPgYip5Sf8XSp64PKHU0qj7IM9zX5oCjq0EeOEoaV9pmrAwDv3Ww2jFNuuYjVszEYeNL2MKdbMZZVlDu1ZDEjEGTzdDk5TwkrxaPqSLYsZts+cXNOIrEVWIFJnlY\ -79Nj6oCkCZQ6k73Y1N+kWZxkTruxtMPlRxnXpknCX6+5Wt66WKp9P8WU+utkdwiXfqwZG5KRaEobIsThnF838Pe7aV2wGc1TrkmVGEBPPdmS8H5Lvh/+BFa6gX/B1LDx7p0Yyz4m1GyilQdCALbAslHp8PfKJnBt\ -V8Ag+oUnnKTpwYlAGQzieJrC5z2Jln9Z70vaao1wfLFy0lBQKQYif7BKX4Iz1/AHO0vxB5IIKBqYkrnhyNSvidRB+g1HrK3+kpfMGpvsuYs5Ke00T+vWUN9Y3lpMjLu734CDLu+l2kp2roIFmo8Dbq+yPLkcyHEG\ -pzAa/WDNyuJ3sQ0wqzg5X+6dbHOCDw7HS2m1pG1GFUMqZYqSXLHiKMBgrocFnXLSP4RnAyafEVzak8UQdbTfhKW54KPSuQWUeEhVCf27uaD/H1meoZr1L9Wla4JaXQyDNZxcJccE7RCHuqwd++CEC6nnpBQkiZTq\ -fOhuDBNWEeXYt7an/RuJmth2YIHqatJzfwitbSXtkFeZflemuhFsghbVH3GNyqHj7M/SzFiLVIs7qSRlR3TBOujgiC4MC6/Sp5amlCMeWFyK85UArPx6mXXBNU9eshksuSCBCrYSYIMKjAhTVReputtz8oxUXcbS\ -3JJpFuDp7d84xR1I/mVGsUp1Pb2y4nVUx0FzOkPCRKpJZyGYRBZjiT4fiDPrqainNj9ljVoa33JCNUpwY8dyzNe/s/KFIiQfdvgnzMAi6bgXaS5q1v2j0TeKA8naowqViB0nEgnWElzDLuLRV08tW/IKMV43eMjK\ -d2Zo0KopOyrN9f0pn261iz+NQgnYrPnpU4kA15UI1FNA7MWfx0HINgO5wCFjGLnes/MdU2Ohf5LOsJrZKZfpuy0MHYC0a7JSn+0/uZ89+HAc+vnDFwwrlJC8X6kT79IcK4l5yXAKku4Vlxq1WsgnGPvjmsiwSjE1\ -xuztMLzzgv6QimH7sRzSnb6kHNpnZyEQ2Gsr3EC3+QuyXZAZMeSzIqzLi/ea2B6zN1Znqws5QQNlhkMSenvOfQvh98F6fm3Gb+tj4rWDlRdIGOf780VmohqozvfH8rnFuv0BOBlyELcgbgrTWY5rHno9vZ1LeuO7\ -fH9qyiE8frzgJD6aoWgtOw7TZoaMG8ZzmoZ1HC0I5qs800t703Pdu0ET3CHSHT0V7NvoBPoJLYknXHNO4GeZ/XW8iuCCKVIEptARmkdjOCnqhNum+g8fr5oXGCrvkDe1Jrl70DTHggjGfL0GBgxydN88Io1VSP/a\ -da3Q+oucktSDxS1CRkFl3k5vkb+lgLqlax3g9Dq959E4773c5NJukYnKcAIbg1FgpxU3VCR1iMThichjPq02Z7jKTkFl/F5uU/gM+D0HLpjBlnJcJzVPqKSYUW0DTydr3Oj3dGAPmAmun0rTqt6j4rWVqGUuD3yA\ -T8cU+DCRT5UooMRYiJhl/F5z+IU+KYRfkRXhQzMfJfFhqmeSDahMaKhY5TqhHXGGi8KXL+YULwog1JnioZy4PwWCf8hDTBiYmRDoWu7rfPVYxv4M2cAn2MsXMDfUuPAsE9XGSM2nW3Pe1/0NBJ5UGKb9eo0Wd9sP\ -ZdUH8Q7TTjp/iZo0XqRiF6a4sm/UbMWHn3K01qlnhwQRtv4K5rgh1M/rUFk9DA8KLW0TBGMtAzu6pREdeE1Dk+Cxn9mjDBk8SBsPQbAF4CmMu8yOQzp8xYAcA+/8zAWGufzsxFnorAF7QF09BlUvSlpcxcPD/mHC\ -S8KUFcHkcfAAz3y1wZqI5bQOZnhBhsnFyBKqeD07H0sKKpcBVBYJQwTkmpQOg+0hnU20cMpIVMFitCVbeMFWIdY3Dtx8dcrEzE8YuLvsq+XqwhyH1V99z8IEi8njO6Sqp+pOa9ifjapCUIGjSPLtd7Q1Fs+ikXBO\ -pYB7z32dB+cLLZpD+pYrotYSmXgPoCEMo8ru5xz2ez7vqqW01vGtmZKPnHJODRcEyHNrPtBiDXVod3txh96LFOOdQDyJhbuG8xs8+8D27ibld0Zq1gVohT+lYl5TjjK8PqbGh8C9niUN/BXTIT92lttlSqx8lSID\ -3QtG7fL5VeyIvquW0gQrVN5FVV+lUNKrlEwOzsUKIfXHrHEmjR9DnNP44VByRIB6+hN8OUPss4R9eOK4Agua4yeLhZpumPOLM1VLzKajB4EHhUmFkVyeKvPHZJ4e0fB6FYIc3+3Ay19E4ypWRlNkjlqKfOhYYIeP\ -oFJchPaOJeiCEyWXpedNrDkMCg5U4EFn2TQT6uYbsnRMdWratW7+MpXpMMDu/mgR4+2YuxPMk3JIgQPgIE2+oIHcMBDqOV+tK9lje7muodOVBK+77zkYZ2h3xRYH+lz6xmezxZGFJXvv5vdXb3DSUAyWSxEIC8Hg\ -gWM3jpS/hYdLuUjw+Tkcdc33P32Gf67mIBevLB5mPFsTo0OcKptkMpna0T6hj9Fyxg+XOiJZBcF0LwAJJ81klvormPwDnRHgwaPos/0ABH8C2j67q20BpE/poF/hQRUbjUMxb/2FZYRi/lBl8KgT5jnNINi0UV0p\ -Z/YUuOGzoUiVLmocrEm/MMtpzDYs90EskKTcNAQXjd2WqJAbhARIypzhMw/oDvjtPHt2P+EQu+QGENb8Ik0M6ScoBGgpnstn4RywCsUQOA8xDPFwbh2vnbDIfMDqS1Cyx1zejOEGfL/0clsNpOiveCIn4XPPkaXl\ -m69ttcdHUXhuXFEZxHk5E8e7xuJkavt8kSGh/5Kul6EqVqyEaGmcyMUqfZzB2LPFiSjmCR9Gx682zP9X+Rpw55SD4vxYUvs1sKdTzRD7zNZUFWIqPzCURVwOtL4qyXEOFpz9Hs6y6xv1wWfJiSGiRekMr2vKjfk2\ -RfkUkuyyOdUc3cA++yLd1jGVnPHEaykw4ib73wI9Z8EaDX57yka9LlPPb4oouPZG6wIRfioT93Lfo145+se6w046oiYqcaDcscuWjcu1cjleuKwGQ6eJKhLbzv0J/p+Mv39cukv4nxla1XVZW1UX4Uv3fnn5WRqN\ -sjMVGlu3dKP/wuHdgx3+kk8UwkM7t8Vv/wXDV5Qe\ -"""))) -ESP32C3ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" -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\ -"""))) -ESP32C6BETAROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" -eNrFWmt700YW/itpEpKWvc3Iug0tiU3tOA6XZftAKTymRRpJWWjrXYKB0F3/9533XCQ5iQ3f9oMTezSaOXPOe95zkf5zuKwvl4d3dsrD+aWx80sbPmUWvuNjXp3NL30evg3ml4WbX+Y0ehAGi0fhT/pD+BOHoTT8\ -r3fDHy93x3T3/LKpnme0xnH4Yx6E9QfLMIrLzfxiflmb8CsaluO9sEG+zzKU0Wx+WUXje7PdcK9JirBxFD5hbp4Pw5/B/HC+wA5Y731YIaH1aJbLVmE0bFAHma0LX5pwxQfhyyabH5Jc/70f5lVhfsn3Nk2Wbbig\ -W49YNXTS8KmqjE7K60H4VHQWtcoLn6A6Fz5+gP/fr0SW/BnOOITwk24fE/7nbsQquHlTd7ySrXmHYU8G3an7bf33MACvHva7vjQsFmdjqCfYxw143WDRYCPn7+vOsHe4qQg3++ZlZlmddsB25uk2Pw2bVNOwth0H\ -FFThzlrVEI9xDdbjXcqYTWTMQ+ybHEPYcV9xltd32J/GwpJZOEad8gQCWcqYwG/nGEs6zouoLh6zCbAl/pfZQauye2SUUZDbWV7CJFNMxXEKwAmyMcyxSV7LOjnpdDeczEXjNdWKavQoHrochb82gKpOeDS3rJ4c\ -mzmsb7+CBwBJhrGAaY17doRJ3dJBOs9+qNrN3QlGgnorGWmqZ2x4aAP6tpksaTvZSgP/81YWGzDUIF9TY9GpOk0PGOEWnCMVjzOCOdtN0RM3ttsd9qhoNxKMtgqnqbCVv74EBPudNy6zCWFT/AAz8mQCuDEw/3Hs\ -Z3KyFLOnPSTjVF5VNOg5qNBZhdn40WOQe5gy4vlWJCfdGVxJcSXag8Fl6TwFQPZ4k0KP31/c2fNOHNo+7jaBLoOuG6UNrElMqTZS+Qusomg0Vw6jkKeTb9YB/RYiMTG2bt1J6IuPJAis9AcOy1t9xVcCJtmIRnAA\ -I16lF1my6i9Zi8CVIJPDDUWNGzmIwgy+TjXCUCR5xGfo0w8AB7YQUBYAcLhUOsJs0Ng3Y7mCUOIO2JXt4DljnOKIP/VPwzJGf479LzgrTIM4cpeY93R4IFqs2S2daBd0AJolnzZMJLW/Ep5wYJ/oIYfkKXO6dz5k\ -fYXl5iTzHIw54GO4bDhmAqJIKaFRwmQ/Ol4kNfuNBvJeXH7/rYRlXx//pqETt49Z8E0HocOUQlQ2YYYooymfgqCb7RyxSWr7bT/6TBWbwxv2g/fnjXArvD6abBJiJ3wBE4XQxuSoOYNQY08FI01g1AuIzJks9pi8\ -FYU9/1iybACr7/Maseb5ijkAqQ2Ch5crgEZe3WHUAVf4X1qm9hyTyz2+wLw4QiBxTHWsicdMlTdG5i7or8cSQMpIGIl0ISXzTTbEWDXoZnua3e1rhVX4vvMejUVX4wWRd97Fia1b2m5L5n9ot8y61EMSCz7qbXFa\ -8esykbAP6VwsYX9TxDXIpIzMooAQTxDWiGrevwVx2NU7XHqNnZMDMWY4QlXzQSvJWqpCJMpWAEbBmYEQ05KVkWtmIMJC8KY4YkGZZHYZnMpFNyvq1cHVlU3yplueGKCYjpjKODk57wVb+kBKJ2pmOTbI50S+9dt7\ -u8Geye52YivMnszwdoIvcDJ8M4nvtFUWw9uwUzGeLwJJNenz5gW0+2LWuiqI1Pl3mKRWe33G5Aor5MmjzwmiCLvCsHW1tszu54kaQRiRxjkl3f3RayYvxi+kS58ytyqiNdO50XkIW/bt29W7H0EEL8BGPwNSpVAM\ -xcZd0OABfPMMZ0Fga1BRVSgSBm+YK2qBaCFmVRWvyRH9lWuWEqotUyYgEISPPnP0HOo59xj9yIvmSaWQyrWU2w753c9bymarPzavSYj0d5hUCYb++uSw8QXfQtSr2ZCdHUnNllxNtbIrnkSJhJV0sEIgwj5NHLJN\ -U/q37GXEJBC5SDKN88BqMoJ/JdkjBlfxJeDSqIdVHCpm5/yH14yXUnnVoTZoIpG7vfKnEds9JxkHH6U0ppE1GX78PMD9up885BzsSxWGMObZDg3gYmeIfGGRAnOcXmk+L0cZrSSnglKSmH/UBd9ZuyP/9w7UCDV1\ -dFeK8M2y0iAh4QfOxheci5OsCXhVi5E2c0UBp5mrtVMehEaRZ1Bz4v9Ns4Se/GnSce37b9/ever1WzImKoLXKfnx7SeA4ZP54jnEn70B6xRnZ/dx8f7tB7j4YL54CL5++bDXdCmzZ6PZ87edAZD24ayB+I/ER4Rv\ -CwTSWJL/iGm1MjzHS6D1EmjpWsGyA+mUQkac6aIwAXcBG94seMI2Nyu8pn35MZhsrQbS/ElzGYyVhecV8LHRLRjDTv7JyWHOek4YN21dtl5TfXV+RIDbkTxYa7EkH2oPDd/6QoBujT3RUlZt2bbIbDKh5SQkbkga\ -wo9ei24+n17HRUlLqg7oiBmljnOWfiNummIrkS9Ey9w9QYlEJynYmqLjRduck44CpIO/kd4ASEuDCX/hfshkO3UU1CM5z+7LsdDesNl6eozMOE+aUaOG6JpJlKIEUZcIx/ibV79mD9Ssm9RcSARwpcYoRm/bU8jk\ -3DmfD4GbxuNuPBROXAwvpBQmtrRsJkyGUrgZNl9iKhVZcr0YrM/Hp0q/oRaQwNfED+mn9ttQRUIphZ18zSoprTJuFney+2xfQGabXejwJRdkLVxBIKjOmlI3+rJ6Y4fpb73Y2Khi16tLKMGttycSLpovBwjVmbbK\ -tL9h+TuRfaJnjLtipj1wIR2NykkJqhEArQ7sRT7gkb/674+g8Nec+nmn9mF7zHh43YjJiRjHZ7c9XDVlK9i2TRekAjhwIQDkopuP1pr/Va7E8+WJV7qhxvisCzTUpmg0qFM3fZbqT8fXyuTotRNxzVUp7xJmyLL3\ -/O8gsJgU4SUUZz9A4g5YT+YXqiThk3bN5CZQWeE3jhKi80TyjkoykuTud9KnJPKl48bSNEICmJzIltJJgl08jGWSiefWARFvLeaq8tO9fWVPlCm1cFIpJXrBxqkp+Yb/NKJsRNLqRFRSoT1cWN1cmmFN3m/cRNi/\ -kv1TdUyMjZVstoAYqqJJPvnOWbCkeis/u7hIV2JjGN3YsdQmDZ7CKF6ofiiqk84sJCaCJ9kRsaaR7nXlEd3zBwjOpzjTKYhsjI78eHtczSvU5Q5czb0XKdMtVdYPVWeMLF8peEd0wbWo/k0uwBityGLrhjqcAi8j\ -6QUZsQ/5trNc8bMEz7ZsBC/xTPEpIR11Uz6YMt97OVDGxV7L3U56DNRfyPb3GEym7NrERUwWuCXsQ23PHWqDo89TUVD1TNgtkgbtMwiaV4iIaXfJts0U5/kqoFqQ3YzvwSqZ3FYnmq3EUWgNWZu6/vd0cNiuKk8D\ -6vyKEtIuDwkuM+rKVzmnuGDdT5h6ys/ttDsEARRNTpi9Fkdkpn3Sp4SxhuFZP87wtUqQF127spQq4ApZU6Zes7LruJPQ6qMWy/AwyXgiXWGBGDXtWnQPV4NYIS08TmqoRN24wVAT9pzVrOpwlP5YqktuRX8BIEfo\ -KblqxQ2+plppmVANeajOxd8L8hppEt4s2Ks/AKVqwaCsJQUtBx9vMZNS3ZKNXnd7+IWUYWb1qRv9BD7cuhMC7kcJEO6M92u0Q7fttsJ80DBI2t8fMaFTjARCYKwyacl6LFWXAJuWFPL5HUlO0ru1mx13s50/lWxH\ -o6n3p0JeVZKzgTjl07sLvruu27uVm2qxrC4hXXbKeZAAuJwL3qU+GomltyKP7cpMisWmTS5OxLOsJPWZdIJr6UBWyuTJhJ+jc8CbyggJLMGEimGcq82IbzbcxV19NvdR6EJKZgQKCMDO8XLai8dxh20Sp6le4p4P\ -LFPVnI637fgzPRX5CZCOhyt4o6Qn+izHGSEirxzecKaUN5pb5uLqoSwfsd9W+ozRSnOGnhMPbp1Rz1y6v1xGIHdv1qLCK2nBad8O7wUQ3xKp1/2UePCxryPpyldEWF/rPVKbEcmVamQqIsStmnzouUlQ91Yz+owz\ -R85RcfaxL71/I05fXgGNNY+6nNUpBVLGo5F+HR+iU8qRB/udyFYecrU9CCLCsP9CsiD6j/jd9uXhb+WaItEw8J+kAdaPurza8xuuOKP32NlUwhzVmd6Xgsh4Mu0wWWf8CHNzXr6GUXs9T6fKpJIUgV6YmN0QyoMD\ -XnTvBPiBH+8gs/U/8VsKWjH+m7TERqpdDxqN+G/adXD4qAVLxLdLCqClkdfxLgJLFMU6dMr4epqMcBi/5GxuQ5R60b17wyccUZ71jgqCXeZ9wmCE4+G9ii6YG+J0Kw0l1tOSgvyP3YNO1R8HEP+YELqQ1Nj8ix4d\ -ctpOCye6cJ52TQw12Cbu8OYpOe/6o09RfuP6gswk9BbyYB0aX5+hiuDe4+GIiutvtgtgzBMJpaGqqnvdkuRtT79qILORBl9N1l6Ogsvfv/rOidQ2pJ5nwmmWkPyeYVzK+0UK65IL7kMhEtA+hugBadZ1+W4Ax0l3\ -ktwcbTv+PVTM/ubsny19DN77xBDiEa4isVZu9XWMD/pYVVGTslNv6ZLJazPNETf/WrxRRMW7WmHBBTcJucMfskl9/BB3O5jkVJ7yN5yMXZzeQ8vwjvQLReScHp3sTqNTeXWEIRRQOufEQZNXbeNY/QyEbJH836zI\ -HcSG6Bmd/lByDmzph2JEhHBK+So5MLWBo2cIBpQHUPGkjzgSKftN760HEYoeZ1R4H0Gfd9MbIEqliEl1Kv12PvnfABjpiDh1x4QTDQofNZRKEXHAyidiq7u9JW6sOyVJHUl5rWWm3mm2ZSg+FFDXXG1zchFvdLlc\ -3kOKJG0jtmskBhVbpRimX+ggufbWXPyQtyrTxwfolqeoZFL0y9NZg355enYCl0/vH6CiTh/gMjrm0cu61zEnKjCHf96h1yt/ebcsLvCSpTVZFlubxyZcqRfLi0/t4GAQ5WGwKpaFvo0JXAWHOpTh/irGprEz8ep/\ -TB3fMw==\ -"""))) - - -def _main(): - try: - main() - except FatalError as e: - print('\nA fatal error occurred: %s' % e) - sys.exit(2) - - -if __name__ == '__main__': - _main() diff --git a/Precompiled/rnode_firmware_latest.hex b/Precompiled/rnode_firmware_latest.hex deleted file mode 100644 index 6643208..0000000 --- a/Precompiled/rnode_firmware_latest.hex +++ /dev/null @@ -1,1555 +0,0 @@ -:100000000C9483000C94B5260C94E0260C940B27DA -:100010000C94AF000C94AF000C94AF000C94AF00A4 -:100020000C94AF000C94AF000C94AF000C94AF0094 -:100030000C94AF000C94AF000C94AF000C94AF0084 -:100040000C94AF000C94AF000C945C280C94AF009F -:100050000C94C32B0C94F92B0C94AF000C94AF00B0 -:100060000C94AF000C94AF000C94AF000C94AF0054 -:100070000C94AF000C94AF000C94AF000C94F924D6 -:100080000C94AF000C94AF000C94AF000000000182 -:100090000200090A00000000040308070000000035 -:1000A0000000000000000000000000000102040841 -:1000B0001020408001020408102040800102040842 -:1000C0001020408001020408102040800202020239 -:1000D00002020202040404040404040403030303EC -:1000E00003030303010101010101010100002200DA -:1000F000250028002B0000002100240027002A00F2 -:10010000CA05811D2D2C2092DC3911241FBECFEF92 -:10011000D0E4DEBFCDBF11E0A0E0B1E0EAE6F0E65A -:1001200000E00BBF02C007900D92AA3AB107D9F7C1 -:1001300029E3AAEAB1E001C01D92AC3DB207E1F7A4 -:1001400010E0C3E8D0E004C02197FE010E946E2EAB -:10015000C038D107C9F70E94B02C0C9433300C94EE -:100160000000CF93DF931F92CDB7DEB76983DC0128 -:10017000ED91FC910280F381E02D41E050E0BE0161 -:100180006F5F7F4F09950F90DF91CF91089508958C -:10019000FC011382128288EE93E0A0E0B0E0848339 -:1001A0009583A683B78389E491E09183808380E57A -:1001B000848781E085878AE090E09787868789E059 -:1001C00090E0918B808B82E090E0938B828B148AFD -:1001D000158A168A178A118E108E138E128E158E1E -:1001E000148E40E050E0BA01CF010C94872CFC0142 -:1001F00077876687518B408B338B228B08950F93C3 -:100200001F93CF93DF93EC01062F142F60E08E85B0 -:100210000E94622A3C852D8580913039882381F0A7 -:100220009FB7F89480913039813081F58DB38093F8 -:100230002E394DB380912F39809584238DBB9FBF7C -:100240003CBD2DBD0EBD00000DB407FEFDCF8EB52B -:100250001EBD00000DB407FEFDCF1EB580913039E4 -:10026000882351F09FB7F8942091303980912E392E -:10027000213079F48DBB9FBF61E08E850E94622A98 -:10028000812FDF91CF911F910F91089590932E3977 -:10029000D7CF8FBFF1CF6F7740E00C94FF0068E1BC -:1002A0000C944B0169E10C944B0169E10E944B01F4 -:1002B000682F880F770B880B990B0E94FC2C20E08D -:1002C00030E040E85EE30E94AC2D08958F929F924B -:1002D000AF92BF92EF92FF920F931F93CF93DF9352 -:1002E0008C016AE10E944B01C82FD0E0C8010E9436 -:1002F00055010E94C42C4B017B01F801648975896A -:10030000868997890E94FC2C24ED30E843E45EE462 -:100310000E94BF2CBE0187FD12C06D597109F7FE06 -:1003200011C0680D791DCB01DF91CF911F910F9105 -:10033000FF90EF90BF90AF909F908F900895645A78 -:100340007109EDCF072E000C880B990B0E94FC2C35 -:1003500020EB32E748E85FE30E94AC2D0E94C42CFA -:10036000E2CFCF93DF93EC0163E10E944B0190E079 -:10037000288D398D821B930BDF91CF910895CF93F8 -:10038000DF93EC01E881F9810480F581E02D099586 -:10039000892B69F0888D998D0196998F888F60E0FF -:1003A000CE010E944B0190E0DF91CF9108958FEF35 -:1003B0009FEFFACF0F931F93CF93DF938C0166E0EB -:1003C0000E944B01D82F67E0C8010E944B01C82F43 -:1003D00068E0C8010E944B014C2F50E070E060E0E3 -:1003E000762F652F542F44276D2B8A019B01082BF4 -:1003F000C901B80120E038E448EE51E00E94892E9E -:1004000003E10E942E2FB901CA01DF91CF911F9104 -:100410000F9108956DE10E944B0190E034E09595B5 -:1004200087953A95E1F78A30910574F4FC01EE0F57 -:10043000FF1FE80FF91FE95DFE4F60817181828126 -:10044000082E000C990B089560E070E0CB01089530 -:100450006CE20C944B01CF93DF93EC0143E861E82D -:100460000E94FF0062E1CE010E944B0183FD03C0A8 -:100470000E945B28F7CF48E062E9CE010E94FF00AE -:1004800081E090E0DF91CF910895AF92BF92CF923B -:10049000DF92FF920F931F93CF93DF936C015B0169 -:1004A000EA0162E20E944B01F82E90E09C012C0FC1 -:1004B0003D1F2F3F310529F020F0CFEFD0E0C81BC2 -:1004C000D90B8501AC0EBD1E0A151B0541F0F801C4 -:1004D00041918F0160E8C6010E94FF00F5CF4C2FCB -:1004E0004F0D62EAC6010E94FF00CE01DF91CF915D -:1004F0001F910F91FF90DF90CF90BF90AF90089524 -:100500000F931F93CF93DF93EC01E881F98104806F -:10051000F581E02D0995892BB1F06DE0CE010E94A7 -:100520004B01082F60E0CE010E944B01182F402F95 -:100530006DE8CE010E94FF00812F90E0DF91CF9106 -:100540001F910F9108958FEF9FEFF8CFCF93DF9317 -:10055000EC017D8F6C8F8A899B89672BA1F160E00C -:100560000E94262A40E060ECCE010E94FF008A89AA -:100570009B8982309105D9F08A309105D1F00B9793 -:10058000D1F481E00E9435268A899B8982309105C9 -:10059000A1F08A30910599F00B9799F481E043E03E -:1005A00050E06AEA75E0DF91CF910C94702682E00A -:1005B000E9CF80E0E7CF8FEFE5CF82E0F0CF80E0BA -:1005C000EECF8FEFECCF82309105B9F08A309105F4 -:1005D000B1F00B97B1F481E00E949F268A899B8934 -:1005E0008230910581F08A30910579F00B9779F48A -:1005F00081E0DF91CF910C94542682E0EDCF80E032 -:10060000EBCF8FEFE9CF82E0F4CF80E0F2CF8FEF36 -:10061000F0CF41E861E80C94FF0040E861E80C94F9 -:10062000FF000E940D030C942126AF92BF92CF923F -:10063000DF92EF92FF920F931F93CF93DF93EC0122 -:10064000DB01CA018C8B9D8BAE8BBF8B6A017B015A -:10065000FF0CCC08DC2C76019A01AB016C2D7C2DB3 -:100660008C2D9C2D03E10E94132FA12CE8E4BE2EBB -:10067000F8EECF2EDD24D394E12CF12C00E010E035 -:100680000E94B62E122FC22ED32E7A01A701662702 -:10069000772766E8CE010E94FF0077276F2D5E2D39 -:1006A0004D2D67E8CE010E94FF00412F68E8CE0182 -:1006B000DF91CF911F910F91FF90EF90DF90CF903E -:1006C000BF90AF900C94FF00CF92DF92EF92FF9219 -:1006D000CF93DF93EC016A017B0161E08E850E947C -:1006E000262A61E08E850E94622A888999898F3F37 -:1006F0009807B9F061E00E94262A60E088890E948C -:10070000622A6AE070E080E090E00E94D72861E011 -:1007100088890E94622A6AE070E080E090E00E948E -:10072000D7280E94EB2562E4CE010E944B01823162 -:1007300091F5CE010E940D03B701A601CE010E94E2 -:10074000150340E06EE8CE010E94FF0040E06FE834 -:10075000CE010E94FF006CE0CE010E944B01482FA9 -:1007600043606CE8CE010E94FF0044E066EACE01DF -:100770000E94FF0040E869E8CE010E94FF00CE0120 -:100780000E94090381E090E0DF91CF91FF90EF900C -:10079000DF90CF90089590E080E0F6CFCF92DF9287 -:1007A000EF92FF92CF93DF93EC016EE10E944B0139 -:1007B000F82ECE010E940A02F2942FE0F222CC24FD -:1007C000C394D12C02C0CC0CDD1CFA94E2F70D2CA2 -:1007D000000CEE08FF0828EE33E040E050E00E94F5 -:1007E0004F2EC701B6010E944F2E21313105410520 -:1007F00051055CF04CE066EACE01DF91CF91FF90AD -:10080000EF90DF90CF900C94FF0044E0F4CF0F9373 -:100810001F93CF93DF938C01EB016630710534F0A9 -:100820006D30710534F56630710529F545EC61EBE5 -:10083000C8010E94FF004CE067EBC8010E94FF0066 -:10084000C6E0D0E06EE1C8010E944B018F7094E0D9 -:10085000CC0FDD1F9A95E1F74C2F482B6EE9C801AC -:100860000E94FF00C801DF91CF911F910F910C945E -:10087000CE03CCE0D0E043EC61EBC8010E94FF0066 -:100880004AE067EBC8010E94FF00DCCFCF92DF9205 -:10089000EF92FF920F931F93CF93DF938C01CB01C5 -:1008A000BA010E94FC2C6B017C0120E030EC43EF8C -:1008B00055E40E94BF2CD0E0C0E018160CF065C0D3 -:1008C00020E030E842E256E4C701B6010E94BF2CA6 -:1008D000C1E0D0E018160CF058C020E030EC43E73F -:1008E00056E4C701B6010E94BF2CC2E0D0E0181642 -:1008F0000CF04BC020E030E842EA56E4C701B601F4 -:100900000E94BF2CC3E0D0E018160CF03EC020E0DF -:1009100034E244EF56E4C701B6010E94BF2CC4E0A4 -:10092000D0E0181694F520E034EE42E257E4C70117 -:10093000B6010E94BF2CC5E0D0E0181634F520E0C7 -:1009400034E244E757E4C701B6010E94BF2CC6E079 -:10095000D0E01816D4F420E034E244EF57E4C701A5 -:10096000B6010E94BF2CC7E0D0E0181674F420E056 -:1009700034E244E758E4C701B6010E94BF2CC9E045 -:10098000D0E0181614F0C8E0D0E06DE1C8010E9474 -:100990004B018F7094E0CC0FDD1F9A95E1F74C2F3F -:1009A000482B6DE9C8010E94FF00C801DF91CF917B -:1009B0001F910F91FF90EF90DF90CF900C94CE039A -:1009C0000F931F93CF93DF938C01EB016930710577 -:1009D00014F0C8E0D0E06DE1C8010E944B01C530C1 -:1009E000D10514F4C5E0D0E02497CC0FDD1F482FCB -:1009F000417F4C2B6DE9C801DF91CF911F910F9181 -:100A00000C94FF00CF93DF93EC016EE10E944B0149 -:100A1000482F44606EE9CE01DF91CF910C94FF0026 -:100A2000CF93DF93EC011B8E1A8E6DE10E944B0178 -:100A3000482F4E7F6DE9CE01DF91CF910C94FF00DE -:100A4000CF93DF93EC0181E090E09B8F8A8F6DE183 -:100A5000CE010E944B01482F41606DE9CE01DF912C -:100A6000CF910C94FF000F931F93CF93DF93EC0172 -:100A70008B010E940903CE01012B99F00E942005F1 -:100A800040E06DE8CE010E94FF0040E062EACE0146 -:100A90000E94FF0081E090E0DF91CF911F910F91C4 -:100AA00008950E941005ECCF0F931F93CF93DF930F -:100AB000EC018B011616170684F40E942005402FC6 -:100AC00062EACE010E94FF0045E861E8CE01DF91B5 -:100AD000CF911F910F910C94FF000E941005F4CF4D -:100AE0000F931F93CF93DF93EC0162E10E944B01C0 -:100AF000182F482F62E9CE010E94FF0015FD25C086 -:100B0000198E188E8A8D9B8D62E2892B09F463E120 -:100B1000CE010E944B01082F10E060E1CE010E943F -:100B20004B01482F6DE8CE010E94FF00EC8DFD8D3A -:100B3000309711F0C801099540E06DE8CE01DF91D2 -:100B4000CF911F910F910C94FF00DF91CF911F91D6 -:100B50000F9108958AEA91E00C947005452B61F499 -:100B6000AB016F30710514F04EE050E057FD40E0EE -:100B7000406769E80C94FF00AB016231710514F025 -:100B800041E150E04230510514F442E050E042505F -:100B90004068EFCF8AEA91E00C94C8002F923F9210 -:100BA0004F925F926F927F928F929F92AF92BF927D -:100BB000CF92DF92EF92FF920F931F93CF93DF9329 -:100BC000CDB7DEB7C857D1090FB6F894DEBF0FBE58 -:100BD000CDBFE9969FAF8EAFE9971A01DC01189659 -:100BE0000D911D912D913C911B9761960CAF1DAFFE -:100BF0002EAF3FAF61971C960D911D912D913C91A9 -:100C00001F9709831A832B833C8350960D911D9166 -:100C10002D913C9153970D831E832F8338875496D3 -:100C20000D911D912D913C91579709871A872B871C -:100C30003C87E6966FAFE697E7967FAFE797A85AAF -:100C4000BF4F6396BFAFAEAF6397FC01E45AFF4F4F -:100C50006596FFAFEEAF65978C01005A1F4F679600 -:100C60001FAF0EAF67979C012C593F4F69963FAF5E -:100C70002EAF699788599F4F6B969FAF8EAF6B973A -:100C8000E996AEADBFADE997A459BF4F6D96BFAF22 -:100C9000AEAF6D97E996EEADFFADE997E059FF4F26 -:100CA0006F96FFAFEEAF6F97E9960EAD1FADE99768 -:100CB0000C581F4FA1961FAF0EAFA197E9962EAD0E -:100CC0003FADE99728583F4FA3963FAF2EAFA3976C -:100CD000E9968EAD9FADE99784589F4FA5969FAF3B -:100CE0008EAFA597E996AEADBFADE997A058BF4FBF -:100CF000A796BFAFAEAFA797E996EEADFFADE99768 -:100D0000EC57FF4FA996FFAFEEAFA997E9960EAD4E -:100D10001FADE99708571F4FAB961FAF0EAFAB97AC -:100D2000E9962EAD3FADE99724573F4FAD963FAFC3 -:100D30002EAFAD97E9968EAD9FADE99780579F4F47 -:100D4000AF969FAF8EAFAF97E996AEADBFADE997C7 -:100D5000AC56BF4FE196BFAFAEAFE197E796AEADF1 -:100D6000BFADE7970D911D912D913C910D871E8789 -:100D70002F87388B6396EEADFFAD639700831183A9 -:100D80002283338361968CAD9DADAEADBFAD6197CF -:100D900088589B45A549B842800F911FA21FB31FD9 -:100DA0004D815E816F81788509851A852B853C850B -:100DB000402751276227732709811A812B813C81A3 -:100DC000402351236223732309851A852B853C8593 -:100DD0004027512762277327840F951FA61FB71F2F -:100DE0008C019D0147E0000F111F221F331F4A9500 -:100DF000D1F7AC01BD01E9E1769567955795479527 -:100E0000EA95D1F7402B512B622B732B89819A8164 -:100E1000AB81BC81480F591F6A1F7B1FE796AEAD9F -:100E2000BFADE79714960D911D912D913C911797A9 -:100E3000098B1A8B2B8B3C8B6596EEADFFAD6597BE -:100E4000008311832283338389859A85AB85BC8592 -:100E50008A5A9844A843B741800F911FA21FB31F1D -:100E6000C980DA80EB80FC800D811E812F8138855E -:100E7000C026D126E226F326C422D522E622F72276 -:100E8000C026D126E226F3268C0D9D1DAE1DBF1D6A -:100E90008C019D01FCE0000F111F221F331FFA95EA -:100EA000D1F76C017D01A4E1F694E794D794C7943F -:100EB000AA95D1F7C02AD12AE22AF32AC40ED51E58 -:100EC000E61EF71EE796AEADBFADE79718968D917B -:100ED0009D910D90BC91A02D8D8B9E8BAF8BB88F6B -:100EE0006796EEADFFAD679780839183A283B3834E -:100EF0008D819E81AF81B88585529F48AF4DBB4D96 -:100F00000D891E892F89388D800F911FA21FB31F55 -:100F100049805A806B807C80442655266626772639 -:100F20004C205D206E207F2009811A812B813C811D -:100F30004026512662267326840D951DA61DB71DD9 -:100F40008C019D01EFE03695279517950795EA9559 -:100F5000D1F72C013D01F1E1440C551C661C771CB6 -:100F6000FA95D1F7402A512A622A732A4C0C5D1C4B -:100F70006E1C7F1CE796AEADBFADE7971C968D91BA -:100F80009D910D90BC91A02D898F9A8FAB8FBC8FB6 -:100F90006996EEADFFAD699780839183A283B38399 -:100FA00089819A81AB81BC8182519143A244BE4325 -:100FB000098D1A8D2B8D3C8D800F911FA21FB31FA1 -:100FC0008A019B010C251D252E253F250421152175 -:100FD000262137210427152726273727800F911F21 -:100FE000A21FB31F4C015D011AE0B694A794979419 -:100FF00087941A95D1F78C019D0186E1000F111F8E -:10100000221F331F8A95D1F7082919292A293B293C -:10101000040D151D261D371DE796AEADBFADE79734 -:1010200050968D919D910D90BC91A02D8D8F9E8F8E -:10103000AF8FB8A36B96EEADFFAD6B9780839183B6 -:10104000A283B3838155904FA348BA40840F951F64 -:10105000A61FB71FB701A60144255525662577258C -:1010600040235123622373234C255D256E257F2564 -:10107000840F951FA61FB71F4C015D0147E0880C28 -:10108000991CAA1CBB1C4A95D1F7AC01BD01E9E132 -:101090007695679557954795EA95D1F74829592947 -:1010A0006A297B29400F511F621F731FE796AEAD5F -:1010B000BFADE79754968D919D910D90BC91A02D59 -:1010C00089A39AA3ABA3BCA36D96EEADFFAD6D97BC -:1010D00080839183A283B383865D9943A847B84BED -:1010E0008C0D9D1DAE1DBF1D73016201C026D12652 -:1010F000E226F326C422D522E622F722C424D524F0 -:10110000E624F7248C0D9D1DAE1DBF1D4C015D0115 -:10111000FCE0880C991CAA1CBB1CFA95D1F76C0149 -:101120007D01A4E1F694E794D794C794AA95D1F7EA -:10113000C828D928EA28FB28C40ED51EE61EF71EAB -:10114000E796AEADBFADE79758968D919D910D9006 -:10115000BC91A02D8DA39EA3AFA3B8A76F96EEADB3 -:10116000FFAD6F9780839183A283B3838D5E994B8C -:10117000AF4CB745840D951DA61DB71D280139013B -:1011800044265526662677264C205D206E207F203B -:101190004026512662267326840D951DA61DB71D77 -:1011A0004C015D01EFE0B694A79497948794EA957B -:1011B000D1F72C013D01F1E1440C551C661C771C54 -:1011C000FA95D1F7482859286A287B284C0C5D1CD1 -:1011D0006E1C7F1CE796AEADBFADE7975C968D9118 -:1011E0009D910D90BC91A02D89A79AA7ABA7BCA7F4 -:1011F000A196EEADFFADA19780839183A283B383C7 -:101200008F5F9A46A94BB240800F911FA21FB31F58 -:101210008A019B010C251D252E253F250421152122 -:10122000262137210427152726273727800F911FCE -:10123000A21FB31F4C015D011AE0B694A7949794C6 -:1012400087941A95D1F78C019D0186E1000F111F3B -:10125000221F331F8A95D1F7082919292A293B29EA -:10126000040D151D261D371DE796AEADBFADE797E2 -:1012700090968D919D910D90BC91A02D8DA79EA7CC -:10128000AFA7B8ABA396EEADFFADA39780839183D4 -:10129000A283B38388529746AF47B649840F951F00 -:1012A000A61FB71FB701A60144255525662577253A -:1012B00040235123622373234C255D256E257F2512 -:1012C000840F951FA61FB71F4C015D0147E0880CD6 -:1012D000991CAA1CBB1C4A95D1F7AC01BD01E9E1E0 -:1012E0007695679557954795EA95D1F748295929F5 -:1012F0006A297B29400F511F621F731FE796AEAD0D -:10130000BFADE79794968D919D910D90BC91A02DC6 -:1013100089AB9AABABABBCABA596EEADFFADA597D9 -:1013200080839183A283B38381559840AB4BB447AC -:101330008C0D9D1DAE1DBF1D73016201C026D126FF -:10134000E226F326C422D522E622F722C424D5249D -:10135000E624F7248C0D9D1DAE1DBF1D4C015D01C3 -:10136000FCE0880C991CAA1CBB1CFA95D1F76C01F7 -:101370007D01A4E1F694E794D794C794AA95D1F798 -:10138000C828D928EA28FB28C40ED51EE61EF71E59 -:10139000E796AEADBFADE79798968D919D910D9074 -:1013A000BC91A02D8DAB9EABAFABB8AFA796EEAD09 -:1013B000FFADA79780839183A283B3838F54944A10 -:1013C000A109B109840D951DA61DB71D280139017C -:1013D00044265526662677264C205D206E207F20E9 -:1013E0004026512662267326840D951DA61DB71D25 -:1013F0004C015D01EFE0B694A79497948794EA9529 -:10140000D1F72C013D01F1E1440C551C661C771C01 -:10141000FA95D1F7482859286A287B284C0C5D1C7E -:101420006E1C7F1CE796AEADBFADE7979C968D9185 -:101430009D910D90BC91A02D89AF9AAFABAFBCAF81 -:10144000A996EEADFFADA99780839183A283B38364 -:1014500082549842A34AB647800F911FA21FB31F20 -:101460008A019B010C251D252E253F2504211521D0 -:10147000262137210427152726273727800F911F7C -:10148000A21FB31F4C015D011AE0B694A794979474 -:1014900087941A95D1F78C019D0186E1000F111FE9 -:1014A000221F331F8A95D1F7082919292A293B2998 -:1014B000040D151D261D371DE796AEADBFADE79790 -:1014C000D0968D919D910D90BC91A02D21968CAFC1 -:1014D0009DAFAEAFBFAF2197AB96EEADFFADAB9773 -:1014E00080839183A283B3834C015D01FEED8F1A4B -:1014F000FEEE9F0AFFE6AF0AF4E9BF0A840E951ECE -:10150000A61EB71EB701A6014425552566257725D9 -:1015100040235123622373234C255D256E257F25AF -:10152000480D591D6A1D7B1D4A015B0197E0880C1F -:10153000991CAA1CBB1C9A95D1F7E9E17695679591 -:1015400057954795EA95D1F7482959296A297B2962 -:10155000400F511F621F731FE796AEADBFADE797F7 -:10156000D4968D919D910D90BC91A02D25968CAF18 -:101570009DAFAEAFBFAF2597AD96EEADFFADAD97CA -:1015800080839183A283B3834C015D01FDE68F1AB2 -:10159000FEE89F0AF7E6AF0AF2E0BF0A8C0C9D1C3A -:1015A000AE1CBF1C73016201C026D126E226F326C1 -:1015B000C422D522E622F722C424D524E624F72427 -:1015C000C80CD91CEA1CFB1C46015701FCE0880C26 -:1015D000991CAA1CBB1CFA95D1F7A4E1F694E794D8 -:1015E000D794C794AA95D1F7C828D928EA28FB2808 -:1015F000C40ED51EE61EF71EE796AEADBFADE7974B -:10160000D8968D919D910D90BC91A02D29968CAF6F -:101610009DAFAEAFBFAF2997AF96EEADFFADAF9721 -:1016200080839183A283B3834C015D01F2E78F1A1B -:10163000FCEB9F0AF6E8AF0AF9E5BF0A840C951C9B -:10164000A61CB71C28013901442655266626772694 -:101650004C205D206E207F20402651266226732676 -:10166000480C591C6A1C7B1C53014201BFE0B69414 -:10167000A79497948794BA95D1F781E1440C551CAF -:10168000661C771C8A95D1F7482859286A287B2838 -:101690004C0C5D1C6E1C7F1CE796AEADBFADE79792 -:1016A000DC968D919D910D90BC91A02D2D968CAFC7 -:1016B0009DAFAEAFBFAF2D97E196EEADFFADE19719 -:1016C00080839183A283B3834C015D01FFED8F1A68 -:1016D000F7EF9F0AFBE4AF0AF6EBBF0A800E911EFC -:1016E000A21EB31E8A019B010C251D252E253F2518 -:1016F00004211521262137210427152726273727DE -:10170000080D191D2A1D3B1D480159019AE0B69488 -:10171000A794979487949A95D1F7E6E1000F111F4B -:10172000221F331FEA95D1F7082919292A293B29B5 -:10173000040D151D261D371D89889A88AB88BC8825 -:101740008EE9881A8AED980A81EEA80A89E0B80A1B -:10175000840E951EA61EB71EB301A2014027512775 -:10176000622773274C215D216E217F214425552559 -:1017700066257725480D591D6A1D7B1D4A015B01B1 -:10178000F5E0880C991CAA1CBB1CFA95D1F7ABE1BB -:101790007695679557954795AA95D1F74829592980 -:1017A0006A297B29400F511F621F731F8DA09EA0C5 -:1017B000AFA0B8A490EC891A9CE4990A9FEBA90AFF -:1017C0009FE3B90A8C0C9D1CAE1CBF1C68017901FB -:1017D000C426D526E626F726C420D520E620F72005 -:1017E000C026D126E226F326C80CD91CEA1CFB1C15 -:1017F00046015701B9E0880C991CAA1CBB1CBA957C -:10180000D1F787E1F694E794D794C7948A95D1F7F6 -:10181000C828D928EA28FB28C40ED51EE61EF71EC4 -:1018200089AC9AACABACBCACAFEA8A1AA5EA9A0A0E -:10183000A1EAAA0AA9EDBA0A840C951CA61CB71C39 -:101840002A013B014C245D246E247F244022512236 -:10185000622273224426552666267726480C591C98 -:101860006A1C7B1C530142019EE0880C991CAA1C37 -:10187000BB1C9A95D1F7E2E176946794579447940C -:10188000EA95D1F7482859286A287B284C0C5D1C1A -:101890006E1C7F1C8D849E84AF84B888B6E58B1A3D -:1018A000B8E39B0AB9E4AB0AB6E1BB0A800E911E0D -:1018B000A21EB31E9701860104251525262537256E -:1018C00004231523262337230C251D252E253F25EC -:1018D000080D191D2A1D3B1D48015901FCE0B69455 -:1018E000A79497948794FA95D1F7A4E1000F111F5C -:1018F000221F331FAA95D1F7082919292A293B2924 -:10190000040D151D261D371D89A09AA0ABA0BCA0F3 -:10191000E3EA8E1AEFEE9E0AE0EDAE0AE9E2BE0AB5 -:10192000840E951EA61EB71EB301A20140275127A3 -:10193000622773274C215D216E217F214425552587 -:1019400066257725480D591D6A1D7B1D4A015B01DF -:10195000B5E0880C991CAA1CBB1CBA95D1F78BE189 -:1019600076956795579547958A95D1F748295929CE -:101970006A297B29400F511F621F731F8DA89EA8E3 -:10198000AFA8B8ACFDEA8F1AFBEE9F0AFBEBAF0ADB -:10199000FDEFBF0A8C0C9D1CAE1CBF1C68017901B9 -:1019A000C426D526E626F726C420D520E620F72033 -:1019B000C026D126E226F326C80CD91CEA1CFB1C43 -:1019C0004601570199E0880C991CAA1CBB1C9A95EA -:1019D000D1F7E7E1F694E794D794C794EA95D1F765 -:1019E000C828D928EA28FB28C40ED51EE61EF71EF3 -:1019F0002D968CAC9DACAEACBFAC2D978FE7881A02 -:101A000089E1980A8EE5A80A87E2B80A840C951C39 -:101A1000A61CB71C2A013B014C245D246E247F24A4 -:101A200040225122622273224426552666267726BA -:101A3000480C591C6A1C7B1C53014201FEE0880CB7 -:101A4000991CAA1CBB1CFA95D1F7A2E17694679465 -:101A500057944794AA95D1F7482859286A287B2893 -:101A60004C0C5D1C6E1C7F1C8D8C9E8CAF8CB8A0AA -:101A700098E3891A94E0990A9CE2A90A98E1B90AC4 -:101A8000800E911EA21EB31E970186010425152506 -:101A90002625372504231523262337230C251D252A -:101AA0002E253F25080D191D2A1D3B1D48015901F2 -:101AB000BCE0B694A79497948794BA95D1F784E143 -:101AC000000F111F221F331F8A95D1F708291929EA -:101AD0002A293B29040D151D261D371D89A89AA802 -:101AE000ABA8BCA8AAE18A1AA2E39A0AAEE1AA0AA4 -:101AF000AEEDBA0A840E951EA61EB71EB301A20152 -:101B000040275127622773274C215D216E217F21B9 -:101B10004425552566257725480D591D6A1D7B1DD1 -:101B20004A015B0195E0880C991CAA1CBB1C9A9584 -:101B3000D1F7EBE17695679557954795EA95D1F7FB -:101B4000482959296A297B29400F511F621F731F99 -:101B500029968CAC9DACAEACBFAC2997BAE28B1A7F -:101B6000B8EF9B0AB8ECAB0ABCE3BB0A8C0C9D1C1B -:101B7000AE1CBF1C68017901C426D526E626F726CF -:101B8000C420D520E620F720C026D126E226F32661 -:101B9000C80CD91CEA1CFB1C46015701F9E0880C53 -:101BA000991CAA1CBB1CFA95D1F7A7E1F694E794FF -:101BB000D794C794AA95D1F7C828D928EA28FB2832 -:101BC000C40ED51EE61EF71E898C9A8CAB8CBC8C7D -:101BD000E9E78E1AE2EF9E0AEAE2AE0AEBE0BE0AFD -:101BE000840C951CA61CB71C2A013B014C245D24C7 -:101BF0006E247F24402251226222732244265526DD -:101C000066267726480C591C6A1C7B1C530142012E -:101C1000BEE0880C991CAA1CBB1CBA95D1F782E1C6 -:101C200076946794579447948A95D1F74828592811 -:101C30006A287B284C0C5D1C6E1C7F1C8DA49EA406 -:101C4000AFA4B8A8F3E18F1AFBEE9F0AF5EAAF0A3A -:101C5000FAEBBF0A800E911EA21EB31E97018601E9 -:101C60000425152526253725042315232623372368 -:101C70000C251D252E253F25080D191D2A1D3B1D50 -:101C8000480159019CE0B694A794979487949A953B -:101C9000D1F7E4E1000F111F221F331FEA95D1F79E -:101CA000082919292A293B29040D151D261D371D30 -:101CB00025968CAC9DACAEACBFAC25978BEF881A4B -:101CC00086E1980A8CE1A80A86E5B80A840E951E7A -:101CD000A61EB71EB301A201402751276227732712 -:101CE0004C215D216E217F214425552566257725D0 -:101CF000480D591D6A1D7B1D4A015B01F5E0880CEA -:101D0000991CAA1CBB1CFA95D1F7ABE17695679597 -:101D100057954795AA95D1F7482959296A297B29CA -:101D2000400F511F621F731F8D889E88AF88B88C2B -:101D300098E0891A9CE5990A90E1A90A93E0B90A0A -:101D40008C0C9D1CAE1CBF1C68017901C426D526D5 -:101D5000E626F726C420D520E620F720C026D12687 -:101D6000E226F326C80CD91CEA1CFB1C46015701CD -:101D7000B9E0880C991CAA1CBB1CBA95D1F787E165 -:101D8000F694E794D794C7948A95D1F7C828D928B0 -:101D9000EA28FB28C40ED51EE61EF71E89A49AA4C5 -:101DA000ABA4BCA4A7E28A1AADEF9A0AA0E9AA0ADA -:101DB000A8E9BA0A840C951CA61CB71C2A013B0191 -:101DC0004C245D246E247F244022512262227322FF -:101DD0004426552666267726480C591C6A1C7B1C0F -:101DE000530142019EE0880C991CAA1CBB1C9A95C9 -:101DF000D1F7E2E17694679457944794EA95D1F746 -:101E0000482859286A287B284C0C5D1C6E1C7F1CB6 -:101E10004601570184249524A624B72421968CAD2D -:101E20009DADAEADBFAD21978657934BA54DB24743 -:101E3000800F911FA21FB31F950184010423152356 -:101E4000262337230C251D252E253F25800F911F86 -:101E5000A21FB31F8C019D01FCE0369527951795B5 -:101E60000795FA95D1F7E5960CAF1DAF2EAF3FAFB2 -:101E7000E5978C019D01A4E1000F111F221F331F64 -:101E8000AA95D1F7E5968CAD9DADAEADBFADE5970A -:101E9000082B192B2A2B3B2B040D151D261D371D36 -:101EA00089A19AA1ABA1BCA18E5B964CA540B109BA -:101EB000840F951FA61FB71F80269126A226B32642 -:101EC000880E991EAA1EBB1ED501C40144E0880FCE -:101ED000991FAA1FBB1F4A95D1F75CE1B694A7943E -:101EE000979487945A95D1F7882A992AAA2ABB2AC7 -:101EF000800E911EA21EB31E4DA55EA56FA578A9EA -:101F00004F5759406E4878474C0D5D1D6E1D7F1D23 -:101F100073016201C026D126E226F326C824D92403 -:101F2000EA24FB24C40ED51EE61EF71EB701A60147 -:101F3000EBE0440F551F661F771FEA95D1F7F5E1D7 -:101F4000F694E794D794C794FA95D1F7C42AD52A82 -:101F5000E62AF72AC80CD91CEA1CFB1C49AD5AAD6D -:101F60006BAD7CAD4E5D5E4962467249440E551EB6 -:101F7000661E771EB901A801482559256A257B25CB -:101F80004C255D256E257F25440E551E661E771E49 -:101F9000D20123013D014C0C5D1C6E1C7F1C299657 -:101FA0004CAD5DAD6EAD7FAD2997445F574C6A4136 -:101FB0007240400F511F621F731F950184010C2551 -:101FC0001D252E253F250425152526253725040FFB -:101FD000151F261F371FB901A801A9E0769567953F -:101FE00057954795AA95D1F7B7E1000F111F221F0A -:101FF000331FBA95D1F7042B152B262B372B040D45 -:10200000151D261D371D49895A896B897C894C5BB2 -:10201000554161447B45480D591D6A1D7B1D460194 -:10202000570184249524A624B72480269126A2262D -:10203000B326840E951EA61EB71EB501A40184E02A -:10204000440F551F661F771F8A95D1F79CE1B69400 -:10205000A794979487949A95D1F7842A952AA62ACB -:10206000B72A800E911EA21EB31E4D8D5E8D6F8D00 -:1020700078A1475550436142744BC40ED51EE61EED -:10208000F71EB301A2014027512762277327482575 -:1020900059256A257B254C0D5D1D6E1D7F1DDB01BD -:1020A000CA01EBE0880F991FAA1FBB1FEA95D1F761 -:1020B000F5E17695679557954795FA95D1F7482BB1 -:1020C000592B6A2B7B2B480D591D6A1D7B1DC9A4FA -:1020D000DAA4EBA4FCA4B0EACB1AB4EBDB0AB4E4B8 -:1020E000EB0AB9E0FB0A4C0C5D1C6E1C7F1C6801FE -:1020F0007901C824D924EA24FB24C426D526E6265F -:10210000F7264C0C5D1C6E1C7F1CF20123013F0165 -:10211000440E551E661E771ECDA8DEA8EFA8F8ACAB -:10212000F0E9CF1AF3E4DF0AF0E4EF0AF1E4FF0A82 -:10213000C00ED11EE21EF31E95018401042715274F -:102140002627372704251525262537250C0D1D1D87 -:102150002E1D3F1D68017901A9E0F694E794D794FC -:10216000C794AA95D1F7B7E1000F111F221F331FA3 -:10217000BA95D1F70C291D292E293F29040D151DCB -:10218000261D371D2596CCACDDACEEACFFAC2597FB -:102190008AE3C81A81E8D80A84E6E80A87EDF80AD3 -:1021A000C80CD91CEA1CFB1C4A015B018424952441 -:1021B000A624B72480269126A226B3268C0C9D1C2B -:1021C000AE1CBF1C7501640184E0CC0CDD1CEE1C50 -:1021D000FF1C8A95D1F79CE1B694A79497948794B5 -:1021E0009A95D1F78C289D28AE28BF28800E911E85 -:1021F000A21EB31ECD84DE84EF84F88896E0C91A4F -:1022000098EDD90A9EE5E90A95E1F90AC40ED51EB2 -:10221000E61EF71EB301A20140275127622773274C -:10222000482559256A257B254C0D5D1D6E1D7F1D9A -:102230006A017B01EBE0CC0CDD1CEE1CFF1CEA9577 -:10224000D1F7F5E17695679557954795FA95D1F7CA -:102250004C295D296E297F29480D591D6A1D7B1D5A -:10226000C98CDA8CEB8CFC8CABE7CA1AAFECDA0ABF -:10227000A0E1EA0AABE2FA0A4C0C5D1C6E1C7F1C62 -:1022800068017901C824D924EA24FB24C426D52670 -:10229000E626F7264C0C5D1C6E1C7F1CF201230108 -:1022A0003F01440E551E661E771ECDA0DEA0EFA096 -:1022B000F8A4FBEFCF1AF2EEDF0AF7E7EF0AFBEF25 -:1022C000FF0AC00ED11EE21EF31E950184010427F1 -:1022D00015272627372704251525262537250C0DF4 -:1022E0001D1D2E1D3F1D68017901A9E0F694E7949C -:1022F000D794C794AA95D1F7B7E1000F111F221FF9 -:10230000331FBA95D1F70C291D292E293F29040D19 -:10231000151D261D371DC9A8DAA8EBA8FCA887EC57 -:10232000C81A8FE2D80A8BE2E80A86E2F80AC80CDB -:10233000D91CEA1CFB1C4A015B0184249524A624B9 -:10234000B72480269126A226B3268C0C9D1CAE1C99 -:10235000BF1C7501640184E0CC0CDD1CEE1CFF1C6D -:102360008A95D1F79CE1B694A794979487949A950F -:10237000D1F78C289D28AE28BF28800E911EA21E62 -:10238000B31E2196CCACDDACEEACFFAC21979BE14B -:10239000C91A96E6D90A94E2E90A99E1F90AC40E43 -:1023A000D51EE61EF71EB301A20140275127622762 -:1023B0007327482559256A257B254C0D5D1D6E1D0B -:1023C0007F1D6A017B01EBE0CC0CDD1CEE1CFF1CC9 -:1023D000EA95D1F7F5E17695679557954795FA9582 -:1023E000D1F74C295D296E297F29480D591D6A1D99 -:1023F0007B1D2D96CCACDDACEEACFFAC2D97A8E0F0 -:10240000CA1AA3E8DA0AADE5EA0AA0EEFA0A4C0C09 -:102410005D1C6E1C7F1C68017901C824D924EA2444 -:10242000FB24C426D526E626F7264C0C5D1C6E1C24 -:102430007F1CF20123013F01440E551E661E771ECC -:10244000CD88DE88EF88F88CFBE9CF1AF9EADF0A3D -:10245000F3E5EF0AFBE3FF0AC00ED11EE21EF31EF6 -:1024600095018401042715272627372704251525DC -:10247000262537250C0D1D1D2E1D3F1D68017901D8 -:10248000A9E0F694E794D794C794AA95D1F7B7E159 -:10249000000F111F221F331FBA95D1F70C291D29D8 -:1024A0002E293F29040D151D261D371DCD84DE84E0 -:1024B000EF84F8888CEBC81A8DEDD80A86EDE80A0F -:1024C0008BE0F80AC80CD91CEA1CFB1C4A015B0112 -:1024D00080949094A094B094802A912AA22AB32A3E -:1024E00084249524A624B7248C0C9D1CAE1CBF1CF0 -:1024F0007501640186E0CC0CDD1CEE1CFF1C8A9586 -:10250000D1F79AE1B694A794979487949A95D1F7C6 -:102510008C289D28AE28BF28800E911EA21EB31EB7 -:10252000C9A4DAA4EBA4FCA499E6C91AD10895EDD4 -:10253000E90A9CEBF90AC40ED51EE61EF71EB3018C -:10254000A2014095509560957095482959296A29AE -:102550007B2940275127622773274C0D5D1D6E1D77 -:102560007F1D6A017B01EAE0CC0CDD1CEE1CFF1C28 -:10257000EA95D1F7F6E17695679557954795FA95DF -:10258000D1F74C295D296E297F29480D591D6A1DF7 -:102590007B1D2996CCACDDACEEACFFAC2997A9E550 -:1025A000CA1AACEDDA0AABE6EA0AA4E5FA0AC40CE8 -:1025B000D51CE61CF71C2801390140945094609406 -:1025C0007094442A552A662A772A482459246A2472 -:1025D0007B244C0C5D1C6E1C7F1C73016201AFE000 -:1025E000CC0CDD1CEE1CFF1CAA95D1F7B1E1769452 -:1025F000679457944794BA95D1F74C285D286E2874 -:102600007F28440E551E661E771EC9A0DAA0EBA0D7 -:10261000FCA0B7ECCB1ABFE5DB0ABCE6EB0AB3E0E3 -:10262000FB0AC00ED11EE21EF31E95018401009527 -:102630001095209530950429152926293729042736 -:102640001527262737270C0D1D1D2E1D3F1D680140 -:1026500079018BE0F694E794D794C7948A95D1F7E3 -:1026600095E1000F111F221F331F9A95D1F70C29F6 -:102670001D292E293F29040D151D261D371D2196C4 -:10268000CCACDDACEEACFFAC2197EDE3CE1AE6EAC4 -:10269000DE0AE4EAEE0AEAE9FE0AC80CD91CEA1CE2 -:1026A000FB1C4A015B0180949094A094B094802A12 -:1026B000912AA22AB32A84249524A624B7248C0C18 -:1026C0009D1CAE1CBF1C75016401E6E0CC0CDD1C3A -:1026D000EE1CFF1CEA95D1F7FAE1B694A794979403 -:1026E0008794FA95D1F78C289D28AE28BF28800EB4 -:1026F000911EA21EB31EC98CDA8CEB8CFC8CFEE6FC -:10270000CF1AF3E3DF0AF3EFEF0AF0E7FF0AC40E94 -:10271000D51EE61EF71EB301A201409550956095A7 -:102720007095482959296A297B2940275127622712 -:1027300073274C0D5D1D6E1D7F1D6A017B01AAE094 -:10274000CC0CDD1CEE1CFF1CAA95D1F7B6E17695EA -:10275000679557954795BA95D1F74C295D296E290C -:102760007F29480D591D6A1D7B1DCDA8DEA8EFA845 -:10277000F8AC83E8C81A8BE0D80A80E1E80AF108CF -:10278000C40CD51CE61CF71C280139014094509458 -:1027900060947094442A552A662A772A482459243A -:1027A0006A247B244C0C5D1C6E1C7F1C730162012F -:1027B0008FE0CC0CDD1CEE1CFF1C8A95D1F791E15B -:1027C00076946794579447949A95D1F74C285D284E -:1027D0006E287F28440E551E661E771EC988DA882B -:1027E000EB88FC889FE2C91A92EAD90A9BE7E90ABA -:1027F0009AE7F90AC00ED11EE21EF31E950184016C -:1028000000951095209530950429152926293729FA -:1028100004271527262737270C0D1D1D2E1D3F1DAC -:1028200068017901EBE0F694E794D794C794EA95B0 -:10283000D1F7F5E1000F111F221F331FFA95D1F7D1 -:102840000C291D292E293F29040D151D261D371D74 -:10285000CDA4DEA4EFA4F8A8A1EBCA1AA1E8DA0A75 -:10286000A7E5EA0AA0E9FA0AC80CD91CEA1CFB1C75 -:102870004A015B0180949094A094B094802A912A9C -:10288000A22AB32A84249524A624B7248C0C9D1C48 -:10289000AE1CBF1C75016401A6E0CC0CDD1CEE1C57 -:1028A000FF1CAA95D1F7BAE1B694A79497948794A0 -:1028B000BA95D1F78C289D28AE28BF28800E911E8E -:1028C000A21EB31E2D968CAD9DADAEADBFAD2D97A6 -:1028D00080529941A34DB140840F951FA61FB71F89 -:1028E000B301A201409550956095709548295929EA -:1028F0006A297B294027512762277327840F951F58 -:10290000A61FB71F6C017D014AE0CC0CDD1CEE1C3C -:10291000FF1C4A95D1F7AC01BD01E6E176956795BC -:1029200057954795EA95D1F74C295D296E297F295E -:10293000480D591D6A1D7B1D8DA19EA1AFA1B8A593 -:102940008C5E9C4BAE4FBC45840D951DA61DB71DDE -:10295000280139014094509460947094442A552A77 -:10296000662A772A482459246A247B24840D951DDD -:10297000A61DB71D6C017D01FFE0CC0CDD1CEE1C1B -:10298000FF1CFA95D1F72C013D01A1E176946794E3 -:1029900057944794AA95D1F74C285D286E287F2834 -:1029A000440E551E661E771E25968CAD9DADAEADB0 -:1029B000BFAD25978F559E4EA74FB14B800F911FEE -:1029C000A21FB31F950184010095109520953095A5 -:1029D00004291529262937290427152726273727CB -:1029E000800F911FA21FB31F6C017D011BE0F694A5 -:1029F000E794D794C7941A95D1F78C019D0185E18E -:102A0000000F111F221F331F8A95D1F70C291D2992 -:102A10002E293F29040D151D261D371D8D8D9E8DD8 -:102A2000AF8DB8A18E579148AC4AB840880D991D1A -:102A3000AA1DBB1D4A015B0180949094A094B094A0 -:102A4000802A912AA22AB32A84249524A624B72472 -:102A5000880D991DAA1DBB1D6C017D01E6E0CC0C03 -:102A6000DD1CEE1CFF1CEA95D1F74C015D01FAE17B -:102A7000B694A79497948794FA95D1F78C289D28BB -:102A8000AE28BF28800E911EA21EB31E89AD9AAD3E -:102A9000ABADBCAD8B5C9D40A54CB244840F951F83 -:102AA000A61FB71FB301A201409550956095709580 -:102AB000482959296A297B294027512762277327EA -:102AC000840F951FA61FB71F6C017D014AE0CC0C37 -:102AD000DD1CEE1CFF1C4A95D1F7AC01BD01E6E1FF -:102AE0007695679557954795EA95D1F74C295D29D5 -:102AF0006E297F29480D591D6A1D7B1D8D899E8970 -:102B0000AF89B88D85549D42A842B54D840D951D61 -:102B1000A61DB71D2801390140945094609470940B -:102B2000442A552A662A772A482459246A247B2471 -:102B3000840D951DA61DB71D6C017D01FFE0CC0C19 -:102B4000DD1CEE1CFF1CFA95D1F72C013D01A1E123 -:102B50007694679457944794AA95D1F74C285D28AA -:102B60006E287F28440E551E661E771E89A99AA9D5 -:102B7000ABA9BCA98F569C42A947B441800F911FB5 -:102B8000A21FB31F950184010095109520953095E3 -:102B90000429152926293729042715272627372709 -:102BA000800F911FA21FB31F6C017D011BE0F694E3 -:102BB000E794D794C7941A95D1F78C019D0185E1CC -:102BC000000F111F221F331F8A95D1F70C291D29D1 -:102BD0002E293F29040D151D261D371D61968CAD2C -:102BE0009DADAEADBFAD6197880D991DAA1DBB1DF2 -:102BF00061968CAF9DAFAEAFBFAF619789819A816F -:102C0000AB81BC81800F911FA21FB31F89839A8360 -:102C1000AB83BC830D811E812F813885040D151D6A -:102C2000261D371D0D831E832F83388789859A853E -:102C3000AB85BC85840F951FA61FB71F89879A8710 -:102C4000AB87BC87E796AEADBFADE797A05CBF4F43 -:102C5000E796BFAFAEAFE797B0E42B1A310811F09B -:102C60000C94AE0661960CAD1DAD2EAD3FAD6197D7 -:102C7000E996AEADBFADE99718960D931D932D93D0 -:102C80003C931B9709811A812B813C811C960D93E3 -:102C90001D932D933C931F970D811E812F813885A5 -:102CA00050960D931D932D933C93539709851A8548 -:102CB0002B853C8554960D931D932D933C935797EC -:102CC000E7968EAD9FADE797C858DF4F0FB6F894E3 -:102CD000DEBF0FBECDBFDF91CF911F910F91FF904F -:102CE000EF90DF90CF90BF90AF909F908F907F90AC -:102CF0006F905F904F903F902F900895CF93DF9308 -:102D0000FC0181E093E2A5E4B7E680879187A28782 -:102D1000B38789E89BEAADECBFEE84879587A687E9 -:102D2000B7878EEF9CEDAAEBB8E9808B918BA28BD5 -:102D3000B38B86E794E5A2E3B0E1848B958BA68BF9 -:102D4000B78B10821182128213821482158216822E -:102D50001782DF01A85ABF4F80E4ED01982F199226 -:102D60009A95E9F77896DF011D928A95E9F7DF9148 -:102D7000CF9108958F929F92AF92BF92EF92FF9260 -:102D80000F931F93CF93DF93EC017B018A0188811E -:102D90009981AA81BB8170E060E04A015B01880EE5 -:102DA000991EAA1EBB1E2FE1B22288829982AA8296 -:102DB000BB8288169906AA06BB0668F48C809D80A3 -:102DC000AE80BF802FEF821A920AA20AB20A8C82CA -:102DD0009D82AE82BF828F739927AA27BB27009757 -:102DE000A105B10579F120E4822E912CA12CB12C02 -:102DF000881A990AAA0ABB0A48968C0F9D1F481583 -:102E000059056A057B0570F4A801B701DF91CF91E0 -:102E10001F910F91FF90EF90BF90AF909F908F9078 -:102E20000C940530A401B7010E940530E80CF91C90 -:102E30000819190940E450E0BE01685E7F4FCE01D9 -:102E40000E94CE050034110548F0A801407CB7016E -:102E5000CE010E94CE057C010F731127A801B70196 -:102E6000CE014896D3CFEF92FF920F931F93CF934B -:102E7000DF938C01EB01488159816A817B814F731B -:102E8000552766277727DB01CA010196A11DB11DD1 -:102E90004C0F5D1F20E8FA01208F40E450E060E015 -:102EA00070E0481B590B6A0B7B0B7E01F8E1EF0EBB -:102EB000F11C4830510561057105A0F4489670E099 -:102EC00060E08C0F9D1F0E940E3040E450E0B7017F -:102ED000CE010E94CE0540E450E060E070E080E06A -:102EE00090E0DC0148505109489670E060E08C0F9A -:102EF0009D1F0E940E3088819981AA81BB8123E0A9 -:102F0000880F991FAA1FBB1F2A95D1F78883998321 -:102F1000AA83BB83FE01E05BFF4F808331969083E1 -:102F20003196A0833196B0833C812D819E818F8123 -:102F30003196308331962083319690833196808309 -:102F400040E450E0B701CE010E94CE058885F8012B -:102F50008083898581838A8582838B8583838C8521 -:102F600084838D8585838E8586838F8587838889F5 -:102F70008087898981878A8982878B8983878C89E1 -:102F800084878D8985878E8986878F89878788E959 -:102F9000FE0111928A95E9F7DF91CF911F910F9170 -:102FA000FF90EF900895CF92DF92EF92FF920F93F0 -:102FB0001F93CF93DF93CDB7DEB7C859D1090FB6B2 -:102FC000F894DEBF0FBECDBF7C016B0180E190E0C5 -:102FD0000E9436278C01CE0101960E947E16A60122 -:102FE000B701CE0101960E94BA16BE016F5F7F4FF6 -:102FF000C8010E943317C801C856DF4F0FB6F894B6 -:10300000DEBF0FBECDBFDF91CF911F910F91FF901B -:10301000EF90DF90CF9008951F93CF93DF93EC0153 -:10302000162F0E941530181739F0612FCE01DF914D -:10303000CF911F910C941D30DF91CF911F91089576 -:103040000C941530DC01ED91FC910190F081E02DA4 -:10305000099461E08CE00C94622A60E08CE00C94AE -:10306000622A61E08DE00C94622A60E08DE00C94AD -:10307000622A88E198E00FB6F894A89580936000E2 -:103080000FBE909360000E9431180E942D18FBCF54 -:103090000F931F93CF93DF938C01EC01892B11F4D5 -:1030A000C1E0D0E01C161D060CF561E08CE00E942A -:1030B000622A60E08DE00E94622A64E670E080E0AF -:1030C00090E00E94D72860E08CE00E94622A61E0D4 -:1030D0008DE00E94622A64E670E080E090E00E9449 -:1030E000D72801151105F1F22197DCCF0E942D1888 -:1030F000DF91CF911F910F910C9435180E943118D8 -:103100000E942D186AE070E080E090E00E94D728CD -:103110000E9429180E94351865E070E080E090E078 -:103120000E94D728EBCF0F931F93CF93DF938C018F -:10313000EC01892B11F4C1E0D0E061E08DE00E9448 -:10314000622A1C161D06ACF40E94351864E670E075 -:1031500080E090E00E94D7280E94311864E670E079 -:1031600080E090E00E94D7280115110551F32197C6 -:10317000E8CFDF91CF911F910F910C9435180F93E9 -:103180001F93CF93DF938C01EC01892B11F4C1E0E5 -:10319000D0E01C161D06ACF40E942D1864E670E009 -:1031A00080E090E00E94D7280E94291864E670E031 -:1031B00080E090E00E94D7280115110551F3219776 -:1031C000E8CFDF91CF911F910F910C942D18809132 -:1031D000113590911235A0911335B09114350196A7 -:1031E000A11DB11D8093113590931235A093133515 -:1031F000B09314354091040150910501609106018E -:1032000070910701481759076A077B0710F510925C -:1032100011351092123510921335109214356091B9 -:10322000103580910901861770F081E080930F3589 -:1032300080910F35680F6093103570E08CE00E942C -:1032400076290C94351880910801681788F38FEF60 -:10325000EDCF08958091113590911235A0911335DD -:10326000B09114350196A11DB11D80931135909335 -:103270001235A0931335B093143540910401509149 -:1032800005016091060170910701481759076A0707 -:103290007B0710F5109211351092123510921335EC -:1032A000109214356091103580910901861770F0E5 -:1032B00081E080930F3580910F35680F6093103552 -:1032C00070E08DE00E9476290C942D188091080101 -:1032D000681788F38FEFEDCF089560EC70E08BE313 -:1032E00099E30E94221866E070E08BE399E30E9464 -:1032F00022186091233970E08BE399E30E94221831 -:1033000060EC70E08BE399E30C94221860EC70E0C1 -:103310008BE399E30E94221867E070E08BE399E366 -:103320000E9422186091150170E08BE399E30E94DE -:10333000221860EC70E08BE399E30C94221860ECA7 -:1033400070E08BE399E30E9422186FE070E08BE35A -:1033500099E30E94221861E070E08BE399E30E94F8 -:10336000221860EC70E08BE399E30C94221860EC77 -:1033700070E08BE399E30E9422186FE070E08BE32A -:1033800099E30E94221870E060E08BE399E30E94C9 -:10339000221860EC70E08BE399E30C94221860EC47 -:1033A00070E08BE399E30E9422186EE070E08BE3FB -:1033B00099E30E9422188091213961E070E0811127 -:1033C00002C070E060E08BE399E30E94221860EC99 -:1033D00070E08BE399E30C94221860EC70E08BE3CF -:1033E00099E30E94221868E070E08BE399E30E9461 -:1033F000221866E470E08BE399E30E94221860ECE7 -:1034000070E08BE399E30C94221860EC70E08BE39E -:1034100099E30E94221860E570E08BE399E30E9433 -:10342000221861E070E08BE399E30E9422186AE1C0 -:1034300070E08BE399E30E94221860EC70E08BE36C -:1034400099E30C94221860EC70E08BE399E30E94FE -:10345000221868E470E08BE399E30E94221860E987 -:1034600070E08BE399E30E94221860EC70E08BE33C -:1034700099E30C94221860EC70E08BE399E30E94CE -:10348000221867E470E08BE399E30E94221861E35D -:1034900070E08BE399E30E94221860EC70E08BE30C -:1034A00099E30C94221860EC70E08BE399E30E949E -:1034B000221869E470E08BE399E30E94221861E925 -:1034C00070E08BE399E30E94221860EC70E08BE3DC -:1034D00099E30C94221880912339882341F060915C -:1034E0002C3970912D398AEA91E00C9407040895E3 -:1034F00080912339882341F060911801709119015E -:103500008AEA91E00C94E004089580931F3988239F -:1035100021F081E080932039089510922039089598 -:10352000809123398823E1F080911E39843A51F447 -:10353000609116017091170150E040E08AEA91E035 -:103540000E94AE0580911E39893A51F460911601AE -:103550007091170141E050E08AEA91E00C94AE05C9 -:10356000089580912339882361F08AEA91E00E94CE -:103570000A02609328397093293980932A399093ED -:103580002B39089580912339882371F0409128398F -:103590005091293960912A3970912B398AEA91E04A -:1035A0000E9446040C94B11A08958091233988230F -:1035B00061F08AEA91E00E94DA0160932439709305 -:1035C000253980932639909327390895809123399E -:1035D000882371F040912439509125396091263922 -:1035E000709127398AEA91E00E9415030C94D51A4C -:1035F000089580912339882321F08AEA91E00C9480 -:10360000280280E0089583ED9FE00E94201891E059 -:10361000833709F090E0892F0895C8E3DFE06FEF6A -:10362000CE010E940C182196C11580E1D807B9F788 -:103630000E94391888E39FE00E942018982F81E0AB -:10364000933021F0903F11F080E00895089589E3D0 -:103650009FE00E942018982F80931E3981E0943AB1 -:1036600021F0993A11F080E0089508958AE39FE0EF -:103670000E94201880931D399FEF980F81E09E3F94 -:1036800008F080E00895BF92CF92DF92EF92FF9210 -:103690000F931F93CF93DF938BE090E00E94362728 -:1036A0007C018C01C8E3DFE0CE010E942018F80104 -:1036B00081938F012196C334FFE0DF07A9F76BE008 -:1036C00070E0C7010E94D317B82EC92E082F192FFA -:1036D000DD24D394CE010E942018F80191918F012E -:1036E0008913D12C2196C335FFE0DF0799F78B2D85 -:1036F0009C2D0E94D227C7010E94D2278D2DDF91D9 -:10370000CF911F910F91FF90EF90DF90CF90BF90DE -:1037100008958FED9FE00E94201891E0833709F013 -:1037200090E0892F0895CF92DF92EF92FF92CF938E -:103730000E94891B882309F461C084ED9FE00E94E8 -:10374000201890E090932D3980932C3985ED9FE0DF -:103750000E94201890E0909319018093180186ED43 -:103760009FE00E94201890E090931701809316012B -:103770008BED9FE00E942018C82F8CED9FE00E94E7 -:10378000201890E0B0E0A0E07C01DD24CC24FC2AED -:103790008DED9FE00E942018C82F8EED9FE00E94C3 -:1037A0002018C82ADC2AC0922439D0922539E09208 -:1037B0002639F092273987ED9FE00E942018C82F04 -:1037C00088ED9FE00E94201890E0B0E0A0E07C012E -:1037D000DD24CC24FC2A89ED9FE00E942018C82F0C -:1037E0008AED9FE00E942018C82ADC2AC09228395E -:1037F000D0922939E0922A39F0922B39CF91FF905B -:10380000EF90DF90CF90089580912239882309F4BA -:103810008AC080912339882309F485C060912C39AE -:1038200084ED9FE00E940C186091180185ED9FE0E7 -:103830000E940C186091160186ED9FE00E940C1802 -:10384000609128397091293980912A3990912B39CA -:10385000692F77278827992787ED9FE00E940C180A -:10386000609128397091293980912A3990912B39AA -:10387000BC018827992788ED9FE00E940C18609171 -:1038800028397091293980912A3990912B39672FE5 -:10389000782F892F992789ED9FE00E940C1860915D -:1038A00028398AED9FE00E940C18609124397091AC -:1038B00025398091263990912739692F77278827D4 -:1038C00099278BED9FE00E940C186091243970912C -:1038D00025398091263990912739BC01882799276D -:1038E0008CED9FE00E940C1860912439709125396D -:1038F0008091263990912739672F782F892F992722 -:103900008DED9FE00E940C18609124398EED9FE0B0 -:103910000E940C1863E78FED9FE00E940C188AE06C -:1039200090E00C94BF188AE090E00C94931860E04B -:103930008FED9FE00C940C1882E390E00E944818F1 -:103940000E940D1B8091203970E060E0882319F0FF -:1039500060911F3970E08AEA91E00C9454058AEA7C -:1039600091E00E9411031092233908958091243927 -:1039700090912539A0912639B0912739892B8A2B2E -:103980008B2BE1F08091283990912939A0912A3927 -:10399000B0912B39892B8A2B8B2B81F080911601CA -:1039A000909117018F3F910549F080912C399091AA -:1039B0002D39892B19F010921501089581E080931B -:1039C000150108958091D101883C50F42091CF01D8 -:1039D0003091D00181E02115384118F480E008953C -:1039E00081E00895CF938AEA91E00E944F01C82FA9 -:1039F0000E94A62860931A3570931B3580931C355E -:103A000090931D35C0FF2DC081E0809325358093B4 -:103A100024358093233580911E3590911F35209158 -:103A20002535211108C020912435211104C0209191 -:103A30002335222319F120910F01309110018217B3 -:103A40009307B0F4019690931F3580931E3581E063 -:103A500080932235809121358823E9F0CF910C9411 -:103A60002918109225351092243510922335D3CF82 -:103A700081E08093223580932135ECCF009741F08F -:103A8000019790931F3580931E3510922235E2CF17 -:103A900010922135FACFCF910C942D180F931F93CC -:103AA0000E94A62800911A3510911B3520911C35D3 -:103AB00030911D35601B710B820B930B00910B0134 -:103AC00010910C0120910D0130910E0160177107CA -:103AD0008207930720F01F910F910C94F21C1F9105 -:103AE0000F9108951092900089E18093910080E8F1 -:103AF0009EE3909397008093960080E280937100FC -:103B0000089580910901809310350895803C61F4F7 -:103B10006BED70E08BE399E30E9422188CED682F27 -:103B20008BE399E30C941A2B8B3DC9F76BED70E096 -:103B30008BE399E30E9422188DEDF1CF60EC70E0E9 -:103B40008BE399E30E94221861E270E08BE399E332 -:103B50000E94221880912D350E94861D80912C355F -:103B60000E94861D80912B350E94861D80912A35EA -:103B70000E94861D60EC70E08BE399E30C942218A0 -:103B800060EC70E08BE399E30E94221862E270E03F -:103B90008BE399E30E942218809129350E94861DAB -:103BA000809128350E94861D809127350E94861DB0 -:103BB000809126350E94861D60EC70E08BE399E3CE -:103BC0000C942218CF93C0911201C35660EC70E0A0 -:103BD0008BE399E30E94221863E270E08BE399E3A0 -:103BE0000E9422188C2F0E94861D60EC70E08BE3EF -:103BF00099E3CF910C94221860EC70E08BE399E389 -:103C00000E94221864E270E08BE399E30E9422187C -:103C100080911C390E94861D60EC70E08BE399E373 -:103C20000C94221860EC70E08BE399E30E94221858 -:103C300062E070E08BE399E30E94221880912B39B7 -:103C40000E94861D80912A390E94861D8091293903 -:103C50000E94861D809128390E94861D60EC70E0CC -:103C60008BE399E30C94221860EC70E08BE399E30A -:103C70000E94221861E070E08BE399E30E94221811 -:103C8000809127390E94861D809126390E94861DC9 -:103C9000809125390E94861D809124390E94861DBD -:103CA00060EC70E08BE399E30C942218CF93DF93E0 -:103CB000C8E3DFE0CE010E9420180E94861D2196F5 -:103CC000C11580E1D807B1F7DF91CF91089560EC7D -:103CD00070E08BE399E30E94221861E570E08BE3CA -:103CE00099E30E9422180E94561E60EC70E08BE35C -:103CF00099E30C942218CF93C82F60EC70E08BE30B -:103D000099E30E94221860E970E08BE399E30E9436 -:103D100022186C2F8BE399E30E941A2B60EC70E061 -:103D20008BE399E3CF910C942218CF93DF93C82FA4 -:103D3000D62F0E94031B81110BC0C83C48F48C2F66 -:103D400090E06D2F885C904FDF91CF910C940C1810 -:103D500083E0DF91CF910C947B1ECF93DF930E9481 -:103D6000B61CC0912339C11149C080911501811140 -:103D70003EC0D0912239DD23D1F1409124395091B8 -:103D8000253960912639709127398AEA91E00E949D -:103D90006403892B59F481E00E947B1E90E080E04F -:103DA0000E9448188C2FDF91CF91089581E0809375 -:103DB00023390E94901A0E94C21A0E946B1A0E9414 -:103DC000781A0E94D51A8AEA91E00E94020568EAF0 -:103DD00072E28AEA91E00E94A6020E94A21C0E945E -:103DE0006D1983E090E00E94BF18CD2FDBCF0E94B9 -:103DF0006D1983E090E00E949318D4CF0E946D1952 -:103E0000D1CF60EC70E08BE399E30E94221864E06C -:103E100070E08BE399E30E94221860912C398BE3C8 -:103E200099E30E941A2B60EC70E08BE399E30C9409 -:103E3000221860EC70E08BE399E30E94221865E0A1 -:103E400070E08BE399E30E942218609118018BE3E4 -:103E500099E30E941A2B60EC70E08BE399E30C94D9 -:103E6000221860EC70E08BE399E30E94221869E06D -:103E700070E08BE399E30E94221860911F398BE375 -:103E800099E30E941A2B60EC70E08BE399E30C94A9 -:103E9000221860EC70E08BE399E30E94221863E043 -:103EA00070E08BE399E30E942218609116018BE386 -:103EB00099E30E941A2B60EC70E08BE399E30C9479 -:103EC0002218CF93C82F60EC70E08BE399E30E9437 -:103ED000221860E470E08BE399E30E9422186C2FB3 -:103EE0008BE399E30E941A2B60EC70E08BE399E37B -:103EF000CF910C94221890911735992309F48EC212 -:103F0000803C09F0ACC080910A0181119DC01092E3 -:103F100017358091021D9091031D6091FE1C7091D8 -:103F2000FF1C8617970761F42091041D3091051D31 -:103F30008091001D9091011D2817390709F471C265 -:103F40002091041D3091051D8091021D9091031D4B -:103F500002972817390709F464C28091CF01909124 -:103F6000D0018115984108F05CC22091CB013091BD -:103F7000CC018091CD019091CE01019710F48FEF8B -:103F800097E18217930709F44BC02817390708F007 -:103F900042C0821B930B0196009709F442C24091E4 -:103FA000D1014F5F4093D101E091041DF091051DB7 -:103FB000318320834091041D5091051D2091001DE7 -:103FC0003091011D4217530771F57093051D6093E1 -:103FD000041DE0916A1BF0916B1B918380832091FB -:103FE0006A1B30916B1B8091661B9091671B281791 -:103FF000390721F58091641B9091651B90936B1B91 -:1040000080936A1B8091CD019091CE019093CC0159 -:104010008093CB010895821B930B8F5F974EBCCF8B -:1040200081E090E0BCCF2091041D3091051D2E5FF2 -:104030003F4F3093051D2093041DCBCF80916A1B09 -:1040400090916B1B0296DACF81E0809317358EEF4B -:1040500080930A0110921935109218350895209115 -:10406000183530911935243F91E0390708F0D9C14E -:1040700090910A012115310529F49E3F19F480938E -:104080000A010895911146C08B3D21F481E080938F -:104090001635089590911635992349F08C3D21F0FD -:1040A0008D3D19F48BED01C080EC10921635909186 -:1040B000D101983C08F0B5C12091CF013091D001D9 -:1040C0002115384108F0ADC12091CF013091D001C8 -:1040D0002F5F3F4F3093D0012093CF012091CD012E -:1040E0003091CE01A9014F5F5F4F5093CE014093B5 -:1040F000CD012E523E4FF90180838091CD019091E8 -:10410000CE018115984109F08CC11092CE01109218 -:10411000CD010895913009F044C08B3DD1F49093C6 -:1041200016358091183590911935049709F079C1A9 -:1041300060912237709123378091243790912537F1 -:104140000E94772E6115710581059105D9F40C94B3 -:10415000341E90911635992349F08C3D21F08D3D08 -:1041600019F48BED01C080EC10921635A9014F5F58 -:104170005F4F50931935409318352E5D384CF90137 -:104180008083CFCF6093243970932539809326396B -:1041900090932739809114018131C9F60E94E61A63 -:1041A000D6CF923009F045C08B3DD9F481E08093A1 -:1041B00016358091183590911935049709F031C161 -:1041C0006091223770912337809124379091253761 -:1041D0000E94772E6115710581059105D9F40C9423 -:1041E000121E90911635992349F08C3D21F08D3D9A -:1041F00019F48BED01C080EC10921635A9014F5FC8 -:104200005F4F50931935409318352E5D384CF901A6 -:104210008083CFCF609328397093293980932A39CE -:1042200090932B39809114018131C9F60E94C21AF2 -:10423000D6CF933099F48F3F11F40C94491F8231FB -:1042400008F081E190E0909317018093160180912E -:104250001401813191F70E94901AEFCF9430D1F47C -:104260008F3F11F40C94011F863088F090E08D3060 -:10427000910514F08CE090E090932D3980932C39C7 -:1042800080911401813171F70E946B1AEBCF86E0A7 -:1042900090E0F2CF9530D1F48F3F11F40C94191FB8 -:1042A000853088F090E08930910514F088E090E046 -:1042B000909319018093180180911401813171F755 -:1042C0000E94781AEBCF85E090E0F2CF993021F48C -:1042D0000E94851A0C94311F963079F48F3F11F4A7 -:1042E0000C946D19811103C00E94AF1CF9CF81306D -:1042F00009F097C00E94AD1EF3CF913211F40C94D7 -:104300009E1D923211F40C94C01D933211F40C9442 -:10431000E21D973021F40E94B61C0C94861990334C -:1043200019F490E00C94BF18903421F40E94F91A0B -:104330000C94611F983029F4833709F072C00C94F3 -:10434000ED199E3059F4813021F4809321390C9479 -:10435000CF198111FCCF10922139F9CF9F3041F450 -:104360000E94E21C811102C00C949F190C94B71991 -:10437000993529F4883F09F054C00E949C1C95355A -:1043800029F4883F09F04DC00E943918913511F485 -:104390000C94671E923551F58B3D79F481E0809342 -:1043A000163580911835909119350297D1F56091A5 -:1043B0002337809122370C94951E909116359923BE -:1043C00049F08C3D21F08D3D19F48BED01C080EC5E -:1043D00010921635A9014F5F5F4F509319354093E6 -:1043E00018352E5D384CF9018083DBCF903511F400 -:1043F0000C94051A983411F40C94231A993411F47E -:104400000C94531A973411F40C943B1A933511F40D -:104410000C94041C943529F40C94971C803C09F4EA -:1044200013CE089581E08093C8014FB7F89420918E -:104430000B3530910C3580910D3590910E354FBF75 -:1044400028173907F1F0E0910B35F0910C35809188 -:10445000093590910A35E817F90761F4809107351D -:104460009091083590930C3580930B3580810E9494 -:104470007B1FDBCFE0910B35F0910C35CF0101961E -:10448000F1CF1092C8010895CF93DF9360EC70E0F4 -:104490008BE399E30E94221870E060E08BE399E3DC -:1044A0000E942218C6E2D7E320911A3930911B39B5 -:1044B000CE018652974382179307C8F46991603CF6 -:1044C00061F46BED70E08BE399E30E9422186CEDD0 -:1044D0008BE399E30E941A2BE7CF6B3DC9F76BED95 -:1044E00070E08BE399E30E9422186DEDF1CF60EC50 -:1044F00070E08BE399E30E94221810921B3910920E -:104500001A39DF91CF910895CF93DF93EC01209773 -:10451000E1F0219780911A3990911B39843F9140A5 -:10452000A0F48AEA91E00E94BF0120911A393091EB -:104530001B39A9014F5F5F4F50931B3940931A39C4 -:104540002A5D384CF9018083E2CFDF91CF91089545 -:104550000F931F93CF93DF93EC0180912139811149 -:104560007BC08AEA91E00E94BF012197409111012E -:1045700080FF57C09C0154E0359527955A95E1F787 -:104580002F7033274F3FD9F410921B3910921A39EC -:10459000209311018AEA91E00E9466019093130131 -:1045A000809312018AEA91E00E94520180931C39A3 -:1045B000CE01DF91CF911F910F910C94842224138F -:1045C000E3CF8AEA91E00E946601209112013091C6 -:1045D0001301820F931F97FD0196959587959093F0 -:1045E00013018093120100911C3910E08AEA91E0D6 -:1045F0000E945201800F912F911D95958795809370 -:104600001C39CE010E9484228FEF809311010E94F9 -:10461000E21D0E94FC1DDF91CF911F910F910C9420 -:1046200044224F3F39F010921B3910921A398FEF04 -:10463000809311018AEA91E00E9466019093130130 -:10464000809312018AEA91E00E94520180931C3902 -:10465000CE010E948422DBCF10921B3910921A39AE -:10466000E9CFCF92DF92EF92FF920F931F93CF93F8 -:10467000DF93EC0180912339882309F479C080917C -:104680002139811153C00E94311860E071E080E04F -:1046900090E00E94952C162F107FCF3FD10508F097 -:1046A000116070E060E08AEA91E00E943305612FBA -:1046B0008AEA91E00E94B1009EE2C92E95E3D92ECC -:1046C000C25DDA4CEE24E394F12CCC16DD06F1F059 -:1046D000F60161916F018AEA91E00E94B100FFEF5B -:1046E000EF1AFF0A8FEFE816F10479F78AEA91E0F2 -:1046F0000E942B0270E060E08AEA91E00E9433059C -:10470000612F8AEA91E00E94B100DCCF8AEA91E051 -:104710000E942B020E943518DF91CF911F910F91BB -:10472000FF90EF90DF90CF900C94A21C0E94311864 -:10473000CF3FD10519F010F0CFEFD0E080912039B4 -:1047400070E060E08111BE018AEA91E00E943305C9 -:104750000EE215E3C8018E5295438C179D07B0F603 -:10476000F80161918F018AEA91E00E94B100F2CFD5 -:1047700082E00E947B1E85E090E0DF91CF911F9147 -:104780000F91FF90EF90DF90CF900C9448180F930B -:104790001F93CF93DF938091C90181116EC081E097 -:1047A0008093C901C0E0D8E14FB7F8942091021D71 -:1047B0003091031D8091041D9091051D4FBF281756 -:1047C000390709F45AC0E091021DF091031D809150 -:1047D000001D9091011DE817F90709F03FC0809175 -:1047E000FE1C9091FF1C9093031D8093021D00817D -:1047F0001181E091681BF091691B2091661B30913B -:10480000671BE217F30789F52091641B3091651B44 -:104810003093691B2093681B20813181A90141508D -:104820005109443F514008F0BFCFEEE2F5E30E528C -:104830001543C8018E0F9F1FBE010E94192EDC0177 -:10484000AE52BE4F8C918193CF018E529543821709 -:10485000930778F3C9010E943123A6CFE091021D8E -:10486000F091031DCF010296BECFE091681BF0913D -:10487000691B9F012E5F3F4FCBCF1092D101109249 -:10488000D0011092CF011092C901DF91CF911F91F9 -:104890000F910895809123398823E1F10E944E1DE4 -:1048A0008091D101882311F180912035811102C0BE -:1048B0000E94F21C80912235811128C080912135FF -:1048C000811124C080912035882331F062E370E0AB -:1048D00080E090E00E94D7280E94F21C809122354F -:1048E000811104C0109220350E94C7234FB7F8945D -:1048F00020910B3530910C3580910D3590910E350E -:104900004FBF2817390791F00C94122281E0809351 -:104910002035ECCF80912239882319F00E94E718C6 -:10492000E5CF0E942A190E94AF1CE0CF0895CF93D3 -:104930008091CA01811127C081E08093CA01C4E13E -:104940008BE399E30E94902A892BD9F08FB7F894D2 -:1049500040910B3550910C35209107353091083539 -:104960004217530791F440910D3550910E35209127 -:10497000093530910A354217530739F48FBFC150BA -:10498000F9F61092CA01CF91089540910D355091DA -:104990000E3520910B3530910C35215031094217DD -:1049A000530761F38FBF8BE399E30E94AA2A2FB7C5 -:1049B000F894E0910D35F0910E35808340910D35DE -:1049C00050910E358091093590910A354817590755 -:1049D00051F4809107359091083590930E3580936E -:1049E0000D352FBFCCCF80910D3590910E350196AE -:1049F000F4CF1F920F920FB60F9211240BB60F92A5 -:104A00002F933F934F935F936F937F938F939F93D6 -:104A1000AF93BF93EF93FF930E949724FF91EF9181 -:104A2000BF91AF919F918F917F916F915F914F91C6 -:104A30003F912F910F900BBE0F900FBE0F901F90C4 -:104A400018958091DC3980FF2AC082E0809315356B -:104A50008091153581508230B8F50E94031B882360 -:104A600081F10E941A1B882361F10E94271B882371 -:104A700041F10E94361B882321F10E94431B8823A9 -:104A800051F181E0809322390E94891B882319F11A -:104A90000E94931B82E1809314010C94AD1E82FF4F -:104AA00002C083E0D3CF83FF02C081E0CFCF42E2D8 -:104AB00050E065E571E08BE399E30E945A2C0E9477 -:104AC0007E18109222390895109222394EE150E05A -:104AD00068E771E0F0CF089580E00E9458290E94B5 -:104AE0008D2CA6E0BDE181E098E1FD019C011192D1 -:104AF00021503040E1F7E7E0F5E3B183A083B783CD -:104B0000A683B583A48386E095E39383828326E01E -:104B100040E052EC61E070E08BE399E30E94612B8E -:104B20000E94721D61E08CE00E94262A61E08DE007 -:104B30000E94262AE6E2F7E384EF91E0DF019C0180 -:104B40001D9221503040E1F7E2E2F7E384E0DF011B -:104B50001D928A95E9F7E2EDF1E080E098E1DF014E -:104B60009C011D9221503040E1F7ACE6BBE182E9A7 -:104B700091E0FD019C01119221503040E1F7EEEFF0 -:104B8000FCE1B183A083B783A683B583A4832CEF14 -:104B90003CE133832283A2EDB9E1FD019C01119236 -:104BA00021503040E1F7E4E6FBE1B183A083B78315 -:104BB000A683B583A48382E69BE19383828322E06C -:104BC00030E043E050E064E070E08AEA91E00E9467 -:104BD000F7000C942125CF93CFB7F8948091313909 -:104BE000811127C0E0EDF0E08491E0EBF0E09491DA -:104BF000E82FF0E0EE0FFF1FEA50FF4FA591B491B0 -:104C0000EC91E92321F461E084E00E94622A61E0F2 -:104C100084E00E94262A8CB580618CBD8CB58064AE -:104C20008CBD61E087E00E94262A61E085E00E9459 -:104C3000262A809131398F5F80933139CFBFCF9150 -:104C400008959FB7F89480913139882319F08150E5 -:104C50008093313980913139811105C08CB58F7BBA -:104C60008CBD109230399FBF08952FB7F8948130D2 -:104C700041F0C0F08230A1F082E08093303980E0D2 -:104C800001C082E090912F39892B80932F39809138 -:104C90003039811103C081E0809330392FBF0895EE -:104CA00084E0F0CF81E0EECF909130399230A1F0E6 -:104CB0009FB7F894833088F4E82FF0E0E956FE4F70 -:104CC0008081809520912F39822380932F39811103 -:104CD00002C0109230399FBF089580E0F2CF08954E -:104CE000833060F5E82FF0E0EE0FFF1FE65EFE4F29 -:104CF00071836083813051F08230A1F0809169002E -:104D00008C7F482B40936900E89A08958091690050 -:104D1000837F440F551F440F551F482B4093690054 -:104D2000E99A0895809169008F7C94E0440F551FA3 -:104D30009A95E1F7482B40936900EA9A08958330E9 -:104D400078F4E82FF0E0813061F0823061F0E8988B -:104D5000EE0FFF1FE65EFE4F8FE696E291838083A3 -:104D60000895E998F5CFEA98F3CF1F920F920FB606 -:104D70000F9211240BB60F922F933F934F935F9393 -:104D80006F937F938F939F93AF93BF93EF93FF9313 -:104D9000E0911A01F0911B010995FF91EF91BF91EC -:104DA000AF919F918F917F916F915F914F913F91C3 -:104DB0002F910F900BBE0F900FBE0F901F90189564 -:104DC0001F920F920FB60F9211240BB60F922F93D2 -:104DD0003F934F935F936F937F938F939F93AF9383 -:104DE000BF93EF93FF93E0911C01F0911D01099592 -:104DF000FF91EF91BF91AF919F918F917F916F91B3 -:104E00005F914F913F912F910F900BBE0F900FBE6E -:104E10000F901F9018951F920F920FB60F921124AA -:104E20000BB60F922F933F934F935F936F937F93A4 -:104E30008F939F93AF93BF93EF93FF93E0911E01E6 -:104E4000F0911F010995FF91EF91BF91AF919F9153 -:104E50008F917F916F915F914F913F912F910F9023 -:104E60000BBE0F900FBE0F901F9018950F931F93BE -:104E7000CF93DF938230910510F482E090E0E091CF -:104E8000DA39F091DB3930E020E0B0E0A0E0309793 -:104E900099F42115310509F44AC0281B390B243037 -:104EA0003105D8F58A819B816115710589F1FB0176 -:104EB00093838283FE0111C040815181028113815D -:104EC00048175907E0F04817590799F4109761F00F -:104ED00012960C93129713961C933296CF01DF9182 -:104EE000CF911F910F9108950093DA391093DB3918 -:104EF000F4CF2115310551F04217530738F0A901BD -:104F0000DB019A01BD01DF01F801C1CFEF01F9CF4B -:104F10009093DB398093DA39CDCFFE01E20FF31F96 -:104F2000819391932250310939832883D7CF2091DF -:104F3000D8393091D939232B41F420912201309175 -:104F400023013093D9392093D83920912001309111 -:104F500021012115310541F42DB73EB740912401BF -:104F600050912501241B350BE091D839F091D939A6 -:104F7000E217F307A0F42E1B3F0B2817390778F030 -:104F8000AC014E5F5F4F2417350748F04E0F5F1F8F -:104F90005093D9394093D839819391939FCFF0E0C2 -:104FA000E0E09CCFCF93DF930097E9F0FC013297CC -:104FB00013821282A091DA39B091DB39ED0130E031 -:104FC00020E01097A1F420813181820F931F20915E -:104FD000D8393091D9392817390709F061C0F093D1 -:104FE000D939E093D839DF91CF910895EA01CE17EE -:104FF000DF07E8F54A815B819E0141155105B1F754 -:10500000E901FB83EA8349915991C40FD51FEC173D -:10501000FD0761F4808191810296840F951FE9015B -:1050200099838883828193819B838A83F0E0E0E087 -:1050300012968D919C9113970097B9F52D913C9103 -:105040001197CD010296820F931F2091D83930918C -:10505000D9392817390739F6309751F51092DB39CD -:105060001092DA39B093D939A093D839BCCFD38311 -:10507000C28340815181840F951FC817D90761F4FD -:105080004E5F5F4F88819981480F591F518340833C -:105090008A819B81938382832115310509F0B0CFEA -:1050A000F093DB39E093DA399ECFFD01DC01C0CF0C -:1050B00013821282D7CF08951F920F920FB60F92CC -:1050C00011242F933F938F939F93AF93BF9380911E -:1050D000333990913439A0913539B09136393091C6 -:1050E000323923E0230F2D3758F50196A11DB11D4C -:1050F000209332398093333990933439A0933539E2 -:10510000B09336398091373990913839A091393937 -:10511000B0913A390196A11DB11D80933739909312 -:105120003839A0933939B0933A39BF91AF919F91F3 -:105130008F913F912F910F900FBE0F901F90189558 -:1051400026E8230F0296A11DB11DD2CF2FB7F894E8 -:105150006091333970913439809135399091363975 -:105160002FBF08953FB7F89480913739909138391F -:10517000A0913939B0913A3926B5A89B05C02F3F87 -:1051800019F00196A11DB11D3FBFBA2FA92F982F6D -:105190008827BC01CD01620F711D811D911D42E068 -:1051A000660F771F881F991F4A95D1F708958F9230 -:1051B0009F92AF92BF92CF92DF92EF92FF926B01DC -:1051C0007C010E94B2284B015C01C114D104E104AE -:1051D000F104E9F00E945B280E94B228681979095D -:1051E0008A099B09683E73408105910570F321E0AF -:1051F000C21AD108E108F10888EE880E83E0981EF3 -:10520000A11CB11CC114D104E104F10429F7FF90E1 -:10521000EF90DF90CF90BF90AF909F908F900895C8 -:10522000789484B5826084BD84B5816084BD85B581 -:10523000826085BD85B5816085BDEEE6F0E0808148 -:1052400081608083E1E8F0E0108280818260808369 -:10525000808181608083E0E8F0E0808181608083EC -:10526000E1EBF0E0808184608083E0EBF0E080811E -:1052700081608083E1E9F0E08081826080838081C9 -:1052800081608083E0E9F0E0808181608083EAE7EB -:10529000F0E080818460808380818260808380816F -:1052A000816080838081806880831092C10008952E -:1052B000883020F0873108F487E188512091260159 -:1052C00090E4299F900111248770822B80937C00A9 -:1052D00080917A00806480937A0080917A0086FDC4 -:1052E000FCCF809178009091790008951F93CF931F -:1052F000DF93182FEB0161E00E94262A209739F4F2 -:1053000060E0812FDF91CF911F910C94622ACF3FF3 -:10531000D10511F461E0F5CFE12FF0E0E457FF4F44 -:10532000E491E150EA3008F054C0F0E0E656F64D62 -:105330000C946E2EA429AC29B129BB29E929E929AD -:10534000C529CD29D529DF2984B5806884BDC7BD8D -:10535000DF91CF911F91089584B5806284BDC8BD4F -:10536000F7CF80918000806880938000D09389007F -:10537000C0938800EDCF8091800080628093800090 -:10538000D0938B00C0938A00E3CF8091B0008068F7 -:105390008093B000C093B300DBCF8091B0008062F7 -:1053A0008093B000C093B400D3CF80919000806808 -:1053B00080939000D0939900C0939800C9CF8091BA -:1053C0009000806280939000D0939B00C0939A00DD -:1053D000BFCFC038D1050CF09DCF92CF81508A301D -:1053E00080F5E82FF0E0E950F64D0C946E2E0B2A74 -:1053F0000F2A012A072A212A212A122A182A1C2ABE -:10540000222A809180008F77809380000895809178 -:1054100080008F7DF9CF84B58F7784BD089584B5E2 -:105420008F7DFBCF8091B0008F778093B00008957F -:105430008091B0008F7DF9CF809190008F7780931D -:1054400090000895809190008F7DF9CFCF93DF93E6 -:1054500090E0FC01E455FF4F249184539F4FFC01E1 -:1054600084918823C9F090E0880F991FFC01EA50CD -:10547000FF4FA591B491FC01E451FF4FC591D49128 -:1054800061110DC09FB7F8948C91209582238C9365 -:105490008881282328839FBFDF91CF9108956230B0 -:1054A00051F49FB7F8943C91822F809583238C937D -:1054B000E8812E2BEFCF8FB7F894EC912E2B2C9305 -:1054C0008FBFEACF1F93CF93DF93282F30E0F901EE -:1054D000E457FF4F8491F901E455FF4FD491F9014E -:1054E000E453FF4FC491CC23A9F0162F81110E94E1 -:1054F000EE29EC2FF0E0EE0FFF1FE451FF4FA591D6 -:10550000B4918FB7F894EC91111108C0D095DE23B7 -:10551000DC938FBFDF91CF911F910895DE2BF8CFE1 -:10552000FC01918D228D892F90E0805C9F4F821B22 -:1055300091098F7399270895FC01918D828D981799 -:1055400031F0828DE80FF11D858D90E008958FEF89 -:105550009FEF0895FC01918D828D981761F0A28DC7 -:10556000AE0FBF2FB11D5D968C91928D9F5F9F7383 -:10557000928F90E008958FEF9FEF089583E29CE271 -:10558000892B49F080E090E0892B29F00E94232CA0 -:1055900081110E94000080E090E0892B49F080E0BA -:1055A00090E0892B29F00E94000081110C940000EA -:1055B0000895FC01A48DA80FB92FB11DA35ABF4FA8 -:1055C0002C91848D90E001968F739927848FA68902 -:1055D000B7892C93A089B1898C9180648C93938D29 -:1055E000848D981306C00288F389E02D80818F7D19 -:1055F00080830895CF93DF93EC01888D8823B9F0E1 -:10560000AA89BB89E889F9898C9185FD03C08081CD -:1056100086FD0DC00FB607FCF7CF8C9185FFF2CF4A -:10562000808185FFEDCFCE010E94D92AE9CFDF919D -:10563000CF910895EF92FF920F931F93CF93DF9333 -:10564000EC0181E0888F9B8D8C8D981316C0E889C2 -:10565000F989808185FF11C0EE89FF896083E8891F -:10566000F98980818064808381E090E0DF91CF912F -:105670001F910F91FF90EF900895F62E0B8D10E083 -:105680000F5F1F4F0F731127E02E8C8D8E110CC0F2 -:105690000FB607FCFACFE889F989808185FFF5CF3D -:1056A000CE010E94D92AF1CFEB8DEC0FFD2FF11D19 -:1056B000E35AFF4FF0820B8FEA89FB898081806279 -:1056C000D2CFCF92DF92EF92FF921F93CF93DF93CF -:1056D000EC016A017B01122FE889F98982E080835D -:1056E000C11481EED806E104F104A1F060E079E094 -:1056F0008DE390E0A70196010E942D2E21503109E3 -:10570000410951095695479537952795211580E10F -:10571000380798F0E889F989108260E874E88EE12A -:1057200090E0A70196010E942D2E215031094109D8 -:1057300051095695479537952795EC85FD8530831A -:10574000EE85FF852083188EEC89FD891083EA8918 -:10575000FB89808180618083EA89FB898081886000 -:105760008083EA89FB89808180688083EA89FB895C -:1057700080818F7D8083DF91CF911F91FF90EF908B -:10578000DF90CF9008951F920F920FB60F921124C1 -:105790000BB60F922F938F939F93EF93FF93E0910C -:1057A0004B39F0914C398081E0915139F091523967 -:1057B00082FD1DC09081809154398F5F8F7320913D -:1057C0005539821741F0E0915439F0E0E55CF64C30 -:1057D000958F80935439FF91EF919F918F912F91E5 -:1057E0000F900BBE0F900FBE0F901F9018958081E9 -:1057F000F2CF1F920F920FB60F9211240BB60F9299 -:105800002F933F934F935F936F937F938F939F93C8 -:10581000AF93BF93EF93FF938BE399E30E94D92A51 -:10582000FF91EF91BF91AF919F918F917F916F9178 -:105830005F914F913F912F910F900BBE0F900FBE34 -:105840000F901F9018958BE399E30E94902A21E016 -:10585000892B09F420E0822F0895EBE3F9E313820A -:10586000128288EE93E0A0E0B0E084839583A68363 -:10587000B7838EE991E09183808385EC90E09587F2 -:10588000848784EC90E09787868780EC90E0918B0A -:10589000808B81EC90E0938B828B82EC90E0958BF7 -:1058A000848B86EC90E0978B868B118E128E138EF4 -:1058B000148E0895AF92BF92CF92DF92EF92FF9233 -:1058C0000F931F93CF93DF936C017B018B01040F28 -:1058D000151FEB015E01AE18BF08C017D10759F0C4 -:1058E0006991D601ED91FC910190F081E02DC60106 -:1058F0000995892B79F7C501DF91CF911F910F9100 -:10590000FF90EF90DF90CF90BF90AF900895FC0193 -:1059100044835583668377830895009729F0BC01FB -:1059200090E080E00C94FC2F0895CF92DF92EF92EC -:10593000FF926B017C01C114D104E104F10441F038 -:105940000E94AD2FA70196010E944F2E6B017C0192 -:10595000C701B601FF90EF90DF90CF9008950895B2 -:105960000E9410290E94AF2C0E946C25CEEBDAE237 -:105970000E944A242097E1F30E94BE2AF9CF0E9498 -:10598000372D08F481E008950E94CB2C6894B11162 -:105990000C94A62D08950E948B2D88F09F5798F0A7 -:1059A000B92F9927B751B0F0E1F0660F771F881F24 -:1059B000991F1AF0BA95C9F714C0B13091F00E943E -:1059C000A52DB1E008950C94A52D672F782F882779 -:1059D000B85F39F0B93FCCF3869577956795B39565 -:1059E000D9F73EF490958095709561957F4F8F4FD4 -:1059F0009F4F0895E89409C097FB3EF490958095D9 -:105A0000709561957F4F8F4F9F4F9923A9F0F92F84 -:105A100096E9BB279395F695879577956795B79502 -:105A2000F111F8CFFAF4BB0F11F460FF1BC06F5FE8 -:105A30007F4F8F4F9F4F16C0882311F096E911C0FA -:105A4000772321F09EE8872F762F05C0662371F01B -:105A500096E8862F70E060E02AF09A95660F771F2F -:105A6000881FDAF7880F9695879597F90895990F0B -:105A70000008550FAA0BE0E8FEEF16161706E80718 -:105A8000F907C0F012161306E407F50798F0621B39 -:105A9000730B840B950B39F40A2661F0232B242B0E -:105AA000252B21F408950A2609F4A140A6958FEF2D -:105AB000811D811D089597F99F6780E870E060E07F -:105AC00008959FEF80EC089500240A941616170697 -:105AD00018060906089500240A94121613061406DF -:105AE00005060895092E0394000C11F4882352F042 -:105AF000BB0F40F4BF2B11F460FF04C06F5F7F4FFA -:105B00008F4F9F4F089557FD9058440F551F59F0E0 -:105B10005F3F71F04795880F97FB991F61F09F3F9A -:105B200079F087950895121613061406551FF2CFC3 -:105B30004695F1DF08C0161617061806991FF1CF13 -:105B400086957105610508940895E894BB2766273A -:105B50007727CB0197F908950E94BF2D0C94722DE1 -:105B60000E94642D38F00E946B2D20F0952311F0D7 -:105B70000C945B2D0C94612D11240C94A62D0E9485 -:105B8000832D70F3959FC1F3950F50E0551F629FD1 -:105B9000F001729FBB27F00DB11D639FAA27F00D86 -:105BA000B11DAA1F649F6627B00DA11D661F829FAD -:105BB0002227B00DA11D621F739FB00DA11D621F92 -:105BC000839FA00D611D221F749F3327A00D611DAF -:105BD000231F849F600D211D822F762F6A2F112491 -:105BE0009F5750409AF0F1F088234AF0EE0FFF1FC4 -:105BF000BB1F661F771F881F91505040A9F79E3F1B -:105C0000510580F00C945B2D0C94A62D5F3FE4F3BE -:105C1000983ED4F3869577956795B795F795E79570 -:105C20009F5FC1F7FE2B880F911D9695879597F979 -:105C30000895AA1BBB1B51E107C0AA1FBB1FA617D3 -:105C4000B70710F0A61BB70B881F991F5A95A9F725 -:105C500080959095BC01CD010895A1E21A2EAA1B52 -:105C6000BB1BFD010DC0AA1FBB1FEE1FFF1FA2170C -:105C7000B307E407F50720F0A21BB30BE40BF50B09 -:105C8000661F771F881F991F1A9469F76095709592 -:105C9000809590959B01AC01BD01CF010895052E23 -:105CA00097FB1EF400940E94662E57FD07D00E94B9 -:105CB0002D2E07FC03D04EF40C94662E5095409583 -:105CC000309521953F4F4F4F5F4F089590958095A8 -:105CD000709561957F4F8F4F9F4F0895EE0FFF1F77 -:105CE0000024001C0BBE0790F691E02D0994692753 -:105CF0009627692778278727782708950E94492FB4 -:105D0000A59F900DB49F900DA49F800D911D11240F -:105D10000895E894DF93CF93FC01DB010E94492FA3 -:105D20007F936F93E9019A01AC01BF93AF933F93C7 -:105D30002F93DF010E94492F26F46C1B7D0B820BF1 -:105D4000930B9E01EB01FC010E94582FAF91BF9174 -:105D50002F913F910E94582FBE01CF01F9012F9141 -:105D60003F91CF91DF910895689401C0E8948F929C -:105D70009F92CF93DF930E94C22EDF91CF919F908D -:105D80008F90089588249924F401E401B0E49F934E -:105D9000AA279A158B049C04ED05FE05CF05D007B4 -:105DA000A10798F4AD2FDC2FCF2FFE2FE92D982CD3 -:105DB000892E982F872F762F652F542F432F322F20 -:105DC0002227B85031F7BF9127C01B2EBF91BB27A8 -:105DD000220F331F441F551F661F771F881F991FEF -:105DE000881C991CEE1FFF1FCC1FDD1FAA1FBB1FA5 -:105DF0008A149B04EC05FD05CE05DF05A007B1075D -:105E000048F08A189B08EC09FD09CE09DF09A00BB0 -:105E1000B10B21601A94E1F62EF49401AF01BE019A -:105E2000CD01000C0895002E083090F0982F872F98 -:105E3000762F652F542F432F322F22270850F4CF6F -:105E4000220F331F441F551F661F771F881F991F7E -:105E50000A95B2F7002D089597FD1094002E083092 -:105E600098F00850232F342F452F562F672F782F67 -:105E7000892F912DF4CF15949795879577956795F0 -:105E800057954795379527950A95AAF71124002D20 -:105E90000895A29FB001B39FC001A39F700D811D03 -:105EA0001124911DB29F700D811D1124911D089523 -:105EB0000E94492F460F571FC81FD91F08F431965B -:105EC00008958F929F92AF92BF92CF92DF92EF92FE -:105ED000FF92CF93DF93EC01688179818A819B8166 -:105EE000611571058105910521F464E279ED8BE579 -:105EF00097E02DE133EF41E050E00E944F2E490141 -:105F00005A019B01AC01A7EAB1E40E947E2E6B010D -:105F10007C01ACEEB4EFA50194010E942E30C60EB8 -:105F2000D71EE81EF91EF7FE06C081E0C81AD10888 -:105F3000E10880E8F80AC882D982EA82FB82C701B8 -:105F4000B6019F77DF91CF91FF90EF90DF90CF90D8 -:105F5000BF90AF909F908F9008958F929F92AF9235 -:105F6000BF92CF92DF92EF92FF9260910001709109 -:105F70000101809102019091030161157105810574 -:105F8000910521F464E279ED8BE597E02DE133EFA3 -:105F900041E050E00E944F2E49015A019B01AC01A3 -:105FA000A7EAB1E40E947E2E6B017C01ACEEB4EF57 -:105FB000A50194010E942E30C60ED71EE81EF91EC0 -:105FC000F7FE06C081E0C81AD108E10880E8F80AA7 -:105FD000C0920001D0920101E0920201F09203010F -:105FE000C701B6019F77FF90EF90DF90CF90BF90F1 -:105FF000AF909F908F90089560930001709301017E -:1060000080930201909303010895FB01DC0102C01B -:1060100001900D9241505040D8F70895DC0101C025 -:106020006D9341505040E0F70895F999FECF92BD2D -:1060300081BDF89A992780B50895262FF999FECF4A -:106040001FBA92BD81BD20BD0FB6F894FA9AF99A95 -:106050000FBE01960895B7FF0C947E2E0E947E2EEF -:0A606000821B930B0895F894FFCF04 -:10606A0001000000F82A00002801FE030000000FCA -:10607A0000FFDCFE1101FF0005006F266F266F2668 -:10608A000000DD39800001781E00A02800F03C00E5 -:10609A00405100127A00E4A20024F40048E801907A -:1060AA00D00320A10700000000B1004502B101BFE2 -:1060BA00018002C7004572726F722C20696E646596 -:1060CA007465726D696E61746520626F6F74207693 -:1060DA006563746F720D0A004572726F722C2069C3 -:1060EA006E636F727265637420626F6F7420766577 -:1060FA0063746F720D0A00010204000000001A2B7B -:0A610A005A2C902AAA2A9C2AFA2A8D -:00000001FF diff --git a/Precompiled/rnode_firmware_latest_esp32_generic.zip b/Precompiled/rnode_firmware_latest_esp32_generic.zip deleted file mode 100644 index 337afb4..0000000 Binary files a/Precompiled/rnode_firmware_latest_esp32_generic.zip and /dev/null differ diff --git a/Precompiled/rnode_firmware_latest_featheresp32.zip b/Precompiled/rnode_firmware_latest_featheresp32.zip deleted file mode 100644 index f4d80e1..0000000 Binary files a/Precompiled/rnode_firmware_latest_featheresp32.zip and /dev/null differ diff --git a/Precompiled/rnode_firmware_latest_lora32v20.zip b/Precompiled/rnode_firmware_latest_lora32v20.zip deleted file mode 100644 index f1eeebe..0000000 Binary files a/Precompiled/rnode_firmware_latest_lora32v20.zip and /dev/null differ diff --git a/Precompiled/rnode_firmware_latest_lora32v20_extled.zip b/Precompiled/rnode_firmware_latest_lora32v20_extled.zip deleted file mode 100644 index ff6ae93..0000000 Binary files a/Precompiled/rnode_firmware_latest_lora32v20_extled.zip and /dev/null differ diff --git a/Precompiled/rnode_firmware_latest_lora32v21.zip b/Precompiled/rnode_firmware_latest_lora32v21.zip deleted file mode 100644 index afbbb27..0000000 Binary files a/Precompiled/rnode_firmware_latest_lora32v21.zip and /dev/null differ diff --git a/Precompiled/rnode_firmware_latest_lora32v21_extled.zip b/Precompiled/rnode_firmware_latest_lora32v21_extled.zip deleted file mode 100644 index 1c79ecf..0000000 Binary files a/Precompiled/rnode_firmware_latest_lora32v21_extled.zip and /dev/null differ diff --git a/Precompiled/rnode_firmware_latest_m2560.hex b/Precompiled/rnode_firmware_latest_m2560.hex deleted file mode 100644 index 92413e0..0000000 --- a/Precompiled/rnode_firmware_latest_m2560.hex +++ /dev/null @@ -1,1505 +0,0 @@ -:100000000C945E010C94D91F0C94AE1F0C94831FAA -:100010000C94581F0C942D1F0C94021F0C94D71E87 -:100020000C94AC1E0C9491010C9491010C949101D0 -:100030000C9491010C9491010C9491010C949101F8 -:100040000C9491010C9491010C9491010C949101E8 -:100050000C9491010C9491010C9491010C94621EEA -:100060000C9491010C942C1E0C94021E0C94910182 -:100070000C9491010C9491010C9491010C94042026 -:100080000C9491010C9491010C9491010C949101A8 -:100090000C9491010C9491010C9491010C94910198 -:1000A0000C9491010C9491010C9491010C94910188 -:1000B0000C9491010C9491010C9491010C94910178 -:1000C0000C9491010C9491010C9491010C94910168 -:1000D0000C9491010C9491010C9491010C94910158 -:1000E0000C9491010C9448030C94E2030C94360395 -:1000F0000C944E030C945B040C94F81A0C94290293 -:100100000C94C9030C9433040C9452030C94130301 -:100110000C9444030C94BF200C94F1010C94061C25 -:100120000C94BD1B0C9451040C94D11A0C94320302 -:100130000C941A040C94F4030C941C1C0C94CB1B0C -:100140000C94C0010C94CF010C94C1030C94E301F6 -:100150000C94B7040C947B020C9417030C94FC02CF -:100160000C943D040C94371B0C94911A0C9440038E -:100170000C94D8030C941B030C94FC030C940604FD -:100180000C9410040C943A030C9428030C9422034E -:100190000C9401030C9493010C94CE030C942C0347 -:1001A0000C94EC030C941F030C94D21A0C94470487 -:1001B0000C94290400002100240027002A002D00AF -:1001C0003000330001010000040107010A010000B2 -:1001D0002200250028002B002E00310034000201EF -:1001E0000000050108010B010505050507050808C4 -:1001F0000808020202020A0A0808040404040101B1 -:1002000001010101010103030303030303030407C5 -:1002100007070C0C0C0C0C0C0C0C0202020206065C -:100220000606060606060B0B0B0B0B0B0B0B01024F -:1002300010202008081020401020408002010201F8 -:1002400008040201010204081020408080402010B0 -:100250000804020180040201804020100804020109 -:100260000804020101020408102040800102040871 -:100270001020408000000A0B02090C0D0E08070335 -:100280000401000000000000000000000000000069 -:10029000000000000000000000000000000000005E -:1002A000121110000000000000000000000000001B -:1002B00000000000000000000000BF202092111F7D -:1002C00011241FBECFEFD1E2DEBFCDBF00E00CBFD7 -:1002D00012E0A0E0B2E0E2E4FDE500E00BBF02C006 -:1002E00007900D92A83AB107D9F72FE1A8EAB2E03A -:1002F00001C01D92A131B207E1F711E0CEE5D1E0D6 -:1003000000E006C021970109802FFE010E94A22C67 -:10031000CD35D10780E00807A9F70E9414210C947D -:100320009F2E0C940000AF92BF92CF92DF92EF927B -:10033000FF920F931F93CF93DF936C017B018B018F -:10034000040F151FEB015E01AE18BF08C017D107DF -:1003500059F06991D601ED91FC910190F081E02D69 -:10036000C6011995892B79F7C501DF91CF911F91AE -:100370000F91FF90EF90DF90CF90BF90AF900895D6 -:10038000FC01538D448D252F30E0842F90E0821B9B -:10039000930B541710F0CF96089501970895FC0120 -:1003A000918D828D981761F0A28DAE0FBF2FB11D78 -:1003B0005D968C91928D9F5F9F73928F90E00895D0 -:1003C0008FEF9FEF0895FC01918D828D981731F08A -:1003D000828DE80FF11D858D90E008958FEF9FEFDE -:1003E0000895FC01918D228D892F90E0805C9F4FB4 -:1003F000821B91098F739927089581E59EE10E94E0 -:10040000F10121E0892B09F420E0822F0895FC01FD -:10041000A48DA80FB92FB11DA35ABF4F2C91848D65 -:1004200090E001968F739927848FA689B7892C93C2 -:10043000A089B1898C91837080648C93938D848D15 -:10044000981306C00288F389E02D80818F7D808318 -:100450000895EF92FF920F931F93CF93DF93EC01D8 -:1004600081E0888F9B8D8C8D98131AC0E889F9895B -:10047000808185FF15C09FB7F894EE89FF8960835E -:10048000E889F98980818370806480839FBF81E0DF -:1004900090E0DF91CF911F910F91FF90EF90089521 -:1004A000F62E0B8D10E00F5F1F4F0F731127E02EFC -:1004B0008C8D8E110CC00FB607FCFACFE889F98934 -:1004C000808185FFF5CFCE010E940702F1CFEB8D31 -:1004D000EC0FFD2FF11DE35AFF4FF0829FB7F89408 -:1004E0000B8FEA89FB8980818062CFCF682F81E5FD -:1004F0009EE10C942902CF93DF93EC01888D882331 -:10050000B9F0AA89BB89E889F9898C9185FD03C076 -:10051000808186FD0DC00FB607FCF7CF8C9185FF5B -:10052000F2CF808185FFEDCFCE010E940702E9CF97 -:10053000DF91CF91089580E090E0892B29F00E940F -:10054000FD0181110E94000080E090E0892B49F0BC -:1005500080E090E0892B29F00E94000081110E9428 -:10056000000080E090E0892B49F080E090E0892B4A -:1005700029F00E94000081110E94000080E090E0BC -:10058000892B49F080E090E0892B29F00E9400003F -:1005900081110C9400000895282F30E0F901EC58E7 -:1005A000FD4F9491F901E25DFD4F449128513E4F7A -:1005B000F9012491222309F435C0992311F19150B6 -:1005C0009231F8F4E92FF0E08827E651FD4F8F4F84 -:1005D0000C94A22C1B031F03FC021303170301033B -:1005E000220328032C03320336033A034003440357 -:1005F000010348034E035203809180008F7780935C -:100600008000E22FF0E0EE0FFF1FE253FE4FA591B6 -:10061000B4918FB7F894EC91611148C040954E2386 -:100620004C938FBF0895809180008F7DE8CF80919B -:100630008000877FE4CF84B58F7784BDE2CF84B517 -:100640008F7DFBCF8091B0008F778093B000D9CFA2 -:100650008091B0008F7DF9CF809190008F7780934B -:100660009000CFCF809190008F7DF9CF8091900046 -:10067000877FF5CF8091A0008F778093A000C1CFB6 -:100680008091A0008F7DF9CF8091A000877FF5CF6A -:10069000809120018F7780932001B3CF809120013A -:1006A0008F7DF9CF80912001877FF5CF4E2BB8CF7A -:1006B000CF93DF9390E0FC01E25DFD4F24918851E0 -:1006C0009E4FFC0184918823D1F090E0880F991F00 -:1006D000FC01EC54FE4FA591B491FC01E253FE4F96 -:1006E000C591D49161110EC09FB7F8948C91E22FFF -:1006F000E0958E238C932881E223E8839FBFDF91CE -:10070000CF9108958FB7F894EC91E22BEC938FBFC3 -:10071000F6CF1F93CF93DF93182FEB0161E00E9478 -:100720005803209739F460E0812FDF91CF911F911A -:100730000C94CC02CF3FD10511F461E0F5CFE12F4D -:10074000F0E0EC58FD4FE491E150E23108F0B3C025 -:10075000F0E08827E155FC4F8F4F0C94A22CC10389 -:10076000C903CE03D803E2035B04EC03F403FC03E8 -:10077000060410041A04290433045B043D044704EE -:10078000510484B5806884BDC7BDDF91CF911F91AE -:10079000089584B5806284BDC8BDF7CF8091800084 -:1007A000806880938000D0938900C0938800EDCF4B -:1007B00080918000806280938000D0938B00C093F2 -:1007C0008A00E3CF80918000886080938000D0937E -:1007D0008D00C0938C00D9CF8091B0008068809349 -:1007E000B000C093B300D1CF8091B00080628093FD -:1007F000B000C093B400C9CF80919000806880930E -:100800009000D0939900C0939800BFCF8091900042 -:10081000806280939000D0939B00C0939A00B5CFE4 -:1008200080919000886080939000D0939D00C09349 -:100830009C00ABCF8091A00080688093A000809145 -:10084000A0008F7B8093A000D093A900C093A80044 -:100850009CCF8091A00080628093A000D093AB00D9 -:10086000C093AA0092CF8091A00088608093A000DE -:10087000D093AD00C093AC0088CF809120018068F8 -:1008800080932001D0932901C09328017ECF8091CD -:100890002001806280932001D0932B01C0932A0114 -:1008A00074CF80912001886080932001D0932D0126 -:1008B000C0932C016ACFC038D1050CF03ECF33CFA6 -:1008C0003FB7F8948091B4029091B502A091B6021E -:1008D000B091B70226B5A89B05C02F3F19F001962D -:1008E000A11DB11D3FBFBA2FA92F982F8827BC018A -:1008F000CD01620F711D811D911D42E0660F771FB2 -:10090000881F991F4A95D1F708958F929F92AF92B1 -:10091000BF92CF92DF92EF92FF924B015C010E9457 -:1009200060046B017C010E9460046C197D098E09D2 -:100930009F09683E734081059105A8F321E0821A62 -:100940009108A108B10888EEC80E83E0D81EE11C0A -:10095000F11C81149104A104B10429F7FF90EF90D8 -:10096000DF90CF90BF90AF909F908F9008950895A3 -:100970008091200A9091210AA091220AB091230A25 -:10098000892B8A2B8B2BE1F080911C0A90911D0AF8 -:10099000A0911E0AB0911F0A892B8A2B8B2B81F004 -:1009A00080911602909117028F3F910549F0809136 -:1009B0001A0A90911B0A892B19F0109213020895BC -:1009C00081E0809313020895CF93DF9380EC90E051 -:1009D0000E94760290E080E00E947602CEE1D3E0B1 -:1009E0002091AA023091AB02CE018E519340821722 -:1009F0009307A8F46991603C51F48BED90E00E945C -:100A000076026CED81E59EE10E942902E9CF6B3D03 -:100A1000C9F78BED90E00E9476026DEDF3CF80EC8C -:100A200090E00E9476021092AB021092AA02DF912F -:100A3000CF91089580EC90E00E94760283E090E0F0 -:100A40000E9476026091160281E59EE10E942902D1 -:100A500080EC90E00C94760280EC90E00E947602AC -:100A600085E090E00E9476026091140281E59EE1AB -:100A70000E94290280EC90E00C94760280EC90E0D9 -:100A80000E94760284E090E00E94760260911A0A49 -:100A900081E59EE10E94290280EC90E00C947602B0 -:100AA000CF93C82F80EC90E00E94760280E990E01E -:100AB0000E9476026C2F81E59EE10E94290280EC63 -:100AC00090E0CF910C947602803C51F48BED90E055 -:100AD0000E9476028CED682F81E59EE10C9429023C -:100AE0008B3DC9F78BED90E00E9476028DEDF3CF40 -:100AF0001F93CF93DF9380EC90E00E94760281E019 -:100B000090E00E947602C091200AD091210A1091B3 -:100B1000220A8091230A0E946405812F0E946405A5 -:100B20008D2F0E9464058C2F0E94640580EC90E05C -:100B3000DF91CF911F910C9476021F93CF93DF9397 -:100B400080EC90E00E94760282E090E00E947602C3 -:100B5000C0911C0AD0911D0A10911E0A80911F0A93 -:100B60000E946405812F0E9464058D2F0E946405F8 -:100B70008C2F0E94640580EC90E0DF91CF911F9153 -:100B80000C94760280EC90E00E94760284E290E081 -:100B90000E9476028091A9020E94640580EC90E098 -:100BA0000C947602CF93C0910102C35680EC90E082 -:100BB0000E94760283E290E00E9476028C2F0E94CF -:100BC000640580EC90E0CF910C94760260E08DE0BB -:100BD0000C94CC0261E08DE00C94CC02CF93DF93B7 -:100BE000EC0161E08DE00E94CC020E94E60564E623 -:100BF00070E080E090E00E9485040E94EA0564E6CF -:100C000070E080E090E00E948504219771F7DF9109 -:100C1000CF910C94E60560E08CE00C94CC020F932D -:100C20001F93CF93DF938C01EC01892B11F4C1E06A -:100C3000D0E00E940B0664E670E080E090E00E9445 -:100C4000850461E08CE00E94CC0264E670E080E004 -:100C500090E00E9485040115110559F3219749F789 -:100C6000DF91CF911F910F910C940B060E94EA0522 -:100C70000E940B066AE070E080E090E00E9485042C -:100C800061E08CE00E94CC020E94E60565E070E025 -:100C900080E090E00E948504E9CF0F931F93CF93EB -:100CA000DF938C01EC01892B11F4C1E0D0E061E00D -:100CB0008CE00E94CC0260E08DE00E94CC0264E6F1 -:100CC00070E080E090E00E94850460E08CE00E948B -:100CD000CC0261E08DE00E94CC0264E670E080E02E -:100CE00090E00E9485040115110509F32197F9F69A -:100CF0000E940B06DF91CF911F910F910C94E60596 -:100D00002F923F924F925F926F927F928F929F921B -:100D1000AF92BF92CF92DF92EF92FF920F931F9309 -:100D2000CF93DF93CDB7DEB7C857D1090FB6F8948C -:100D3000DEBF0FBECDBFE9969FAF8EAFE9971A0118 -:100D4000DC0118960D911D912D913C911B97619698 -:100D50000CAF1DAF2EAF3FAF61971C960D911D914B -:100D60002D913C911F9709831A832B833C835096C6 -:100D70000D911D912D913C9153970D831E832F83CF -:100D8000388754960D911D912D913C915797098765 -:100D90001A872B873C87E6966FAFE697E7967FAF7B -:100DA000E797A85ABF4F6396BFAFAEAF6397FC01FA -:100DB000E45AFF4F6596FFAFEEAF65978C01005A7E -:100DC0001F4F67961FAF0EAF67979C012C593F4F7F -:100DD00069963FAF2EAF699788599F4F6B969FAF2B -:100DE0008EAF6B97E996AEADBFADE997A459BF4FF3 -:100DF0006D96BFAFAEAF6D97E996EEADFFADE997DB -:100E0000E059FF4F6F96FFAFEEAF6F97E9960EADCB -:100E10001FADE9970C581F4FA1961FAF0EAFA197BA -:100E2000E9962EAD3FADE99728583F4FA3963FAFC7 -:100E30002EAFA397E9968EAD9FADE99784589F4F4B -:100E4000A5969FAF8EAFA597E996AEADBFADE997DA -:100E5000A058BF4FA796BFAFAEAFA797E996EEAD2C -:100E6000FFADE997EC57FF4FA996FFAFEEAFA997FB -:100E7000E9960EAD1FADE99708571F4FAB961FAF10 -:100E80000EAFAB97E9962EAD3FADE99724573F4F94 -:100E9000AD963FAF2EAFAD97E9968EAD9FADE9977A -:100EA00080579F4FAF969FAF8EAFAF97E996AEAD8D -:100EB000BFADE997AC56BF4FE196BFAFAEAFE1977C -:100EC000E796AEADBFADE7970D911D912D913C9189 -:100ED0000D871E872F87388B6396EEADFFAD639726 -:100EE000008311832283338361968CAD9DADAEADBB -:100EF000BFAD619788589B45A549B842800F911FA7 -:100F0000A21FB31F4D815E816F81788509851A8587 -:100F10002B853C85402751276227732709811A8139 -:100F20002B813C81402351236223732309851A8539 -:100F30002B853C854027512762277327840F951FF7 -:100F4000A61FB71F8C019D0147E0000F111F221F34 -:100F5000331F4A95D1F7AC01BD01E9E1769567955C -:100F600057954795EA95D1F7402B512B622B732B60 -:100F700089819A81AB81BC81480F591F6A1F7B1FF1 -:100F8000E796AEADBFADE79714960D911D912D91EB -:100F90003C911797098B1A8B2B8B3C8B6596EEAD8A -:100FA000FFAD6597008311832283338389859A85FA -:100FB000AB85BC858A5A9844A843B741800F911FDE -:100FC000A21FB31FC980DA80EB80FC800D811E81D7 -:100FD0002F813885C026D126E226F326C422D522C9 -:100FE000E622F722C026D126E226F3268C0D9D1D8F -:100FF000AE1DBF1D8C019D01FCE0000F111F221FC3 -:10100000331FFA95D1F76C017D01A4E1F694E794C2 -:10101000D794C794AA95D1F7C02AD12AE22AF32AF5 -:10102000C40ED51EE61EF71EE796AEADBFADE79720 -:1010300018968D919D910D90BC91A02D8D8B9E8BBE -:10104000AF8BB88F6796EEADFFAD679780839183C6 -:10105000A283B3838D819E81AF81B88585529F48DD -:10106000AF4DBB4D0D891E892F89388D800F911F83 -:10107000A21FB31F49805A806B807C80442655266E -:10108000662677264C205D206E207F2009811A81FC -:101090002B813C814026512662267326840D951DA6 -:1010A000A61DB71D8C019D01EFE03695279517957C -:1010B0000795EA95D1F72C013D01F1E1440C551C4F -:1010C000661C771CFA95D1F7402A512A622A732AA6 -:1010D0004C0C5D1C6E1C7F1CE796AEADBFADE79758 -:1010E0001C968D919D910D90BC91A02D898F9A8F0A -:1010F000AB8FBC8F6996EEADFFAD6997808391830E -:10110000A283B38389819A81AB81BC81825191434F -:10111000A244BE43098D1A8D2B8D3C8D800F911FEB -:10112000A21FB31F8A019B010C251D252E253F25DB -:1011300004211521262137210427152726273727A3 -:10114000800F911FA21FB31F4C015D011AE0B694DE -:10115000A794979487941A95D1F78C019D0186E105 -:10116000000F111F221F331F8A95D1F70829192953 -:101170002A293B29040D151D261D371DE796AEAD06 -:10118000BFADE79750968D919D910D90BC91A02D8C -:101190008D8F9E8FAF8FB8A36B96EEADFFAD6B9723 -:1011A00080839183A283B3838155904FA348BA4033 -:1011B000840F951FA61FB71FB701A601442555250B -:1011C0006625772540235123622373234C255D2513 -:1011D0006E257F25840F951FA61FB71F4C015D014B -:1011E00047E0880C991CAA1CBB1C4A95D1F7AC019E -:1011F000BD01E9E17695679557954795EA95D1F751 -:10120000482959296A297B29400F511F621F731FE2 -:10121000E796AEADBFADE79754968D919D910D9039 -:10122000BC91A02D89A39AA3ABA3BCA36D96EEADF0 -:10123000FFAD6D9780839183A283B383865D9943CD -:10124000A847B84B8C0D9D1DAE1DBF1D73016201DB -:10125000C026D126E226F326C422D522E622F72292 -:10126000C424D524E624F7248C0D9D1DAE1DBF1D7E -:101270004C015D01FCE0880C991CAA1CBB1CFA9572 -:10128000D1F76C017D01A4E1F694E794D794C7945B -:10129000AA95D1F7C828D928EA28FB28C40ED51E5C -:1012A000E61EF71EE796AEADBFADE79758968D9157 -:1012B0009D910D90BC91A02D8DA39EA3AFA3B8A727 -:1012C0006F96EEADFFAD6F9780839183A283B3835A -:1012D0008D5E994BAF4CB745840D951DA61DB71D6E -:1012E0002801390144265526662677264C205D20A4 -:1012F0006E207F204026512662267326840D951D80 -:10130000A61DB71D4C015D01EFE0B694A79497941C -:101310008794EA95D1F72C013D01F1E1440C551C6D -:10132000661C771CFA95D1F7482859286A287B282B -:101330004C0C5D1C6E1C7F1CE796AEADBFADE797F5 -:101340005C968D919D910D90BC91A02D89A79AA737 -:10135000ABA7BCA7A196EEADFFADA197808391830B -:10136000A283B3838F5F9A46A94BB240800F911F2F -:10137000A21FB31F8A019B010C251D252E253F2589 -:101380000421152126213721042715272627372751 -:10139000800F911FA21FB31F4C015D011AE0B6948C -:1013A000A794979487941A95D1F78C019D0186E1B3 -:1013B000000F111F221F331F8A95D1F70829192901 -:1013C0002A293B29040D151D261D371DE796AEADB4 -:1013D000BFADE79790968D919D910D90BC91A02DFA -:1013E0008DA79EA7AFA7B8ABA396EEADFFADA39711 -:1013F00080839183A283B38388529746AF47B649CF -:10140000840F951FA61FB71FB701A60144255525B8 -:101410006625772540235123622373234C255D25C0 -:101420006E257F25840F951FA61FB71F4C015D01F8 -:1014300047E0880C991CAA1CBB1C4A95D1F7AC014B -:10144000BD01E9E17695679557954795EA95D1F7FE -:10145000482959296A297B29400F511F621F731F90 -:10146000E796AEADBFADE79794968D919D910D90A7 -:10147000BC91A02D89AB9AABABABBCABA596EEAD46 -:10148000FFADA59780839183A283B3838155984054 -:10149000AB4BB4478C0D9D1DAE1DBF1D730162018A -:1014A000C026D126E226F326C422D522E622F72240 -:1014B000C424D524E624F7248C0D9D1DAE1DBF1D2C -:1014C0004C015D01FCE0880C991CAA1CBB1CFA9520 -:1014D000D1F76C017D01A4E1F694E794D794C79409 -:1014E000AA95D1F7C828D928EA28FB28C40ED51E0A -:1014F000E61EF71EE796AEADBFADE79798968D91C5 -:101500009D910D90BC91A02D8DAB9EABAFABB8AFB4 -:10151000A796EEADFFADA79780839183A283B38397 -:101520008F54944AA109B109840D951DA61DB71DBC -:101530002801390144265526662677264C205D2051 -:101540006E207F204026512662267326840D951D2D -:10155000A61DB71D4C015D01EFE0B694A7949794CA -:101560008794EA95D1F72C013D01F1E1440C551C1B -:10157000661C771CFA95D1F7482859286A287B28D9 -:101580004C0C5D1C6E1C7F1CE796AEADBFADE797A3 -:101590009C968D919D910D90BC91A02D89AF9AAF95 -:1015A000ABAFBCAFA996EEADFFADA9978083918399 -:1015B000A283B38382549842A34AB647800F911FF7 -:1015C000A21FB31F8A019B010C251D252E253F2537 -:1015D00004211521262137210427152726273727FF -:1015E000800F911FA21FB31F4C015D011AE0B6943A -:1015F000A794979487941A95D1F78C019D0186E161 -:10160000000F111F221F331F8A95D1F708291929AE -:101610002A293B29040D151D261D371DE796AEAD61 -:10162000BFADE797D0968D919D910D90BC91A02D67 -:1016300021968CAF9DAFAEAFBFAF2197AB96EEAD0D -:10164000FFADAB9780839183A283B3834C015D018F -:10165000FEED8F1AFEEE9F0AFFE6AF0AF4E9BF0A1D -:10166000840E951EA61EB71EB701A601442555255A -:101670006625772540235123622373234C255D255E -:101680006E257F25480D591D6A1D7B1D4A015B0192 -:1016900097E0880C991CAA1CBB1C9A95D1F7E9E12C -:1016A0007695679557954795EA95D1F74829592931 -:1016B0006A297B29400F511F621F731FE796AEAD49 -:1016C000BFADE797D4968D919D910D90BC91A02DC3 -:1016D00025968CAF9DAFAEAFBFAF2597AD96EEAD63 -:1016E000FFADAD9780839183A283B3834C015D01ED -:1016F000FDE68F1AFEE89F0AF7E6AF0AF2E0BF0A9E -:101700008C0C9D1CAE1CBF1C73016201C026D1262F -:10171000E226F326C422D522E622F722C424D524C9 -:10172000E624F724C80CD91CEA1CFB1C460157010F -:10173000FCE0880C991CAA1CBB1CFA95D1F7A4E10B -:10174000F694E794D794C794AA95D1F7C828D928D6 -:10175000EA28FB28C40ED51EE61EF71EE796AEAD9E -:10176000BFADE797D8968D919D910D90BC91A02D1E -:1017700029968CAF9DAFAEAFBFAF2997AF96EEADB8 -:10178000FFADAF9780839183A283B3834C015D014A -:10179000F2E78F1AFCEB9F0AF6E8AF0AF9E5BF0AF9 -:1017A000840C951CA61CB71C28013901442655261B -:1017B000662677264C205D206E207F20402651260D -:1017C00062267326480C591C6A1C7B1C530142017B -:1017D000BFE0B694A79497948794BA95D1F781E126 -:1017E000440C551C661C771C8A95D1F7482859284B -:1017F0006A287B284C0C5D1C6E1C7F1CE796AEADE6 -:10180000BFADE797DC968D919D910D90BC91A02D79 -:101810002D968CAF9DAFAEAFBFAF2D97E196EEADDD -:10182000FFADE19780839183A283B3834C015D0177 -:10183000FFED8F1AF7EF9F0AFBE4AF0AF6EBBF0A42 -:10184000800E911EA21EB31E8A019B010C251D2530 -:101850002E253F2504211521262137210427152770 -:1018600026273727080D191D2A1D3B1D4801590140 -:101870009AE0B694A794979487949A95D1F7E6E165 -:10188000000F111F221F331FEA95D1F708291929CC -:101890002A293B29040D151D261D371D89889A8884 -:1018A000AB88BC888EE9881A8AED980A81EEA80A6E -:1018B00089E0B80A840E951EA61EB71EB301A201C8 -:1018C00040275127622773274C215D216E217F21FC -:1018D0004425552566257725480D591D6A1D7B1D14 -:1018E0004A015B01F5E0880C991CAA1CBB1CFA9507 -:1018F000D1F7ABE17695679557954795AA95D1F7BE -:10190000482959296A297B29400F511F621F731FDB -:101910008DA09EA0AFA0B8A490EC891A9CE4990A6F -:101920009FEBA90A9FE3B90A8C0C9D1CAE1CBF1C3F -:1019300068017901C426D526E626F726C420D520DD -:10194000E620F720C026D126E226F326C80CD91CB3 -:10195000EA1CFB1C46015701B9E0880C991CAA1C23 -:10196000BB1CBA95D1F787E1F694E794D794C79456 -:101970008A95D1F7C828D928EA28FB28C40ED51E95 -:10198000E61EF71E89AC9AACABACBCACAFEA8A1AC7 -:10199000A5EA9A0AA1EAAA0AA9EDBA0A840C951C3A -:1019A000A61CB71C2A013B014C245D246E247F2415 -:1019B000402251226222732244265526662677262B -:1019C000480C591C6A1C7B1C530142019EE0880C88 -:1019D000991CAA1CBB1C9A95D1F7E2E176946794F6 -:1019E00057944794EA95D1F7482859286A287B28C4 -:1019F0004C0C5D1C6E1C7F1C8D849E84AF84B8884B -:101A0000B6E58B1AB8E39B0AB9E4AB0AB6E1BB0AA8 -:101A1000800E911EA21EB31E970186010425152576 -:101A20002625372504231523262337230C251D259A -:101A30002E253F25080D191D2A1D3B1D4801590162 -:101A4000FCE0B694A79497948794FA95D1F7A4E113 -:101A5000000F111F221F331FAA95D1F7082919293A -:101A60002A293B29040D151D261D371D89A09AA082 -:101A7000ABA0BCA0E3EA8E1AEFEE9E0AE0EDAE0A40 -:101A8000E9E2BE0A840E951EA61EB71EB301A2018E -:101A900040275127622773274C215D216E217F212A -:101AA0004425552566257725480D591D6A1D7B1D42 -:101AB0004A015B01B5E0880C991CAA1CBB1CBA95B5 -:101AC000D1F78BE176956795579547958A95D1F72C -:101AD000482959296A297B29400F511F621F731F0A -:101AE0008DA89EA8AFA8B8ACFDEA8F1AFBEE9F0A9E -:101AF000FBEBAF0AFDEFBF0A8C0C9D1CAE1CBF1C9C -:101B000068017901C426D526E626F726C420D5200B -:101B1000E620F720C026D126E226F326C80CD91CE1 -:101B2000EA1CFB1C4601570199E0880C991CAA1C71 -:101B3000BB1C9A95D1F7E7E1F694E794D794C79444 -:101B4000EA95D1F7C828D928EA28FB28C40ED51E63 -:101B5000E61EF71E2D968CAC9DACAEACBFAC2D979F -:101B60008FE7881A89E1980A8EE5A80A87E2B80A01 -:101B7000840C951CA61CB71C2A013B014C245D2437 -:101B80006E247F244022512262227322442655264D -:101B900066267726480C591C6A1C7B1C530142019F -:101BA000FEE0880C991CAA1CBB1CFA95D1F7A2E197 -:101BB0007694679457944794AA95D1F74828592862 -:101BC0006A287B284C0C5D1C6E1C7F1C8D8C9E8CA7 -:101BD000AF8CB8A098E3891A94E0990A9CE2A90A0C -:101BE00098E1B90A800E911EA21EB31E97018601CC -:101BF00004251525262537250423152326233723D9 -:101C00000C251D252E253F25080D191D2A1D3B1DC0 -:101C100048015901BCE0B694A79497948794BA956B -:101C2000D1F784E1000F111F221F331F8A95D1F7CE -:101C3000082919292A293B29040D151D261D371DA0 -:101C400089A89AA8ABA8BCA8AAE18A1AA2E39A0A12 -:101C5000AEE1AA0AAEEDBA0A840E951EA61EB71E04 -:101C6000B301A20140275127622773274C215D2130 -:101C70006E217F214425552566257725480D591D60 -:101C80006A1D7B1D4A015B0195E0880C991CAA1C0A -:101C9000BB1C9A95D1F7EBE17695679557954795DB -:101CA000EA95D1F7482959296A297B29400F511F04 -:101CB000621F731F29968CAC9DACAEACBFAC29974C -:101CC000BAE28B1AB8EF9B0AB8ECAB0ABCE3BB0ACA -:101CD0008C0C9D1CAE1CBF1C68017901C426D52646 -:101CE000E626F726C420D520E620F720C026D126F8 -:101CF000E226F326C80CD91CEA1CFB1C460157013E -:101D0000F9E0880C991CAA1CBB1CFA95D1F7A7E135 -:101D1000F694E794D794C794AA95D1F7C828D92800 -:101D2000EA28FB28C40ED51EE61EF71E898C9A8C65 -:101D3000AB8CBC8CE9E78E1AE2EF9E0AEAE2AE0AAF -:101D4000EBE0BE0A840C951CA61CB71C2A013B01C3 -:101D50004C245D246E247F2440225122622273226F -:101D60004426552666267726480C591C6A1C7B1C7F -:101D700053014201BEE0880C991CAA1CBB1CBA95F9 -:101D8000D1F782E176946794579447948A95D1F776 -:101D9000482859286A287B284C0C5D1C6E1C7F1C27 -:101DA0008DA49EA4AFA4B8A8F3E18F1AFBEE9F0AFE -:101DB000F5EAAF0AFAEBBF0A800E911EA21EB31E0F -:101DC000970186010425152526253725042315238B -:101DD000262337230C251D252E253F25080D191DEB -:101DE0002A1D3B1D480159019CE0B694A794979485 -:101DF00087949A95D1F7E4E1000F111F221F331F3A -:101E0000EA95D1F7082919292A293B29040D151D1E -:101E1000261D371D25968CAC9DACAEACBFAC25976E -:101E20008BEF881A86E1980A8CE1A80A86E5B80A41 -:101E3000840E951EA61EB71EB301A201402751278E -:101E4000622773274C215D216E217F214425552572 -:101E500066257725480D591D6A1D7B1D4A015B01CA -:101E6000F5E0880C991CAA1CBB1CFA95D1F7ABE1D4 -:101E70007695679557954795AA95D1F74829592999 -:101E80006A297B29400F511F621F731F8D889E880E -:101E9000AF88B88C98E0891A9CE5990A90E1A90A64 -:101EA00093E0B90A8C0C9D1CAE1CBF1C6801790123 -:101EB000C426D526E626F726C420D520E620F7201E -:101EC000C026D126E226F326C80CD91CEA1CFB1C2E -:101ED00046015701B9E0880C991CAA1CBB1CBA9595 -:101EE000D1F787E1F694E794D794C7948A95D1F710 -:101EF000C828D928EA28FB28C40ED51EE61EF71EDE -:101F000089A49AA4ABA4BCA4A7E28A1AADEF9A0A4A -:101F1000A0E9AA0AA8E9BA0A840C951CA61CB71C59 -:101F20002A013B014C245D246E247F24402251224F -:101F3000622273224426552666267726480C591CB1 -:101F40006A1C7B1C530142019EE0880C991CAA1C50 -:101F5000BB1C9A95D1F7E2E1769467945794479425 -:101F6000EA95D1F7482859286A287B284C0C5D1C33 -:101F70006E1C7F1C4601570184249524A624B72497 -:101F800021968CAD9DADAEADBFAD21978657934BDD -:101F9000A54DB247800F911FA21FB31F9501840169 -:101FA00004231523262337230C251D252E253F2505 -:101FB000800F911FA21FB31F8C019D01FCE036957D -:101FC000279517950795FA95D1F7E5960CAF1DAFB4 -:101FD0002EAF3FAFE5978C019D01A4E1000F111FCB -:101FE000221F331FAA95D1F7E5968CAD9DADAEADFE -:101FF000BFADE597082B192B2A2B3B2B040D151D84 -:10200000261D371D89A19AA1ABA1BCA18E5B964C60 -:10201000A540B109840F951FA61FB71F80269126E2 -:10202000A226B326880E991EAA1EBB1ED501C40186 -:1020300044E0880F991FAA1FBB1F4A95D1F75CE1A6 -:10204000B694A794979487945A95D1F7882A992A99 -:10205000AA2ABB2A800E911EA21EB31E4DA55EA504 -:102060006FA578A94F5759406E4878474C0D5D1DB4 -:102070006E1D7F1D73016201C026D126E226F32664 -:10208000C824D924EA24FB24C40ED51EE61EF71E5C -:10209000B701A601EBE0440F551F661F771FEA95B5 -:1020A000D1F7F5E1F694E794D794C794FA95D1F770 -:1020B000C42AD52AE62AF72AC80CD91CEA1CFB1C1C -:1020C00049AD5AAD6BAD7CAD4E5D5E49624672491D -:1020D000440E551E661E771EB901A80148255925D4 -:1020E0006A257B254C255D256E257F25440E551ED2 -:1020F000661E771ED20123013D014C0C5D1C6E1C37 -:102100007F1C29964CAD5DAD6EAD7FAD2997445FC8 -:10211000574C6A417240400F511F621F731F950157 -:1021200084010C251D252E253F2504251525262552 -:102130003725040F151F261F371FB901A801A9E075 -:102140007695679557954795AA95D1F7B7E1000F12 -:10215000111F221F331FBA95D1F7042B152B262BE5 -:10216000372B040D151D261D371D49895A896B898A -:102170007C894C5B554161447B45480D591D6A1D66 -:102180007B1D4601570184249524A624B72480266C -:102190009126A226B326840E951EA61EB71EB50153 -:1021A000A40184E0440F551F661F771F8A95D1F75D -:1021B0009CE1B694A794979487949A95D1F7842A32 -:1021C000952AA62AB72A800E911EA21EB31E4D8DF7 -:1021D0005E8D6F8D78A1475550436142744BC40E9C -:1021E000D51EE61EF71EB301A20140275127622724 -:1021F0007327482559256A257B254C0D5D1D6E1DCD -:102200007F1DDB01CA01EBE0880F991FAA1FBB1FCE -:10221000EA95D1F7F5E17695679557954795FA9543 -:10222000D1F7482B592B6A2B7B2B480D591D6A1D62 -:102230007B1DC9A4DAA4EBA4FCA4B0EACB1AB4EBCE -:10224000DB0AB4E4EB0AB9E0FB0A4C0C5D1C6E1C23 -:102250007F1C68017901C824D924EA24FB24C42600 -:10226000D526E626F7264C0C5D1C6E1C7F1CF20161 -:1022700023013F01440E551E661E771ECDA8DEA821 -:10228000EFA8F8ACF0E9CF1AF3E4DF0AF0E4EF0AC4 -:10229000F1E4FF0AC00ED11EE21EF31E9501840177 -:1022A0000427152726273727042515252625372512 -:1022B0000C0D1D1D2E1D3F1D68017901A9E0F6942E -:1022C000E794D794C794AA95D1F7B7E1000F111FEF -:1022D000221F331FBA95D1F70C291D292E293F291A -:1022E000040D151D261D371D2596CCACDDACEEACBE -:1022F000FFAC25978AE3C81A81E8D80A84E6E80A81 -:1023000087EDF80AC80CD91CEA1CFB1C4A015B01CA -:1023100084249524A624B72480269126A226B326B9 -:102320008C0C9D1CAE1CBF1C7501640184E0CC0CA0 -:10233000DD1CEE1CFF1C8A95D1F79CE1B694A79496 -:10234000979487949A95D1F78C289D28AE28BF281A -:10235000800E911EA21EB31ECD84DE84EF84F88809 -:1023600096E0C91A98EDD90A9EE5E90A95E1F90ABD -:10237000C40ED51EE61EF71EB301A2014027512749 -:1023800062277327482559256A257B254C0D5D1D3D -:102390006E1D7F1D6A017B01EBE0CC0CDD1CEE1C89 -:1023A000FF1CEA95D1F7F5E1769567955795479526 -:1023B000FA95D1F74C295D296E297F29480D591DC1 -:1023C0006A1D7B1DC98CDA8CEB8CFC8CABE7CA1ABE -:1023D000AFECDA0AA0E1EA0AABE2FA0A4C0C5D1CA7 -:1023E0006E1C7F1C68017901C824D924EA24FB24CF -:1023F000C426D526E626F7264C0C5D1C6E1C7F1CD9 -:10240000F20123013F01440E551E661E771ECDA02A -:10241000DEA0EFA0F8A4FBEFCF1AF2EEDF0AF7E799 -:10242000EF0AFBEFFF0AC00ED11EE21EF31E95015C -:102430008401042715272627372704251525262557 -:1024400037250C0D1D1D2E1D3F1D68017901A9E0CA -:10245000F694E794D794C794AA95D1F7B7E1000F03 -:10246000111F221F331FBA95D1F70C291D292E29C0 -:102470003F29040D151D261D371DC9A8DAA8EBA894 -:10248000FCA887ECC81A8FE2D80A8BE2E80A86E239 -:10249000F80AC80CD91CEA1CFB1C4A015B01842405 -:1024A0009524A624B72480269126A226B3268C0C38 -:1024B0009D1CAE1CBF1C7501640184E0CC0CDD1CAE -:1024C000EE1CFF1C8A95D1F79CE1B694A7949794D3 -:1024D00087949A95D1F78C289D28AE28BF28800E26 -:1024E000911EA21EB31E2196CCACDDACEEACFFACAF -:1024F00021979BE1C91A96E6D90A94E2E90A99E183 -:10250000F90AC40ED51EE61EF71EB301A20140272C -:10251000512762277327482559256A257B254C0DAD -:102520005D1D6E1D7F1D6A017B01EBE0CC0CDD1C87 -:10253000EE1CFF1CEA95D1F7F5E176956795579566 -:102540004795FA95D1F74C295D296E297F29480DC9 -:10255000591D6A1D7B1D2D96CCACDDACEEACFFACDD -:102560002D97A8E0CA1AA3E8DA0AADE5EA0AA0EEB8 -:10257000FA0A4C0C5D1C6E1C7F1C68017901C82492 -:10258000D924EA24FB24C426D526E626F7264C0CBB -:102590005D1C6E1C7F1CF20123013F01440E551E81 -:1025A000661E771ECD88DE88EF88F88CFBE9CF1A8F -:1025B000F9EADF0AF3E5EF0AFBE3FF0AC00ED11EDA -:1025C000E21EF31E950184010427152726273727CD -:1025D00004251525262537250C0D1D1D2E1D3F1DF7 -:1025E00068017901A9E0F694E794D794C794AA9575 -:1025F000D1F7B7E1000F111F221F331FBA95D1F792 -:102600000C291D292E293F29040D151D261D371DB6 -:10261000CD84DE84EF84F8888CEBC81A8DEDD80A5F -:1026200086EDE80A8BE0F80AC80CD91CEA1CFB1CF2 -:102630004A015B0180949094A094B094802A912ADE -:10264000A22AB32A84249524A624B7248C0C9D1C8A -:10265000AE1CBF1C7501640186E0CC0CDD1CEE1CB9 -:10266000FF1C8A95D1F79AE1B694A7949794879422 -:102670009A95D1F78C289D28AE28BF28800E911EF0 -:10268000A21EB31EC9A4DAA4EBA4FCA499E6C91A3D -:10269000D10895EDE90A9CEBF90AC40ED51EE61E99 -:1026A000F71EB301A2014095509560957095482999 -:1026B00059296A297B2940275127622773274C0D06 -:1026C0005D1D6E1D7F1D6A017B01EAE0CC0CDD1CE7 -:1026D000EE1CFF1CEA95D1F7F6E1769567955795C4 -:1026E0004795FA95D1F74C295D296E297F29480D28 -:1026F000591D6A1D7B1D2996CCACDDACEEACFFAC40 -:102700002997A9E5CA1AACEDDA0AABE6EA0AA4E50C -:10271000FA0AC40CD51CE61CF71C280139014094A8 -:10272000509460947094442A552A662A772A482443 -:1027300059246A247B244C0C5D1C6E1C7F1C730185 -:102740006201AFE0CC0CDD1CEE1CFF1CAA95D1F79A -:10275000B1E17694679457944794BA95D1F74C2891 -:102760005D286E287F28440E551E661E771EC9A060 -:10277000DAA0EBA0FCA0B7ECCB1ABFE5DB0ABCE605 -:10278000EB0AB3E0FB0AC00ED11EE21EF31E950158 -:102790008401009510952095309504291529262946 -:1027A000372904271527262737270C0D1D1D2E1D19 -:1027B0003F1D680179018BE0F694E794D794C794A4 -:1027C0008A95D1F795E1000F111F221F331F9A95AB -:1027D000D1F70C291D292E293F29040D151D261D71 -:1027E000371D2196CCACDDACEEACFFAC2197EDE310 -:1027F000CE1AE6EADE0AE4EAEE0AEAE9FE0AC80CC4 -:10280000D91CEA1CFB1C4A015B0180949094A094A3 -:10281000B094802A912AA22AB32A84249524A6243B -:10282000B7248C0C9D1CAE1CBF1C75016401E6E036 -:10283000CC0CDD1CEE1CFF1CEA95D1F7FAE1B69436 -:10284000A79497948794FA95D1F78C289D28AE2861 -:10285000BF28800E911EA21EB31EC98CDA8CEB8C91 -:10286000FC8CFEE6CF1AF3E3DF0AF3EFEF0AF0E7A2 -:10287000FF0AC40ED51EE61EF71EB301A201409545 -:10288000509560957095482959296A297B294027D8 -:102890005127622773274C0D5D1D6E1D7F1D6A0138 -:1028A0007B01AAE0CC0CDD1CEE1CFF1CAA95D1F725 -:1028B000B6E17695679557954795BA95D1F74C2926 -:1028C0005D296E297F29480D591D6A1D7B1DCDA8E4 -:1028D000DEA8EFA8F8AC83E8C81A8BE0D80A80E13C -:1028E000E80AF108C40CD51CE61CF71C28013901C4 -:1028F0004094509460947094442A552A662A772A0A -:10290000482459246A247B244C0C5D1C6E1C7F1CBB -:10291000730162018FE0CC0CDD1CEE1CFF1C8A955C -:10292000D1F791E176946794579447949A95D1F7AB -:102930004C285D286E287F28440E551E661E771E83 -:10294000C988DA88EB88FC889FE2C91A92EAD90A1A -:102950009BE7E90A9AE7F90AC00ED11EE21EF31EB0 -:10296000950184010095109520953095042915292D -:102970002629372904271527262737270C0D1D1D43 -:102980002E1D3F1D68017901EBE0F694E794D79482 -:10299000C794EA95D1F7F5E1000F111F221F331FED -:1029A000FA95D1F70C291D292E293F29040D151D53 -:1029B000261D371DCDA4DEA4EFA4F8A8A1EBCA1AEA -:1029C000A1E8DA0AA7E5EA0AA0E9FA0AC80CD91CC4 -:1029D000EA1CFB1C4A015B0180949094A094B09483 -:1029E000802A912AA22AB32A84249524A624B724D3 -:1029F0008C0C9D1CAE1CBF1C75016401A6E0CC0CA8 -:102A0000DD1CEE1CFF1CAA95D1F7BAE1B694A79481 -:102A100097948794BA95D1F78C289D28AE28BF2823 -:102A2000800E911EA21EB31E2D968CAD9DADAEAD37 -:102A3000BFAD2D9780529941A34DB140840F951F92 -:102A4000A61FB71FB301A2014095509560957095E0 -:102A5000482959296A297B2940275127622773274A -:102A6000840F951FA61FB71F6C017D014AE0CC0C97 -:102A7000DD1CEE1CFF1C4A95D1F7AC01BD01E6E15F -:102A80007695679557954795EA95D1F74C295D2935 -:102A90006E297F29480D591D6A1D7B1D8DA19EA1A0 -:102AA000AFA1B8A58C5E9C4BAE4FBC45840D951D67 -:102AB000A61DB71D2801390140945094609470946C -:102AC000442A552A662A772A482459246A247B24D2 -:102AD000840D951DA61DB71D6C017D01FFE0CC0C7A -:102AE000DD1CEE1CFF1CFA95D1F72C013D01A1E184 -:102AF0007694679457944794AA95D1F74C285D280B -:102B00006E287F28440E551E661E771E25968CADB6 -:102B10009DADAEADBFAD25978F559E4EA74FB14B26 -:102B2000800F911FA21FB31F95018401009510957E -:102B3000209530950429152926293729042715279A -:102B400026273727800F911FA21FB31F6C017D011D -:102B50001BE0F694E794D794C7941A95D1F78C01AB -:102B60009D0185E1000F111F221F331F8A95D1F7A8 -:102B70000C291D292E293F29040D151D261D371D41 -:102B80008D8D9E8DAF8DB8A18E579148AC4AB840BF -:102B9000880D991DAA1DBB1D4A015B01809490946C -:102BA000A094B094802A912AA22AB32A842495243E -:102BB000A624B724880D991DAA1DBB1D6C017D019B -:102BC000E6E0CC0CDD1CEE1CFF1CEA95D1F74C01B5 -:102BD0005D01FAE1B694A79497948794FA95D1F79A -:102BE0008C289D28AE28BF28800E911EA21EB31EE1 -:102BF00089AD9AADABADBCAD8B5C9D40A54CB244EC -:102C0000840F951FA61FB71FB301A20140955095D1 -:102C100060957095482959296A297B2940275127B1 -:102C200062277327840F951FA61FB71F6C017D01B4 -:102C30004AE0CC0CDD1CEE1CFF1C4A95D1F7AC0120 -:102C4000BD01E6E17695679557954795EA95D1F7E9 -:102C50004C295D296E297F29480D591D6A1D7B1D50 -:102C60008D899E89AF89B88D85549D42A842B54D06 -:102C7000840D951DA61DB71D28013901409450945F -:102C800060947094442A552A662A772A4824592445 -:102C90006A247B24840D951DA61DB71D6C017D0142 -:102CA000FFE0CC0CDD1CEE1CFF1CFA95D1F72C01CB -:102CB0003D01A1E17694679457944794AA95D1F782 -:102CC0004C285D286E287F28440E551E661E771EF0 -:102CD00089A99AA9ABA9BCA98F569C42A947B4411E -:102CE000800F911FA21FB31F9501840100951095BD -:102CF00020953095042915292629372904271527D9 -:102D000026273727800F911FA21FB31F6C017D015B -:102D10001BE0F694E794D794C7941A95D1F78C01E9 -:102D20009D0185E1000F111F221F331F8A95D1F7E6 -:102D30000C291D292E293F29040D151D261D371D7F -:102D400061968CAD9DADAEADBFAD6197880D991DFF -:102D5000AA1DBB1D61968CAF9DAFAEAFBFAF619793 -:102D600089819A81AB81BC81800F911FA21FB31F03 -:102D700089839A83AB83BC830D811E812F81388523 -:102D8000040D151D261D371D0D831E832F833887C7 -:102D900089859A85AB85BC85840F951FA61FB71FB3 -:102DA00089879A87AB87BC87E796AEADBFADE797BB -:102DB000A05CBF4FE796BFAFAEAFE797B0E42B1A6A -:102DC000310811F00C94600761960CAD1DAD2EAD6D -:102DD0003FAD6197E996AEADBFADE99718960D93FB -:102DE0001D932D933C931B9709811A812B813C8164 -:102DF0001C960D931D932D933C931F970D811E815F -:102E00002F81388550960D931D932D933C935397A6 -:102E100009851A852B853C8554960D931D932D931A -:102E20003C935797E7968EAD9FADE797C858DF4F15 -:102E30000FB6F894DEBF0FBECDBFDF91CF911F91CB -:102E40000F91FF90EF90DF90CF90BF90AF909F9049 -:102E50008F907F906F905F904F903F902F9008954C -:102E60000F931F93CF93DF93EC01062F142F60E095 -:102E70008E850E94CC023C852D858091170A88237F -:102E800081F09FB7F8948091170A813081F58DB356 -:102E90008093A8024DB38091AC02809584238DBBB2 -:102EA0009FBF3CBD2DBD0EBD00000DB407FEFDCF84 -:102EB0008EB51EBD00000DB407FEFDCF1EB580917E -:102EC000170A882351F09FB7F8942091170A809130 -:102ED000A802213079F48DBB9FBF61E08E850E94EE -:102EE000CC02812FDF91CF911F910F910895909384 -:102EF000A802D7CF8FBFF1CFAF92BF92CF92DF9210 -:102F0000EF92FF920F931F93CF936093031F7093E1 -:102F1000041F8093051F9093061F6B017C01FF0C1B -:102F2000CC08DC2C76019B01AC016C2D7C2D8C2D0A -:102F30009C2D03E10E943B2DA12CE8E4BE2EF8EE6F -:102F4000CF2EDD24D394E12CF12C00E010E00E9480 -:102F5000DE2CC22FC22ED32E7A01A7016627772737 -:102F600066E88FEE9EE10E94301777276F2D5E2D69 -:102F70004D2D67E88FEE9EE10E9430174C2F68E8D8 -:102F80008FEE9EE1CF911F910F91FF90EF90DF9018 -:102F9000CF90BF90AF900C94301740E00C94301756 -:102FA000CF92DF92EF92FF920F931F93CF93DF9315 -:102FB0006AE18FEE9EE10E94CD17C82FD0E069E153 -:102FC0008FEE9EE10E94CD17682F880F770B880B3C -:102FD000990B0E94C22A20E030E040E85EE30E94A4 -:102FE000212B0E94582C6B018B016091031F709163 -:102FF000041F8091051F9091061F0E94C22A24ED94 -:1030000030E843E45EE40E94BB2ABE0187FD10C0A5 -:103010006D59710917FF0FC06C0D7D1DCB01DF913C -:10302000CF911F910F91FF90EF90DF90CF90089577 -:10303000645A7109EFCF072E000C880B990B0E9480 -:10304000C22A20EB32E748E85FE30E94212B0E946E -:10305000582CE4CF43E861E88FEE9EE10E943017E0 -:1030600062E18FEE9EE10E94CD1783FFF9CF48E029 -:1030700062E98FEE9EE10E94301781E090E00895B2 -:10308000CF92DF92EF92FF926EE18FEE9EE10E946F -:10309000CD1782958F70CC24C394D12C02C0CC0C58 -:1030A000DD1C8A95E2F70D2C000CEE08FF086DE19F -:1030B0008FEE9EE10E94CD1790E044E095958795B4 -:1030C0004A95E1F78A30910534F5FC01EE0FFF1FB8 -:1030D000E80FF91FEC5DFD4F608171818281082E40 -:1030E000000C990B28EE33E040E050E00E94F52BF5 -:1030F000C701B6010E94F52B21313105410551056B -:1031000074F04CE066EA8FEE9EE1FF90EF90DF9066 -:10311000CF900C94301760E070E0CB01E3CF44E037 -:10312000F1CFCF93DF93EC01863091052CF08D30F9 -:10313000910534F5069731F545EC61EB8FEE9EE194 -:103140000E9430174CE067EB8FEE9EE10E94301733 -:10315000C6E0D0E06EE18FEE9EE10E94CD178F7049 -:1031600094E0CC0FDD1F9A95E1F74C2F482B6EE9C8 -:103170008FEE9EE10E943017DF91CF910C944018A2 -:10318000CCE0D0E043EC61EB8FEE9EE10E94301783 -:103190004AE067EB8FEE9EE10E943017DBCFCF93C2 -:1031A000DF93EC01099714F0C8E0D0E06DE18FEEF9 -:1031B0009EE10E94CD17C530D10514F4C5E0D0E0E2 -:1031C0002497CC0FDD1F482F417F4C2B6DE98FEEEC -:1031D0009EE1DF91CF910C94301710920A1F10924C -:1031E000091F6DE18FEE9EE10E94CD17482F4E7FA3 -:1031F0006DE98FEE9EE10C94301781E090E09093A2 -:103200000A1F8093091F6DE18FEE9EE10E94CD178A -:10321000482F41606DE98FEE9EE10C943017CF93FB -:10322000DF93EC0141E861E88FEE9EE10E943017E8 -:10323000CD2B99F00E94FD1840E06DE88FEE9EE1E5 -:103240000E94301740E062EA8FEE9EE10E94301744 -:1032500081E090E0DF91CF9108950E94ED18ECCFCE -:103260000F931F93CF93DF9366E08FEE9EE10E9452 -:10327000CD17D82F67E08FEE9EE10E94CD17C82FA3 -:1032800068E08FEE9EE10E94CD174C2F50E070E079 -:1032900060E0762F652F542F44276D2B8A019B0108 -:1032A000082BC901B80120E038E448EE51E00E9443 -:1032B000B12C03E10E94562D2093200A3093210A5D -:1032C0004093220A5093230ADF91CF911F910F91CF -:1032D0000895CF92DF92EF92FF92CF93DF936091A8 -:1032E0001C0A70911D0A80911E0A90911F0A0E946B -:1032F000C22A6B017C0120E030EC43EF55E40E94D0 -:10330000BB2AD0E0C0E018160CF065C020E030E821 -:1033100042E256E4C701B6010E94BB2AC1E0D0E0F8 -:1033200018160CF058C020E030EC43E756E4C70113 -:10333000B6010E94BB2AC2E0D0E018160CF04BC0C8 -:1033400020E030E842EA56E4C701B6010E94BB2AF9 -:10335000C3E0D0E018160CF03EC020E034E244EFA9 -:1033600056E4C701B6010E94BB2AC4E0D0E018169B -:1033700094F520E034EE42E257E4C701B6010E9422 -:10338000BB2AC5E0D0E0181634F520E034E244E76B -:1033900057E4C701B6010E94BB2AC6E0D0E0181668 -:1033A000D4F420E034E244EF57E4C701B6010E94B0 -:1033B000BB2AC7E0D0E0181674F420E034E244E7FA -:1033C00058E4C701B6010E94BB2AC9E0D0E0181634 -:1033D00014F0C8E0D0E06DE18FEE9EE10E94CD17C1 -:1033E0008F7044E0CC0FDD1F4A95E1F74C2F482B3E -:1033F0006DE98FEE9EE10E9430170E944018809187 -:10340000501E882319F16DE18FEE9EE10E94CD17C9 -:1034100090E034E0959587953A95E1F78A309105EB -:10342000E4F4FC01EE0FFF1FE80FF91FEC5DFD4F08 -:1034300080819181A2810A2E000CBB0B80931C0A13 -:1034400090931D0AA0931E0AB0931F0ADF91CF919B -:10345000FF90EF90DF90CF90089580E090E0DC0146 -:10346000EDCF68E18FEE9EE10E94CD179FB7F894F3 -:103470004091130850911408609115087091160836 -:103480009FBF4093170850931808609319087093D2 -:103490001A0880FF2AC081E08093B1028093B002B5 -:1034A0008093AF028091AD029091AE022091B10263 -:1034B000211108C02091B002211104C02091AF0257 -:1034C000222301F18F309105B8F401969093AE025A -:1034D0008093AD0281E080934D1E80914C1E882325 -:1034E000F1F061E08CE00C94CC021092B1021092E9 -:1034F000B0021092AF02D6CF81E080934D1E809330 -:103500004C1EEBCF009741F001979093AE02809351 -:10351000AD0210924D1EE1CF10924C1EFACF0C94CA -:103520000B06CF93DF9362E18FEE9EE10E94CD17F1 -:10353000C82F482F62E98FEE9EE10E943017C5FD2B -:103540002DC01092081F1092071F8091091F9091A3 -:103550000A1F62E2892B09F463E18FEE9EE10E946B -:10356000CD17C82FD0E060E18FEE9EE10E94CD170D -:10357000482F6DE88FEE9EE10E943017E0910B1FFF -:10358000F0910C1F309711F0CE01199540E06DE8D5 -:103590008FEE9EE1DF91CF910C943017DF91CF91A8 -:1035A000089508950F931F93CF93DF93EC01E88163 -:1035B000F9810084F185E02D1995892BB1F06DE03A -:1035C000CE010E94CD17082F60E0CE010E94CD17DA -:1035D000182F402F6DE8CE010E943017812F90E008 -:1035E000DF91CF911F910F9108958FEF9FEFF8CF4B -:1035F000CF93DF93EC01E881F9810084F185E02D20 -:103600001995892B69F0888D998D0196998F888FEE -:1036100060E0CE010E94CD1790E0DF91CF91089538 -:103620008FEF9FEFFACFCF93DF93EC012097E1F07C -:1036300021978091AA029091AB02843F9140A0F41F -:103640008FEE9EE10E94F81A2091AA023091AB02FF -:10365000A9014F5F5F4F5093AB024093AA02225ED5 -:103660003C4FF9018083E2CFDF91CF9108950F9312 -:103670001F93CF93DF93EC018091190A811178C0D9 -:103680008FEE9EE10E94F81A21974091000280FF80 -:1036900055C09C0154E0359527955A95E1F72F7058 -:1036A00033274F3FD1F41092AB021092AA0220931D -:1036B00000020E94D017909302028093010269E1F8 -:1036C0008FEE9EE10E94CD178093A902CE01DF917B -:1036D000CF911F910F910C94131B2413E4CF0E94E0 -:1036E000D0172091010230910202820F931F97FDA3 -:1036F000019695958795909302028093010200911F -:10370000A90210E069E18FEE9EE10E94CD17800FC3 -:10371000912F911D959587958093A902CE010E94C6 -:10372000131B8FEF809300020E94D2050E94C205F6 -:10373000DF91CF911F910F910C94E4044F3F39F02A -:103740001092AB021092AA028FEF809300020E94A7 -:10375000D017909302028093010269E18FEE9EE1FF -:103760000E94CD178093A902CE010E94131BDCCFCB -:103770001092AB021092AA02EACFCF93DF93EC0132 -:1037800063E10E94CD1790E0288D398D821B930B49 -:10379000DF91CF910895AF92BF92CF92DF92FF92C7 -:1037A0000F931F93CF93DF936C015B01EA0162E2F9 -:1037B0000E94CD17F82E90E09C012C0F3D1F2F3F4B -:1037C000310529F020F0CFEFD0E0C81BD90B8501DF -:1037D000AC0EBD1E0A151B0541F0F80141918F0189 -:1037E00060E8C6010E943017F5CF4C2F4F0D62EAFA -:1037F000C6010E943017CE01DF91CF911F910F912A -:10380000FF90DF90CF90BF90AF900895CF93DF935C -:103810001F92CDB7DEB76983DC01ED91FC91028088 -:10382000F381E02D41E050E0BE016F5F7F4F1995BD -:103830000F90DF91CF91089590E080E008950C946F -:10384000DF2BCF938091150A811103C00E94ED18E0 -:103850000CC0C091160ACC23C9F30E94FD184C2F4E -:1038600062EA8FEE9EE10E94301745E861E88FEE34 -:103870009EE1CF910C9430178091501E882341F126 -:103880008091B802843A89F440911602509117024F -:103890004F30510514F04EE050E057FD40E04067D6 -:1038A00069E88FEE9EE10C943017893A89F44091D3 -:1038B0001602509117024231510514F041E150E0D7 -:1038C0004230510514F442E050E042504068E8CFE5 -:1038D0000895CF92DF92EF92FF92CF93DF930E94F1 -:1038E000B804D091501ED1117DC080911302811176 -:1038F00084C1C0914B1ECC2309F47FC1C090200A23 -:10390000D090210AE090220AF090230A61E0809191 -:10391000FD1E0E94580361E08091FD1E0E94CC02B2 -:103920008091FF1E9091001F8F3F9807C9F061E0C2 -:103930000E94580360E08091FF1E0E94CC026AE062 -:1039400070E080E090E00E94850461E08091FF1EBD -:103950000E94CC026AE070E080E090E00E94850462 -:10396000DFB7F8948091180A811127C0EDE1F2E0E9 -:103970008491E3E6F2E09491E82FF0E0EE0FFF1F70 -:10398000EC54FE4FA591B491EC91E92321F461E050 -:1039900085E30E94CC0261E085E30E9458038CB568 -:1039A00080618CBD8CB580648CBD61E084E30E9435 -:1039B000580361E083E30E9458038091180A8F5FE7 -:1039C0008093180ADFBF62E48FEE9EE10E94CD175C -:1039D000823181F081E00E94500590E080E00E94F9 -:1039E0004D06D0E08D2FDF91CF91FF90EF90DF90CB -:1039F000CF90089540E861E88FEE9EE10E94301775 -:103A0000C701B6010E947C1740E06EE88FEE9EE190 -:103A10000E94301740E06FE88FEE9EE10E94301761 -:103A20006CE08FEE9EE10E94CD17482F43606CE85A -:103A30008FEE9EE10E94301744E066EA8FEE9EE131 -:103A40000E94301740E869E88FEE9EE10E9430172F -:103A500041E861E88FEE9EE10E94301781E080939B -:103A6000501E0E943C1C0E94691980911A0A909174 -:103A70001B0A0E94911880911402909115020E94D5 -:103A8000CF180E9430196EE18FEE9EE10E94CD1793 -:103A9000482F44606EE98FEE9EE10E94301787E365 -:103AA0009BE190930C1F80930B1F60E08091011F9E -:103AB0000E94580340E060EC8FEE9EE10E943017B8 -:103AC0002091011F3091021F80E02230310551F01A -:103AD00081E02330310531F0C9014297049788F421 -:103AE00087E1821B2FB7F8948230E9F160F48823D4 -:103AF00009F43FC0813071F082E08093170A80E0C2 -:103B00000AC08FEFEFCF843091F178F18530A1F7C3 -:103B100088E001C080E29091AC02892B8093AC02D6 -:103B20008091170A811103C081E08093170A2FBF8B -:103B30002091011F3091021F22303105E1F481E90B -:103B40009AE1909304028093030280916A0083605B -:103B500080936A00EC9A0E94211C83E090E00E940E -:103B60000F06DC2F3FCF81E0D6CF82E0D4CF84E0B8 -:103B7000D2CF80E1D0CF2330310569F481E99AE1D9 -:103B8000909306028093050280916A008C60809376 -:103B90006A00ED9AE0CFC90142970497E0F687E109 -:103BA000821BE82FF0E0EE0FFF1FED5FFD4F21E9D4 -:103BB0003AE131832083843091F08530B9F083304D -:103BC00039F080916900836080936900E89AC3CFDF -:103BD000809169008C6080936900E99ABCCF8091E4 -:103BE0006900806380936900EA9AB5CF809169008B -:103BF000806C80936900EB9AAECF83E090E00E94E6 -:103C0000EE05F0CE1F920F920FB60F9211240BB655 -:103C10000F922F933F934F935F936F937F938F9365 -:103C20009F93AF93BF93EF93FF9381E59EE10E9433 -:103C30000702FF91EF91BF91AF919F918F917F917B -:103C40006F915F914F913F912F910F900BBE0F900D -:103C50000FBE0F901F9018951F920F920FB60F92E4 -:103C600011240BB60F922F938F939F93EF93FF9393 -:103C7000E091611EF091621E8081E091671EF091DB -:103C8000681E82FD1DC0908180916A1E8F5F8F73B8 -:103C900020916B1E821741F0E0916A1EF0E0EF5A0E -:103CA000F14E958F80936A1EFF91EF919F918F91B6 -:103CB0002F910F900BBE0F900FBE0F901F90189575 -:103CC0008081F2CF1F920F920FB60F9211242F9383 -:103CD0003F938F939F93AF93BF93809113089091DD -:103CE0001408A0911508B09116083091B30223E092 -:103CF000230F2D3758F50196A11DB11D2093B30256 -:103D00008093130890931408A0931508B093160895 -:103D10008091B4029091B502A091B602B091B70221 -:103D20000196A11DB11D8093B4029093B502A0939A -:103D3000B602B093B702BF91AF919F918F913F911F -:103D40002F910F900FBE0F901F90189526E8230F0C -:103D50000296A11DB11DD2CF1F920F920FB60F92E6 -:103D600011240BB60F922F933F934F935F936F9352 -:103D70007F938F939F93AF93BF93EF93FF93E091C4 -:103D80001102F09112021995FF91EF91BF91AF913D -:103D90009F918F917F916F915F914F913F912F9163 -:103DA0000F900BBE0F900FBE0F901F9018951F9293 -:103DB0000F920FB60F9211240BB60F922F933F93D1 -:103DC0004F935F936F937F938F939F93AF93BF9323 -:103DD000EF93FF93E0910F02F09110021995FF917C -:103DE000EF91BF91AF919F918F917F916F915F9173 -:103DF0004F913F912F910F900BBE0F900FBE0F90E0 -:103E00001F9018951F920F920FB60F9211240BB6A8 -:103E10000F922F933F934F935F936F937F938F9363 -:103E20009F93AF93BF93EF93FF93E0910502F091BF -:103E300006021995FF91EF91BF91AF919F918F91DC -:103E40007F916F915F914F913F912F910F900BBE9A -:103E50000F900FBE0F901F9018951F920F920FB6E4 -:103E60000F9211240BB60F922F933F934F935F93B2 -:103E70006F937F938F939F93AF93BF93EF93FF9332 -:103E8000E0910302F09104021995FF91EF91BF9127 -:103E9000AF919F918F917F916F915F914F913F91E2 -:103EA0002F910F900BBE0F900FBE0F901F90189583 -:103EB0001F920F920FB60F9211240BB60F922F93F1 -:103EC0003F934F935F936F937F938F939F93AF93A2 -:103ED000BF93EF93FF93E0910D02F0910E021995BD -:103EE000FF91EF91BF91AF919F918F917F916F91D2 -:103EF0005F914F913F912F910F900BBE0F900FBE8E -:103F00000F901F9018951F920F920FB60F921124C9 -:103F10000BB60F922F933F934F935F936F937F93C3 -:103F20008F939F93AF93BF93EF93FF93E0910B0217 -:103F3000F0910C021995FF91EF91BF91AF919F9174 -:103F40008F917F916F915F914F913F912F910F9042 -:103F50000BBE0F900FBE0F901F9018951F920F92DF -:103F60000FB60F9211240BB60F922F933F934F93DE -:103F70005F936F937F938F939F93AF93BF93EF93D1 -:103F8000FF93E0910902F0910A021995FF91EF91D8 -:103F9000BF91AF919F918F917F916F915F914F9161 -:103FA0003F912F910F900BBE0F900FBE0F901F905F -:103FB00018951F920F920FB60F9211240BB60F9205 -:103FC0002F933F934F935F936F937F938F939F9321 -:103FD000AF93BF93EF93FF93E0910702F091080234 -:103FE0001995FF91EF91BF91AF919F918F917F9123 -:103FF0006F915F914F913F912F910F900BBE0F905A -:104000000FBE0F901F9018951F920F920FB60F9230 -:1040100011240BB60F922F933F934F935F936F939F -:104020007F938F939F93AF93BF93CF93EF93FF9320 -:104030008091B202811127C081E08093B202C4E175 -:1040400081E59EE10E94F101892BD9F08FB7F894A8 -:104050004091471E5091481E2091431E3091441EAE -:104060004217530721F54091491E50914A1E209155 -:10407000451E3091461E42175307C9F48FBFC150E9 -:10408000F9F61092B202FF91EF91CF91BF91AF91EB -:104090009F918F917F916F915F914F913F912F9160 -:1040A0000F900BBE0F900FBE0F901F901895409170 -:1040B000491E50914A1E2091471E3091481E2150A2 -:1040C000310942175307D1F28FBF81E59EE10E946B -:1040D000CF012FB7F894E091491EF0914A1E8083DA -:1040E0004091491E50914A1E8091451E9091461E56 -:1040F0004817590751F48091431E9091441E9093A4 -:104100004A1E8093491E2FBFBACF8091491E9091BD -:104110004A1E0196F4CF80EC90E00E94760286E081 -:1041200090E00E9476028091501E90E00E947602FC -:1041300080EC90E00C94760288E198E00FB6F89459 -:10414000A895809360000FBE909360000E94EA05DE -:104150000E940B06FBCF1F93CF93DF93EC01162F2A -:104160000E94DF2B181739F0612FCE01DF91CF911C -:104170001F910C94E72BDF91CF911F910895E1E5FA -:10418000FEE11382128288EE93E0A0E0B0E0848327 -:104190009583A683B78386E492E09183808385EC40 -:1041A00090E09587848784EC90E09787868780EC01 -:1041B00090E0918B808B81EC90E0938B828B82ECF2 -:1041C00090E0958B848B86EC90E0978B868B118E9C -:1041D000128E138E148E81E08093EE1EEFEEFEE1C0 -:1041E0001382128228E532E03183208390E59487A0 -:1041F00085878AE090E09787868789E090E0918BB9 -:10420000808B82E090E0938B828B148A158A168AC9 -:10421000178A118E108E138E128E158E148E1482A4 -:104220001582168217820895CF93DF93CDB7DEB73C -:10423000C859D1090FB6F894DEBF0FBECDBF789430 -:1042400084B5826084BD84B5816084BD85B582609B -:1042500085BD85B5816085BD80916E00816080934C -:104260006E001092810080918100826080938100B5 -:1042700080918100816080938100809180008160C5 -:10428000809380008091B10084608093B100809120 -:10429000B00081608093B000809191008260809333 -:1042A00091008091910081608093910080919000B5 -:1042B0008160809390008091A10082608093A10032 -:1042C0008091A10081608093A1008091A000816015 -:1042D0008093A000809121018260809321018091D0 -:1042E0002101816080932101809120018160809370 -:1042F000200180917A00846080937A0080917A0016 -:10430000826080937A0080917A00816080937A0045 -:1043100080917A00806880937A001092C100809129 -:104320007B00877F80937B0080E480937C0080917A -:104330007A00806480937A0080917A0086FDFCCFB9 -:104340008091780060917900762F682F072E000CFD -:10435000880B990B611571058105910511F00E947B -:10436000B22AE2E1F5E081E093E0DF019C011D92D9 -:1043700021503040E1F7F093441EE093431EF09348 -:104380004A1EE093491EF093481EE093471E82E1C7 -:1043900098E09093461E8093451EE091611EF09137 -:1043A000621E82E08083E0915D1EF0915E1E1082AD -:1043B000E0915F1EF091601E80E180831092691E83 -:1043C000E091651EF091661E86E08083E091631E99 -:1043D000F091641E808180618083E091631EF09182 -:1043E000641E808188608083E091631EF091641E6A -:1043F000808180688083E091631EF091641E8081DB -:104400008F7D80831092900089E18093910080E8F5 -:104410009EE3909397008093960080E280937100D2 -:1044200061E08CE00E94580361E08DE00E94580337 -:10443000EEE1F3E084EF91E0DF019C011D92215059 -:104440003040E1F7E4E2FAE084E0DF011D928A9572 -:10445000E9F7E8E2FAE080E094E1DF019C011D92D7 -:1044600021503040E1F7ECEEF2E082E3DF01282F4B -:104470001D922A95E9F7F0933A1EE093391EF093C6 -:10448000401EE0933F1EF0933E1EE0933D1E2CE144 -:1044900033E030933C1E20933B1EEAEBF2E0DF0159 -:1044A0001D928A95E9F7F0932C1EE0932B1EF09352 -:1044B000321EE093311EF093301EE0932F1E8AEEE1 -:1044C00092E090932E1E80932D1E85E090E09093B5 -:1044D000FE1E8093FD1E84E090E09093001F809369 -:1044E000FF1E82E090E09093021F8093011F809155 -:1044F000111F811101C083E0282F80FF6FC182E06E -:104500008093B9028091B9028150823008F07BC259 -:1045100083ED9FE00E941F1C833709F074C188E37C -:104520009FE00E941F1C833009F069C189E39FE06E -:104530000E941F1C8093B802982F9F7B943A29F009 -:10454000993A19F08F3F09F05EC18AE39FE00E941B -:104550001F1C81508E3F08F056C18BE090E00E94F6 -:104560007A2D6C018C01E8E3EE2EEFE0FE2EC70100 -:104570000E941F1CF80181938F01FFEFEF1AFF0AC1 -:1045800023E4E2162FE0F20691F780E190E00E942A -:104590007A2D8C0181E093E2A5E4B7E689879A87BA -:1045A000AB87BC8789E89BEAADECBFEE8D879E8721 -:1045B000AF87B88B8EEF9CEDAAEBB8E9898B9A8B0D -:1045C000AB8BBC8B86E794E5A2E3B0E18D8B9E8B31 -:1045D000AF8BB88F1D821E821F821886FE01E75A9C -:1045E000FF4F80E4DF011D928A95E9F7CE018F5AD3 -:1045F0009F4F28E0FC0111922A95E9F72BE0F60184 -:10460000DE01599601900D922A95E1F720E82CA33E -:10461000FE01B5962CE2DF011D922A95E9F748E5E7 -:1046200050E060E070E049835A836B837C8328E527 -:10463000FC01208340E450E0BE01675E7F4F80555F -:1046400091090E94800639852A859B858C85D80131 -:104650003C9311962C93119712969C931297139654 -:104660008C9313973D852E859F85888914963C935E -:10467000149715962C93159716969C931697179644 -:104680008C93179739892A899B898C8918963C9332 -:10469000189719962C9319971A969C931A971B960C -:1046A0008C931B973D892E899F89888D1C963C93FE -:1046B0001C971D962C931D971E969C931E971F96D4 -:1046C0008C934801BB24B394C7010E941F1CF401C2 -:1046D00091914F018913B12CFFEFEF1AFF0A23E5E7 -:1046E000E2162FE0F20681F7C8010E94162EC601DD -:1046F0000E94162EBB2009F488C081E080934B1ED7 -:104700008FED9FE00E941F1C833709F07EC084ED6F -:104710009FE00E941F1C90E090931B0A80931A0A4E -:1047200085ED9FE00E941F1C90E0909315028093FE -:10473000140286ED9FE00E941F1C90E090931702E8 -:10474000809316028BED9FE00E941F1C182F8CEDAA -:104750009FE00E941F1C90E0B0E0A0E07C01DD24FF -:10476000CC24F12A8DED9FE00E941F1C182F8EEDA6 -:104770009FE00E941F1CC82AD12AC092200AD09212 -:10478000210AE092220AF092230A87ED9FE00E941C -:104790001F1C182F88ED9FE00E941F1C90E0B0E0C6 -:1047A000A0E07C01DD24CC24F12A89ED9FE00E9469 -:1047B0001F1C182F8AED9FE00E941F1CC82AD12AB7 -:1047C000C0921C0AD0921D0AE0921E0AF0921F0AA3 -:1047D00082E1809318020E94691C17C083E022FDC9 -:1047E0008FCE23FF02C081E08BCE42E250E066E62E -:1047F00072E081E59EE10E9493010E9436068F7E61 -:10480000803E09F493CE10924B1E812C34E1932EFE -:10481000412C512C32015394AA24A39454E1B52E77 -:104820008091501E882309F407C22FB7F894809115 -:10483000130890911408A0911508B09116082FBF85 -:1048400040911708509118086091190870911A0842 -:10485000841B950BA60BB70B0397A105B10510F0B0 -:104860000E94311A80914F1E882309F415C11091BE -:104870004E1E111102C00E94311A80914D1E8111ED -:10488000D7C180914C1E8111D3C1112331F062E355 -:1048900070E080E090E00E9485040E94311A8091CF -:1048A0004D1E8111F9C010924E1E80910F0A811188 -:1048B000EBC081E080930F0A4FB7F89420913D1E22 -:1048C00030913E1E80913F1E9091401E4FBF281791 -:1048D000390709F4D9C0E0913D1EF0913E1E809148 -:1048E0003B1E90913C1EE817F90709F093C0809198 -:1048F000391E90913A1E90933E1E80933D1E20815A -:104900003181E0912F1EF091301E80912D1E9091EB -:104910002E1EE817F90709F084C080912B1E909194 -:104920002C1E9093301E80932F1E00811181C80190 -:104930000197843F914008F0BFCFEBE1F8E02B51A5 -:104940003840C9018E0F9F1FB4010E948E2CDC01DC -:10495000A85DB54F8C918193CF018B519840801702 -:10496000910778F38091501E882309F485C08091C7 -:10497000190A81115DC00E94EA050E94632AA30101 -:1049800092010E94F52B607F362E0F3F110510F02B -:10499000689430F890E080E00E940F19632D8FEE4C -:1049A0009EE10E94061C9BE1C92E98E0D92EEE24C0 -:1049B000E394F12CD6016D916D018FEE9EE10E9482 -:1049C000061CBFEFEB1AFB0AEFEFEE16F10471F4D1 -:1049D0000E942A1890E080E00E940F19632D8FEE4C -:1049E0009EE10E94061CEE24E394F12CC6018B513B -:1049F000984080179107F0F20E942A180E94E6055D -:104A00000E94211C59CF10924B1E4EE150E069E8E4 -:104A100072E0EFCEE0913D1EF0913E1ECF01029676 -:104A20006ACFE0912F1EF091301ECF01029679CF10 -:104A30000E94EA050F3F110519F010F00FEF10E08A -:104A40008091150A811116C090E080E00E940F1934 -:104A5000BBE1EB2EB8E0FB2ED7016D917D018FEE0F -:104A60009EE10E94061CC7018B5198408017910758 -:104A700098F3C2CFC801EACF82E00E94500585E0DA -:104A800090E00E944D0618CF10924F1E1092381ED3 -:104A90001092371E10920F0A4FB7F8942091471EBC -:104AA0003091481E8091491E90914A1E4FBF281791 -:104AB000390709F4ADC481E08093421EFF24F394CA -:104AC000AEEFEA2E4FB7F8942091471E3091481E62 -:104AD0008091491E90914A1E4FBF2817390709F44B -:104AE00095C4E091471EF091481E8091451E90911B -:104AF000461EE817F90709F0FFC08091431E909108 -:104B0000441E9093481E8093471E10818091411E41 -:104B1000882309F480C4103C09F00FC18091190268 -:104B200081117CC41092411E80913D1E90913E1EC9 -:104B30004091391E50913A1E8417950761F42091D7 -:104B40003F1E3091401E80913B1E90913C1E2817C5 -:104B5000390709F4B7CF20913F1E3091401E809154 -:104B60003D1E90913E1E02972817390709F4AACFDF -:104B70008091371E9091381E8115944108F0A2CF84 -:104B80002091351E3091361E8091331E9091341E37 -:104B9000019710F48FEF93E12817390709F4B9C092 -:104BA0002817390708F0AFC0821B930B01960097B6 -:104BB00009F488CF60914F1E6F5F60934F1EE091A4 -:104BC0003F1EF091401E3183208360913F1E709103 -:104BD000401E20913B1E30913C1E6217730709F066 -:104BE0009BC05093401E40933F1EE091311EF091B8 -:104BF000321E918380832091311E3091321E80912C -:104C00002D1E90912E1E2817390709F090C0809113 -:104C10002B1E90912C1E9093321E8093311E8091FA -:104C2000331E9091341E9093361E8093351E4ACFCA -:104C300081E080934E1E30CF8091110A9091120A2C -:104C4000A091130AB091140A0196A11DB11D2091E3 -:104C50004B1E222369F1893FEAE29E07A105B105B7 -:104C600048F48093110A9093120AA093130AB09308 -:104C7000140A12CF1092110A1092120A1092130AFB -:104C80001092140A6091EE1E81E0623018F06832D2 -:104C900018F08FEF8093100A8091100A680F6093CC -:104CA000EE1E70E08CE00E9489030E94E605F4CEBF -:104CB000893FFAE29F07A105B10598F21092110A07 -:104CC0001092120A1092130A1092140A6091EE1EAA -:104CD00081E0623018F0683218F08FEF8093100A8C -:104CE0008091100A680F6093EE1E70E08DE00E94C4 -:104CF00089030E940B06D0CEE091471EF091481E1A -:104D0000CF010196FECEB501621B730B860F971F74 -:104D10004ECF81E090E04ECF20913F1E3091401E5B -:104D20002E5F3F4F3093401E20933F1E5ECF8091F9 -:104D3000311E9091321E02966ECF2091291E309125 -:104D40002A1E243F81E0380708F0BCCE909119025A -:104D50002115310529F49E3F19F410931902B2CEA2 -:104D6000911144C01B3D19F4F092281EABCE8091E6 -:104D7000281E882349F01C3D21F01D3D19F41BED30 -:104D800001C010EC1092281E80914F1E883108F04F -:104D900099CE8091371E9091381E8115944108F06C -:104DA00091CE8091371E9091381E01969093381EB7 -:104DB0008093371E8091331E9091341E9C012F5F8B -:104DC0003F4F3093341E2093331E885D954FFC0176 -:104DD00010838091331E9091341E8115944109F007 -:104DE00071CE1092341E1092331E6CCE913009F0A9 -:104DF0004AC01B3DD9F49093281E8091291E9091A2 -:104E00002A1E049709F05ECE6091240A7091250A4B -:104E10008091260A9091270A0E94AA2C611571059B -:104E200081059105D9F40E9478054CCE8091281E09 -:104E3000882349F01C3D21F01D3D19F41BED01C0F4 -:104E400010EC1092281EC901019690932A1E80939F -:104E5000291E2C5D354FD9011C93CFCF6093200ABA -:104E60007093210A8093220A9093230A20911802BA -:104E70002131C9F62091501E2223A9F20E947C17ED -:104E80000E943019D0CF923009F048C01B3DD9F4B0 -:104E9000F092281E8091291E90912A1E049709F0F5 -:104EA00011CE6091240A7091250A8091260A909172 -:104EB000270A0E94AA2C6115710581059105D9F474 -:104EC0000E949D05FFCD8091281E882349F01C3D3E -:104ED00021F01D3D19F41BED01C010EC1092281EAD -:104EE000C901019690932A1E8093291E2C5D354F8F -:104EF000F9011083CFCF60931C0A70931D0A809331 -:104F00001E0A90931F0A809118028131C9F6809180 -:104F1000501E8823A9F20E946919D2CF9330A9F4B8 -:104F20001F3F19F40E941A05CDCD123108F011E18E -:104F3000812F90E0909317028093160280911802BF -:104F4000813181F70E943C1CEDCF9430F9F41F3F72 -:104F500019F40E943E05B6CD86E090E0163038F098 -:104F6000812F90E08D30910514F08CE090E09093CB -:104F70001B0A80931A0A20911802213151F72091BF -:104F8000501E222331F30E949118E3CF9530F9F49B -:104F90001F3F19F40E942C0595CD85E090E0153057 -:104FA00038F0812F90E08930910514F088E090E08E -:104FB000909315028093140220911802213151F729 -:104FC0002091501E222331F30E94CF18E3CF993055 -:104FD000B9F41093160A112381F0F092150A80ECAF -:104FE00090E00E94760289E090E00E947602612FB4 -:104FF00081E59EE10E94290244C01092150AEFCF7C -:10500000963049F51F3F19F40E948B205BCD11119A -:105010001CC040E861E88FEE9EE10E9430179FB708 -:10502000F8948091180A882319F081508093180A07 -:105030008091180A811105C08CB58F7B8CBD1092B0 -:10504000170A9FBF1092501EDFCF113009F03ACDE2 -:105050000E94691CD9CF9132C9F480EC90E00E9483 -:10506000760281E290E00E94760280E00E94640570 -:1050700080E00E94640580E00E94640580E00E9458 -:10508000640580EC90E00E9476021CCD923239F4E7 -:1050900080EC90E00E94760282E290E0E4CF9332CE -:1050A00019F40E94D2050ECD973081F40E94B80405 -:1050B00080EC90E00E94760287E090E00E94760209 -:1050C0008091130290E00E947602DBCF903329F4A6 -:1050D000812F90E00E940F06F5CC903491F48091DE -:1050E000501E10E0882331F06CE28FEE9EE10E94AA -:1050F000CD17182F80EC90E00E94760280E490E0BB -:1051000074CF983071F4133709F0DCCC80EC90E068 -:105110000E94760288E090E00E94760286E490E0A9 -:10512000D2CF9E30C1F4113089F41093190A80EC6B -:1051300090E00E9476028EE090E00E9476028091DC -:10514000190A882331F181E090E0BDCF1111EFCF32 -:105150001092190AECCF9F30F9F480914F1E8831DC -:1051600080F48091371E9091381E8115944148F447 -:1051700080EC90E00E9476028FE090E00E94760240 -:10518000E2CF80EC90E00E9476028FE090E00E94F7 -:10519000760290E080E097CF9935A9F4183F09F0A6 -:1051A00091CC82E390E00E944D0608E31FE06FEF90 -:1051B000C8010E94AB200F5F1F4F011520E11207AD -:1051C000B1F70E949C20953521F4183F09F07ACC64 -:1051D000F8CF9135B1F480EC90E00E94760281E541 -:1051E00090E00E94760208E31FE0C8010E941F1CA5 -:1051F0000E9464050F5F1F4F011530E11307A9F7E7 -:1052000040CF9235D9F51B3DE9F4F092281E8091EC -:10521000291E90912A1E029709F054CC0091250A6C -:105220001091240A83ED9FE00E941F1C833711F127 -:10523000183C00F5812F90E0602F885C904F0E9411 -:10524000AB2040CC8091281E882349F01C3D21F0E2 -:105250001D3D19F41BED01C010EC1092281EC90170 -:10526000019690932A1E8093291E2C5D354FD901FB -:105270001C93CDCF83E00E94500524CC903579F467 -:1052800080EC90E00E94760280E590E00E94760239 -:1052900081E090E00E94760287E190E014CF98349C -:1052A00059F480EC90E00E94760288E490E00E943D -:1052B000760280E990E007CF993459F480EC90E0D1 -:1052C0000E94760289E490E00E94760282E990E0F2 -:1052D000FACE933509F093C080914B1E882309F4D0 -:1052E00089C08091501E882309F484C060911A0AF5 -:1052F00084ED9FE00E94AB206091140285ED9FE059 -:105300000E94AB206091160286ED9FE00E94AB20C8 -:1053100060911C0A70911D0A80911E0A90911F0ACB -:10532000692F77278827992787ED9FE00E94AB2078 -:1053300060911C0A70911D0A80911E0A90911F0AAB -:10534000BC018827992788ED9FE00E94AB206091DF -:105350001C0A70911D0A80911E0A90911F0A672FE6 -:10536000782F892F992789ED9FE00E94AB206091CB -:105370001C0A8AED9FE00E94AB206091200A709188 -:10538000210A8091220A9091230A692F7727882782 -:1053900099278BED9FE00E94AB206091200A7091CD -:1053A000210A8091220A9091230ABC01882799271B -:1053B0008CED9FE00E94AB206091200A7091210A41 -:1053C0008091220A9091230A672F782F892F99279D -:1053D0008DED9FE00E94AB206091200A8EED9FE052 -:1053E0000E94AB2063E78FED9FE00E94AB208AE034 -:1053F00090E070CE8AE090E00E94EE0563CB943599 -:1054000009F060CB60E08FED9FE019CF1092421E53 -:105410000E949B0205CA103C09F054CBF092411E39 -:10542000E092190210922A1E1092291E4BCB8F92E5 -:105430009F92AF92BF92CF92DF92EF92FF92CF9363 -:10544000DF93EC01688179818A819B816115710507 -:105450008105910521F464E279ED8BE597E02DE17A -:1054600033EF41E050E00E94F52B49015A019B01C6 -:10547000AC01A7EAB1E40E94142C6B017C01ACEEF4 -:10548000B4EFA50194010E94222CC60ED71EE81E7F -:10549000F91EF7FE06C081E0C81AD108E10880E8CD -:1054A000F80AC882D982EA82FB82C701B6019F77D7 -:1054B000DF91CF91FF90EF90DF90CF90BF90AF90B2 -:1054C0009F908F9008958F929F92AF92BF92CF92AC -:1054D000DF92EF92FF9260911A0270911B0280910D -:1054E0001C0290911D02611571058105910521F441 -:1054F00064E279ED8BE597E02DE133EF41E050E098 -:105500000E94F52B49015A019B01AC01A7EAB1E4C5 -:105510000E94142C6B017C01ACEEB4EFA501940148 -:105520000E94222CC60ED71EE81EF91EF7FE06C0EA -:1055300081E0C81AD108E10880E8F80AC0921A028E -:10554000D0921B02E0921C02F0921D02C701B6012C -:105550009F77FF90EF90DF90CF90BF90AF909F909C -:105560008F90089560931A0270931B0280931C021F -:1055700090931D0208950E94FD2A08F481E0089589 -:10558000E89409C097FB3EF49095809570956195DD -:105590007F4F8F4F9F4F9923A9F0F92F96E9BB2793 -:1055A0009395F695879577956795B795F111F8CF0F -:1055B000FAF4BB0F11F460FF1BC06F5F7F4F8F4F7A -:1055C0009F4F16C0882311F096E911C0772321F070 -:1055D0009EE8872F762F05C0662371F096E8862F08 -:1055E00070E060E02AF09A95660F771F881FDAF75F -:1055F000880F9695879597F90895990F0008550F8C -:10560000AA0BE0E8FEEF16161706E807F907C0F048 -:1056100012161306E407F50798F0621B730B840B50 -:10562000950B39F40A2661F0232B242B252B21F42A -:1056300008950A2609F4A140A6958FEF811D811DCA -:1056400008950E94342B0C94A52B0E94972B38F0C0 -:105650000E949E2B20F0952311F00C948E2B0C941D -:10566000942B11240C94D92B0E94B62B70F3959F88 -:10567000C1F3950F50E0551F629FF001729FBB2749 -:10568000F00DB11D639FAA27F00DB11DAA1F649FE5 -:105690006627B00DA11D661F829F2227B00DA11D98 -:1056A000621F739FB00DA11D621F839FA00D611D1E -:1056B000221F749F3327A00D611D231F849F600D3F -:1056C000211D822F762F6A2F11249F5750409AF068 -:1056D000F1F088234AF0EE0FFF1FBB1F661F771FF4 -:1056E000881F91505040A9F79E3F510580F00C94BF -:1056F0008E2B0C94D92B5F3FE4F3983ED4F3869520 -:1057000077956795B795F795E7959F5FC1F7FE2B5E -:10571000880F911D9695879597F9089597F99F673A -:1057200080E870E060E008959FEF80EC0895002429 -:105730000A941616170618060906089500240A94F6 -:1057400012161306140605060895092E0394000C7C -:1057500011F4882352F0BB0F40F4BF2B11F460FF0B -:1057600004C06F5F7F4F8F4F9F4F089557FD905834 -:10577000440F551F59F05F3F71F04795880F97FB15 -:10578000991F61F09F3F79F08795089512161306CF -:105790001406551FF2CF4695F1DF08C016161706FE -:1057A0001806991FF1CF8695710561050894089533 -:1057B000E894BB2766277727CB0197F90895F999D5 -:1057C000FECF92BD81BDF89A992780B50895262F06 -:1057D000F999FECF1FBA92BD81BD20BD0FB6F894D6 -:1057E000FA9AF99A0FBE01960895052E97FB1EF4BA -:1057F00000940E940C2C57FD07D00E94272C07FC18 -:1058000003D04EF40C940C2C509540953095219576 -:105810003F4F4F4F5F4F08959095809570956195DC -:105820007F4F8F4F9F4F08950E94492CA59F900D49 -:10583000B49F900DA49F800D911D11240895B7FF72 -:105840000C94142C0E94142C821B930B0895A1E23B -:105850001A2EAA1BBB1BFD010DC0AA1FBB1FEE1FEA -:10586000FF1FA217B307E407F50720F0A21BB30B35 -:10587000E40BF50B661F771F881F991F1A9469F7B1 -:1058800060957095809590959B01AC01BD01CF010D -:105890000895A29FB001B39FC001A39F700D811D09 -:1058A0001124911DB29F700D811D1124911D089529 -:1058B0000E945F2C6894B1110C94D92B08950E941A -:1058C000BE2B88F09F5798F0B92F9927B751B0F0A9 -:1058D000E1F0660F771F881F991F1AF0BA95C9F774 -:1058E00014C0B13091F00E94D82BB1E008950C940F -:1058F000D82B672F782F8827B85F39F0B93FCCF3C2 -:10590000869577956795B395D9F73EF490958095F0 -:10591000709561957F4F8F4F9F4F0895AA1BBB1BBA -:1059200051E107C0AA1FBB1FA617B70710F0A61B9F -:10593000B70B881F991F5A95A9F780959095BC01C0 -:10594000CD010895EE0FFF1F881F8BBF0790F691C2 -:10595000E02D1994692796276927782787277827C4 -:105960000895E894DF93CF93FC01DB010E94492C5A -:105970007F936F93E9019A01AC01BF93AF933F937B -:105980002F93DF010E94492C26F46C1B7D0B820BA8 -:10599000930B9E01EB01FC010E94712DAF91BF9111 -:1059A0002F913F910E94712DBE01CF01F9012F91DE -:1059B0003F91CF91DF910895689401C0E8948F9250 -:1059C0009F92CF93DF930E94EA2CDF91CF919F901B -:1059D0008F90089588249924F401E401B0E49F9302 -:1059E000AA279A158B049C04ED05FE05CF05D00768 -:1059F000A10798F4AD2FDC2FCF2FFE2FE92D982C87 -:105A0000892E982F872F762F652F542F432F322FD3 -:105A10002227B85031F7BF9127C01B2EBF91BB275B -:105A2000220F331F441F551F661F771F881F991FA2 -:105A3000881C991CEE1FFF1FCC1FDD1FAA1FBB1F58 -:105A40008A149B04EC05FD05CE05DF05A007B10710 -:105A500048F08A189B08EC09FD09CE09DF09A00B64 -:105A6000B10B21601A94E1F62EF49401AF01BE014E -:105A7000CD01000C0895002E083090F0982F872F4C -:105A8000762F652F542F432F322F22270850F4CF23 -:105A9000220F331F441F551F661F771F881F991F32 -:105AA0000A95B2F7002D089597FD1094002E083046 -:105AB00098F00850232F342F452F562F672F782F1B -:105AC000892F912DF4CF15949795879577956795A4 -:105AD00057954795379527950A95AAF71124002DD4 -:105AE00008950E94492C460F571FC81FD91F08F45C -:105AF000319608950F931F93CF93DF9382309105D2 -:105B000010F482E090E0E0910F1FF091101F30E060 -:105B100020E0B0E0A0E0309799F42115310509F4B8 -:105B20004AC0281B390B24303105D8F58A819B8166 -:105B30006115710589F1FB0193838283FE0111C018 -:105B4000408151810281138148175907E0F04817BD -:105B5000590799F4109761F012960C9312971396C7 -:105B60001C933296CF01DF91CF911F910F91089531 -:105B700000930F1F1093101FF4CF2115310551F022 -:105B80004217530738F0A901DB019A01BD01DF017B -:105B9000F801C1CFEF01F9CF9093101F80930F1F31 -:105BA000CDCFFE01E20FF31F819391932250310973 -:105BB00039832883D7CF20910D1F30910E1F232BBF -:105BC00041F4209120023091210230930E1F209346 -:105BD0000D1F20911E0230911F022115310541F445 -:105BE0002DB73EB74091220250912302241B350B62 -:105BF000E0910D1FF0910E1FE217F307A0F42E1B8A -:105C00003F0B2817390778F0AC014E5F5F4F241720 -:105C1000350748F04E0F5F1F50930E1F40930D1F26 -:105C2000819391939FCFF0E0E0E09CCFCF93DF93FF -:105C30000097E9F0FC01329713821282A0910F1FA6 -:105C4000B091101FED0130E020E01097A1F4208109 -:105C50003181820F931F20910D1F30910E1F281745 -:105C6000390709F061C0F0930E1FE0930D1FDF911B -:105C7000CF910895EA01CE17DF07E8F54A815B81ED -:105C80009E0141155105B1F7E901FB83EA83499172 -:105C90005991C40FD51FEC17FD0761F480819181E4 -:105CA0000296840F951FE9019983888382819381ED -:105CB0009B838A83F0E0E0E012968D919C9113978C -:105CC0000097B9F52D913C911197CD010296820F65 -:105CD000931F20910D1F30910E1F2817390739F699 -:105CE000309751F51092101F10920F1FB0930E1F96 -:105CF000A0930D1FBCCFD383C28340815181840FF9 -:105D0000951FC817D90761F44E5F5F4F888199814D -:105D1000480F591F518340838A819B8193838283DB -:105D20002115310509F0B0CFF093101FE0930F1F3C -:105D30009ECFFD01DC01C0CF13821282D7CFF89431 -:025D4000FFCF93 -:105D4200FFDCFEB704B704B704B704B704B704B75F -:105D520004B704010500FF0011FE0100000000006D -:105D6200121F8000781E00A02800F03C0040510065 -:105D7200127A00E4A20024F40048E80190D0032043 -:105D8200A1070000000029029301C0017B02F1017A -:105D9200CF01E30100000000061CCB1B1C1CD11A22 -:105DA200BD1BF81AD21A4572726F722C20696E648A -:105DB200657465726D696E61746520626F6F7420BF -:105DC200766563746F720D0A004572726F722C20D1 -:105DD200696E636F727265637420626F6F7420768E -:085DE2006563746F720D0A0085 -:00000001FF diff --git a/Precompiled/rnode_firmware_latest_tbeam.zip b/Precompiled/rnode_firmware_latest_tbeam.zip deleted file mode 100644 index 09ddc19..0000000 Binary files a/Precompiled/rnode_firmware_latest_tbeam.zip and /dev/null differ diff --git a/Release/README.md b/Release/README.md new file mode 100644 index 0000000..37384d1 --- /dev/null +++ b/Release/README.md @@ -0,0 +1,2 @@ +# Precompiled Firmware +This directory contains the latest version of the precompiled firmware. You can download and flash the firmware to supported boards using the [RNode Config Utility](https://github.com/markqvist/rnodeconfigutil). diff --git a/Release/esptool/esptool.py b/Release/esptool/esptool.py new file mode 100755 index 0000000..01176af --- /dev/null +++ b/Release/esptool/esptool.py @@ -0,0 +1,4582 @@ +#!/usr/bin/env python +# +# ESP8266 & ESP32 family ROM Bootloader Utility +# Copyright (C) 2014-2021 Fredrik Ahlberg, Angus Gratton, Espressif Systems (Shanghai) CO LTD, other contributors as noted. +# https://github.com/espressif/esptool +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin +# Street, Fifth Floor, Boston, MA 02110-1301 USA. + +from __future__ import division, print_function + +import argparse +import base64 +import binascii +import copy +import hashlib +import inspect +import io +import itertools +import os +import shlex +import string +import struct +import sys +import time +import zlib + +try: + import serial +except ImportError: + print("Pyserial is not installed for %s. Check the README for installation instructions." % (sys.executable)) + raise + +# check 'serial' is 'pyserial' and not 'serial' https://github.com/espressif/esptool/issues/269 +try: + if "serialization" in serial.__doc__ and "deserialization" in serial.__doc__: + raise ImportError(""" +esptool.py depends on pyserial, but there is a conflict with a currently installed package named 'serial'. + +You may be able to work around this by 'pip uninstall serial; pip install pyserial' \ +but this may break other installed Python software that depends on 'serial'. + +There is no good fix for this right now, apart from configuring virtualenvs. \ +See https://github.com/espressif/esptool/issues/269#issuecomment-385298196 for discussion of the underlying issue(s).""") +except TypeError: + pass # __doc__ returns None for pyserial + +try: + import serial.tools.list_ports as list_ports +except ImportError: + print("The installed version (%s) of pyserial appears to be too old for esptool.py (Python interpreter %s). " + "Check the README for installation instructions." % (sys.VERSION, sys.executable)) + raise +except Exception: + if sys.platform == "darwin": + # swallow the exception, this is a known issue in pyserial+macOS Big Sur preview ref https://github.com/espressif/esptool/issues/540 + list_ports = None + else: + raise + + +__version__ = "3.1" + +MAX_UINT32 = 0xffffffff +MAX_UINT24 = 0xffffff + +DEFAULT_TIMEOUT = 3 # timeout for most flash operations +START_FLASH_TIMEOUT = 20 # timeout for starting flash (may perform erase) +CHIP_ERASE_TIMEOUT = 120 # timeout for full chip erase +MAX_TIMEOUT = CHIP_ERASE_TIMEOUT * 2 # longest any command can run +SYNC_TIMEOUT = 0.1 # timeout for syncing with bootloader +MD5_TIMEOUT_PER_MB = 8 # timeout (per megabyte) for calculating md5sum +ERASE_REGION_TIMEOUT_PER_MB = 30 # timeout (per megabyte) for erasing a region +ERASE_WRITE_TIMEOUT_PER_MB = 40 # timeout (per megabyte) for erasing and writing data +MEM_END_ROM_TIMEOUT = 0.05 # special short timeout for ESP_MEM_END, as it may never respond +DEFAULT_SERIAL_WRITE_TIMEOUT = 10 # timeout for serial port write +DEFAULT_CONNECT_ATTEMPTS = 7 # default number of times to try connection + + +def timeout_per_mb(seconds_per_mb, size_bytes): + """ Scales timeouts which are size-specific """ + result = seconds_per_mb * (size_bytes / 1e6) + if result < DEFAULT_TIMEOUT: + return DEFAULT_TIMEOUT + return result + + +def _chip_to_rom_loader(chip): + return { + 'esp8266': ESP8266ROM, + 'esp32': ESP32ROM, + 'esp32s2': ESP32S2ROM, + 'esp32s3beta2': ESP32S3BETA2ROM, + 'esp32s3beta3': ESP32S3BETA3ROM, + 'esp32c3': ESP32C3ROM, + 'esp32c6beta': ESP32C6BETAROM, + }[chip] + + +def get_default_connected_device(serial_list, port, connect_attempts, initial_baud, chip='auto', trace=False, + before='default_reset'): + _esp = None + for each_port in reversed(serial_list): + print("Serial port %s" % each_port) + try: + if chip == 'auto': + _esp = ESPLoader.detect_chip(each_port, initial_baud, before, trace, + connect_attempts) + else: + chip_class = _chip_to_rom_loader(chip) + _esp = chip_class(each_port, initial_baud, trace) + _esp.connect(before, connect_attempts) + break + except (FatalError, OSError) as err: + if port is not None: + raise + print("%s failed to connect: %s" % (each_port, err)) + _esp = None + return _esp + + +DETECTED_FLASH_SIZES = {0x12: '256KB', 0x13: '512KB', 0x14: '1MB', + 0x15: '2MB', 0x16: '4MB', 0x17: '8MB', + 0x18: '16MB', 0x19: '32MB', 0x1a: '64MB'} + + +def check_supported_function(func, check_func): + """ + Decorator implementation that wraps a check around an ESPLoader + bootloader function to check if it's supported. + + This is used to capture the multidimensional differences in + functionality between the ESP8266 & ESP32/32S2/32S3/32C3 ROM loaders, and the + software stub that runs on both. Not possible to do this cleanly + via inheritance alone. + """ + def inner(*args, **kwargs): + obj = args[0] + if check_func(obj): + return func(*args, **kwargs) + else: + raise NotImplementedInROMError(obj, func) + return inner + + +def stub_function_only(func): + """ Attribute for a function only supported in the software stub loader """ + return check_supported_function(func, lambda o: o.IS_STUB) + + +def stub_and_esp32_function_only(func): + """ Attribute for a function only supported by software stubs or ESP32/32S2/32S3/32C3 ROM """ + return check_supported_function(func, lambda o: o.IS_STUB or isinstance(o, ESP32ROM)) + + +PYTHON2 = sys.version_info[0] < 3 # True if on pre-Python 3 + +# Function to return nth byte of a bitstring +# Different behaviour on Python 2 vs 3 +if PYTHON2: + def byte(bitstr, index): + return ord(bitstr[index]) +else: + def byte(bitstr, index): + return bitstr[index] + +# Provide a 'basestring' class on Python 3 +try: + basestring +except NameError: + basestring = str + + +def print_overwrite(message, last_line=False): + """ Print a message, overwriting the currently printed line. + + If last_line is False, don't append a newline at the end (expecting another subsequent call will overwrite this one.) + + After a sequence of calls with last_line=False, call once with last_line=True. + + If output is not a TTY (for example redirected a pipe), no overwriting happens and this function is the same as print(). + """ + if sys.stdout.isatty(): + print("\r%s" % message, end='\n' if last_line else '') + else: + print(message) + + +def _mask_to_shift(mask): + """ Return the index of the least significant bit in the mask """ + shift = 0 + while mask & 0x1 == 0: + shift += 1 + mask >>= 1 + return shift + + +def esp8266_function_only(func): + """ Attribute for a function only supported on ESP8266 """ + return check_supported_function(func, lambda o: o.CHIP_NAME == "ESP8266") + + +class ESPLoader(object): + """ Base class providing access to ESP ROM & software stub bootloaders. + Subclasses provide ESP8266 & ESP32 specific functionality. + + Don't instantiate this base class directly, either instantiate a subclass or + call ESPLoader.detect_chip() which will interrogate the chip and return the + appropriate subclass instance. + + """ + CHIP_NAME = "Espressif device" + IS_STUB = False + + DEFAULT_PORT = "/dev/ttyUSB0" + + # Commands supported by ESP8266 ROM bootloader + ESP_FLASH_BEGIN = 0x02 + ESP_FLASH_DATA = 0x03 + ESP_FLASH_END = 0x04 + ESP_MEM_BEGIN = 0x05 + ESP_MEM_END = 0x06 + ESP_MEM_DATA = 0x07 + ESP_SYNC = 0x08 + ESP_WRITE_REG = 0x09 + ESP_READ_REG = 0x0a + + # Some comands supported by ESP32 ROM bootloader (or -8266 w/ stub) + ESP_SPI_SET_PARAMS = 0x0B + ESP_SPI_ATTACH = 0x0D + ESP_READ_FLASH_SLOW = 0x0e # ROM only, much slower than the stub flash read + ESP_CHANGE_BAUDRATE = 0x0F + ESP_FLASH_DEFL_BEGIN = 0x10 + ESP_FLASH_DEFL_DATA = 0x11 + ESP_FLASH_DEFL_END = 0x12 + ESP_SPI_FLASH_MD5 = 0x13 + + # Commands supported by ESP32-S2/S3/C3/C6 ROM bootloader only + ESP_GET_SECURITY_INFO = 0x14 + + # Some commands supported by stub only + ESP_ERASE_FLASH = 0xD0 + ESP_ERASE_REGION = 0xD1 + ESP_READ_FLASH = 0xD2 + ESP_RUN_USER_CODE = 0xD3 + + # Flash encryption encrypted data command + ESP_FLASH_ENCRYPT_DATA = 0xD4 + + # Response code(s) sent by ROM + ROM_INVALID_RECV_MSG = 0x05 # response if an invalid message is received + + # Maximum block sized for RAM and Flash writes, respectively. + ESP_RAM_BLOCK = 0x1800 + + FLASH_WRITE_SIZE = 0x400 + + # Default baudrate. The ROM auto-bauds, so we can use more or less whatever we want. + ESP_ROM_BAUD = 115200 + + # First byte of the application image + ESP_IMAGE_MAGIC = 0xe9 + + # Initial state for the checksum routine + ESP_CHECKSUM_MAGIC = 0xef + + # Flash sector size, minimum unit of erase. + FLASH_SECTOR_SIZE = 0x1000 + + UART_DATE_REG_ADDR = 0x60000078 + + CHIP_DETECT_MAGIC_REG_ADDR = 0x40001000 # This ROM address has a different value on each chip model + + UART_CLKDIV_MASK = 0xFFFFF + + # Memory addresses + IROM_MAP_START = 0x40200000 + IROM_MAP_END = 0x40300000 + + # The number of bytes in the UART response that signify command status + STATUS_BYTES_LENGTH = 2 + + # Response to ESP_SYNC might indicate that flasher stub is running instead of the ROM bootloader + sync_stub_detected = False + + # Device PIDs + USB_JTAG_SERIAL_PID = 0x1001 + + def __init__(self, port=DEFAULT_PORT, baud=ESP_ROM_BAUD, trace_enabled=False): + """Base constructor for ESPLoader bootloader interaction + + Don't call this constructor, either instantiate ESP8266ROM + or ESP32ROM, or use ESPLoader.detect_chip(). + + This base class has all of the instance methods for bootloader + functionality supported across various chips & stub + loaders. Subclasses replace the functions they don't support + with ones which throw NotImplementedInROMError(). + + """ + self.secure_download_mode = False # flag is set to True if esptool detects the ROM is in Secure Download Mode + + if isinstance(port, basestring): + self._port = serial.serial_for_url(port) + else: + self._port = port + self._slip_reader = slip_reader(self._port, self.trace) + # setting baud rate in a separate step is a workaround for + # CH341 driver on some Linux versions (this opens at 9600 then + # sets), shouldn't matter for other platforms/drivers. See + # https://github.com/espressif/esptool/issues/44#issuecomment-107094446 + self._set_port_baudrate(baud) + self._trace_enabled = trace_enabled + # set write timeout, to prevent esptool blocked at write forever. + try: + self._port.write_timeout = DEFAULT_SERIAL_WRITE_TIMEOUT + except NotImplementedError: + # no write timeout for RFC2217 ports + # need to set the property back to None or it will continue to fail + self._port.write_timeout = None + + @property + def serial_port(self): + return self._port.port + + def _set_port_baudrate(self, baud): + try: + self._port.baudrate = baud + except IOError: + raise FatalError("Failed to set baud rate %d. The driver may not support this rate." % baud) + + @staticmethod + def detect_chip(port=DEFAULT_PORT, baud=ESP_ROM_BAUD, connect_mode='default_reset', trace_enabled=False, + connect_attempts=DEFAULT_CONNECT_ATTEMPTS): + """ Use serial access to detect the chip type. + + We use the UART's datecode register for this, it's mapped at + the same address on ESP8266 & ESP32 so we can use one + memory read and compare to the datecode register for each chip + type. + + This routine automatically performs ESPLoader.connect() (passing + connect_mode parameter) as part of querying the chip. + """ + detect_port = ESPLoader(port, baud, trace_enabled=trace_enabled) + detect_port.connect(connect_mode, connect_attempts, detecting=True) + try: + print('Detecting chip type...', end='') + sys.stdout.flush() + chip_magic_value = detect_port.read_reg(ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR) + + for cls in [ESP8266ROM, ESP32ROM, ESP32S2ROM, ESP32S3BETA2ROM, ESP32S3BETA3ROM, ESP32C3ROM, ESP32C6BETAROM]: + if chip_magic_value in cls.CHIP_DETECT_MAGIC_VALUE: + # don't connect a second time + inst = cls(detect_port._port, baud, trace_enabled=trace_enabled) + inst._post_connect() + print(' %s' % inst.CHIP_NAME, end='') + if detect_port.sync_stub_detected: + inst = inst.STUB_CLASS(inst) + inst.sync_stub_detected = True + return inst + except UnsupportedCommandError: + raise FatalError("Unsupported Command Error received. Probably this means Secure Download Mode is enabled, " + "autodetection will not work. Need to manually specify the chip.") + finally: + print('') # end line + raise FatalError("Unexpected CHIP magic value 0x%08x. Failed to autodetect chip type." % (chip_magic_value)) + + """ 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' + self.trace("Write %d bytes: %s", len(buf), HexFormatter(buf)) + self._port.write(buf) + + def trace(self, message, *format_args): + if self._trace_enabled: + now = time.time() + try: + + delta = now - self._last_trace + except AttributeError: + delta = 0.0 + self._last_trace = now + prefix = "TRACE +%.3f " % delta + print(prefix + (message % format_args)) + + """ Calculate checksum of a blob, as it is defined by the ROM """ + @staticmethod + def checksum(data, state=ESP_CHECKSUM_MAGIC): + for b in data: + if type(b) is int: # python 2/3 compat + state ^= b + else: + state ^= ord(b) + + return state + + """ Send a request and read the response """ + def command(self, op=None, data=b"", chk=0, wait_response=True, timeout=DEFAULT_TIMEOUT): + saved_timeout = self._port.timeout + new_timeout = min(timeout, MAX_TIMEOUT) + if new_timeout != saved_timeout: + self._port.timeout = new_timeout + + try: + if op is not None: + self.trace("command op=0x%02x data len=%s wait_response=%d timeout=%.3f data=%s", + op, len(data), 1 if wait_response else 0, timeout, HexFormatter(data)) + pkt = struct.pack(b' self.STATUS_BYTES_LENGTH: + return data[:-self.STATUS_BYTES_LENGTH] + else: # otherwise, just return the 'val' field which comes from the reply header (this is used by read_reg) + return val + + def flush_input(self): + self._port.flushInput() + self._slip_reader = slip_reader(self._port, self.trace) + + def sync(self): + val, _ = self.command(self.ESP_SYNC, b'\x07\x07\x12\x20' + 32 * b'\x55', + timeout=SYNC_TIMEOUT) + + # ROM bootloaders send some non-zero "val" response. The flasher stub sends 0. If we receive 0 then it + # probably indicates that the chip wasn't or couldn't be reseted properly and esptool is talking to the + # flasher stub. + self.sync_stub_detected = val == 0 + + for _ in range(7): + val, _ = self.command() + self.sync_stub_detected &= val == 0 + + def _setDTR(self, state): + self._port.setDTR(state) + + def _setRTS(self, state): + self._port.setRTS(state) + # Work-around for adapters on Windows using the usbser.sys driver: + # generate a dummy change to DTR so that the set-control-line-state + # request is sent with the updated RTS state and the same DTR state + self._port.setDTR(self._port.dtr) + + def _get_pid(self): + if list_ports is None: + print("\nListing all serial ports is currently not available. Can't get device PID.") + return + active_port = self._port.port + + # Pyserial only identifies regular ports, URL handlers are not supported + if not active_port.startswith(("COM", "/dev/")): + print("\nDevice PID identification is only supported on COM and /dev/ serial ports.") + return + # Return the real path if the active port is a symlink + if active_port.startswith("/dev/") and os.path.islink(active_port): + active_port = os.path.realpath(active_port) + + ports = list_ports.comports() + for p in ports: + if p.device == active_port: + return p.pid + print("\nFailed to get PID of a device on {}, using standard reset sequence.".format(active_port)) + + def bootloader_reset(self, esp32r0_delay=False, usb_jtag_serial=False): + """ Issue a reset-to-bootloader, with esp32r0 workaround options + and USB-JTAG-Serial custom reset sequence option + """ + # RTS = either CH_PD/EN or nRESET (both active low = chip in reset) + # DTR = GPIO0 (active low = boot to flasher) + # + # DTR & RTS are active low signals, + # ie True = pin @ 0V, False = pin @ VCC. + if usb_jtag_serial: + # Custom reset sequence, which is required when the device + # is connecting via its USB-JTAG-Serial peripheral + self._setRTS(False) + self._setDTR(False) # Idle + time.sleep(0.1) + self._setDTR(True) # Set IO0 + self._setRTS(False) + time.sleep(0.1) + self._setRTS(True) # Reset. Note dtr/rts calls inverted so we go through (1,1) instead of (0,0) + self._setDTR(False) + self._setRTS(True) # Extra RTS set for RTS as Windows only propagates DTR on RTS setting + time.sleep(0.1) + self._setDTR(False) + self._setRTS(False) + else: + self._setDTR(False) # IO0=HIGH + self._setRTS(True) # EN=LOW, chip in reset + time.sleep(0.1) + if esp32r0_delay: + # Some chips are more likely to trigger the esp32r0 + # watchdog reset silicon bug if they're held with EN=LOW + # for a longer period + time.sleep(1.2) + self._setDTR(True) # IO0=LOW + self._setRTS(False) # EN=HIGH, chip out of reset + if esp32r0_delay: + # Sleep longer after reset. + # This workaround only works on revision 0 ESP32 chips, + # it exploits a silicon bug spurious watchdog reset. + time.sleep(0.4) # allow watchdog reset to occur + time.sleep(0.05) + self._setDTR(False) # IO0=HIGH, done + + def _connect_attempt(self, mode='default_reset', esp32r0_delay=False, usb_jtag_serial=False): + """ A single connection attempt, with esp32r0 workaround options """ + # esp32r0_delay is a workaround for bugs with the most common auto reset + # circuit and Windows, if the EN pin on the dev board does not have + # enough capacitance. + # + # Newer dev boards shouldn't have this problem (higher value capacitor + # on the EN pin), and ESP32 revision 1 can't use this workaround as it + # relies on a silicon bug. + # + # Details: https://github.com/espressif/esptool/issues/136 + last_error = None + + # If we're doing no_sync, we're likely communicating as a pass through + # with an intermediate device to the ESP32 + if mode == "no_reset_no_sync": + return last_error + + if mode != 'no_reset': + self.bootloader_reset(esp32r0_delay, usb_jtag_serial) + + for _ in range(5): + try: + self.flush_input() + self._port.flushOutput() + self.sync() + return None + except FatalError as e: + if esp32r0_delay: + print('_', end='') + else: + print('.', end='') + sys.stdout.flush() + time.sleep(0.05) + last_error = e + return last_error + + def get_memory_region(self, name): + """ Returns a tuple of (start, end) for the memory map entry with the given name, or None if it doesn't exist + """ + try: + return [(start, end) for (start, end, n) in self.MEMORY_MAP if n == name][0] + except IndexError: + return None + + def connect(self, mode='default_reset', attempts=DEFAULT_CONNECT_ATTEMPTS, detecting=False): + """ Try connecting repeatedly until successful, or giving up """ + if mode in ['no_reset', 'no_reset_no_sync']: + print('WARNING: Pre-connection option "{}" was selected.'.format(mode), + 'Connection may fail if the chip is not in bootloader or flasher stub mode.') + print('Connecting...', end='') + sys.stdout.flush() + last_error = None + + usb_jtag_serial = (mode == 'usb_reset') or (self._get_pid() == self.USB_JTAG_SERIAL_PID) + + try: + for _ in range(attempts) if attempts > 0 else itertools.count(): + last_error = self._connect_attempt(mode=mode, esp32r0_delay=False, usb_jtag_serial=usb_jtag_serial) + if last_error is None: + break + last_error = self._connect_attempt(mode=mode, esp32r0_delay=True, usb_jtag_serial=usb_jtag_serial) + if last_error is None: + break + finally: + print('') # end 'Connecting...' line + + if last_error is not None: + raise FatalError('Failed to connect to %s: %s' % (self.CHIP_NAME, last_error)) + + if not detecting: + try: + # check the date code registers match what we expect to see + chip_magic_value = self.read_reg(ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR) + if chip_magic_value not in self.CHIP_DETECT_MAGIC_VALUE: + actually = None + for cls in [ESP8266ROM, ESP32ROM, ESP32S2ROM, ESP32S3BETA2ROM, ESP32S3BETA3ROM, ESP32C3ROM]: + if chip_magic_value in cls.CHIP_DETECT_MAGIC_VALUE: + actually = cls + break + if actually is None: + print(("WARNING: This chip doesn't appear to be a %s (chip magic value 0x%08x). " + "Probably it is unsupported by this version of esptool.") % (self.CHIP_NAME, chip_magic_value)) + else: + raise FatalError("This chip is %s not %s. Wrong --chip argument?" % (actually.CHIP_NAME, self.CHIP_NAME)) + except UnsupportedCommandError: + self.secure_download_mode = True + self._post_connect() + + def _post_connect(self): + """ + Additional initialization hook, may be overridden by the chip-specific class. + Gets called after connect, and after auto-detection. + """ + pass + + def read_reg(self, addr, timeout=DEFAULT_TIMEOUT): + """ Read memory address in target """ + # we don't call check_command here because read_reg() function is called + # when detecting chip type, and the way we check for success (STATUS_BYTES_LENGTH) is different + # for different chip types (!) + val, data = self.command(self.ESP_READ_REG, struct.pack(' 0: + # add a dummy write to a date register as an excuse to have a delay + command += struct.pack(' start: + raise FatalError(("Software loader is resident at 0x%08x-0x%08x. " + "Can't load binary at overlapping address range 0x%08x-0x%08x. " + "Either change binary loading address, or use the --no-stub " + "option to disable the software loader.") % (start, end, load_start, load_end)) + + return self.check_command("enter RAM download mode", self.ESP_MEM_BEGIN, + struct.pack(' length: + raise FatalError('Read more than expected') + + digest_frame = self.read() + if len(digest_frame) != 16: + raise FatalError('Expected digest, got: %s' % hexify(digest_frame)) + expected_digest = hexify(digest_frame).upper() + digest = hashlib.md5(data).hexdigest().upper() + if digest != expected_digest: + raise FatalError('Digest mismatch: expected %s, got %s' % (expected_digest, digest)) + return data + + def flash_spi_attach(self, hspi_arg): + """Send SPI attach command to enable the SPI flash pins + + ESP8266 ROM does this when you send flash_begin, ESP32 ROM + has it as a SPI command. + """ + # last 3 bytes in ESP_SPI_ATTACH argument are reserved values + arg = struct.pack(' 0: + self.write_reg(SPI_MOSI_DLEN_REG, mosi_bits - 1) + if miso_bits > 0: + self.write_reg(SPI_MISO_DLEN_REG, miso_bits - 1) + else: + + def set_data_lengths(mosi_bits, miso_bits): + SPI_DATA_LEN_REG = SPI_USR1_REG + SPI_MOSI_BITLEN_S = 17 + SPI_MISO_BITLEN_S = 8 + mosi_mask = 0 if (mosi_bits == 0) else (mosi_bits - 1) + miso_mask = 0 if (miso_bits == 0) else (miso_bits - 1) + self.write_reg(SPI_DATA_LEN_REG, + (miso_mask << SPI_MISO_BITLEN_S) | ( + mosi_mask << SPI_MOSI_BITLEN_S)) + + # SPI peripheral "command" bitmasks for SPI_CMD_REG + SPI_CMD_USR = (1 << 18) + + # shift values + SPI_USR2_COMMAND_LEN_SHIFT = 28 + + if read_bits > 32: + raise FatalError("Reading more than 32 bits back from a SPI flash operation is unsupported") + if len(data) > 64: + raise FatalError("Writing more than 64 bytes of data with one SPI command is unsupported") + + data_bits = len(data) * 8 + old_spi_usr = self.read_reg(SPI_USR_REG) + old_spi_usr2 = self.read_reg(SPI_USR2_REG) + flags = SPI_USR_COMMAND + if read_bits > 0: + flags |= SPI_USR_MISO + if data_bits > 0: + flags |= SPI_USR_MOSI + set_data_lengths(data_bits, read_bits) + self.write_reg(SPI_USR_REG, flags) + self.write_reg(SPI_USR2_REG, + (7 << SPI_USR2_COMMAND_LEN_SHIFT) | spiflash_command) + if data_bits == 0: + self.write_reg(SPI_W0_REG, 0) # clear data register before we read it + else: + data = pad_to(data, 4, b'\00') # pad to 32-bit multiple + words = struct.unpack("I" * (len(data) // 4), data) + next_reg = SPI_W0_REG + for word in words: + self.write_reg(next_reg, word) + next_reg += 4 + self.write_reg(SPI_CMD_REG, SPI_CMD_USR) + + def wait_done(): + for _ in range(10): + if (self.read_reg(SPI_CMD_REG) & SPI_CMD_USR) == 0: + return + raise FatalError("SPI command did not complete in time") + wait_done() + + status = self.read_reg(SPI_W0_REG) + # restore some SPI controller registers + self.write_reg(SPI_USR_REG, old_spi_usr) + self.write_reg(SPI_USR2_REG, old_spi_usr2) + return status + + def read_status(self, num_bytes=2): + """Read up to 24 bits (num_bytes) of SPI flash status register contents + via RDSR, RDSR2, RDSR3 commands + + Not all SPI flash supports all three commands. The upper 1 or 2 + bytes may be 0xFF. + """ + SPIFLASH_RDSR = 0x05 + SPIFLASH_RDSR2 = 0x35 + SPIFLASH_RDSR3 = 0x15 + + status = 0 + shift = 0 + for cmd in [SPIFLASH_RDSR, SPIFLASH_RDSR2, SPIFLASH_RDSR3][0:num_bytes]: + status += self.run_spiflash_command(cmd, read_bits=8) << shift + shift += 8 + return status + + def write_status(self, new_status, num_bytes=2, set_non_volatile=False): + """Write up to 24 bits (num_bytes) of new status register + + num_bytes can be 1, 2 or 3. + + Not all flash supports the additional commands to write the + second and third byte of the status register. When writing 2 + bytes, esptool also sends a 16-byte WRSR command (as some + flash types use this instead of WRSR2.) + + If the set_non_volatile flag is set, non-volatile bits will + be set as well as volatile ones (WREN used instead of WEVSR). + + """ + SPIFLASH_WRSR = 0x01 + SPIFLASH_WRSR2 = 0x31 + SPIFLASH_WRSR3 = 0x11 + SPIFLASH_WEVSR = 0x50 + SPIFLASH_WREN = 0x06 + SPIFLASH_WRDI = 0x04 + + enable_cmd = SPIFLASH_WREN if set_non_volatile else SPIFLASH_WEVSR + + # try using a 16-bit WRSR (not supported by all chips) + # this may be redundant, but shouldn't hurt + if num_bytes == 2: + self.run_spiflash_command(enable_cmd) + self.run_spiflash_command(SPIFLASH_WRSR, struct.pack(">= 8 + + self.run_spiflash_command(SPIFLASH_WRDI) + + def get_crystal_freq(self): + # Figure out the crystal frequency from the UART clock divider + # Returns a normalized value in integer MHz (40 or 26 are the only supported values) + # + # The logic here is: + # - We know that our baud rate and the ESP UART baud rate are roughly the same, or we couldn't communicate + # - We can read the UART clock divider register to know how the ESP derives this from the APB bus frequency + # - Multiplying these two together gives us the bus frequency which is either the crystal frequency (ESP32) + # or double the crystal frequency (ESP8266). See the self.XTAL_CLK_DIVIDER parameter for this factor. + uart_div = self.read_reg(self.UART_CLKDIV_REG) & self.UART_CLKDIV_MASK + est_xtal = (self._port.baudrate * uart_div) / 1e6 / self.XTAL_CLK_DIVIDER + norm_xtal = 40 if est_xtal > 33 else 26 + if abs(norm_xtal - est_xtal) > 1: + print("WARNING: Detected crystal freq %.2fMHz is quite different to normalized freq %dMHz. Unsupported crystal in use?" % (est_xtal, norm_xtal)) + return norm_xtal + + def hard_reset(self): + print('Hard resetting via RTS pin...') + self._setRTS(True) # EN->LOW + time.sleep(0.1) + self._setRTS(False) + + def soft_reset(self, stay_in_bootloader): + if not self.IS_STUB: + if stay_in_bootloader: + return # ROM bootloader is already in bootloader! + else: + # 'run user code' is as close to a soft reset as we can do + self.flash_begin(0, 0) + self.flash_finish(False) + else: + if stay_in_bootloader: + # soft resetting from the stub loader + # will re-load the ROM bootloader + self.flash_begin(0, 0) + self.flash_finish(True) + elif self.CHIP_NAME != "ESP8266": + raise FatalError("Soft resetting is currently only supported on ESP8266") + else: + # running user code from stub loader requires some hacks + # in the stub loader + self.command(self.ESP_RUN_USER_CODE, wait_response=False) + + +class ESP8266ROM(ESPLoader): + """ Access class for ESP8266 ROM bootloader + """ + CHIP_NAME = "ESP8266" + IS_STUB = False + + CHIP_DETECT_MAGIC_VALUE = [0xfff0c101] + + # OTP ROM addresses + ESP_OTP_MAC0 = 0x3ff00050 + ESP_OTP_MAC1 = 0x3ff00054 + ESP_OTP_MAC3 = 0x3ff0005c + + SPI_REG_BASE = 0x60000200 + SPI_USR_OFFS = 0x1c + SPI_USR1_OFFS = 0x20 + SPI_USR2_OFFS = 0x24 + SPI_MOSI_DLEN_OFFS = None + SPI_MISO_DLEN_OFFS = None + SPI_W0_OFFS = 0x40 + + UART_CLKDIV_REG = 0x60000014 + + XTAL_CLK_DIVIDER = 2 + + FLASH_SIZES = { + '512KB': 0x00, + '256KB': 0x10, + '1MB': 0x20, + '2MB': 0x30, + '4MB': 0x40, + '2MB-c1': 0x50, + '4MB-c1': 0x60, + '8MB': 0x80, + '16MB': 0x90, + } + + BOOTLOADER_FLASH_OFFSET = 0 + + MEMORY_MAP = [[0x3FF00000, 0x3FF00010, "DPORT"], + [0x3FFE8000, 0x40000000, "DRAM"], + [0x40100000, 0x40108000, "IRAM"], + [0x40201010, 0x402E1010, "IROM"]] + + def get_efuses(self): + # Return the 128 bits of ESP8266 efuse as a single Python integer + result = self.read_reg(0x3ff0005c) << 96 + result |= self.read_reg(0x3ff00058) << 64 + result |= self.read_reg(0x3ff00054) << 32 + result |= self.read_reg(0x3ff00050) + return result + + def _get_flash_size(self, efuses): + # rX_Y = EFUSE_DATA_OUTX[Y] + r0_4 = (efuses & (1 << 4)) != 0 + r3_25 = (efuses & (1 << 121)) != 0 + r3_26 = (efuses & (1 << 122)) != 0 + r3_27 = (efuses & (1 << 123)) != 0 + + if r0_4 and not r3_25: + if not r3_27 and not r3_26: + return 1 + elif not r3_27 and r3_26: + return 2 + if not r0_4 and r3_25: + if not r3_27 and not r3_26: + return 2 + elif not r3_27 and r3_26: + return 4 + return -1 + + def get_chip_description(self): + efuses = self.get_efuses() + is_8285 = (efuses & ((1 << 4) | 1 << 80)) != 0 # One or the other efuse bit is set for ESP8285 + if is_8285: + flash_size = self._get_flash_size(efuses) + max_temp = (efuses & (1 << 5)) != 0 # This efuse bit identifies the max flash temperature + chip_name = { + 1: "ESP8285H08" if max_temp else "ESP8285N08", + 2: "ESP8285H16" if max_temp else "ESP8285N16" + }.get(flash_size, "ESP8285") + return chip_name + return "ESP8266EX" + + def get_chip_features(self): + features = ["WiFi"] + if "ESP8285" in self.get_chip_description(): + features += ["Embedded Flash"] + return features + + def flash_spi_attach(self, hspi_arg): + if self.IS_STUB: + super(ESP8266ROM, self).flash_spi_attach(hspi_arg) + else: + # ESP8266 ROM has no flash_spi_attach command in serial protocol, + # but flash_begin will do it + self.flash_begin(0, 0) + + def flash_set_parameters(self, size): + # not implemented in ROM, but OK to silently skip for ROM + if self.IS_STUB: + super(ESP8266ROM, self).flash_set_parameters(size) + + def chip_id(self): + """ Read Chip ID from efuse - the equivalent of the SDK system_get_chip_id() function """ + id0 = self.read_reg(self.ESP_OTP_MAC0) + id1 = self.read_reg(self.ESP_OTP_MAC1) + return (id0 >> 24) | ((id1 & MAX_UINT24) << 8) + + def read_mac(self): + """ Read MAC from OTP ROM """ + mac0 = self.read_reg(self.ESP_OTP_MAC0) + mac1 = self.read_reg(self.ESP_OTP_MAC1) + mac3 = self.read_reg(self.ESP_OTP_MAC3) + if (mac3 != 0): + oui = ((mac3 >> 16) & 0xff, (mac3 >> 8) & 0xff, mac3 & 0xff) + elif ((mac1 >> 16) & 0xff) == 0: + oui = (0x18, 0xfe, 0x34) + elif ((mac1 >> 16) & 0xff) == 1: + oui = (0xac, 0xd0, 0x74) + else: + raise FatalError("Unknown OUI") + return oui + ((mac1 >> 8) & 0xff, mac1 & 0xff, (mac0 >> 24) & 0xff) + + def get_erase_size(self, offset, size): + """ Calculate an erase size given a specific size in bytes. + + Provides a workaround for the bootloader erase bug.""" + + sectors_per_block = 16 + sector_size = self.FLASH_SECTOR_SIZE + num_sectors = (size + sector_size - 1) // sector_size + start_sector = offset // sector_size + + head_sectors = sectors_per_block - (start_sector % sectors_per_block) + if num_sectors < head_sectors: + head_sectors = num_sectors + + if num_sectors < 2 * head_sectors: + return (num_sectors + 1) // 2 * sector_size + else: + return (num_sectors - head_sectors) * sector_size + + def override_vddsdio(self, new_voltage): + raise NotImplementedInROMError("Overriding VDDSDIO setting only applies to ESP32") + + +class ESP8266StubLoader(ESP8266ROM): + """ Access class for ESP8266 stub loader, runs on top of ROM. + """ + FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c + IS_STUB = True + + def __init__(self, rom_loader): + self.secure_download_mode = rom_loader.secure_download_mode + self._port = rom_loader._port + self._trace_enabled = rom_loader._trace_enabled + self.flush_input() # resets _slip_reader + + def get_erase_size(self, offset, size): + return size # stub doesn't have same size bug as ROM loader + + +ESP8266ROM.STUB_CLASS = ESP8266StubLoader + + +class ESP32ROM(ESPLoader): + """Access class for ESP32 ROM bootloader + + """ + CHIP_NAME = "ESP32" + IMAGE_CHIP_ID = 0 + IS_STUB = False + + CHIP_DETECT_MAGIC_VALUE = [0x00f01d83] + + IROM_MAP_START = 0x400d0000 + IROM_MAP_END = 0x40400000 + + DROM_MAP_START = 0x3F400000 + DROM_MAP_END = 0x3F800000 + + # ESP32 uses a 4 byte status reply + STATUS_BYTES_LENGTH = 4 + + SPI_REG_BASE = 0x3ff42000 + SPI_USR_OFFS = 0x1c + SPI_USR1_OFFS = 0x20 + SPI_USR2_OFFS = 0x24 + SPI_MOSI_DLEN_OFFS = 0x28 + SPI_MISO_DLEN_OFFS = 0x2c + EFUSE_RD_REG_BASE = 0x3ff5a000 + + EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE + 0x18 + EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = (1 << 7) # EFUSE_RD_DISABLE_DL_ENCRYPT + + DR_REG_SYSCON_BASE = 0x3ff66000 + + SPI_W0_OFFS = 0x80 + + UART_CLKDIV_REG = 0x3ff40014 + + XTAL_CLK_DIVIDER = 1 + + FLASH_SIZES = { + '1MB': 0x00, + '2MB': 0x10, + '4MB': 0x20, + '8MB': 0x30, + '16MB': 0x40 + } + + BOOTLOADER_FLASH_OFFSET = 0x1000 + + OVERRIDE_VDDSDIO_CHOICES = ["1.8V", "1.9V", "OFF"] + + MEMORY_MAP = [[0x00000000, 0x00010000, "PADDING"], + [0x3F400000, 0x3F800000, "DROM"], + [0x3F800000, 0x3FC00000, "EXTRAM_DATA"], + [0x3FF80000, 0x3FF82000, "RTC_DRAM"], + [0x3FF90000, 0x40000000, "BYTE_ACCESSIBLE"], + [0x3FFAE000, 0x40000000, "DRAM"], + [0x3FFE0000, 0x3FFFFFFC, "DIRAM_DRAM"], + [0x40000000, 0x40070000, "IROM"], + [0x40070000, 0x40078000, "CACHE_PRO"], + [0x40078000, 0x40080000, "CACHE_APP"], + [0x40080000, 0x400A0000, "IRAM"], + [0x400A0000, 0x400BFFFC, "DIRAM_IRAM"], + [0x400C0000, 0x400C2000, "RTC_IRAM"], + [0x400D0000, 0x40400000, "IROM"], + [0x50000000, 0x50002000, "RTC_DATA"]] + + FLASH_ENCRYPTED_WRITE_ALIGN = 32 + + """ Try to read the BLOCK1 (encryption key) and check if it is valid """ + + def is_flash_encryption_key_valid(self): + + """ Bit 0 of efuse_rd_disable[3:0] is mapped to BLOCK1 + this bit is at position 16 in EFUSE_BLK0_RDATA0_REG """ + word0 = self.read_efuse(0) + rd_disable = (word0 >> 16) & 0x1 + + # reading of BLOCK1 is NOT ALLOWED so we assume valid key is programmed + if rd_disable: + return True + else: + # reading of BLOCK1 is ALLOWED so we will read and verify for non-zero. + # When ESP32 has not generated AES/encryption key in BLOCK1, the contents will be readable and 0. + # If the flash encryption is enabled it is expected to have a valid non-zero key. We break out on + # first occurance of non-zero value + key_word = [0] * 7 + for i in range(len(key_word)): + key_word[i] = self.read_efuse(14 + i) + # key is non-zero so break & return + if key_word[i] != 0: + return True + return False + + def get_flash_crypt_config(self): + """ For flash encryption related commands we need to make sure + user has programmed all the relevant efuse correctly so before + writing encrypted write_flash_encrypt esptool will verify the values + of flash_crypt_config to be non zero if they are not read + protected. If the values are zero a warning will be printed + + bit 3 in efuse_rd_disable[3:0] is mapped to flash_crypt_config + this bit is at position 19 in EFUSE_BLK0_RDATA0_REG """ + word0 = self.read_efuse(0) + rd_disable = (word0 >> 19) & 0x1 + + if rd_disable == 0: + """ we can read the flash_crypt_config efuse value + so go & read it (EFUSE_BLK0_RDATA5_REG[31:28]) """ + word5 = self.read_efuse(5) + word5 = (word5 >> 28) & 0xF + return word5 + else: + # if read of the efuse is disabled we assume it is set correctly + return 0xF + + def get_encrypted_download_disabled(self): + if self.read_reg(self.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG) & self.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT: + return True + else: + return False + + def get_pkg_version(self): + word3 = self.read_efuse(3) + pkg_version = (word3 >> 9) & 0x07 + pkg_version += ((word3 >> 2) & 0x1) << 3 + return pkg_version + + def get_chip_revision(self): + word3 = self.read_efuse(3) + word5 = self.read_efuse(5) + apb_ctl_date = self.read_reg(self.DR_REG_SYSCON_BASE + 0x7C) + + rev_bit0 = (word3 >> 15) & 0x1 + rev_bit1 = (word5 >> 20) & 0x1 + rev_bit2 = (apb_ctl_date >> 31) & 0x1 + if rev_bit0: + if rev_bit1: + if rev_bit2: + return 3 + else: + return 2 + else: + return 1 + return 0 + + def get_chip_description(self): + pkg_version = self.get_pkg_version() + chip_revision = self.get_chip_revision() + rev3 = (chip_revision == 3) + single_core = self.read_efuse(3) & (1 << 0) # CHIP_VER DIS_APP_CPU + + chip_name = { + 0: "ESP32-S0WDQ6" if single_core else "ESP32-D0WDQ6", + 1: "ESP32-S0WD" if single_core else "ESP32-D0WD", + 2: "ESP32-D2WD", + 4: "ESP32-U4WDH", + 5: "ESP32-PICO-V3" if rev3 else "ESP32-PICO-D4", + 6: "ESP32-PICO-V3-02", + }.get(pkg_version, "unknown ESP32") + + # ESP32-D0WD-V3, ESP32-D0WDQ6-V3 + if chip_name.startswith("ESP32-D0WD") and rev3: + chip_name += "-V3" + + return "%s (revision %d)" % (chip_name, chip_revision) + + def get_chip_features(self): + features = ["WiFi"] + word3 = self.read_efuse(3) + + # names of variables in this section are lowercase + # versions of EFUSE names as documented in TRM and + # ESP-IDF efuse_reg.h + + chip_ver_dis_bt = word3 & (1 << 1) + if chip_ver_dis_bt == 0: + features += ["BT"] + + chip_ver_dis_app_cpu = word3 & (1 << 0) + if chip_ver_dis_app_cpu: + features += ["Single Core"] + else: + features += ["Dual Core"] + + chip_cpu_freq_rated = word3 & (1 << 13) + if chip_cpu_freq_rated: + chip_cpu_freq_low = word3 & (1 << 12) + if chip_cpu_freq_low: + features += ["160MHz"] + else: + features += ["240MHz"] + + pkg_version = self.get_pkg_version() + if pkg_version in [2, 4, 5, 6]: + features += ["Embedded Flash"] + + if pkg_version == 6: + features += ["Embedded PSRAM"] + + word4 = self.read_efuse(4) + adc_vref = (word4 >> 8) & 0x1F + if adc_vref: + features += ["VRef calibration in efuse"] + + blk3_part_res = word3 >> 14 & 0x1 + if blk3_part_res: + features += ["BLK3 partially reserved"] + + word6 = self.read_efuse(6) + coding_scheme = word6 & 0x3 + features += ["Coding Scheme %s" % { + 0: "None", + 1: "3/4", + 2: "Repeat (UNSUPPORTED)", + 3: "Invalid"}[coding_scheme]] + + return features + + def read_efuse(self, n): + """ Read the nth word of the ESP3x EFUSE region. """ + return self.read_reg(self.EFUSE_RD_REG_BASE + (4 * n)) + + def chip_id(self): + raise NotSupportedError(self, "chip_id") + + def read_mac(self): + """ Read MAC from EFUSE region """ + words = [self.read_efuse(2), self.read_efuse(1)] + bitstring = struct.pack(">II", *words) + bitstring = bitstring[2:8] # trim the 2 byte CRC + try: + return tuple(ord(b) for b in bitstring) + except TypeError: # Python 3, bitstring elements are already bytes + return tuple(bitstring) + + def get_erase_size(self, offset, size): + return size + + def override_vddsdio(self, new_voltage): + new_voltage = new_voltage.upper() + if new_voltage not in self.OVERRIDE_VDDSDIO_CHOICES: + raise FatalError("The only accepted VDDSDIO overrides are '1.8V', '1.9V' and 'OFF'") + RTC_CNTL_SDIO_CONF_REG = 0x3ff48074 + RTC_CNTL_XPD_SDIO_REG = (1 << 31) + RTC_CNTL_DREFH_SDIO_M = (3 << 29) + RTC_CNTL_DREFM_SDIO_M = (3 << 27) + RTC_CNTL_DREFL_SDIO_M = (3 << 25) + # RTC_CNTL_SDIO_TIEH = (1 << 23) # not used here, setting TIEH=1 would set 3.3V output, not safe for esptool.py to do + RTC_CNTL_SDIO_FORCE = (1 << 22) + RTC_CNTL_SDIO_PD_EN = (1 << 21) + + reg_val = RTC_CNTL_SDIO_FORCE # override efuse setting + reg_val |= RTC_CNTL_SDIO_PD_EN + if new_voltage != "OFF": + reg_val |= RTC_CNTL_XPD_SDIO_REG # enable internal LDO + if new_voltage == "1.9V": + reg_val |= (RTC_CNTL_DREFH_SDIO_M | RTC_CNTL_DREFM_SDIO_M | RTC_CNTL_DREFL_SDIO_M) # boost voltage + self.write_reg(RTC_CNTL_SDIO_CONF_REG, reg_val) + print("VDDSDIO regulator set to %s" % new_voltage) + + def read_flash_slow(self, offset, length, progress_fn): + BLOCK_LEN = 64 # ROM read limit per command (this limit is why it's so slow) + + data = b'' + while len(data) < length: + block_len = min(BLOCK_LEN, length - len(data)) + r = self.check_command("read flash block", self.ESP_READ_FLASH_SLOW, + struct.pack('> 21) & 0x0F + return pkg_version + + def get_chip_description(self): + chip_name = { + 0: "ESP32-S2", + 1: "ESP32-S2FH16", + 2: "ESP32-S2FH32", + }.get(self.get_pkg_version(), "unknown ESP32-S2") + + return "%s" % (chip_name) + + def get_chip_features(self): + features = ["WiFi"] + + if self.secure_download_mode: + features += ["Secure Download Mode Enabled"] + + pkg_version = self.get_pkg_version() + + if pkg_version in [1, 2]: + if pkg_version == 1: + features += ["Embedded 2MB Flash"] + elif pkg_version == 2: + features += ["Embedded 4MB Flash"] + features += ["105C temp rating"] + + num_word = 4 + block2_addr = self.EFUSE_BASE + 0x05C + word4 = self.read_reg(block2_addr + (4 * num_word)) + block2_version = (word4 >> 4) & 0x07 + + if block2_version == 1: + features += ["ADC and temperature sensor calibration in BLK2 of efuse"] + return features + + def get_crystal_freq(self): + # ESP32-S2 XTAL is fixed to 40MHz + return 40 + + def override_vddsdio(self, new_voltage): + raise NotImplementedInROMError("VDD_SDIO overrides are not supported for ESP32-S2") + + def read_mac(self): + mac0 = self.read_reg(self.MAC_EFUSE_REG) + mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC + bitstring = struct.pack(">II", mac1, mac0)[2:] + try: + return tuple(ord(b) for b in bitstring) + except TypeError: # Python 3, bitstring elements are already bytes + return tuple(bitstring) + + def get_flash_crypt_config(self): + return None # doesn't exist on ESP32-S2 + + def get_key_block_purpose(self, key_block): + if key_block < 0 or key_block > 5: + raise FatalError("Valid key block numbers must be in range 0-5") + + reg, shift = [(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT), + (self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT), + (self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT), + (self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT), + (self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT), + (self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT)][key_block] + return (self.read_reg(reg) >> shift) & 0xF + + def is_flash_encryption_key_valid(self): + # Need to see either an AES-128 key or two AES-256 keys + purposes = [self.get_key_block_purpose(b) for b in range(6)] + + if any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes): + return True + + return any(p == self.PURPOSE_VAL_XTS_AES256_KEY_1 for p in purposes) \ + and any(p == self.PURPOSE_VAL_XTS_AES256_KEY_2 for p in purposes) + + def uses_usb(self, _cache=[]): + if self.secure_download_mode: + return False # can't detect native USB in secure download mode + if not _cache: + buf_no = self.read_reg(self.UARTDEV_BUF_NO) & 0xff + _cache.append(buf_no == self.UARTDEV_BUF_NO_USB) + return _cache[0] + + def _post_connect(self): + if self.uses_usb(): + self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK + + def _check_if_can_reset(self): + """ + Check the strapping register to see if we can reset out of download mode. + """ + if os.getenv("ESPTOOL_TESTING") is not None: + print("ESPTOOL_TESTING is set, ignoring strapping mode check") + # Esptool tests over USB CDC run with GPIO0 strapped low, don't complain in this case. + return + strap_reg = self.read_reg(self.GPIO_STRAP_REG) + force_dl_reg = self.read_reg(self.RTC_CNTL_OPTION1_REG) + if strap_reg & self.GPIO_STRAP_SPI_BOOT_MASK == 0 and force_dl_reg & self.RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK == 0: + print("ERROR: {} chip was placed into download mode using GPIO0.\n" + "esptool.py can not exit the download mode over USB. " + "To run the app, reset the chip manually.\n" + "To suppress this error, set --after option to 'no_reset'.".format(self.get_chip_description())) + raise SystemExit(1) + + def hard_reset(self): + if self.uses_usb(): + self._check_if_can_reset() + + self._setRTS(True) # EN->LOW + if self.uses_usb(): + # Give the chip some time to come out of reset, to be able to handle further DTR/RTS transitions + time.sleep(0.2) + self._setRTS(False) + time.sleep(0.2) + else: + self._setRTS(False) + + +class ESP32S3ROM(ESP32ROM): + CHIP_NAME = "ESP32-S3" + + IROM_MAP_START = 0x42000000 + IROM_MAP_END = 0x44000000 + DROM_MAP_START = 0x3c000000 + DROM_MAP_END = 0x3e000000 + + UART_DATE_REG_ADDR = 0x60000080 + + SPI_REG_BASE = 0x60002000 + SPI_USR_OFFS = 0x18 + SPI_USR1_OFFS = 0x1c + SPI_USR2_OFFS = 0x20 + SPI_MOSI_DLEN_OFFS = 0x24 + SPI_MISO_DLEN_OFFS = 0x28 + SPI_W0_OFFS = 0x58 + + FLASH_ENCRYPTED_WRITE_ALIGN = 16 + + # todo: use espefuse APIs to get this info + EFUSE_BASE = 0x6001A000 # BLOCK0 read base address + MAC_EFUSE_REG = EFUSE_BASE + 0x044 + + EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030 # BLOCK0 read base address + + EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34 + EFUSE_PURPOSE_KEY0_SHIFT = 24 + EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34 + EFUSE_PURPOSE_KEY1_SHIFT = 28 + EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY2_SHIFT = 0 + EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY3_SHIFT = 4 + EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY4_SHIFT = 8 + EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY5_SHIFT = 12 + + EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE + EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20 + + PURPOSE_VAL_XTS_AES256_KEY_1 = 2 + PURPOSE_VAL_XTS_AES256_KEY_2 = 3 + PURPOSE_VAL_XTS_AES128_KEY = 4 + + UART_CLKDIV_REG = 0x60000014 + + GPIO_STRAP_REG = 0x60004038 + + MEMORY_MAP = [[0x00000000, 0x00010000, "PADDING"], + [0x3C000000, 0x3D000000, "DROM"], + [0x3D000000, 0x3E000000, "EXTRAM_DATA"], + [0x600FE000, 0x60100000, "RTC_DRAM"], + [0x3FC88000, 0x3FD00000, "BYTE_ACCESSIBLE"], + [0x3FC88000, 0x403E2000, "MEM_INTERNAL"], + [0x3FC88000, 0x3FD00000, "DRAM"], + [0x40000000, 0x4001A100, "IROM_MASK"], + [0x40370000, 0x403E0000, "IRAM"], + [0x600FE000, 0x60100000, "RTC_IRAM"], + [0x42000000, 0x42800000, "IROM"], + [0x50000000, 0x50002000, "RTC_DATA"]] + + def get_chip_description(self): + return "ESP32-S3" + + def get_chip_features(self): + return ["WiFi", "BLE"] + + def get_crystal_freq(self): + # ESP32S3 XTAL is fixed to 40MHz + return 40 + + def get_flash_crypt_config(self): + return None # doesn't exist on ESP32-S3 + + def get_key_block_purpose(self, key_block): + if key_block < 0 or key_block > 5: + raise FatalError("Valid key block numbers must be in range 0-5") + + reg, shift = [(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT), + (self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT), + (self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT), + (self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT), + (self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT), + (self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT)][key_block] + return (self.read_reg(reg) >> shift) & 0xF + + def is_flash_encryption_key_valid(self): + # Need to see either an AES-128 key or two AES-256 keys + purposes = [self.get_key_block_purpose(b) for b in range(6)] + + if any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes): + return True + + return any(p == self.PURPOSE_VAL_XTS_AES256_KEY_1 for p in purposes) \ + and any(p == self.PURPOSE_VAL_XTS_AES256_KEY_2 for p in purposes) + + def override_vddsdio(self, new_voltage): + raise NotImplementedInROMError("VDD_SDIO overrides are not supported for ESP32-S3") + + def read_mac(self): + mac0 = self.read_reg(self.MAC_EFUSE_REG) + mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC + bitstring = struct.pack(">II", mac1, mac0)[2:] + try: + return tuple(ord(b) for b in bitstring) + except TypeError: # Python 3, bitstring elements are already bytes + return tuple(bitstring) + + +class ESP32S3BETA2ROM(ESP32S3ROM): + CHIP_NAME = "ESP32-S3(beta2)" + IMAGE_CHIP_ID = 4 + + CHIP_DETECT_MAGIC_VALUE = [0xeb004136] + + def get_chip_description(self): + return "ESP32-S3(beta2)" + + +class ESP32S3BETA3ROM(ESP32S3ROM): + CHIP_NAME = "ESP32-S3(beta3)" + IMAGE_CHIP_ID = 6 + + CHIP_DETECT_MAGIC_VALUE = [0x9] + + def get_chip_description(self): + return "ESP32-S3(beta3)" + + +class ESP32C3ROM(ESP32ROM): + CHIP_NAME = "ESP32-C3" + IMAGE_CHIP_ID = 5 + + IROM_MAP_START = 0x42000000 + IROM_MAP_END = 0x42800000 + DROM_MAP_START = 0x3c000000 + DROM_MAP_END = 0x3c800000 + + SPI_REG_BASE = 0x60002000 + SPI_USR_OFFS = 0x18 + SPI_USR1_OFFS = 0x1C + SPI_USR2_OFFS = 0x20 + SPI_MOSI_DLEN_OFFS = 0x24 + SPI_MISO_DLEN_OFFS = 0x28 + SPI_W0_OFFS = 0x58 + + BOOTLOADER_FLASH_OFFSET = 0x0 + + # Magic value for ESP32C3 eco 1+2 and ESP32C3 eco3 respectivly + CHIP_DETECT_MAGIC_VALUE = [0x6921506f, 0x1b31506f] + + UART_DATE_REG_ADDR = 0x60000000 + 0x7c + + EFUSE_BASE = 0x60008800 + MAC_EFUSE_REG = EFUSE_BASE + 0x044 + + EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030 # BLOCK0 read base address + + EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34 + EFUSE_PURPOSE_KEY0_SHIFT = 24 + EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34 + EFUSE_PURPOSE_KEY1_SHIFT = 28 + EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY2_SHIFT = 0 + EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY3_SHIFT = 4 + EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY4_SHIFT = 8 + EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY5_SHIFT = 12 + + EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE + EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20 + + PURPOSE_VAL_XTS_AES128_KEY = 4 + + GPIO_STRAP_REG = 0x3f404038 + + FLASH_ENCRYPTED_WRITE_ALIGN = 16 + + MEMORY_MAP = [[0x00000000, 0x00010000, "PADDING"], + [0x3C000000, 0x3C800000, "DROM"], + [0x3FC80000, 0x3FCE0000, "DRAM"], + [0x3FC88000, 0x3FD00000, "BYTE_ACCESSIBLE"], + [0x3FF00000, 0x3FF20000, "DROM_MASK"], + [0x40000000, 0x40060000, "IROM_MASK"], + [0x42000000, 0x42800000, "IROM"], + [0x4037C000, 0x403E0000, "IRAM"], + [0x50000000, 0x50002000, "RTC_IRAM"], + [0x50000000, 0x50002000, "RTC_DRAM"], + [0x600FE000, 0x60100000, "MEM_INTERNAL2"]] + + def get_pkg_version(self): + num_word = 3 + block1_addr = self.EFUSE_BASE + 0x044 + word3 = self.read_reg(block1_addr + (4 * num_word)) + pkg_version = (word3 >> 21) & 0x0F + return pkg_version + + def get_chip_revision(self): + # reads WAFER_VERSION field from EFUSE_RD_MAC_SPI_SYS_3_REG + block1_addr = self.EFUSE_BASE + 0x044 + num_word = 3 + pos = 18 + return (self.read_reg(block1_addr + (4 * num_word)) & (0x7 << pos)) >> pos + + def get_chip_description(self): + chip_name = { + 0: "ESP32-C3", + }.get(self.get_pkg_version(), "unknown ESP32-C3") + chip_revision = self.get_chip_revision() + + return "%s (revision %d)" % (chip_name, chip_revision) + + def get_chip_features(self): + return ["Wi-Fi"] + + def get_crystal_freq(self): + # ESP32C3 XTAL is fixed to 40MHz + return 40 + + def override_vddsdio(self, new_voltage): + raise NotImplementedInROMError("VDD_SDIO overrides are not supported for ESP32-C3") + + def read_mac(self): + mac0 = self.read_reg(self.MAC_EFUSE_REG) + mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC + bitstring = struct.pack(">II", mac1, mac0)[2:] + try: + return tuple(ord(b) for b in bitstring) + except TypeError: # Python 3, bitstring elements are already bytes + return tuple(bitstring) + + def get_flash_crypt_config(self): + return None # doesn't exist on ESP32-C3 + + def get_key_block_purpose(self, key_block): + if key_block < 0 or key_block > 5: + raise FatalError("Valid key block numbers must be in range 0-5") + + reg, shift = [(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT), + (self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT), + (self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT), + (self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT), + (self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT), + (self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT)][key_block] + return (self.read_reg(reg) >> shift) & 0xF + + def is_flash_encryption_key_valid(self): + # Need to see an AES-128 key + purposes = [self.get_key_block_purpose(b) for b in range(6)] + + return any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes) + + +class ESP32C6BETAROM(ESP32C3ROM): + CHIP_NAME = "ESP32-C6 BETA" + IMAGE_CHIP_ID = 7 + + CHIP_DETECT_MAGIC_VALUE = [0x0da1806f] + + UART_DATE_REG_ADDR = 0x00000500 + + def get_chip_description(self): + chip_name = { + 0: "ESP32-C6", + }.get(self.get_pkg_version(), "unknown ESP32-C6") + chip_revision = self.get_chip_revision() + + return "%s (revision %d)" % (chip_name, chip_revision) + + +class ESP32StubLoader(ESP32ROM): + """ Access class for ESP32 stub loader, runs on top of ROM. + """ + FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c + STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM + IS_STUB = True + + def __init__(self, rom_loader): + self.secure_download_mode = rom_loader.secure_download_mode + self._port = rom_loader._port + self._trace_enabled = rom_loader._trace_enabled + self.flush_input() # resets _slip_reader + + +ESP32ROM.STUB_CLASS = ESP32StubLoader + + +class ESP32S2StubLoader(ESP32S2ROM): + """ Access class for ESP32-S2 stub loader, runs on top of ROM. + + (Basically the same as ESP32StubLoader, but different base class. + Can possibly be made into a mixin.) + """ + FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c + STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM + IS_STUB = True + + def __init__(self, rom_loader): + self.secure_download_mode = rom_loader.secure_download_mode + self._port = rom_loader._port + self._trace_enabled = rom_loader._trace_enabled + self.flush_input() # resets _slip_reader + + if rom_loader.uses_usb(): + self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK + self.FLASH_WRITE_SIZE = self.USB_RAM_BLOCK + + +ESP32S2ROM.STUB_CLASS = ESP32S2StubLoader + + +class ESP32S3BETA2StubLoader(ESP32S3BETA2ROM): + """ Access class for ESP32S3 stub loader, runs on top of ROM. + + (Basically the same as ESP32StubLoader, but different base class. + Can possibly be made into a mixin.) + """ + FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c + STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM + IS_STUB = True + + def __init__(self, rom_loader): + self.secure_download_mode = rom_loader.secure_download_mode + self._port = rom_loader._port + self._trace_enabled = rom_loader._trace_enabled + self.flush_input() # resets _slip_reader + + +ESP32S3BETA2ROM.STUB_CLASS = ESP32S3BETA2StubLoader + + +class ESP32S3BETA3StubLoader(ESP32S3BETA3ROM): + """ Access class for ESP32S3 stub loader, runs on top of ROM. + + (Basically the same as ESP32StubLoader, but different base class. + Can possibly be made into a mixin.) + """ + FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c + STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM + IS_STUB = True + + def __init__(self, rom_loader): + self.secure_download_mode = rom_loader.secure_download_mode + self._port = rom_loader._port + self._trace_enabled = rom_loader._trace_enabled + self.flush_input() # resets _slip_reader + + +ESP32S3BETA3ROM.STUB_CLASS = ESP32S3BETA3StubLoader + + +class ESP32C3StubLoader(ESP32C3ROM): + """ Access class for ESP32C3 stub loader, runs on top of ROM. + + (Basically the same as ESP32StubLoader, but different base class. + Can possibly be made into a mixin.) + """ + FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c + STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM + IS_STUB = True + + def __init__(self, rom_loader): + self.secure_download_mode = rom_loader.secure_download_mode + self._port = rom_loader._port + self._trace_enabled = rom_loader._trace_enabled + self.flush_input() # resets _slip_reader + + +ESP32C3ROM.STUB_CLASS = ESP32C3StubLoader + + +class ESPBOOTLOADER(object): + """ These are constants related to software ESP8266 bootloader, working with 'v2' image files """ + + # First byte of the "v2" application image + IMAGE_V2_MAGIC = 0xea + + # First 'segment' value in a "v2" application image, appears to be a constant version value? + IMAGE_V2_SEGMENT = 4 + + +def LoadFirmwareImage(chip, filename): + """ Load a firmware image. Can be for any supported SoC. + + ESP8266 images will be examined to determine if they are original ROM firmware images (ESP8266ROMFirmwareImage) + or "v2" OTA bootloader images. + + Returns a BaseFirmwareImage subclass, either ESP8266ROMFirmwareImage (v1) or ESP8266V2FirmwareImage (v2). + """ + chip = chip.lower().replace("-", "") + with open(filename, 'rb') as f: + if chip == 'esp32': + return ESP32FirmwareImage(f) + elif chip == "esp32s2": + return ESP32S2FirmwareImage(f) + elif chip == "esp32s3beta2": + return ESP32S3BETA2FirmwareImage(f) + elif chip == "esp32s3beta3": + return ESP32S3BETA3FirmwareImage(f) + elif chip == 'esp32c3': + return ESP32C3FirmwareImage(f) + elif chip == 'esp32c6beta': + return ESP32C6BETAFirmwareImage(f) + else: # Otherwise, ESP8266 so look at magic to determine the image type + magic = ord(f.read(1)) + f.seek(0) + if magic == ESPLoader.ESP_IMAGE_MAGIC: + return ESP8266ROMFirmwareImage(f) + elif magic == ESPBOOTLOADER.IMAGE_V2_MAGIC: + return ESP8266V2FirmwareImage(f) + else: + raise FatalError("Invalid image magic number: %d" % magic) + + +class ImageSegment(object): + """ Wrapper class for a segment in an ESP image + (very similar to a section in an ELFImage also) """ + def __init__(self, addr, data, file_offs=None): + self.addr = addr + self.data = data + self.file_offs = file_offs + self.include_in_checksum = True + if self.addr != 0: + self.pad_to_alignment(4) # pad all "real" ImageSegments 4 byte aligned length + + def copy_with_new_addr(self, new_addr): + """ Return a new ImageSegment with same data, but mapped at + a new address. """ + return ImageSegment(new_addr, self.data, 0) + + def split_image(self, split_len): + """ Return a new ImageSegment which splits "split_len" bytes + from the beginning of the data. Remaining bytes are kept in + this segment object (and the start address is adjusted to match.) """ + result = copy.copy(self) + result.data = self.data[:split_len] + self.data = self.data[split_len:] + self.addr += split_len + self.file_offs = None + result.file_offs = None + return result + + def __repr__(self): + r = "len 0x%05x load 0x%08x" % (len(self.data), self.addr) + if self.file_offs is not None: + r += " file_offs 0x%08x" % (self.file_offs) + return r + + def get_memory_type(self, image): + """ + Return a list describing the memory type(s) that is covered by this + segment's start address. + """ + return [map_range[2] for map_range in image.ROM_LOADER.MEMORY_MAP if map_range[0] <= self.addr < map_range[1]] + + def pad_to_alignment(self, alignment): + self.data = pad_to(self.data, alignment, b'\x00') + + +class ELFSection(ImageSegment): + """ Wrapper class for a section in an ELF image, has a section + name as well as the common properties of an ImageSegment. """ + def __init__(self, name, addr, data): + super(ELFSection, self).__init__(addr, data) + self.name = name.decode("utf-8") + + def __repr__(self): + return "%s %s" % (self.name, super(ELFSection, self).__repr__()) + + +class BaseFirmwareImage(object): + SEG_HEADER_LEN = 8 + SHA256_DIGEST_LEN = 32 + + """ Base class with common firmware image functions """ + def __init__(self): + self.segments = [] + self.entrypoint = 0 + self.elf_sha256 = None + self.elf_sha256_offset = 0 + + def load_common_header(self, load_file, expected_magic): + (magic, segments, self.flash_mode, self.flash_size_freq, self.entrypoint) = struct.unpack(' 16: + raise FatalError('Invalid segment count %d (max 16). Usually this indicates a linker script problem.' % len(self.segments)) + + def load_segment(self, f, is_irom_segment=False): + """ Load the next segment from the image file """ + file_offs = f.tell() + (offset, size) = struct.unpack(' 0x40200000 or offset < 0x3ffe0000 or size > 65536: + print('WARNING: Suspicious segment 0x%x, length %d' % (offset, size)) + + def maybe_patch_segment_data(self, f, segment_data): + """If SHA256 digest of the ELF file needs to be inserted into this segment, do so. Returns segment data.""" + segment_len = len(segment_data) + file_pos = f.tell() # file_pos is position in the .bin file + if self.elf_sha256_offset >= file_pos and self.elf_sha256_offset < file_pos + segment_len: + # SHA256 digest needs to be patched into this binary segment, + # calculate offset of the digest inside the binary segment. + patch_offset = self.elf_sha256_offset - file_pos + # Sanity checks + if patch_offset < self.SEG_HEADER_LEN or patch_offset + self.SHA256_DIGEST_LEN > segment_len: + raise FatalError('Cannot place SHA256 digest on segment boundary' + '(elf_sha256_offset=%d, file_pos=%d, segment_size=%d)' % + (self.elf_sha256_offset, file_pos, segment_len)) + # offset relative to the data part + patch_offset -= self.SEG_HEADER_LEN + if segment_data[patch_offset:patch_offset + self.SHA256_DIGEST_LEN] != b'\x00' * self.SHA256_DIGEST_LEN: + raise FatalError('Contents of segment at SHA256 digest offset 0x%x are not all zero. Refusing to overwrite.' % + self.elf_sha256_offset) + assert(len(self.elf_sha256) == self.SHA256_DIGEST_LEN) + segment_data = segment_data[0:patch_offset] + self.elf_sha256 + \ + segment_data[patch_offset + self.SHA256_DIGEST_LEN:] + return segment_data + + def save_segment(self, f, segment, checksum=None): + """ Save the next segment to the image file, return next checksum value if provided """ + segment_data = self.maybe_patch_segment_data(f, segment.data) + f.write(struct.pack(' 0: + if len(irom_segments) != 1: + raise FatalError('Found %d segments that could be irom0. Bad ELF file?' % len(irom_segments)) + return irom_segments[0] + return None + + def get_non_irom_segments(self): + irom_segment = self.get_irom_segment() + return [s for s in self.segments if s != irom_segment] + + def merge_adjacent_segments(self): + if not self.segments: + return # nothing to merge + + segments = [] + # The easiest way to merge the sections is the browse them backward. + for i in range(len(self.segments) - 1, 0, -1): + # elem is the previous section, the one `next_elem` may need to be + # merged in + elem = self.segments[i - 1] + next_elem = self.segments[i] + if all((elem.get_memory_type(self) == next_elem.get_memory_type(self), + elem.include_in_checksum == next_elem.include_in_checksum, + next_elem.addr == elem.addr + len(elem.data))): + # Merge any segment that ends where the next one starts, without spanning memory types + # + # (don't 'pad' any gaps here as they may be excluded from the image due to 'noinit' + # or other reasons.) + elem.data += next_elem.data + else: + # The section next_elem cannot be merged into the previous one, + # which means it needs to be part of the final segments. + # As we are browsing the list backward, the elements need to be + # inserted at the beginning of the final list. + segments.insert(0, next_elem) + + # The first segment will always be here as it cannot be merged into any + # "previous" section. + segments.insert(0, self.segments[0]) + + # note: we could sort segments here as well, but the ordering of segments is sometimes + # important for other reasons (like embedded ELF SHA-256), so we assume that the linker + # script will have produced any adjacent sections in linear order in the ELF, anyhow. + self.segments = segments + + +class ESP8266ROMFirmwareImage(BaseFirmwareImage): + """ 'Version 1' firmware image, segments loaded directly by the ROM bootloader. """ + + ROM_LOADER = ESP8266ROM + + def __init__(self, load_file=None): + super(ESP8266ROMFirmwareImage, self).__init__() + self.flash_mode = 0 + self.flash_size_freq = 0 + self.version = 1 + + if load_file is not None: + segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC) + + for _ in range(segments): + self.load_segment(load_file) + self.checksum = self.read_checksum(load_file) + + self.verify() + + def default_output_name(self, input_file): + """ Derive a default output name from the ELF name. """ + return input_file + '-' + + def save(self, basename): + """ Save a set of V1 images for flashing. Parameter is a base filename. """ + # IROM data goes in its own plain binary file + irom_segment = self.get_irom_segment() + if irom_segment is not None: + with open("%s0x%05x.bin" % (basename, irom_segment.addr - ESP8266ROM.IROM_MAP_START), "wb") as f: + f.write(irom_segment.data) + + # everything but IROM goes at 0x00000 in an image file + normal_segments = self.get_non_irom_segments() + with open("%s0x00000.bin" % basename, 'wb') as f: + self.write_common_header(f, normal_segments) + checksum = ESPLoader.ESP_CHECKSUM_MAGIC + for segment in normal_segments: + checksum = self.save_segment(f, segment, checksum) + self.append_checksum(f, checksum) + + +ESP8266ROM.BOOTLOADER_IMAGE = ESP8266ROMFirmwareImage + + +class ESP8266V2FirmwareImage(BaseFirmwareImage): + """ 'Version 2' firmware image, segments loaded by software bootloader stub + (ie Espressif bootloader or rboot) + """ + + ROM_LOADER = ESP8266ROM + + def __init__(self, load_file=None): + super(ESP8266V2FirmwareImage, self).__init__() + self.version = 2 + if load_file is not None: + segments = self.load_common_header(load_file, ESPBOOTLOADER.IMAGE_V2_MAGIC) + if segments != ESPBOOTLOADER.IMAGE_V2_SEGMENT: + # segment count is not really segment count here, but we expect to see '4' + print('Warning: V2 header has unexpected "segment" count %d (usually 4)' % segments) + + # irom segment comes before the second header + # + # the file is saved in the image with a zero load address + # in the header, so we need to calculate a load address + irom_segment = self.load_segment(load_file, True) + irom_segment.addr = 0 # for actual mapped addr, add ESP8266ROM.IROM_MAP_START + flashing_addr + 8 + irom_segment.include_in_checksum = False + + first_flash_mode = self.flash_mode + first_flash_size_freq = self.flash_size_freq + first_entrypoint = self.entrypoint + # load the second header + + segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC) + + if first_flash_mode != self.flash_mode: + print('WARNING: Flash mode value in first header (0x%02x) disagrees with second (0x%02x). Using second value.' + % (first_flash_mode, self.flash_mode)) + if first_flash_size_freq != self.flash_size_freq: + print('WARNING: Flash size/freq value in first header (0x%02x) disagrees with second (0x%02x). Using second value.' + % (first_flash_size_freq, self.flash_size_freq)) + if first_entrypoint != self.entrypoint: + print('WARNING: Entrypoint address in first header (0x%08x) disagrees with second header (0x%08x). Using second value.' + % (first_entrypoint, self.entrypoint)) + + # load all the usual segments + for _ in range(segments): + self.load_segment(load_file) + self.checksum = self.read_checksum(load_file) + + self.verify() + + def default_output_name(self, input_file): + """ Derive a default output name from the ELF name. """ + irom_segment = self.get_irom_segment() + if irom_segment is not None: + irom_offs = irom_segment.addr - ESP8266ROM.IROM_MAP_START + else: + irom_offs = 0 + return "%s-0x%05x.bin" % (os.path.splitext(input_file)[0], + irom_offs & ~(ESPLoader.FLASH_SECTOR_SIZE - 1)) + + def save(self, filename): + with open(filename, 'wb') as f: + # Save first header for irom0 segment + f.write(struct.pack(b' 0: + last_addr = flash_segments[0].addr + for segment in flash_segments[1:]: + if segment.addr // self.IROM_ALIGN == last_addr // self.IROM_ALIGN: + raise FatalError(("Segment loaded at 0x%08x lands in same 64KB flash mapping as segment loaded at 0x%08x. " + "Can't generate binary. Suggest changing linker script or ELF to merge sections.") % + (segment.addr, last_addr)) + last_addr = segment.addr + + def get_alignment_data_needed(segment): + # Actual alignment (in data bytes) required for a segment header: positioned so that + # after we write the next 8 byte header, file_offs % IROM_ALIGN == segment.addr % IROM_ALIGN + # + # (this is because the segment's vaddr may not be IROM_ALIGNed, more likely is aligned + # IROM_ALIGN+0x18 to account for the binary file header + align_past = (segment.addr % self.IROM_ALIGN) - self.SEG_HEADER_LEN + pad_len = (self.IROM_ALIGN - (f.tell() % self.IROM_ALIGN)) + align_past + if pad_len == 0 or pad_len == self.IROM_ALIGN: + return 0 # already aligned + + # subtract SEG_HEADER_LEN a second time, as the padding block has a header as well + pad_len -= self.SEG_HEADER_LEN + if pad_len < 0: + pad_len += self.IROM_ALIGN + return pad_len + + # try to fit each flash segment on a 64kB aligned boundary + # by padding with parts of the non-flash segments... + while len(flash_segments) > 0: + segment = flash_segments[0] + pad_len = get_alignment_data_needed(segment) + if pad_len > 0: # need to pad + if len(ram_segments) > 0 and pad_len > self.SEG_HEADER_LEN: + pad_segment = ram_segments[0].split_image(pad_len) + if len(ram_segments[0].data) == 0: + ram_segments.pop(0) + else: + pad_segment = ImageSegment(0, b'\x00' * pad_len, f.tell()) + checksum = self.save_segment(f, pad_segment, checksum) + total_segments += 1 + else: + # write the flash segment + assert (f.tell() + 8) % self.IROM_ALIGN == segment.addr % self.IROM_ALIGN + checksum = self.save_flash_segment(f, segment, checksum) + flash_segments.pop(0) + total_segments += 1 + + # flash segments all written, so write any remaining RAM segments + for segment in ram_segments: + checksum = self.save_segment(f, segment, checksum) + total_segments += 1 + + if self.secure_pad: + # pad the image so that after signing it will end on a a 64KB boundary. + # This ensures all mapped flash content will be verified. + if not self.append_digest: + raise FatalError("secure_pad only applies if a SHA-256 digest is also appended to the image") + align_past = (f.tell() + self.SEG_HEADER_LEN) % self.IROM_ALIGN + # 16 byte aligned checksum (force the alignment to simplify calculations) + checksum_space = 16 + if self.secure_pad == '1': + # after checksum: SHA-256 digest + (to be added by signing process) version, signature + 12 trailing bytes due to alignment + space_after_checksum = 32 + 4 + 64 + 12 + elif self.secure_pad == '2': # Secure Boot V2 + # after checksum: SHA-256 digest + signature sector, but we place signature sector after the 64KB boundary + space_after_checksum = 32 + pad_len = (self.IROM_ALIGN - align_past - checksum_space - space_after_checksum) % self.IROM_ALIGN + pad_segment = ImageSegment(0, b'\x00' * pad_len, f.tell()) + + checksum = self.save_segment(f, pad_segment, checksum) + total_segments += 1 + + # done writing segments + self.append_checksum(f, checksum) + image_length = f.tell() + + if self.secure_pad: + assert ((image_length + space_after_checksum) % self.IROM_ALIGN) == 0 + + # kinda hacky: go back to the initial header and write the new segment count + # that includes padding segments. This header is not checksummed + f.seek(1) + try: + f.write(chr(total_segments)) + except TypeError: # Python 3 + f.write(bytes([total_segments])) + + if self.append_digest: + # calculate the SHA256 of the whole file and append it + f.seek(0) + digest = hashlib.sha256() + digest.update(f.read(image_length)) + f.write(digest.digest()) + + with open(filename, 'wb') as real_file: + real_file.write(f.getvalue()) + + def save_flash_segment(self, f, segment, checksum=None): + """ Save the next segment to the image file, return next checksum value if provided """ + segment_end_pos = f.tell() + len(segment.data) + self.SEG_HEADER_LEN + segment_len_remainder = segment_end_pos % self.IROM_ALIGN + if segment_len_remainder < 0x24: + # Work around a bug in ESP-IDF 2nd stage bootloader, that it didn't map the + # last MMU page, if an IROM/DROM segment was < 0x24 bytes over the page boundary. + segment.data += b'\x00' * (0x24 - segment_len_remainder) + return self.save_segment(f, segment, checksum) + + def load_extended_header(self, load_file): + def split_byte(n): + return (n & 0x0F, (n >> 4) & 0x0F) + + fields = list(struct.unpack(self.EXTENDED_HEADER_STRUCT_FMT, load_file.read(16))) + + self.wp_pin = fields[0] + + # SPI pin drive stengths are two per byte + self.clk_drv, self.q_drv = split_byte(fields[1]) + self.d_drv, self.cs_drv = split_byte(fields[2]) + self.hd_drv, self.wp_drv = split_byte(fields[3]) + + chip_id = fields[4] + if chip_id != self.ROM_LOADER.IMAGE_CHIP_ID: + print(("Unexpected chip id in image. Expected %d but value was %d. " + "Is this image for a different chip model?") % (self.ROM_LOADER.IMAGE_CHIP_ID, chip_id)) + + # reserved fields in the middle should all be zero + if any(f for f in fields[6:-1] if f != 0): + print("Warning: some reserved header fields have non-zero values. This image may be from a newer esptool.py?") + + append_digest = fields[-1] # last byte is append_digest + if append_digest in [0, 1]: + self.append_digest = (append_digest == 1) + else: + raise RuntimeError("Invalid value for append_digest field (0x%02x). Should be 0 or 1.", append_digest) + + def save_extended_header(self, save_file): + def join_byte(ln, hn): + return (ln & 0x0F) + ((hn & 0x0F) << 4) + + append_digest = 1 if self.append_digest else 0 + + fields = [self.wp_pin, + join_byte(self.clk_drv, self.q_drv), + join_byte(self.d_drv, self.cs_drv), + join_byte(self.hd_drv, self.wp_drv), + self.ROM_LOADER.IMAGE_CHIP_ID, + self.min_rev] + fields += [0] * 8 # padding + fields += [append_digest] + + packed = struct.pack(self.EXTENDED_HEADER_STRUCT_FMT, *fields) + save_file.write(packed) + + +ESP32ROM.BOOTLOADER_IMAGE = ESP32FirmwareImage + + +class ESP32S2FirmwareImage(ESP32FirmwareImage): + """ ESP32S2 Firmware Image almost exactly the same as ESP32FirmwareImage """ + ROM_LOADER = ESP32S2ROM + + +ESP32S2ROM.BOOTLOADER_IMAGE = ESP32S2FirmwareImage + + +class ESP32S3BETA2FirmwareImage(ESP32FirmwareImage): + """ ESP32S3 Firmware Image almost exactly the same as ESP32FirmwareImage """ + ROM_LOADER = ESP32S3BETA2ROM + + +ESP32S3BETA2ROM.BOOTLOADER_IMAGE = ESP32S3BETA2FirmwareImage + + +class ESP32S3BETA3FirmwareImage(ESP32FirmwareImage): + """ ESP32S3 Firmware Image almost exactly the same as ESP32FirmwareImage """ + ROM_LOADER = ESP32S3BETA3ROM + + +ESP32S3BETA3ROM.BOOTLOADER_IMAGE = ESP32S3BETA3FirmwareImage + + +class ESP32C3FirmwareImage(ESP32FirmwareImage): + """ ESP32C3 Firmware Image almost exactly the same as ESP32FirmwareImage """ + ROM_LOADER = ESP32C3ROM + + +ESP32C3ROM.BOOTLOADER_IMAGE = ESP32C3FirmwareImage + + +class ESP32C6BETAFirmwareImage(ESP32FirmwareImage): + """ ESP32C6 Firmware Image almost exactly the same as ESP32FirmwareImage """ + ROM_LOADER = ESP32C6BETAROM + + +ESP32C6BETAROM.BOOTLOADER_IMAGE = ESP32C6BETAFirmwareImage + + +class ELFFile(object): + SEC_TYPE_PROGBITS = 0x01 + SEC_TYPE_STRTAB = 0x03 + + LEN_SEC_HEADER = 0x28 + + SEG_TYPE_LOAD = 0x01 + LEN_SEG_HEADER = 0x20 + + def __init__(self, name): + # Load sections from the ELF file + self.name = name + with open(self.name, 'rb') as f: + self._read_elf_file(f) + + def get_section(self, section_name): + for s in self.sections: + if s.name == section_name: + return s + raise ValueError("No section %s in ELF file" % section_name) + + def _read_elf_file(self, f): + # read the ELF file header + LEN_FILE_HEADER = 0x34 + try: + (ident, _type, machine, _version, + self.entrypoint, _phoff, shoff, _flags, + _ehsize, _phentsize, _phnum, shentsize, + shnum, shstrndx) = struct.unpack("<16sHHLLLLLHHHHHH", f.read(LEN_FILE_HEADER)) + except struct.error as e: + raise FatalError("Failed to read a valid ELF header from %s: %s" % (self.name, e)) + + if byte(ident, 0) != 0x7f or ident[1:4] != b'ELF': + raise FatalError("%s has invalid ELF magic header" % self.name) + if machine not in [0x5e, 0xf3]: + raise FatalError("%s does not appear to be an Xtensa or an RISCV ELF file. e_machine=%04x" % (self.name, machine)) + if shentsize != self.LEN_SEC_HEADER: + raise FatalError("%s has unexpected section header entry size 0x%x (not 0x%x)" % (self.name, shentsize, self.LEN_SEC_HEADER)) + if shnum == 0: + raise FatalError("%s has 0 section headers" % (self.name)) + self._read_sections(f, shoff, shnum, shstrndx) + self._read_segments(f, _phoff, _phnum, shstrndx) + + def _read_sections(self, f, section_header_offs, section_header_count, shstrndx): + f.seek(section_header_offs) + len_bytes = section_header_count * self.LEN_SEC_HEADER + section_header = f.read(len_bytes) + if len(section_header) == 0: + raise FatalError("No section header found at offset %04x in ELF file." % section_header_offs) + if len(section_header) != (len_bytes): + raise FatalError("Only read 0x%x bytes from section header (expected 0x%x.) Truncated ELF file?" % (len(section_header), len_bytes)) + + # walk through the section header and extract all sections + section_header_offsets = range(0, len(section_header), self.LEN_SEC_HEADER) + + def read_section_header(offs): + name_offs, sec_type, _flags, lma, sec_offs, size = struct.unpack_from(" 0] + self.sections = prog_sections + + def _read_segments(self, f, segment_header_offs, segment_header_count, shstrndx): + f.seek(segment_header_offs) + len_bytes = segment_header_count * self.LEN_SEG_HEADER + segment_header = f.read(len_bytes) + if len(segment_header) == 0: + raise FatalError("No segment header found at offset %04x in ELF file." % segment_header_offs) + if len(segment_header) != (len_bytes): + raise FatalError("Only read 0x%x bytes from segment header (expected 0x%x.) Truncated ELF file?" % (len(segment_header), len_bytes)) + + # walk through the segment header and extract all segments + segment_header_offsets = range(0, len(segment_header), self.LEN_SEG_HEADER) + + def read_segment_header(offs): + seg_type, seg_offs, _vaddr, lma, size, _memsize, _flags, _align = struct.unpack_from(" 0] + self.segments = prog_segments + + def sha256(self): + # return SHA256 hash of the input ELF file + sha256 = hashlib.sha256() + with open(self.name, 'rb') as f: + sha256.update(f.read()) + return sha256.digest() + + +def slip_reader(port, trace_function): + """Generator to read SLIP packets from a serial port. + Yields one full SLIP packet at a time, raises exception on timeout or invalid data. + + Designed to avoid too many calls to serial.read(1), which can bog + down on slow systems. + """ + 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'': + waiting_for = "header" if partial_packet is None else "content" + trace_function("Timed out waiting for packet %s", waiting_for) + raise FatalError("Timed out waiting for packet %s" % waiting_for) + trace_function("Read %d bytes: %s", len(read_bytes), HexFormatter(read_bytes)) + 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: + trace_function("Read invalid data: %s", HexFormatter(read_bytes)) + trace_function("Remaining data in serial buffer: %s", HexFormatter(port.read(port.inWaiting()))) + raise FatalError('Invalid head of packet (0x%s)' % hexify(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: + trace_function("Read invalid data: %s", HexFormatter(read_bytes)) + trace_function("Remaining data in serial buffer: %s", HexFormatter(port.read(port.inWaiting()))) + raise FatalError('Invalid SLIP escape (0xdb, 0x%s)' % (hexify(b))) + elif b == b'\xdb': # start of escape sequence + in_escape = True + elif b == b'\xc0': # end of packet + trace_function("Received full packet: %s", HexFormatter(partial_packet)) + yield partial_packet + partial_packet = None + else: # normal byte in packet + partial_packet += b + + +def arg_auto_int(x): + return int(x, 0) + + +def div_roundup(a, b): + """ Return a/b rounded up to nearest integer, + equivalent result to int(math.ceil(float(int(a)) / float(int(b))), only + without possible floating point accuracy errors. + """ + return (int(a) + int(b) - 1) // int(b) + + +def align_file_position(f, size): + """ Align the position in the file to the next block of specified size """ + align = (size - 1) - (f.tell() % size) + f.seek(align, 1) + + +def flash_size_bytes(size): + """ Given a flash size of the type passed in args.flash_size + (ie 512KB or 1MB) then return the size in bytes. + """ + if "MB" in size: + return int(size[:size.index("MB")]) * 1024 * 1024 + elif "KB" in size: + return int(size[:size.index("KB")]) * 1024 + else: + raise FatalError("Unknown size %s" % size) + + +def hexify(s, uppercase=True): + format_str = '%02X' if uppercase else '%02x' + if not PYTHON2: + return ''.join(format_str % c for c in s) + else: + return ''.join(format_str % ord(c) for c in s) + + +class HexFormatter(object): + """ + Wrapper class which takes binary data in its constructor + and returns a hex string as it's __str__ method. + + This is intended for "lazy formatting" of trace() output + in hex format. Avoids overhead (significant on slow computers) + of generating long hex strings even if tracing is disabled. + + Note that this doesn't save any overhead if passed as an + argument to "%", only when passed to trace() + + If auto_split is set (default), any long line (> 16 bytes) will be + printed as separately indented lines, with ASCII decoding at the end + of each line. + """ + def __init__(self, binary_string, auto_split=True): + self._s = binary_string + self._auto_split = auto_split + + def __str__(self): + if self._auto_split and len(self._s) > 16: + result = "" + s = self._s + while len(s) > 0: + line = s[:16] + ascii_line = "".join(c if (c == ' ' or (c in string.printable and c not in string.whitespace)) + else '.' for c in line.decode('ascii', 'replace')) + s = s[16:] + result += "\n %-16s %-16s | %s" % (hexify(line[:8], False), hexify(line[8:], False), ascii_line) + return result + else: + return hexify(self._s, False) + + +def pad_to(data, alignment, pad_character=b'\xFF'): + """ Pad to the next alignment boundary """ + pad_mod = len(data) % alignment + if pad_mod != 0: + data += pad_character * (alignment - pad_mod) + return data + + +class FatalError(RuntimeError): + """ + Wrapper class for runtime errors that aren't caused by internal bugs, but by + ESP8266 responses or input content. + """ + def __init__(self, message): + RuntimeError.__init__(self, message) + + @staticmethod + def WithResult(message, result): + """ + Return a fatal error object that appends the hex values of + 'result' as a string formatted argument. + """ + message += " (result was %s)" % hexify(result) + return FatalError(message) + + +class NotImplementedInROMError(FatalError): + """ + Wrapper class for the error thrown when a particular ESP bootloader function + is not implemented in the ROM bootloader. + """ + def __init__(self, bootloader, func): + FatalError.__init__(self, "%s ROM does not support function %s." % (bootloader.CHIP_NAME, func.__name__)) + + +class NotSupportedError(FatalError): + def __init__(self, esp, function_name): + FatalError.__init__(self, "Function %s is not supported for %s." % (function_name, esp.CHIP_NAME)) + +# "Operation" commands, executable at command line. One function each +# +# Each function takes either two args (, ) or a single +# argument. + + +class UnsupportedCommandError(RuntimeError): + """ + Wrapper class for when ROM loader returns an invalid command response. + + Usually this indicates the loader is running in Secure Download Mode. + """ + def __init__(self, esp, op): + if esp.secure_download_mode: + msg = "This command (0x%x) is not supported in Secure Download Mode" % op + else: + msg = "Invalid (unsupported) command 0x%x" % op + RuntimeError.__init__(self, msg) + + +def load_ram(esp, args): + image = LoadFirmwareImage(esp.CHIP_NAME, args.filename) + + print('RAM boot...') + for seg in image.segments: + size = len(seg.data) + print('Downloading %d bytes at %08x...' % (size, seg.addr), end=' ') + sys.stdout.flush() + esp.mem_begin(size, div_roundup(size, esp.ESP_RAM_BLOCK), esp.ESP_RAM_BLOCK, seg.addr) + + seq = 0 + while len(seg.data) > 0: + esp.mem_block(seg.data[0:esp.ESP_RAM_BLOCK], seq) + seg.data = seg.data[esp.ESP_RAM_BLOCK:] + seq += 1 + print('done!') + + print('All segments done, executing at %08x' % image.entrypoint) + esp.mem_finish(image.entrypoint) + + +def read_mem(esp, args): + print('0x%08x = 0x%08x' % (args.address, esp.read_reg(args.address))) + + +def write_mem(esp, args): + esp.write_reg(args.address, args.value, args.mask, 0) + print('Wrote %08x, mask %08x to %08x' % (args.value, args.mask, args.address)) + + +def dump_mem(esp, args): + with open(args.filename, 'wb') as f: + for i in range(args.size // 4): + d = esp.read_reg(args.address + (i * 4)) + f.write(struct.pack(b'> 16 + args.flash_size = DETECTED_FLASH_SIZES.get(size_id) + if args.flash_size is None: + print('Warning: Could not auto-detect Flash size (FlashID=0x%x, SizeID=0x%x), defaulting to 4MB' % (flash_id, size_id)) + args.flash_size = '4MB' + else: + print('Auto-detected Flash size:', args.flash_size) + + +def _update_image_flash_params(esp, address, args, image): + """ Modify the flash mode & size bytes if this looks like an executable bootloader image """ + if len(image) < 8: + return image # not long enough to be a bootloader image + + # unpack the (potential) image header + magic, _, flash_mode, flash_size_freq = struct.unpack("BBBB", image[:4]) + if address != esp.BOOTLOADER_FLASH_OFFSET: + return image # not flashing bootloader offset, so don't modify this + + if (args.flash_mode, args.flash_freq, args.flash_size) == ('keep',) * 3: + return image # all settings are 'keep', not modifying anything + + # easy check if this is an image: does it start with a magic byte? + if magic != esp.ESP_IMAGE_MAGIC: + print("Warning: Image file at 0x%x doesn't look like an image file, so not changing any flash settings." % address) + return image + + # make sure this really is an image, and not just data that + # starts with esp.ESP_IMAGE_MAGIC (mostly a problem for encrypted + # images that happen to start with a magic byte + try: + test_image = esp.BOOTLOADER_IMAGE(io.BytesIO(image)) + test_image.verify() + except Exception: + print("Warning: Image file at 0x%x is not a valid %s image, so not changing any flash settings." % (address, esp.CHIP_NAME)) + return image + + if args.flash_mode != 'keep': + flash_mode = {'qio': 0, 'qout': 1, 'dio': 2, 'dout': 3}[args.flash_mode] + + flash_freq = flash_size_freq & 0x0F + if args.flash_freq != 'keep': + flash_freq = {'40m': 0, '26m': 1, '20m': 2, '80m': 0xf}[args.flash_freq] + + flash_size = flash_size_freq & 0xF0 + if args.flash_size != 'keep': + flash_size = esp.parse_flash_size_arg(args.flash_size) + + flash_params = struct.pack(b'BB', flash_mode, flash_size + flash_freq) + if flash_params != image[2:4]: + print('Flash params set to 0x%04x' % struct.unpack(">H", flash_params)) + image = image[0:2] + flash_params + image[4:] + return image + + +def write_flash(esp, args): + # set args.compress based on default behaviour: + # -> if either --compress or --no-compress is set, honour that + # -> otherwise, set --compress unless --no-stub is set + if args.compress is None and not args.no_compress: + args.compress = not args.no_stub + + # In case we have encrypted files to write, we first do few sanity checks before actual flash + if args.encrypt or args.encrypt_files is not None: + do_write = True + + if not esp.secure_download_mode: + if esp.get_encrypted_download_disabled(): + raise FatalError("This chip has encrypt functionality in UART download mode disabled. " + "This is the Flash Encryption configuration for Production mode instead of Development mode.") + + crypt_cfg_efuse = esp.get_flash_crypt_config() + + if crypt_cfg_efuse is not None and crypt_cfg_efuse != 0xF: + print('Unexpected FLASH_CRYPT_CONFIG value: 0x%x' % (crypt_cfg_efuse)) + do_write = False + + enc_key_valid = esp.is_flash_encryption_key_valid() + + if not enc_key_valid: + print('Flash encryption key is not programmed') + do_write = False + + # Determine which files list contain the ones to encrypt + files_to_encrypt = args.addr_filename if args.encrypt else args.encrypt_files + + for address, argfile in files_to_encrypt: + if address % esp.FLASH_ENCRYPTED_WRITE_ALIGN: + print("File %s address 0x%x is not %d byte aligned, can't flash encrypted" % + (argfile.name, address, esp.FLASH_ENCRYPTED_WRITE_ALIGN)) + do_write = False + + if not do_write and not args.ignore_flash_encryption_efuse_setting: + raise FatalError("Can't perform encrypted flash write, consult Flash Encryption documentation for more information") + + # verify file sizes fit in flash + if args.flash_size != 'keep': # TODO: check this even with 'keep' + flash_end = flash_size_bytes(args.flash_size) + for address, argfile in args.addr_filename: + argfile.seek(0, os.SEEK_END) + if address + argfile.tell() > flash_end: + raise FatalError(("File %s (length %d) at offset %d will not fit in %d bytes of flash. " + "Use --flash-size argument, or change flashing address.") + % (argfile.name, argfile.tell(), address, flash_end)) + argfile.seek(0) + + if args.erase_all: + erase_flash(esp, args) + else: + for address, argfile in args.addr_filename: + argfile.seek(0, os.SEEK_END) + write_end = address + argfile.tell() + argfile.seek(0) + bytes_over = address % esp.FLASH_SECTOR_SIZE + if bytes_over != 0: + print("WARNING: Flash address {:#010x} is not aligned to a {:#x} byte flash sector. " + "{:#x} bytes before this address will be erased." + .format(address, esp.FLASH_SECTOR_SIZE, bytes_over)) + # Print the address range of to-be-erased flash memory region + print("Flash will be erased from {:#010x} to {:#010x}..." + .format(address - bytes_over, div_roundup(write_end, esp.FLASH_SECTOR_SIZE) * esp.FLASH_SECTOR_SIZE - 1)) + + """ Create a list describing all the files we have to flash. Each entry holds an "encrypt" flag + marking whether the file needs encryption or not. This list needs to be sorted. + + First, append to each entry of our addr_filename list the flag args.encrypt + For example, if addr_filename is [(0x1000, "partition.bin"), (0x8000, "bootloader")], + all_files will be [(0x1000, "partition.bin", args.encrypt), (0x8000, "bootloader", args.encrypt)], + where, of course, args.encrypt is either True or False + """ + all_files = [(offs, filename, args.encrypt) for (offs, filename) in args.addr_filename] + + """Now do the same with encrypt_files list, if defined. + In this case, the flag is True + """ + if args.encrypt_files is not None: + encrypted_files_flag = [(offs, filename, True) for (offs, filename) in args.encrypt_files] + + # Concatenate both lists and sort them. + # As both list are already sorted, we could simply do a merge instead, + # but for the sake of simplicity and because the lists are very small, + # let's use sorted. + all_files = sorted(all_files + encrypted_files_flag, key=lambda x: x[0]) + + for address, argfile, encrypted in all_files: + compress = args.compress + + # Check whether we can compress the current file before flashing + if compress and encrypted: + print('\nWARNING: - compress and encrypt options are mutually exclusive ') + print('Will flash %s uncompressed' % argfile.name) + compress = False + + if args.no_stub: + print('Erasing flash...') + image = pad_to(argfile.read(), esp.FLASH_ENCRYPTED_WRITE_ALIGN if encrypted else 4) + if len(image) == 0: + print('WARNING: File %s is empty' % argfile.name) + continue + image = _update_image_flash_params(esp, address, args, image) + calcmd5 = hashlib.md5(image).hexdigest() + uncsize = len(image) + if compress: + uncimage = image + image = zlib.compress(uncimage, 9) + # Decompress the compressed binary a block at a time, to dynamically calculate the + # timeout based on the real write size + decompress = zlib.decompressobj() + blocks = esp.flash_defl_begin(uncsize, len(image), address) + else: + blocks = esp.flash_begin(uncsize, address, begin_rom_encrypted=encrypted) + argfile.seek(0) # in case we need it again + seq = 0 + bytes_sent = 0 # bytes sent on wire + bytes_written = 0 # bytes written to flash + t = time.time() + + timeout = DEFAULT_TIMEOUT + + while len(image) > 0: + print_overwrite('Writing at 0x%08x... (%d %%)' % (address + bytes_written, 100 * (seq + 1) // blocks)) + sys.stdout.flush() + block = image[0:esp.FLASH_WRITE_SIZE] + if compress: + # feeding each compressed block into the decompressor lets us see block-by-block how much will be written + block_uncompressed = len(decompress.decompress(block)) + bytes_written += block_uncompressed + block_timeout = max(DEFAULT_TIMEOUT, timeout_per_mb(ERASE_WRITE_TIMEOUT_PER_MB, block_uncompressed)) + if not esp.IS_STUB: + timeout = block_timeout # ROM code writes block to flash before ACKing + esp.flash_defl_block(block, seq, timeout=timeout) + if esp.IS_STUB: + timeout = block_timeout # Stub ACKs when block is received, then writes to flash while receiving the block after it + else: + # Pad the last block + block = block + b'\xff' * (esp.FLASH_WRITE_SIZE - len(block)) + if encrypted: + esp.flash_encrypt_block(block, seq) + else: + esp.flash_block(block, seq) + bytes_written += len(block) + bytes_sent += len(block) + image = image[esp.FLASH_WRITE_SIZE:] + seq += 1 + + if esp.IS_STUB: + # Stub only writes each block to flash after 'ack'ing the receive, so do a final dummy operation which will + # not be 'ack'ed until the last block has actually been written out to flash + esp.read_reg(ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR, timeout=timeout) + + t = time.time() - t + speed_msg = "" + if compress: + if t > 0.0: + speed_msg = " (effective %.1f kbit/s)" % (uncsize / t * 8 / 1000) + print_overwrite('Wrote %d bytes (%d compressed) at 0x%08x in %.1f seconds%s...' % (uncsize, + bytes_sent, + address, t, speed_msg), last_line=True) + else: + if t > 0.0: + speed_msg = " (%.1f kbit/s)" % (bytes_written / t * 8 / 1000) + print_overwrite('Wrote %d bytes at 0x%08x in %.1f seconds%s...' % (bytes_written, address, t, speed_msg), last_line=True) + + if not encrypted and not esp.secure_download_mode: + try: + res = esp.flash_md5sum(address, uncsize) + if res != calcmd5: + print('File md5: %s' % calcmd5) + print('Flash md5: %s' % res) + print('MD5 of 0xFF is %s' % (hashlib.md5(b'\xFF' * uncsize).hexdigest())) + raise FatalError("MD5 of file does not match data in flash!") + else: + print('Hash of data verified.') + except NotImplementedInROMError: + pass + + print('\nLeaving...') + + if esp.IS_STUB: + # skip sending flash_finish to ROM loader here, + # as it causes the loader to exit and run user code + esp.flash_begin(0, 0) + + # Get the "encrypted" flag for the last file flashed + # Note: all_files list contains triplets like: + # (address: Integer, filename: String, encrypted: Boolean) + last_file_encrypted = all_files[-1][2] + + # Check whether the last file flashed was compressed or not + if args.compress and not last_file_encrypted: + esp.flash_defl_finish(False) + else: + esp.flash_finish(False) + + if args.verify: + print('Verifying just-written flash...') + print('(This option is deprecated, flash contents are now always read back after flashing.)') + # If some encrypted files have been flashed print a warning saying that we won't check them + if args.encrypt or args.encrypt_files is not None: + print('WARNING: - cannot verify encrypted files, they will be ignored') + # Call verify_flash function only if there at least one non-encrypted file flashed + if not args.encrypt: + verify_flash(esp, args) + + +def image_info(args): + image = LoadFirmwareImage(args.chip, args.filename) + print('Image version: %d' % image.version) + print('Entry point: %08x' % image.entrypoint if image.entrypoint != 0 else 'Entry point not set') + print('%d segments' % len(image.segments)) + print() + idx = 0 + for seg in image.segments: + idx += 1 + segs = seg.get_memory_type(image) + seg_name = ",".join(segs) + print('Segment %d: %r [%s]' % (idx, seg, seg_name)) + calc_checksum = image.calculate_checksum() + print('Checksum: %02x (%s)' % (image.checksum, + 'valid' if image.checksum == calc_checksum else 'invalid - calculated %02x' % calc_checksum)) + try: + digest_msg = 'Not appended' + if image.append_digest: + is_valid = image.stored_digest == image.calc_digest + digest_msg = "%s (%s)" % (hexify(image.calc_digest).lower(), + "valid" if is_valid else "invalid") + print('Validation Hash: %s' % digest_msg) + except AttributeError: + pass # ESP8266 image has no append_digest field + + +def make_image(args): + image = ESP8266ROMFirmwareImage() + if len(args.segfile) == 0: + raise FatalError('No segments specified') + if len(args.segfile) != len(args.segaddr): + raise FatalError('Number of specified files does not match number of specified addresses') + for (seg, addr) in zip(args.segfile, args.segaddr): + with open(seg, 'rb') as f: + data = f.read() + image.segments.append(ImageSegment(addr, data)) + image.entrypoint = args.entrypoint + image.save(args.output) + + +def elf2image(args): + e = ELFFile(args.input) + if args.chip == 'auto': # Default to ESP8266 for backwards compatibility + print("Creating image for ESP8266...") + args.chip = 'esp8266' + + if args.chip == 'esp32': + image = ESP32FirmwareImage() + if args.secure_pad: + image.secure_pad = '1' + elif args.secure_pad_v2: + image.secure_pad = '2' + elif args.chip == 'esp32s2': + image = ESP32S2FirmwareImage() + if args.secure_pad_v2: + image.secure_pad = '2' + elif args.chip == 'esp32s3beta2': + image = ESP32S3BETA2FirmwareImage() + if args.secure_pad_v2: + image.secure_pad = '2' + elif args.chip == 'esp32s3beta3': + image = ESP32S3BETA3FirmwareImage() + if args.secure_pad_v2: + image.secure_pad = '2' + elif args.chip == 'esp32c3': + image = ESP32C3FirmwareImage() + if args.secure_pad_v2: + image.secure_pad = '2' + elif args.chip == 'esp32c6beta': + image = ESP32C6BETAFirmwareImage() + if args.secure_pad_v2: + image.secure_pad = '2' + elif args.version == '1': # ESP8266 + image = ESP8266ROMFirmwareImage() + else: + image = ESP8266V2FirmwareImage() + image.entrypoint = e.entrypoint + image.flash_mode = {'qio': 0, 'qout': 1, 'dio': 2, 'dout': 3}[args.flash_mode] + + if args.chip != 'esp8266': + image.min_rev = int(args.min_rev) + + # ELFSection is a subclass of ImageSegment, so can use interchangeably + image.segments = e.segments if args.use_segments else e.sections + + image.flash_size_freq = image.ROM_LOADER.FLASH_SIZES[args.flash_size] + image.flash_size_freq += {'40m': 0, '26m': 1, '20m': 2, '80m': 0xf}[args.flash_freq] + + if args.elf_sha256_offset: + image.elf_sha256 = e.sha256() + image.elf_sha256_offset = args.elf_sha256_offset + + before = len(image.segments) + image.merge_adjacent_segments() + if len(image.segments) != before: + delta = before - len(image.segments) + print("Merged %d ELF section%s" % (delta, "s" if delta > 1 else "")) + + image.verify() + + if args.output is None: + args.output = image.default_output_name(args.input) + image.save(args.output) + + +def read_mac(esp, args): + mac = esp.read_mac() + + def print_mac(label, mac): + print('%s: %s' % (label, ':'.join(map(lambda x: '%02x' % x, mac)))) + print_mac("MAC", mac) + + +def chip_id(esp, args): + try: + chipid = esp.chip_id() + print('Chip ID: 0x%08x' % chipid) + except NotSupportedError: + print('Warning: %s has no Chip ID. Reading MAC instead.' % esp.CHIP_NAME) + read_mac(esp, args) + + +def erase_flash(esp, args): + print('Erasing flash (this may take a while)...') + t = time.time() + esp.erase_flash() + print('Chip erase completed successfully in %.1fs' % (time.time() - t)) + + +def erase_region(esp, args): + print('Erasing region (may be slow depending on size)...') + t = time.time() + esp.erase_region(args.address, args.size) + print('Erase completed successfully in %.1f seconds.' % (time.time() - t)) + + +def run(esp, args): + esp.run() + + +def flash_id(esp, args): + flash_id = esp.flash_id() + print('Manufacturer: %02x' % (flash_id & 0xff)) + flid_lowbyte = (flash_id >> 16) & 0xFF + print('Device: %02x%02x' % ((flash_id >> 8) & 0xff, flid_lowbyte)) + print('Detected flash size: %s' % (DETECTED_FLASH_SIZES.get(flid_lowbyte, "Unknown"))) + + +def read_flash(esp, args): + if args.no_progress: + flash_progress = None + else: + def flash_progress(progress, length): + msg = '%d (%d %%)' % (progress, progress * 100.0 / length) + padding = '\b' * len(msg) + if progress == length: + padding = '\n' + sys.stdout.write(msg + padding) + sys.stdout.flush() + t = time.time() + data = esp.read_flash(args.address, args.size, flash_progress) + t = time.time() - t + print_overwrite('Read %d bytes at 0x%x in %.1f seconds (%.1f kbit/s)...' + % (len(data), args.address, t, len(data) / t * 8 / 1000), last_line=True) + with open(args.filename, 'wb') as f: + f.write(data) + + +def verify_flash(esp, args): + differences = False + + for address, argfile in args.addr_filename: + image = pad_to(argfile.read(), 4) + argfile.seek(0) # rewind in case we need it again + + image = _update_image_flash_params(esp, address, args, image) + + image_size = len(image) + print('Verifying 0x%x (%d) bytes @ 0x%08x in flash against %s...' % (image_size, image_size, address, argfile.name)) + # Try digest first, only read if there are differences. + digest = esp.flash_md5sum(address, image_size) + expected_digest = hashlib.md5(image).hexdigest() + if digest == expected_digest: + print('-- verify OK (digest matched)') + continue + else: + differences = True + if getattr(args, 'diff', 'no') != 'yes': + print('-- verify FAILED (digest mismatch)') + continue + + flash = esp.read_flash(address, image_size) + assert flash != image + diff = [i for i in range(image_size) if flash[i] != image[i]] + print('-- verify FAILED: %d differences, first @ 0x%08x' % (len(diff), address + diff[0])) + for d in diff: + flash_byte = flash[d] + image_byte = image[d] + if PYTHON2: + flash_byte = ord(flash_byte) + image_byte = ord(image_byte) + print(' %08x %02x %02x' % (address + d, flash_byte, image_byte)) + if differences: + raise FatalError("Verify failed.") + + +def read_flash_status(esp, args): + print('Status value: 0x%04x' % esp.read_status(args.bytes)) + + +def write_flash_status(esp, args): + fmt = "0x%%0%dx" % (args.bytes * 2) + args.value = args.value & ((1 << (args.bytes * 8)) - 1) + print(('Initial flash status: ' + fmt) % esp.read_status(args.bytes)) + print(('Setting flash status: ' + fmt) % args.value) + esp.write_status(args.value, args.bytes, args.non_volatile) + print(('After flash status: ' + fmt) % esp.read_status(args.bytes)) + + +def get_security_info(esp, args): + (flags, flash_crypt_cnt, key_purposes) = esp.get_security_info() + # TODO: better display + print('Flags: 0x%08x (%s)' % (flags, bin(flags))) + print('Flash_Crypt_Cnt: 0x%x' % flash_crypt_cnt) + print('Key_Purposes: %s' % (key_purposes,)) + + +def merge_bin(args): + chip_class = _chip_to_rom_loader(args.chip) + + # sort the files by offset. The AddrFilenamePairAction has already checked for overlap + input_files = sorted(args.addr_filename, key=lambda x: x[0]) + if not input_files: + raise FatalError("No input files specified") + first_addr = input_files[0][0] + if first_addr < args.target_offset: + raise FatalError("Output file target offset is 0x%x. Input file offset 0x%x is before this." % (args.target_offset, first_addr)) + + if args.format != 'raw': + raise FatalError("This version of esptool only supports the 'raw' output format") + + with open(args.output, 'wb') as of: + def pad_to(flash_offs): + # account for output file offset if there is any + of.write(b'\xFF' * (flash_offs - args.target_offset - of.tell())) + for addr, argfile in input_files: + pad_to(addr) + image = argfile.read() + image = _update_image_flash_params(chip_class, addr, args, image) + of.write(image) + if args.fill_flash_size: + pad_to(flash_size_bytes(args.fill_flash_size)) + print("Wrote 0x%x bytes to file %s, ready to flash to offset 0x%x" % (of.tell(), args.output, args.target_offset)) + + +def version(args): + print(__version__) + +# +# End of operations functions +# + + +def main(argv=None, esp=None): + """ + Main function for esptool + + argv - Optional override for default arguments parsing (that uses sys.argv), can be a list of custom arguments + as strings. Arguments and their values need to be added as individual items to the list e.g. "-b 115200" thus + becomes ['-b', '115200']. + + esp - Optional override of the connected device previously returned by get_default_connected_device() + """ + + external_esp = esp is not None + + parser = argparse.ArgumentParser(description='esptool.py v%s - ESP8266 ROM Bootloader Utility' % __version__, prog='esptool') + + parser.add_argument('--chip', '-c', + help='Target chip type', + type=lambda c: c.lower().replace('-', ''), # support ESP32-S2, etc. + choices=['auto', 'esp8266', 'esp32', 'esp32s2', 'esp32s3beta2', 'esp32s3beta3', 'esp32c3', 'esp32c6beta'], + default=os.environ.get('ESPTOOL_CHIP', 'auto')) + + parser.add_argument( + '--port', '-p', + help='Serial port device', + default=os.environ.get('ESPTOOL_PORT', None)) + + parser.add_argument( + '--baud', '-b', + help='Serial port baud rate used when flashing/reading', + type=arg_auto_int, + default=os.environ.get('ESPTOOL_BAUD', ESPLoader.ESP_ROM_BAUD)) + + parser.add_argument( + '--before', + help='What to do before connecting to the chip', + choices=['default_reset', 'usb_reset', 'no_reset', 'no_reset_no_sync'], + default=os.environ.get('ESPTOOL_BEFORE', 'default_reset')) + + parser.add_argument( + '--after', '-a', + help='What to do after esptool.py is finished', + choices=['hard_reset', 'soft_reset', 'no_reset', 'no_reset_stub'], + default=os.environ.get('ESPTOOL_AFTER', 'hard_reset')) + + parser.add_argument( + '--no-stub', + help="Disable launching the flasher stub, only talk to ROM bootloader. Some features will not be available.", + action='store_true') + + parser.add_argument( + '--trace', '-t', + help="Enable trace-level output of esptool.py interactions.", + action='store_true') + + parser.add_argument( + '--override-vddsdio', + help="Override ESP32 VDDSDIO internal voltage regulator (use with care)", + choices=ESP32ROM.OVERRIDE_VDDSDIO_CHOICES, + nargs='?') + + parser.add_argument( + '--connect-attempts', + help=('Number of attempts to connect, negative or 0 for infinite. ' + 'Default: %d.' % DEFAULT_CONNECT_ATTEMPTS), + type=int, + default=os.environ.get('ESPTOOL_CONNECT_ATTEMPTS', DEFAULT_CONNECT_ATTEMPTS)) + + subparsers = parser.add_subparsers( + dest='operation', + help='Run esptool {command} -h for additional help') + + def add_spi_connection_arg(parent): + parent.add_argument('--spi-connection', '-sc', help='ESP32-only argument. Override default SPI Flash connection. ' + 'Value can be SPI, HSPI or a comma-separated list of 5 I/O numbers to use for SPI flash (CLK,Q,D,HD,CS).', + action=SpiConnectionAction) + + parser_load_ram = subparsers.add_parser( + 'load_ram', + help='Download an image to RAM and execute') + parser_load_ram.add_argument('filename', help='Firmware image') + + parser_dump_mem = subparsers.add_parser( + 'dump_mem', + help='Dump arbitrary memory to disk') + parser_dump_mem.add_argument('address', help='Base address', type=arg_auto_int) + parser_dump_mem.add_argument('size', help='Size of region to dump', type=arg_auto_int) + parser_dump_mem.add_argument('filename', help='Name of binary dump') + + parser_read_mem = subparsers.add_parser( + 'read_mem', + help='Read arbitrary memory location') + parser_read_mem.add_argument('address', help='Address to read', type=arg_auto_int) + + parser_write_mem = subparsers.add_parser( + 'write_mem', + help='Read-modify-write to arbitrary memory location') + parser_write_mem.add_argument('address', help='Address to write', type=arg_auto_int) + parser_write_mem.add_argument('value', help='Value', type=arg_auto_int) + parser_write_mem.add_argument('mask', help='Mask of bits to write', type=arg_auto_int, nargs='?', default='0xFFFFFFFF') + + def add_spi_flash_subparsers(parent, allow_keep, auto_detect): + """ Add common parser arguments for SPI flash properties """ + extra_keep_args = ['keep'] if allow_keep else [] + + if auto_detect and allow_keep: + extra_fs_message = ", detect, or keep" + elif auto_detect: + extra_fs_message = ", or detect" + elif allow_keep: + extra_fs_message = ", or keep" + else: + extra_fs_message = "" + + parent.add_argument('--flash_freq', '-ff', help='SPI Flash frequency', + choices=extra_keep_args + ['40m', '26m', '20m', '80m'], + default=os.environ.get('ESPTOOL_FF', 'keep' if allow_keep else '40m')) + parent.add_argument('--flash_mode', '-fm', help='SPI Flash mode', + choices=extra_keep_args + ['qio', 'qout', 'dio', 'dout'], + default=os.environ.get('ESPTOOL_FM', 'keep' if allow_keep else 'qio')) + parent.add_argument('--flash_size', '-fs', help='SPI Flash size in MegaBytes (1MB, 2MB, 4MB, 8MB, 16M)' + ' plus ESP8266-only (256KB, 512KB, 2MB-c1, 4MB-c1)' + extra_fs_message, + action=FlashSizeAction, auto_detect=auto_detect, + default=os.environ.get('ESPTOOL_FS', 'keep' if allow_keep else '1MB')) + add_spi_connection_arg(parent) + + parser_write_flash = subparsers.add_parser( + 'write_flash', + help='Write a binary blob to flash') + + parser_write_flash.add_argument('addr_filename', metavar='
', help='Address followed by binary filename, separated by space', + action=AddrFilenamePairAction) + parser_write_flash.add_argument('--erase-all', '-e', + help='Erase all regions of flash (not just write areas) before programming', + action="store_true") + + add_spi_flash_subparsers(parser_write_flash, allow_keep=True, auto_detect=True) + parser_write_flash.add_argument('--no-progress', '-p', help='Suppress progress output', action="store_true") + parser_write_flash.add_argument('--verify', help='Verify just-written data on flash ' + '(mostly superfluous, data is read back during flashing)', action='store_true') + parser_write_flash.add_argument('--encrypt', help='Apply flash encryption when writing data (required correct efuse settings)', + action='store_true') + # In order to not break backward compatibility, our list of encrypted files to flash is a new parameter + parser_write_flash.add_argument('--encrypt-files', metavar='
', + help='Files to be encrypted on the flash. Address followed by binary filename, separated by space.', + action=AddrFilenamePairAction) + parser_write_flash.add_argument('--ignore-flash-encryption-efuse-setting', help='Ignore flash encryption efuse settings ', + action='store_true') + + compress_args = parser_write_flash.add_mutually_exclusive_group(required=False) + compress_args.add_argument('--compress', '-z', help='Compress data in transfer (default unless --no-stub is specified)', + action="store_true", default=None) + compress_args.add_argument('--no-compress', '-u', help='Disable data compression during transfer (default if --no-stub is specified)', + action="store_true") + + subparsers.add_parser( + 'run', + help='Run application code in flash') + + parser_image_info = subparsers.add_parser( + 'image_info', + help='Dump headers from an application image') + parser_image_info.add_argument('filename', help='Image file to parse') + + parser_make_image = subparsers.add_parser( + 'make_image', + help='Create an application image from binary files') + parser_make_image.add_argument('output', help='Output image file') + parser_make_image.add_argument('--segfile', '-f', action='append', help='Segment input file') + parser_make_image.add_argument('--segaddr', '-a', action='append', help='Segment base address', type=arg_auto_int) + parser_make_image.add_argument('--entrypoint', '-e', help='Address of entry point', type=arg_auto_int, default=0) + + parser_elf2image = subparsers.add_parser( + 'elf2image', + help='Create an application image from ELF file') + parser_elf2image.add_argument('input', help='Input ELF file') + parser_elf2image.add_argument('--output', '-o', help='Output filename prefix (for version 1 image), or filename (for version 2 single image)', type=str) + parser_elf2image.add_argument('--version', '-e', help='Output image version', choices=['1', '2'], default='1') + parser_elf2image.add_argument('--min-rev', '-r', help='Minimum chip revision', choices=['0', '1', '2', '3'], default='0') + parser_elf2image.add_argument('--secure-pad', action='store_true', + help='Pad image so once signed it will end on a 64KB boundary. For Secure Boot v1 images only.') + parser_elf2image.add_argument('--secure-pad-v2', action='store_true', + help='Pad image to 64KB, so once signed its signature sector will start at the next 64K block. ' + 'For Secure Boot v2 images only.') + parser_elf2image.add_argument('--elf-sha256-offset', help='If set, insert SHA256 hash (32 bytes) of the input ELF file at specified offset in the binary.', + type=arg_auto_int, default=None) + parser_elf2image.add_argument('--use_segments', help='If set, ELF segments will be used instead of ELF sections to genereate the image.', + action='store_true') + + add_spi_flash_subparsers(parser_elf2image, allow_keep=False, auto_detect=False) + + subparsers.add_parser( + 'read_mac', + help='Read MAC address from OTP ROM') + + subparsers.add_parser( + 'chip_id', + help='Read Chip ID from OTP ROM') + + parser_flash_id = subparsers.add_parser( + 'flash_id', + help='Read SPI flash manufacturer and device ID') + add_spi_connection_arg(parser_flash_id) + + parser_read_status = subparsers.add_parser( + 'read_flash_status', + help='Read SPI flash status register') + + add_spi_connection_arg(parser_read_status) + parser_read_status.add_argument('--bytes', help='Number of bytes to read (1-3)', type=int, choices=[1, 2, 3], default=2) + + parser_write_status = subparsers.add_parser( + 'write_flash_status', + help='Write SPI flash status register') + + add_spi_connection_arg(parser_write_status) + parser_write_status.add_argument('--non-volatile', help='Write non-volatile bits (use with caution)', action='store_true') + parser_write_status.add_argument('--bytes', help='Number of status bytes to write (1-3)', type=int, choices=[1, 2, 3], default=2) + parser_write_status.add_argument('value', help='New value', type=arg_auto_int) + + parser_read_flash = subparsers.add_parser( + 'read_flash', + help='Read SPI flash content') + add_spi_connection_arg(parser_read_flash) + parser_read_flash.add_argument('address', help='Start address', type=arg_auto_int) + parser_read_flash.add_argument('size', help='Size of region to dump', type=arg_auto_int) + parser_read_flash.add_argument('filename', help='Name of binary dump') + parser_read_flash.add_argument('--no-progress', '-p', help='Suppress progress output', action="store_true") + + parser_verify_flash = subparsers.add_parser( + 'verify_flash', + help='Verify a binary blob against flash') + parser_verify_flash.add_argument('addr_filename', help='Address and binary file to verify there, separated by space', + action=AddrFilenamePairAction) + parser_verify_flash.add_argument('--diff', '-d', help='Show differences', + choices=['no', 'yes'], default='no') + add_spi_flash_subparsers(parser_verify_flash, allow_keep=True, auto_detect=True) + + parser_erase_flash = subparsers.add_parser( + 'erase_flash', + help='Perform Chip Erase on SPI flash') + add_spi_connection_arg(parser_erase_flash) + + parser_erase_region = subparsers.add_parser( + 'erase_region', + help='Erase a region of the flash') + add_spi_connection_arg(parser_erase_region) + parser_erase_region.add_argument('address', help='Start address (must be multiple of 4096)', type=arg_auto_int) + parser_erase_region.add_argument('size', help='Size of region to erase (must be multiple of 4096)', type=arg_auto_int) + + parser_merge_bin = subparsers.add_parser( + 'merge_bin', + help='Merge multiple raw binary files into a single file for later flashing') + + parser_merge_bin.add_argument('--output', '-o', help='Output filename', type=str, required=True) + parser_merge_bin.add_argument('--format', '-f', help='Format of the output file', choices='raw', default='raw') # for future expansion + add_spi_flash_subparsers(parser_merge_bin, allow_keep=True, auto_detect=False) + + parser_merge_bin.add_argument('--target-offset', '-t', help='Target offset where the output file will be flashed', + type=arg_auto_int, default=0) + parser_merge_bin.add_argument('--fill-flash-size', help='If set, the final binary file will be padded with FF ' + 'bytes up to this flash size.', action=FlashSizeAction) + parser_merge_bin.add_argument('addr_filename', metavar='
', + help='Address followed by binary filename, separated by space', + action=AddrFilenamePairAction) + + subparsers.add_parser( + 'version', help='Print esptool version') + + subparsers.add_parser('get_security_info', help='Get some security-related data') + + # internal sanity check - every operation matches a module function of the same name + for operation in subparsers.choices.keys(): + assert operation in globals(), "%s should be a module function" % operation + + argv = expand_file_arguments(argv or sys.argv[1:]) + + args = parser.parse_args(argv) + print('esptool.py v%s' % __version__) + + # operation function can take 1 arg (args), 2 args (esp, arg) + # or be a member function of the ESPLoader class. + + if args.operation is None: + parser.print_help() + sys.exit(1) + + # Forbid the usage of both --encrypt, which means encrypt all the given files, + # and --encrypt-files, which represents the list of files to encrypt. + # The reason is that allowing both at the same time increases the chances of + # having contradictory lists (e.g. one file not available in one of list). + if args.operation == "write_flash" and args.encrypt and args.encrypt_files is not None: + raise FatalError("Options --encrypt and --encrypt-files must not be specified at the same time.") + + operation_func = globals()[args.operation] + + if PYTHON2: + # This function is depreciated in Python3 + operation_args = inspect.getargspec(operation_func).args + else: + operation_args = inspect.getfullargspec(operation_func).args + + if operation_args[0] == 'esp': # operation function takes an ESPLoader connection object + if args.before != "no_reset_no_sync": + initial_baud = min(ESPLoader.ESP_ROM_BAUD, args.baud) # don't sync faster than the default baud rate + else: + initial_baud = args.baud + + if args.port is None: + ser_list = get_port_list() + print("Found %d serial ports" % len(ser_list)) + else: + ser_list = [args.port] + esp = esp or get_default_connected_device(ser_list, port=args.port, connect_attempts=args.connect_attempts, + initial_baud=initial_baud, chip=args.chip, trace=args.trace, + before=args.before) + if esp is None: + raise FatalError("Could not connect to an Espressif device on any of the %d available serial ports." % len(ser_list)) + + if esp.secure_download_mode: + print("Chip is %s in Secure Download Mode" % esp.CHIP_NAME) + else: + print("Chip is %s" % (esp.get_chip_description())) + print("Features: %s" % ", ".join(esp.get_chip_features())) + print("Crystal is %dMHz" % esp.get_crystal_freq()) + read_mac(esp, args) + + if not args.no_stub: + if esp.secure_download_mode: + print("WARNING: Stub loader is not supported in Secure Download Mode, setting --no-stub") + args.no_stub = True + else: + esp = esp.run_stub() + + if args.override_vddsdio: + esp.override_vddsdio(args.override_vddsdio) + + if args.baud > initial_baud: + try: + esp.change_baud(args.baud) + except NotImplementedInROMError: + print("WARNING: ROM doesn't support changing baud rate. Keeping initial baud rate %d" % initial_baud) + + # override common SPI flash parameter stuff if configured to do so + if hasattr(args, "spi_connection") and args.spi_connection is not None: + if esp.CHIP_NAME != "ESP32": + raise FatalError("Chip %s does not support --spi-connection option." % esp.CHIP_NAME) + print("Configuring SPI flash mode...") + esp.flash_spi_attach(args.spi_connection) + elif args.no_stub: + print("Enabling default SPI flash mode...") + # ROM loader doesn't enable flash unless we explicitly do it + esp.flash_spi_attach(0) + + if hasattr(args, "flash_size"): + print("Configuring flash size...") + detect_flash_size(esp, args) + if args.flash_size != 'keep': # TODO: should set this even with 'keep' + esp.flash_set_parameters(flash_size_bytes(args.flash_size)) + + try: + operation_func(esp, args) + finally: + try: # Clean up AddrFilenamePairAction files + for address, argfile in args.addr_filename: + argfile.close() + except AttributeError: + pass + + # Handle post-operation behaviour (reset or other) + if operation_func == load_ram: + # the ESP is now running the loaded image, so let it run + print('Exiting immediately.') + elif args.after == 'hard_reset': + esp.hard_reset() + elif args.after == 'soft_reset': + print('Soft resetting...') + # flash_finish will trigger a soft reset + esp.soft_reset(False) + elif args.after == 'no_reset_stub': + print('Staying in flasher stub.') + else: # args.after == 'no_reset' + print('Staying in bootloader.') + if esp.IS_STUB: + esp.soft_reset(True) # exit stub back to ROM loader + + if not external_esp: + esp._port.close() + + else: + operation_func(args) + + +def get_port_list(): + if list_ports is None: + raise FatalError("Listing all serial ports is currently not available. Please try to specify the port when " + "running esptool.py or update the pyserial package to the latest version") + return sorted(ports.device for ports in list_ports.comports()) + + +def expand_file_arguments(argv): + """ Any argument starting with "@" gets replaced with all values read from a text file. + Text file arguments can be split by newline or by space. + Values are added "as-is", as if they were specified in this order on the command line. + """ + new_args = [] + expanded = False + for arg in argv: + if arg.startswith("@"): + expanded = True + with open(arg[1:], "r") as f: + for line in f.readlines(): + new_args += shlex.split(line) + else: + new_args.append(arg) + if expanded: + print("esptool.py %s" % (" ".join(new_args[1:]))) + return new_args + return argv + + +class FlashSizeAction(argparse.Action): + """ Custom flash size parser class to support backwards compatibility with megabit size arguments. + + (At next major relase, remove deprecated sizes and this can become a 'normal' choices= argument again.) + """ + def __init__(self, option_strings, dest, nargs=1, auto_detect=False, **kwargs): + super(FlashSizeAction, self).__init__(option_strings, dest, nargs, **kwargs) + self._auto_detect = auto_detect + + def __call__(self, parser, namespace, values, option_string=None): + try: + value = { + '2m': '256KB', + '4m': '512KB', + '8m': '1MB', + '16m': '2MB', + '32m': '4MB', + '16m-c1': '2MB-c1', + '32m-c1': '4MB-c1', + }[values[0]] + print("WARNING: Flash size arguments in megabits like '%s' are deprecated." % (values[0])) + print("Please use the equivalent size '%s'." % (value)) + print("Megabit arguments may be removed in a future release.") + except KeyError: + value = values[0] + + known_sizes = dict(ESP8266ROM.FLASH_SIZES) + known_sizes.update(ESP32ROM.FLASH_SIZES) + if self._auto_detect: + known_sizes['detect'] = 'detect' + known_sizes['keep'] = 'keep' + if value not in known_sizes: + raise argparse.ArgumentError(self, '%s is not a known flash size. Known sizes: %s' % (value, ", ".join(known_sizes.keys()))) + setattr(namespace, self.dest, value) + + +class SpiConnectionAction(argparse.Action): + """ Custom action to parse 'spi connection' override. Values are SPI, HSPI, or a sequence of 5 pin numbers separated by commas. + """ + def __call__(self, parser, namespace, value, option_string=None): + if value.upper() == "SPI": + value = 0 + elif value.upper() == "HSPI": + value = 1 + elif "," in value: + values = value.split(",") + if len(values) != 5: + raise argparse.ArgumentError(self, '%s is not a valid list of comma-separate pin numbers. Must be 5 numbers - CLK,Q,D,HD,CS.' % value) + try: + values = tuple(int(v, 0) for v in values) + except ValueError: + raise argparse.ArgumentError(self, '%s is not a valid argument. All pins must be numeric values' % values) + if any([v for v in values if v > 33 or v < 0]): + raise argparse.ArgumentError(self, 'Pin numbers must be in the range 0-33.') + # encode the pin numbers as a 32-bit integer with packed 6-bit values, the same way ESP32 ROM takes them + # TODO: make this less ESP32 ROM specific somehow... + clk, q, d, hd, cs = values + value = (hd << 24) | (cs << 18) | (d << 12) | (q << 6) | clk + else: + raise argparse.ArgumentError(self, '%s is not a valid spi-connection value. ' + 'Values are SPI, HSPI, or a sequence of 5 pin numbers CLK,Q,D,HD,CS).' % value) + setattr(namespace, self.dest, value) + + +class AddrFilenamePairAction(argparse.Action): + """ Custom parser class for the address/filename pairs passed as arguments """ + def __init__(self, option_strings, dest, nargs='+', **kwargs): + super(AddrFilenamePairAction, self).__init__(option_strings, dest, nargs, **kwargs) + + def __call__(self, parser, namespace, values, option_string=None): + # validate pair arguments + pairs = [] + for i in range(0, len(values), 2): + try: + address = int(values[i], 0) + except ValueError: + raise argparse.ArgumentError(self, 'Address "%s" must be a number' % values[i]) + try: + argfile = open(values[i + 1], 'rb') + except IOError as e: + raise argparse.ArgumentError(self, e) + except IndexError: + raise argparse.ArgumentError(self, 'Must be pairs of an address and the binary filename to write there') + pairs.append((address, argfile)) + + # Sort the addresses and check for overlapping + end = 0 + for address, argfile in sorted(pairs, key=lambda x: x[0]): + argfile.seek(0, 2) # seek to end + size = argfile.tell() + argfile.seek(0) + sector_start = address & ~(ESPLoader.FLASH_SECTOR_SIZE - 1) + sector_end = ((address + size + ESPLoader.FLASH_SECTOR_SIZE - 1) & ~(ESPLoader.FLASH_SECTOR_SIZE - 1)) - 1 + if sector_start < end: + message = 'Detected overlap at address: 0x%x for file: %s' % (address, argfile.name) + raise argparse.ArgumentError(self, message) + end = sector_end + setattr(namespace, self.dest, pairs) + + +# Binary stub code (see flasher_stub dir for source & details) +ESP8266ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" +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\ +"""))) +ESP32ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" +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\ +"""))) +ESP32S2ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" +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\ +"""))) +ESP32S3BETA2ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" +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\ +"""))) +ESP32S3BETA3ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" +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\ +"""))) +ESP32C3ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" +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\ +"""))) +ESP32C6BETAROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" +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\ +TB3fMw==\ +"""))) + + +def _main(): + try: + main() + except FatalError as e: + print('\nA fatal error occurred: %s' % e) + sys.exit(2) + + +if __name__ == '__main__': + _main() diff --git a/Release/rnode_firmware_latest_esp32_generic.zip b/Release/rnode_firmware_latest_esp32_generic.zip new file mode 100644 index 0000000..337afb4 Binary files /dev/null and b/Release/rnode_firmware_latest_esp32_generic.zip differ diff --git a/Release/rnode_firmware_latest_featheresp32.zip b/Release/rnode_firmware_latest_featheresp32.zip new file mode 100644 index 0000000..f4d80e1 Binary files /dev/null and b/Release/rnode_firmware_latest_featheresp32.zip differ diff --git a/Release/rnode_firmware_latest_lora32v20.zip b/Release/rnode_firmware_latest_lora32v20.zip new file mode 100644 index 0000000..f1eeebe Binary files /dev/null and b/Release/rnode_firmware_latest_lora32v20.zip differ diff --git a/Release/rnode_firmware_latest_lora32v20_extled.zip b/Release/rnode_firmware_latest_lora32v20_extled.zip new file mode 100644 index 0000000..ff6ae93 Binary files /dev/null and b/Release/rnode_firmware_latest_lora32v20_extled.zip differ diff --git a/Release/rnode_firmware_latest_lora32v21.zip b/Release/rnode_firmware_latest_lora32v21.zip new file mode 100644 index 0000000..afbbb27 Binary files /dev/null and b/Release/rnode_firmware_latest_lora32v21.zip differ diff --git a/Release/rnode_firmware_latest_lora32v21_extled.zip b/Release/rnode_firmware_latest_lora32v21_extled.zip new file mode 100644 index 0000000..1c79ecf Binary files /dev/null and b/Release/rnode_firmware_latest_lora32v21_extled.zip differ diff --git a/Release/rnode_firmware_latest_tbeam.zip b/Release/rnode_firmware_latest_tbeam.zip new file mode 100644 index 0000000..09ddc19 Binary files /dev/null and b/Release/rnode_firmware_latest_tbeam.zip differ