]> git.tnoah.ca Git - lora-radio.git/commitdiff
Renamed release folder
authorMark Qvist <mark@unsigned.io>
Fri, 28 Jan 2022 15:30:10 +0000 (16:30 +0100)
committerMark Qvist <mark@unsigned.io>
Fri, 28 Jan 2022 15:30:10 +0000 (16:30 +0100)
21 files changed:
Makefile
Precompiled/README.md [deleted file]
Precompiled/esptool/esptool.py [deleted file]
Precompiled/rnode_firmware_latest.hex [deleted file]
Precompiled/rnode_firmware_latest_esp32_generic.zip [deleted file]
Precompiled/rnode_firmware_latest_featheresp32.zip [deleted file]
Precompiled/rnode_firmware_latest_lora32v20.zip [deleted file]
Precompiled/rnode_firmware_latest_lora32v20_extled.zip [deleted file]
Precompiled/rnode_firmware_latest_lora32v21.zip [deleted file]
Precompiled/rnode_firmware_latest_lora32v21_extled.zip [deleted file]
Precompiled/rnode_firmware_latest_m2560.hex [deleted file]
Precompiled/rnode_firmware_latest_tbeam.zip [deleted file]
Release/README.md [new file with mode: 0644]
Release/esptool/esptool.py [new file with mode: 0755]
Release/rnode_firmware_latest_esp32_generic.zip [new file with mode: 0644]
Release/rnode_firmware_latest_featheresp32.zip [new file with mode: 0644]
Release/rnode_firmware_latest_lora32v20.zip [new file with mode: 0644]
Release/rnode_firmware_latest_lora32v20_extled.zip [new file with mode: 0644]
Release/rnode_firmware_latest_lora32v21.zip [new file with mode: 0644]
Release/rnode_firmware_latest_lora32v21_extled.zip [new file with mode: 0644]
Release/rnode_firmware_latest_tbeam.zip [new file with mode: 0644]

index 25ccaccf198900481d588ee89798c05f795557e9..7e3e41621e50738686282a9e0ab6a94411319ee2 100644 (file)
--- 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 (file)
index 37384d1..0000000
+++ /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 (executable)
index 01176af..0000000
+++ /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'<BBHI', 0x00, op, len(data), chk) + data
-                self.write(pkt)
-
-            if not wait_response:
-                return
-
-            # tries to get a response until that response has the
-            # same operation as the request or a retries limit has
-            # exceeded. This is needed for some esp8266s that
-            # reply with more sync responses than expected.
-            for retry in range(100):
-                p = self.read()
-                if len(p) < 8:
-                    continue
-                (resp, op_ret, len_ret, val) = struct.unpack('<BBHI', p[:8])
-                if resp != 1:
-                    continue
-                data = p[8:]
-
-                if op is None or op_ret == op:
-                    return val, data
-                if byte(data, 0) != 0 and byte(data, 1) == self.ROM_INVALID_RECV_MSG:
-                    self.flush_input()  # Unsupported read_reg can result in more than one error response for some reason
-                    raise UnsupportedCommandError(self, op)
-
-        finally:
-            if new_timeout != saved_timeout:
-                self._port.timeout = saved_timeout
-
-        raise FatalError("Response doesn't match request")
-
-    def check_command(self, op_description, op=None, data=b'', chk=0, timeout=DEFAULT_TIMEOUT):
-        """
-        Execute a command with 'command', check the result code and throw an appropriate
-        FatalError if it fails.
-
-        Returns the "result" of a successful command.
-        """
-        val, data = self.command(op, data, chk, timeout=timeout)
-
-        # things are a bit weird here, bear with us
-
-        # the status bytes are the last 2/4 bytes in the data (depending on chip)
-        if len(data) < self.STATUS_BYTES_LENGTH:
-            raise FatalError("Failed to %s. Only got %d byte status response." % (op_description, len(data)))
-        status_bytes = data[-self.STATUS_BYTES_LENGTH:]
-        # we only care if the first one is non-zero. If it is, the second byte is a reason.
-        if byte(status_bytes, 0) != 0:
-            raise FatalError.WithResult('Failed to %s' % op_description, status_bytes)
-
-        # if we had more data than just the status bytes, return it as the result
-        # (this is used by the md5sum command, maybe other commands?)
-        if len(data) > 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('<I', addr), timeout=timeout)
-        if byte(data, 0) != 0:
-            raise FatalError.WithResult("Failed to read register address %08x" % addr, data)
-        return val
-
-    """ Write to memory address in target """
-    def write_reg(self, addr, value, mask=0xFFFFFFFF, delay_us=0, delay_after_us=0):
-        command = struct.pack('<IIII', addr, value, mask, delay_us)
-        if delay_after_us > 0:
-            # add a dummy write to a date register as an excuse to have a delay
-            command += struct.pack('<IIII', self.UART_DATE_REG_ADDR, 0, 0, delay_after_us)
-
-        return self.check_command("write target memory", self.ESP_WRITE_REG, command)
-
-    def update_reg(self, addr, mask, new_val):
-        """ Update register at 'addr', replace the bits masked out by 'mask'
-        with new_val. new_val is shifted left to match the LSB of 'mask'
-
-        Returns just-written value of register.
-        """
-        shift = _mask_to_shift(mask)
-        val = self.read_reg(addr)
-        val &= ~mask
-        val |= (new_val << shift) & mask
-        self.write_reg(addr, val)
-
-        return val
-
-    """ Start downloading an application image to RAM """
-    def mem_begin(self, size, blocks, blocksize, offset):
-        if self.IS_STUB:  # check we're not going to overwrite a running stub with this data
-            stub = self.STUB_CODE
-            load_start = offset
-            load_end = offset + size
-            for (start, end) in [(stub["data_start"], stub["data_start"] + len(stub["data"])),
-                                 (stub["text_start"], stub["text_start"] + len(stub["text"]))]:
-                if load_start < end and load_end > 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('<IIII', size, blocks, blocksize, offset))
-
-    """ Send a block of an image to RAM """
-    def mem_block(self, data, seq):
-        return self.check_command("write to target RAM", self.ESP_MEM_DATA,
-                                  struct.pack('<IIII', len(data), seq, 0, 0) + data,
-                                  self.checksum(data))
-
-    """ Leave download mode and run the application """
-    def mem_finish(self, entrypoint=0):
-        # Sending ESP_MEM_END usually sends a correct response back, however sometimes
-        # (with ROM loader) the executed code may reset the UART or change the baud rate
-        # before the transmit FIFO is empty. So in these cases we set a short timeout and
-        # ignore errors.
-        timeout = DEFAULT_TIMEOUT if self.IS_STUB else MEM_END_ROM_TIMEOUT
-        data = struct.pack('<II', int(entrypoint == 0), entrypoint)
-        try:
-            return self.check_command("leave RAM download mode", self.ESP_MEM_END,
-                                      data=data, timeout=timeout)
-        except FatalError:
-            if self.IS_STUB:
-                raise
-            pass
-
-    """ Start downloading to Flash (performs an erase)
-
-    Returns number of blocks (of size self.FLASH_WRITE_SIZE) to write.
-    """
-    def flash_begin(self, size, offset, begin_rom_encrypted=False):
-        num_blocks = (size + self.FLASH_WRITE_SIZE - 1) // self.FLASH_WRITE_SIZE
-        erase_size = self.get_erase_size(offset, size)
-
-        t = time.time()
-        if self.IS_STUB:
-            timeout = DEFAULT_TIMEOUT
-        else:
-            timeout = timeout_per_mb(ERASE_REGION_TIMEOUT_PER_MB, size)  # ROM performs the erase up front
-
-        params = struct.pack('<IIII', erase_size, num_blocks, self.FLASH_WRITE_SIZE, offset)
-        if isinstance(self, (ESP32S2ROM, ESP32S3BETA2ROM, ESP32S3BETA3ROM, ESP32C3ROM, ESP32C6BETAROM)) and not self.IS_STUB:
-            params += struct.pack('<I', 1 if begin_rom_encrypted else 0)
-        self.check_command("enter Flash download mode", self.ESP_FLASH_BEGIN,
-                           params, timeout=timeout)
-        if size != 0 and not self.IS_STUB:
-            print("Took %.2fs to erase flash block" % (time.time() - t))
-        return num_blocks
-
-    """ Write block to flash """
-    def flash_block(self, data, seq, timeout=DEFAULT_TIMEOUT):
-        self.check_command("write to target Flash after seq %d" % seq,
-                           self.ESP_FLASH_DATA,
-                           struct.pack('<IIII', len(data), seq, 0, 0) + data,
-                           self.checksum(data),
-                           timeout=timeout)
-
-    """ Encrypt before writing to flash """
-    def flash_encrypt_block(self, data, seq, timeout=DEFAULT_TIMEOUT):
-        if isinstance(self, (ESP32S2ROM, ESP32C3ROM)) and not self.IS_STUB:
-            # ROM support performs the encrypted writes via the normal write command,
-            # triggered by flash_begin(begin_rom_encrypted=True)
-            return self.flash_block(data, seq, timeout)
-
-        self.check_command("Write encrypted to target Flash after seq %d" % seq,
-                           self.ESP_FLASH_ENCRYPT_DATA,
-                           struct.pack('<IIII', len(data), seq, 0, 0) + data,
-                           self.checksum(data),
-                           timeout=timeout)
-
-    """ Leave flash mode and run/reboot """
-    def flash_finish(self, reboot=False):
-        pkt = struct.pack('<I', int(not reboot))
-        # stub sends a reply to this command
-        self.check_command("leave Flash mode", self.ESP_FLASH_END, pkt)
-
-    """ Run application code in flash """
-    def run(self, reboot=False):
-        # Fake flash begin immediately followed by flash end
-        self.flash_begin(0, 0)
-        self.flash_finish(reboot)
-
-    """ Read SPI flash manufacturer and device id """
-    def flash_id(self):
-        SPIFLASH_RDID = 0x9F
-        return self.run_spiflash_command(SPIFLASH_RDID, b"", 24)
-
-    def get_security_info(self):
-        # TODO: this only works on the ESP32S2 ROM code loader and needs to work in stub loader also
-        res = self.check_command('get security info', self.ESP_GET_SECURITY_INFO, b'')
-        res = struct.unpack("<IBBBBBBBB", res)
-        flags, flash_crypt_cnt, key_purposes = res[0], res[1], res[2:]
-        # TODO: pack this as some kind of better data type
-        return (flags, flash_crypt_cnt, key_purposes)
-
-    @classmethod
-    def parse_flash_size_arg(cls, arg):
-        try:
-            return cls.FLASH_SIZES[arg]
-        except KeyError:
-            raise FatalError("Flash size '%s' is not supported by this chip type. Supported sizes: %s"
-                             % (arg, ", ".join(cls.FLASH_SIZES.keys())))
-
-    def run_stub(self, stub=None):
-        if stub is None:
-            stub = self.STUB_CODE
-
-        if self.sync_stub_detected:
-            print("Stub is already running. No upload is necessary.")
-            return self.STUB_CLASS(self)
-
-        # Upload
-        print("Uploading stub...")
-        for field in ['text', 'data']:
-            if field in stub:
-                offs = stub[field + "_start"]
-                length = len(stub[field])
-                blocks = (length + self.ESP_RAM_BLOCK - 1) // self.ESP_RAM_BLOCK
-                self.mem_begin(length, blocks, self.ESP_RAM_BLOCK, offs)
-                for seq in range(blocks):
-                    from_offs = seq * self.ESP_RAM_BLOCK
-                    to_offs = from_offs + self.ESP_RAM_BLOCK
-                    self.mem_block(stub[field][from_offs:to_offs], seq)
-        print("Running stub...")
-        self.mem_finish(stub['entry'])
-
-        p = self.read()
-        if p != b'OHAI':
-            raise FatalError("Failed to start stub. Unexpected response: %s" % p)
-        print("Stub running...")
-        return self.STUB_CLASS(self)
-
-    @stub_and_esp32_function_only
-    def flash_defl_begin(self, size, compsize, offset):
-        """ Start downloading compressed data to Flash (performs an erase)
-
-        Returns number of blocks (size self.FLASH_WRITE_SIZE) to write.
-        """
-        num_blocks = (compsize + self.FLASH_WRITE_SIZE - 1) // self.FLASH_WRITE_SIZE
-        erase_blocks = (size + self.FLASH_WRITE_SIZE - 1) // self.FLASH_WRITE_SIZE
-
-        t = time.time()
-        if self.IS_STUB:
-            write_size = size  # stub expects number of bytes here, manages erasing internally
-            timeout = DEFAULT_TIMEOUT
-        else:
-            write_size = erase_blocks * self.FLASH_WRITE_SIZE  # ROM expects rounded up to erase block size
-            timeout = timeout_per_mb(ERASE_REGION_TIMEOUT_PER_MB, write_size)  # ROM performs the erase up front
-        print("Compressed %d bytes to %d..." % (size, compsize))
-        params = struct.pack('<IIII', write_size, num_blocks, self.FLASH_WRITE_SIZE, offset)
-        if isinstance(self, (ESP32S2ROM, ESP32S3BETA2ROM, ESP32S3BETA3ROM, ESP32C3ROM, ESP32C6BETAROM)) and not self.IS_STUB:
-            params += struct.pack('<I', 0)  # extra param is to enter encrypted flash mode via ROM (not supported currently)
-        self.check_command("enter compressed flash mode", self.ESP_FLASH_DEFL_BEGIN, params, timeout=timeout)
-        if size != 0 and not self.IS_STUB:
-            # (stub erases as it writes, but ROM loaders erase on begin)
-            print("Took %.2fs to erase flash block" % (time.time() - t))
-        return num_blocks
-
-    """ Write block to flash, send compressed """
-    @stub_and_esp32_function_only
-    def flash_defl_block(self, data, seq, timeout=DEFAULT_TIMEOUT):
-        self.check_command("write compressed data to flash after seq %d" % seq,
-                           self.ESP_FLASH_DEFL_DATA, struct.pack('<IIII', len(data), seq, 0, 0) + data, self.checksum(data), timeout=timeout)
-
-    """ Leave compressed flash mode and run/reboot """
-    @stub_and_esp32_function_only
-    def flash_defl_finish(self, reboot=False):
-        if not reboot and not self.IS_STUB:
-            # skip sending flash_finish to ROM loader, as this
-            # exits the bootloader. Stub doesn't do this.
-            return
-        pkt = struct.pack('<I', int(not reboot))
-        self.check_command("leave compressed flash mode", self.ESP_FLASH_DEFL_END, pkt)
-        self.in_bootloader = False
-
-    @stub_and_esp32_function_only
-    def flash_md5sum(self, addr, size):
-        # the MD5 command returns additional bytes in the standard
-        # command reply slot
-        timeout = timeout_per_mb(MD5_TIMEOUT_PER_MB, size)
-        res = self.check_command('calculate md5sum', self.ESP_SPI_FLASH_MD5, struct.pack('<IIII', addr, size, 0, 0),
-                                 timeout=timeout)
-
-        if len(res) == 32:
-            return res.decode("utf-8")  # already hex formatted
-        elif len(res) == 16:
-            return hexify(res).lower()
-        else:
-            raise FatalError("MD5Sum command returned unexpected result: %r" % res)
-
-    @stub_and_esp32_function_only
-    def change_baud(self, baud):
-        print("Changing baud rate to %d" % baud)
-        # stub takes the new baud rate and the old one
-        second_arg = self._port.baudrate if self.IS_STUB else 0
-        self.command(self.ESP_CHANGE_BAUDRATE, struct.pack('<II', baud, second_arg))
-        print("Changed.")
-        self._set_port_baudrate(baud)
-        time.sleep(0.05)  # get rid of crap sent during baud rate change
-        self.flush_input()
-
-    @stub_function_only
-    def erase_flash(self):
-        # depending on flash chip model the erase may take this long (maybe longer!)
-        self.check_command("erase flash", self.ESP_ERASE_FLASH,
-                           timeout=CHIP_ERASE_TIMEOUT)
-
-    @stub_function_only
-    def erase_region(self, offset, size):
-        if offset % self.FLASH_SECTOR_SIZE != 0:
-            raise FatalError("Offset to erase from must be a multiple of 4096")
-        if size % self.FLASH_SECTOR_SIZE != 0:
-            raise FatalError("Size of data to erase must be a multiple of 4096")
-        timeout = timeout_per_mb(ERASE_REGION_TIMEOUT_PER_MB, size)
-        self.check_command("erase region", self.ESP_ERASE_REGION, struct.pack('<II', offset, size), timeout=timeout)
-
-    def read_flash_slow(self, offset, length, progress_fn):
-        raise NotImplementedInROMError(self, self.read_flash_slow)
-
-    def read_flash(self, offset, length, progress_fn=None):
-        if not self.IS_STUB:
-            return self.read_flash_slow(offset, length, progress_fn)  # ROM-only routine
-
-        # issue a standard bootloader command to trigger the read
-        self.check_command("read flash", self.ESP_READ_FLASH,
-                           struct.pack('<IIII',
-                                       offset,
-                                       length,
-                                       self.FLASH_SECTOR_SIZE,
-                                       64))
-        # now we expect (length // block_size) SLIP frames with the data
-        data = b''
-        while len(data) < length:
-            p = self.read()
-            data += p
-            if len(data) < length and len(p) < self.FLASH_SECTOR_SIZE:
-                raise FatalError('Corrupt data, expected 0x%x bytes but received 0x%x bytes' % (self.FLASH_SECTOR_SIZE, len(p)))
-            self.write(struct.pack('<I', len(data)))
-            if progress_fn and (len(data) % 1024 == 0 or len(data) == length):
-                progress_fn(len(data), length)
-        if progress_fn:
-            progress_fn(len(data), length)
-        if len(data) > 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('<I', hspi_arg)
-        if not self.IS_STUB:
-            # ESP32 ROM loader takes additional 'is legacy' arg, which is not
-            # currently supported in the stub loader or esptool.py (as it's not usually needed.)
-            is_legacy = 0
-            arg += struct.pack('BBBB', is_legacy, 0, 0, 0)
-        self.check_command("configure SPI flash pins", ESP32ROM.ESP_SPI_ATTACH, arg)
-
-    def flash_set_parameters(self, size):
-        """Tell the ESP bootloader the parameters of the chip
-
-        Corresponds to the "flashchip" data structure that the ROM
-        has in RAM.
-
-        'size' is in bytes.
-
-        All other flash parameters are currently hardcoded (on ESP8266
-        these are mostly ignored by ROM code, on ESP32 I'm not sure.)
-        """
-        fl_id = 0
-        total_size = size
-        block_size = 64 * 1024
-        sector_size = 4 * 1024
-        page_size = 256
-        status_mask = 0xffff
-        self.check_command("set SPI params", ESP32ROM.ESP_SPI_SET_PARAMS,
-                           struct.pack('<IIIIII', fl_id, total_size, block_size, sector_size, page_size, status_mask))
-
-    def run_spiflash_command(self, spiflash_command, data=b"", read_bits=0):
-        """Run an arbitrary SPI flash command.
-
-        This function uses the "USR_COMMAND" functionality in the ESP
-        SPI hardware, rather than the precanned commands supported by
-        hardware. So the value of spiflash_command is an actual command
-        byte, sent over the wire.
-
-        After writing command byte, writes 'data' to MOSI and then
-        reads back 'read_bits' of reply on MISO. Result is a number.
-        """
-
-        # SPI_USR register flags
-        SPI_USR_COMMAND = (1 << 31)
-        SPI_USR_MISO    = (1 << 28)
-        SPI_USR_MOSI    = (1 << 27)
-
-        # SPI registers, base address differs ESP32* vs 8266
-        base = self.SPI_REG_BASE
-        SPI_CMD_REG       = base + 0x00
-        SPI_USR_REG       = base + self.SPI_USR_OFFS
-        SPI_USR1_REG      = base + self.SPI_USR1_OFFS
-        SPI_USR2_REG      = base + self.SPI_USR2_OFFS
-        SPI_W0_REG        = base + self.SPI_W0_OFFS
-
-        # following two registers are ESP32 & 32S2/32C3 only
-        if self.SPI_MOSI_DLEN_OFFS is not None:
-            # ESP32/32S2/32C3 has a more sophisticated way to set up "user" commands
-            def set_data_lengths(mosi_bits, miso_bits):
-                SPI_MOSI_DLEN_REG = base + self.SPI_MOSI_DLEN_OFFS
-                SPI_MISO_DLEN_REG = base + self.SPI_MISO_DLEN_OFFS
-                if mosi_bits > 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("<H", new_status))
-
-        # also try using individual commands (also not supported by all chips for num_bytes 2 & 3)
-        for cmd in [SPIFLASH_WRSR, SPIFLASH_WRSR2, SPIFLASH_WRSR3][0:num_bytes]:
-            self.run_spiflash_command(enable_cmd)
-            self.run_spiflash_command(cmd, struct.pack("B", new_status & 0xFF))
-            new_status >>= 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('<II', offset + len(data), block_len))
-            if len(r) < block_len:
-                raise FatalError("Expected %d byte block, got %d bytes. Serial errors?" % (block_len, len(r)))
-            data += r[:block_len]  # command always returns 64 byte buffer, regardless of how many bytes were actually read from flash
-            if progress_fn and (len(data) % 1024 == 0 or len(data) == length):
-                progress_fn(len(data), length)
-        return data
-
-
-class ESP32S2ROM(ESP32ROM):
-    CHIP_NAME = "ESP32-S2"
-    IMAGE_CHIP_ID = 2
-
-    IROM_MAP_START = 0x40080000
-    IROM_MAP_END   = 0x40b80000
-    DROM_MAP_START = 0x3F000000
-    DROM_MAP_END   = 0x3F3F0000
-
-    CHIP_DETECT_MAGIC_VALUE = [0x000007c6]
-
-    SPI_REG_BASE = 0x3f402000
-    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
-
-    MAC_EFUSE_REG = 0x3f41A044  # ESP32-S2 has special block for MAC efuses
-
-    UART_CLKDIV_REG = 0x3f400014
-
-    FLASH_ENCRYPTED_WRITE_ALIGN = 16
-
-    # todo: use espefuse APIs to get this info
-    EFUSE_BASE = 0x3f41A000
-    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 << 19
-
-    PURPOSE_VAL_XTS_AES256_KEY_1 = 2
-    PURPOSE_VAL_XTS_AES256_KEY_2 = 3
-    PURPOSE_VAL_XTS_AES128_KEY = 4
-
-    UARTDEV_BUF_NO = 0x3ffffd14  # Variable in ROM .bss which indicates the port in use
-    UARTDEV_BUF_NO_USB = 2  # Value of the above variable indicating that USB is in use
-
-    USB_RAM_BLOCK = 0x800  # Max block size USB CDC is used
-
-    GPIO_STRAP_REG = 0x3f404038
-    GPIO_STRAP_SPI_BOOT_MASK = 0x8   # Not download mode
-    RTC_CNTL_OPTION1_REG = 0x3f408128
-    RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK = 0x1  # Is download mode forced over USB?
-
-    MEMORY_MAP = [[0x00000000, 0x00010000, "PADDING"],
-                  [0x3F000000, 0x3FF80000, "DROM"],
-                  [0x3F500000, 0x3FF80000, "EXTRAM_DATA"],
-                  [0x3FF9E000, 0x3FFA0000, "RTC_DRAM"],
-                  [0x3FF9E000, 0x40000000, "BYTE_ACCESSIBLE"],
-                  [0x3FF9E000, 0x40072000, "MEM_INTERNAL"],
-                  [0x3FFB0000, 0x40000000, "DRAM"],
-                  [0x40000000, 0x4001A100, "IROM_MASK"],
-                  [0x40020000, 0x40070000, "IRAM"],
-                  [0x40070000, 0x40072000, "RTC_IRAM"],
-                  [0x40080000, 0x40800000, "IROM"],
-                  [0x50000000, 0x50002000, "RTC_DATA"]]
-
-    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_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('<BBBBI', load_file.read(8))
-
-        if magic != expected_magic:
-            raise FatalError('Invalid firmware image magic=0x%x' % (magic))
-        return segments
-
-    def verify(self):
-        if len(self.segments) > 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('<II', f.read(8))
-        self.warn_if_unusual_segment(offset, size, is_irom_segment)
-        segment_data = f.read(size)
-        if len(segment_data) < size:
-            raise FatalError('End of file reading segment 0x%x, length %d (actual length %d)' % (offset, size, len(segment_data)))
-        segment = ImageSegment(offset, segment_data, file_offs)
-        self.segments.append(segment)
-        return segment
-
-    def warn_if_unusual_segment(self, offset, size, is_irom_segment):
-        if not is_irom_segment:
-            if offset > 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('<II', segment.addr, len(segment_data)))
-        f.write(segment_data)
-        if checksum is not None:
-            return ESPLoader.checksum(segment_data, checksum)
-
-    def read_checksum(self, f):
-        """ Return ESPLoader checksum from end of just-read image """
-        # Skip the padding. The checksum is stored in the last byte so that the
-        # file is a multiple of 16 bytes.
-        align_file_position(f, 16)
-        return ord(f.read(1))
-
-    def calculate_checksum(self):
-        """ Calculate checksum of loaded image, based on segments in
-        segment array.
-        """
-        checksum = ESPLoader.ESP_CHECKSUM_MAGIC
-        for seg in self.segments:
-            if seg.include_in_checksum:
-                checksum = ESPLoader.checksum(seg.data, checksum)
-        return checksum
-
-    def append_checksum(self, f, checksum):
-        """ Append ESPLoader checksum to the just-written image """
-        align_file_position(f, 16)
-        f.write(struct.pack(b'B', checksum))
-
-    def write_common_header(self, f, segments):
-        f.write(struct.pack('<BBBBI', ESPLoader.ESP_IMAGE_MAGIC, len(segments),
-                            self.flash_mode, self.flash_size_freq, self.entrypoint))
-
-    def is_irom_addr(self, addr):
-        """ Returns True if an address starts in the irom region.
-        Valid for ESP8266 only.
-        """
-        return ESP8266ROM.IROM_MAP_START <= addr < ESP8266ROM.IROM_MAP_END
-
-    def get_irom_segment(self):
-        irom_segments = [s for s in self.segments if self.is_irom_addr(s.addr)]
-        if len(irom_segments) > 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'<BBBBI', ESPBOOTLOADER.IMAGE_V2_MAGIC, ESPBOOTLOADER.IMAGE_V2_SEGMENT,
-                                self.flash_mode, self.flash_size_freq, self.entrypoint))
-
-            irom_segment = self.get_irom_segment()
-            if irom_segment is not None:
-                # save irom0 segment, make sure it has load addr 0 in the file
-                irom_segment = irom_segment.copy_with_new_addr(0)
-                irom_segment.pad_to_alignment(16)  # irom_segment must end on a 16 byte boundary
-                self.save_segment(f, irom_segment)
-
-            # second header, matches V1 header and contains loadable segments
-            normal_segments = self.get_non_irom_segments()
-            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)
-
-        # calculate a crc32 of entire file and append
-        # (algorithm used by recent 8266 SDK bootloaders)
-        with open(filename, 'rb') as f:
-            crc = esp8266_crc32(f.read())
-        with open(filename, 'ab') as f:
-            f.write(struct.pack(b'<I', crc))
-
-
-# Backwards compatibility for previous API, remove in esptool.py V3
-ESPFirmwareImage = ESP8266ROMFirmwareImage
-OTAFirmwareImage = ESP8266V2FirmwareImage
-
-
-def esp8266_crc32(data):
-    """
-    CRC32 algorithm used by 8266 SDK bootloader (and gen_appbin.py).
-    """
-    crc = binascii.crc32(data, 0) & 0xFFFFFFFF
-    if crc & 0x80000000:
-        return crc ^ 0xFFFFFFFF
-    else:
-        return crc + 1
-
-
-class ESP32FirmwareImage(BaseFirmwareImage):
-    """ ESP32 firmware image is very similar to V1 ESP8266 image,
-    except with an additional 16 byte reserved header at top of image,
-    and because of new flash mapping capabilities the flash-mapped regions
-    can be placed in the normal image (just @ 64kB padded offsets).
-    """
-
-    ROM_LOADER = ESP32ROM
-
-    # ROM bootloader will read the wp_pin field if SPI flash
-    # pins are remapped via flash. IDF actually enables QIO only
-    # from software bootloader, so this can be ignored. But needs
-    # to be set to this value so ROM bootloader will skip it.
-    WP_PIN_DISABLED = 0xEE
-
-    EXTENDED_HEADER_STRUCT_FMT = "<BBBBHB" + ("B" * 8) + "B"
-
-    IROM_ALIGN = 65536
-
-    def __init__(self, load_file=None):
-        super(ESP32FirmwareImage, self).__init__()
-        self.secure_pad = None
-        self.flash_mode = 0
-        self.flash_size_freq = 0
-        self.version = 1
-        self.wp_pin = self.WP_PIN_DISABLED
-        # SPI pin drive levels
-        self.clk_drv = 0
-        self.q_drv = 0
-        self.d_drv = 0
-        self.cs_drv = 0
-        self.hd_drv = 0
-        self.wp_drv = 0
-        self.min_rev = 0
-
-        self.append_digest = True
-
-        if load_file is not None:
-            start = load_file.tell()
-
-            segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC)
-            self.load_extended_header(load_file)
-
-            for _ in range(segments):
-                self.load_segment(load_file)
-            self.checksum = self.read_checksum(load_file)
-
-            if self.append_digest:
-                end = load_file.tell()
-                self.stored_digest = load_file.read(32)
-                load_file.seek(start)
-                calc_digest = hashlib.sha256()
-                calc_digest.update(load_file.read(end - start))
-                self.calc_digest = calc_digest.digest()  # TODO: decide what to do here?
-
-            self.verify()
-
-    def is_flash_addr(self, addr):
-        return (self.ROM_LOADER.IROM_MAP_START <= addr < self.ROM_LOADER.IROM_MAP_END) \
-            or (self.ROM_LOADER.DROM_MAP_START <= addr < self.ROM_LOADER.DROM_MAP_END)
-
-    def default_output_name(self, input_file):
-        """ Derive a default output name from the ELF name. """
-        return "%s.bin" % (os.path.splitext(input_file)[0])
-
-    def warn_if_unusual_segment(self, offset, size, is_irom_segment):
-        pass  # TODO: add warnings for ESP32 segment offset/size combinations that are wrong
-
-    def save(self, filename):
-        total_segments = 0
-        with io.BytesIO() as f:  # write file to memory first
-            self.write_common_header(f, self.segments)
-
-            # first 4 bytes of header are read by ROM bootloader for SPI
-            # config, but currently unused
-            self.save_extended_header(f)
-
-            checksum = ESPLoader.ESP_CHECKSUM_MAGIC
-
-            # split segments into flash-mapped vs ram-loaded, and take copies so we can mutate them
-            flash_segments = [copy.deepcopy(s) for s in sorted(self.segments, key=lambda s:s.addr) if self.is_flash_addr(s.addr)]
-            ram_segments = [copy.deepcopy(s) for s in sorted(self.segments, key=lambda s:s.addr) if not self.is_flash_addr(s.addr)]
-
-            # check for multiple ELF sections that are mapped in the same flash mapping region.
-            # this is usually a sign of a broken linker script, but if you have a legitimate
-            # use case then let us know
-            if len(flash_segments) > 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("<LLLLLL", section_header[offs:])
-            return (name_offs, sec_type, lma, size, sec_offs)
-        all_sections = [read_section_header(offs) for offs in section_header_offsets]
-        prog_sections = [s for s in all_sections if s[1] == ELFFile.SEC_TYPE_PROGBITS]
-
-        # search for the string table section
-        if not (shstrndx * self.LEN_SEC_HEADER) in section_header_offsets:
-            raise FatalError("ELF file has no STRTAB section at shstrndx %d" % shstrndx)
-        _, sec_type, _, sec_size, sec_offs = read_section_header(shstrndx * self.LEN_SEC_HEADER)
-        if sec_type != ELFFile.SEC_TYPE_STRTAB:
-            print('WARNING: ELF file has incorrect STRTAB section type 0x%02x' % sec_type)
-        f.seek(sec_offs)
-        string_table = f.read(sec_size)
-
-        # build the real list of ELFSections by reading the actual section names from the
-        # string table section, and actual data for each section from the ELF file itself
-        def lookup_string(offs):
-            raw = string_table[offs:]
-            return raw[:raw.index(b'\x00')]
-
-        def read_data(offs, size):
-            f.seek(offs)
-            return f.read(size)
-
-        prog_sections = [ELFSection(lookup_string(n_offs), lma, read_data(offs, size)) for (n_offs, _type, lma, size, offs) in prog_sections
-                         if lma != 0 and size > 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("<LLLLLLLL", segment_header[offs:])
-            return (seg_type, lma, size, seg_offs)
-        all_segments = [read_segment_header(offs) for offs in segment_header_offsets]
-        prog_segments = [s for s in all_segments if s[0] == ELFFile.SEG_TYPE_LOAD]
-
-        def read_data(offs, size):
-            f.seek(offs)
-            return f.read(size)
-
-        prog_segments = [ELFSection(b'PHDR', lma, read_data(offs, size)) for (_type, lma, size, offs) in prog_segments
-                         if lma != 0 and size > 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 (<ESPLoader instance>, <args>) or a single <args>
-# 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'<I', d))
-            if f.tell() % 1024 == 0:
-                print_overwrite('%d bytes read... (%d %%)' % (f.tell(),
-                                                              f.tell() * 100 // args.size))
-            sys.stdout.flush()
-        print_overwrite("Read %d bytes" % f.tell(), last_line=True)
-    print('Done!')
-
-
-def detect_flash_size(esp, args):
-    if args.flash_size == 'detect':
-        if esp.secure_download_mode:
-            raise FatalError("Detecting flash size is not supported in secure download mode. Need to manually specify flash size.")
-        flash_id = esp.flash_id()
-        size_id = flash_id >> 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='<address> <filename>', 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='<address> <filename>',
-                                    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='<address> <filename>',
-                                  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"""
-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"""
-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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/Precompiled/rnode_firmware_latest.hex b/Precompiled/rnode_firmware_latest.hex
deleted file mode 100644 (file)
index 6643208..0000000
+++ /dev/null
@@ -1,1555 +0,0 @@
-:100000000C9483000C94B5260C94E0260C940B27DA\r
-:100010000C94AF000C94AF000C94AF000C94AF00A4\r
-:100020000C94AF000C94AF000C94AF000C94AF0094\r
-:100030000C94AF000C94AF000C94AF000C94AF0084\r
-:100040000C94AF000C94AF000C945C280C94AF009F\r
-:100050000C94C32B0C94F92B0C94AF000C94AF00B0\r
-:100060000C94AF000C94AF000C94AF000C94AF0054\r
-:100070000C94AF000C94AF000C94AF000C94F924D6\r
-:100080000C94AF000C94AF000C94AF000000000182\r
-:100090000200090A00000000040308070000000035\r
-:1000A0000000000000000000000000000102040841\r
-:1000B0001020408001020408102040800102040842\r
-:1000C0001020408001020408102040800202020239\r
-:1000D00002020202040404040404040403030303EC\r
-:1000E00003030303010101010101010100002200DA\r
-:1000F000250028002B0000002100240027002A00F2\r
-:10010000CA05811D2D2C2092DC3911241FBECFEF92\r
-:10011000D0E4DEBFCDBF11E0A0E0B1E0EAE6F0E65A\r
-:1001200000E00BBF02C007900D92AA3AB107D9F7C1\r
-:1001300029E3AAEAB1E001C01D92AC3DB207E1F7A4\r
-:1001400010E0C3E8D0E004C02197FE010E946E2EAB\r
-:10015000C038D107C9F70E94B02C0C9433300C94EE\r
-:100160000000CF93DF931F92CDB7DEB76983DC0128\r
-:10017000ED91FC910280F381E02D41E050E0BE0161\r
-:100180006F5F7F4F09950F90DF91CF91089508958C\r
-:10019000FC011382128288EE93E0A0E0B0E0848339\r
-:1001A0009583A683B78389E491E09183808380E57A\r
-:1001B000848781E085878AE090E09787868789E059\r
-:1001C00090E0918B808B82E090E0938B828B148AFD\r
-:1001D000158A168A178A118E108E138E128E158E1E\r
-:1001E000148E40E050E0BA01CF010C94872CFC0142\r
-:1001F00077876687518B408B338B228B08950F93C3\r
-:100200001F93CF93DF93EC01062F142F60E08E85B0\r
-:100210000E94622A3C852D8580913039882381F0A7\r
-:100220009FB7F89480913039813081F58DB38093F8\r
-:100230002E394DB380912F39809584238DBB9FBF7C\r
-:100240003CBD2DBD0EBD00000DB407FEFDCF8EB52B\r
-:100250001EBD00000DB407FEFDCF1EB580913039E4\r
-:10026000882351F09FB7F8942091303980912E392E\r
-:10027000213079F48DBB9FBF61E08E850E94622A98\r
-:10028000812FDF91CF911F910F91089590932E3977\r
-:10029000D7CF8FBFF1CF6F7740E00C94FF0068E1BC\r
-:1002A0000C944B0169E10C944B0169E10E944B01F4\r
-:1002B000682F880F770B880B990B0E94FC2C20E08D\r
-:1002C00030E040E85EE30E94AC2D08958F929F924B\r
-:1002D000AF92BF92EF92FF920F931F93CF93DF9352\r
-:1002E0008C016AE10E944B01C82FD0E0C8010E9436\r
-:1002F00055010E94C42C4B017B01F801648975896A\r
-:10030000868997890E94FC2C24ED30E843E45EE462\r
-:100310000E94BF2CBE0187FD12C06D597109F7FE06\r
-:1003200011C0680D791DCB01DF91CF911F910F9105\r
-:10033000FF90EF90BF90AF909F908F900895645A78\r
-:100340007109EDCF072E000C880B990B0E94FC2C35\r
-:1003500020EB32E748E85FE30E94AC2D0E94C42CFA\r
-:10036000E2CFCF93DF93EC0163E10E944B0190E079\r
-:10037000288D398D821B930BDF91CF910895CF93F8\r
-:10038000DF93EC01E881F9810480F581E02D099586\r
-:10039000892B69F0888D998D0196998F888F60E0FF\r
-:1003A000CE010E944B0190E0DF91CF9108958FEF35\r
-:1003B0009FEFFACF0F931F93CF93DF938C0166E0EB\r
-:1003C0000E944B01D82F67E0C8010E944B01C82F43\r
-:1003D00068E0C8010E944B014C2F50E070E060E0E3\r
-:1003E000762F652F542F44276D2B8A019B01082BF4\r
-:1003F000C901B80120E038E448EE51E00E94892E9E\r
-:1004000003E10E942E2FB901CA01DF91CF911F9104\r
-:100410000F9108956DE10E944B0190E034E09595B5\r
-:1004200087953A95E1F78A30910574F4FC01EE0F57\r
-:10043000FF1FE80FF91FE95DFE4F60817181828126\r
-:10044000082E000C990B089560E070E0CB01089530\r
-:100450006CE20C944B01CF93DF93EC0143E861E82D\r
-:100460000E94FF0062E1CE010E944B0183FD03C0A8\r
-:100470000E945B28F7CF48E062E9CE010E94FF00AE\r
-:1004800081E090E0DF91CF910895AF92BF92CF923B\r
-:10049000DF92FF920F931F93CF93DF936C015B0169\r
-:1004A000EA0162E20E944B01F82E90E09C012C0FC1\r
-:1004B0003D1F2F3F310529F020F0CFEFD0E0C81BC2\r
-:1004C000D90B8501AC0EBD1E0A151B0541F0F801C4\r
-:1004D00041918F0160E8C6010E94FF00F5CF4C2FCB\r
-:1004E0004F0D62EAC6010E94FF00CE01DF91CF915D\r
-:1004F0001F910F91FF90DF90CF90BF90AF90089524\r
-:100500000F931F93CF93DF93EC01E881F98104806F\r
-:10051000F581E02D0995892BB1F06DE0CE010E94A7\r
-:100520004B01082F60E0CE010E944B01182F402F95\r
-:100530006DE8CE010E94FF00812F90E0DF91CF9106\r
-:100540001F910F9108958FEF9FEFF8CFCF93DF9317\r
-:10055000EC017D8F6C8F8A899B89672BA1F160E00C\r
-:100560000E94262A40E060ECCE010E94FF008A89AA\r
-:100570009B8982309105D9F08A309105D1F00B9793\r
-:10058000D1F481E00E9435268A899B8982309105C9\r
-:10059000A1F08A30910599F00B9799F481E043E03E\r
-:1005A00050E06AEA75E0DF91CF910C94702682E00A\r
-:1005B000E9CF80E0E7CF8FEFE5CF82E0F0CF80E0BA\r
-:1005C000EECF8FEFECCF82309105B9F08A309105F4\r
-:1005D000B1F00B97B1F481E00E949F268A899B8934\r
-:1005E0008230910581F08A30910579F00B9779F48A\r
-:1005F00081E0DF91CF910C94542682E0EDCF80E032\r
-:10060000EBCF8FEFE9CF82E0F4CF80E0F2CF8FEF36\r
-:10061000F0CF41E861E80C94FF0040E861E80C94F9\r
-:10062000FF000E940D030C942126AF92BF92CF923F\r
-:10063000DF92EF92FF920F931F93CF93DF93EC0122\r
-:10064000DB01CA018C8B9D8BAE8BBF8B6A017B015A\r
-:10065000FF0CCC08DC2C76019A01AB016C2D7C2DB3\r
-:100660008C2D9C2D03E10E94132FA12CE8E4BE2EBB\r
-:10067000F8EECF2EDD24D394E12CF12C00E010E035\r
-:100680000E94B62E122FC22ED32E7A01A701662702\r
-:10069000772766E8CE010E94FF0077276F2D5E2D39\r
-:1006A0004D2D67E8CE010E94FF00412F68E8CE0182\r
-:1006B000DF91CF911F910F91FF90EF90DF90CF903E\r
-:1006C000BF90AF900C94FF00CF92DF92EF92FF9219\r
-:1006D000CF93DF93EC016A017B0161E08E850E947C\r
-:1006E000262A61E08E850E94622A888999898F3F37\r
-:1006F0009807B9F061E00E94262A60E088890E948C\r
-:10070000622A6AE070E080E090E00E94D72861E011\r
-:1007100088890E94622A6AE070E080E090E00E948E\r
-:10072000D7280E94EB2562E4CE010E944B01823162\r
-:1007300091F5CE010E940D03B701A601CE010E94E2\r
-:10074000150340E06EE8CE010E94FF0040E06FE834\r
-:10075000CE010E94FF006CE0CE010E944B01482FA9\r
-:1007600043606CE8CE010E94FF0044E066EACE01DF\r
-:100770000E94FF0040E869E8CE010E94FF00CE0120\r
-:100780000E94090381E090E0DF91CF91FF90EF900C\r
-:10079000DF90CF90089590E080E0F6CFCF92DF9287\r
-:1007A000EF92FF92CF93DF93EC016EE10E944B0139\r
-:1007B000F82ECE010E940A02F2942FE0F222CC24FD\r
-:1007C000C394D12C02C0CC0CDD1CFA94E2F70D2CA2\r
-:1007D000000CEE08FF0828EE33E040E050E00E94F5\r
-:1007E0004F2EC701B6010E944F2E21313105410520\r
-:1007F00051055CF04CE066EACE01DF91CF91FF90AD\r
-:10080000EF90DF90CF900C94FF0044E0F4CF0F9373\r
-:100810001F93CF93DF938C01EB016630710534F0A9\r
-:100820006D30710534F56630710529F545EC61EBE5\r
-:10083000C8010E94FF004CE067EBC8010E94FF0066\r
-:10084000C6E0D0E06EE1C8010E944B018F7094E0D9\r
-:10085000CC0FDD1F9A95E1F74C2F482B6EE9C801AC\r
-:100860000E94FF00C801DF91CF911F910F910C945E\r
-:10087000CE03CCE0D0E043EC61EBC8010E94FF0066\r
-:100880004AE067EBC8010E94FF00DCCFCF92DF9205\r
-:10089000EF92FF920F931F93CF93DF938C01CB01C5\r
-:1008A000BA010E94FC2C6B017C0120E030EC43EF8C\r
-:1008B00055E40E94BF2CD0E0C0E018160CF065C0D3\r
-:1008C00020E030E842E256E4C701B6010E94BF2CA6\r
-:1008D000C1E0D0E018160CF058C020E030EC43E73F\r
-:1008E00056E4C701B6010E94BF2CC2E0D0E0181642\r
-:1008F0000CF04BC020E030E842EA56E4C701B601F4\r
-:100900000E94BF2CC3E0D0E018160CF03EC020E0DF\r
-:1009100034E244EF56E4C701B6010E94BF2CC4E0A4\r
-:10092000D0E0181694F520E034EE42E257E4C70117\r
-:10093000B6010E94BF2CC5E0D0E0181634F520E0C7\r
-:1009400034E244E757E4C701B6010E94BF2CC6E079\r
-:10095000D0E01816D4F420E034E244EF57E4C701A5\r
-:10096000B6010E94BF2CC7E0D0E0181674F420E056\r
-:1009700034E244E758E4C701B6010E94BF2CC9E045\r
-:10098000D0E0181614F0C8E0D0E06DE1C8010E9474\r
-:100990004B018F7094E0CC0FDD1F9A95E1F74C2F3F\r
-:1009A000482B6DE9C8010E94FF00C801DF91CF917B\r
-:1009B0001F910F91FF90EF90DF90CF900C94CE039A\r
-:1009C0000F931F93CF93DF938C01EB016930710577\r
-:1009D00014F0C8E0D0E06DE1C8010E944B01C530C1\r
-:1009E000D10514F4C5E0D0E02497CC0FDD1F482FCB\r
-:1009F000417F4C2B6DE9C801DF91CF911F910F9181\r
-:100A00000C94FF00CF93DF93EC016EE10E944B0149\r
-:100A1000482F44606EE9CE01DF91CF910C94FF0026\r
-:100A2000CF93DF93EC011B8E1A8E6DE10E944B0178\r
-:100A3000482F4E7F6DE9CE01DF91CF910C94FF00DE\r
-:100A4000CF93DF93EC0181E090E09B8F8A8F6DE183\r
-:100A5000CE010E944B01482F41606DE9CE01DF912C\r
-:100A6000CF910C94FF000F931F93CF93DF93EC0172\r
-:100A70008B010E940903CE01012B99F00E942005F1\r
-:100A800040E06DE8CE010E94FF0040E062EACE0146\r
-:100A90000E94FF0081E090E0DF91CF911F910F91C4\r
-:100AA00008950E941005ECCF0F931F93CF93DF930F\r
-:100AB000EC018B011616170684F40E942005402FC6\r
-:100AC00062EACE010E94FF0045E861E8CE01DF91B5\r
-:100AD000CF911F910F910C94FF000E941005F4CF4D\r
-:100AE0000F931F93CF93DF93EC0162E10E944B01C0\r
-:100AF000182F482F62E9CE010E94FF0015FD25C086\r
-:100B0000198E188E8A8D9B8D62E2892B09F463E120\r
-:100B1000CE010E944B01082F10E060E1CE010E943F\r
-:100B20004B01482F6DE8CE010E94FF00EC8DFD8D3A\r
-:100B3000309711F0C801099540E06DE8CE01DF91D2\r
-:100B4000CF911F910F910C94FF00DF91CF911F91D6\r
-:100B50000F9108958AEA91E00C947005452B61F499\r
-:100B6000AB016F30710514F04EE050E057FD40E0EE\r
-:100B7000406769E80C94FF00AB016231710514F025\r
-:100B800041E150E04230510514F442E050E042505F\r
-:100B90004068EFCF8AEA91E00C94C8002F923F9210\r
-:100BA0004F925F926F927F928F929F92AF92BF927D\r
-:100BB000CF92DF92EF92FF920F931F93CF93DF9329\r
-:100BC000CDB7DEB7C857D1090FB6F894DEBF0FBE58\r
-:100BD000CDBFE9969FAF8EAFE9971A01DC01189659\r
-:100BE0000D911D912D913C911B9761960CAF1DAFFE\r
-:100BF0002EAF3FAF61971C960D911D912D913C91A9\r
-:100C00001F9709831A832B833C8350960D911D9166\r
-:100C10002D913C9153970D831E832F8338875496D3\r
-:100C20000D911D912D913C91579709871A872B871C\r
-:100C30003C87E6966FAFE697E7967FAFE797A85AAF\r
-:100C4000BF4F6396BFAFAEAF6397FC01E45AFF4F4F\r
-:100C50006596FFAFEEAF65978C01005A1F4F679600\r
-:100C60001FAF0EAF67979C012C593F4F69963FAF5E\r
-:100C70002EAF699788599F4F6B969FAF8EAF6B973A\r
-:100C8000E996AEADBFADE997A459BF4F6D96BFAF22\r
-:100C9000AEAF6D97E996EEADFFADE997E059FF4F26\r
-:100CA0006F96FFAFEEAF6F97E9960EAD1FADE99768\r
-:100CB0000C581F4FA1961FAF0EAFA197E9962EAD0E\r
-:100CC0003FADE99728583F4FA3963FAF2EAFA3976C\r
-:100CD000E9968EAD9FADE99784589F4FA5969FAF3B\r
-:100CE0008EAFA597E996AEADBFADE997A058BF4FBF\r
-:100CF000A796BFAFAEAFA797E996EEADFFADE99768\r
-:100D0000EC57FF4FA996FFAFEEAFA997E9960EAD4E\r
-:100D10001FADE99708571F4FAB961FAF0EAFAB97AC\r
-:100D2000E9962EAD3FADE99724573F4FAD963FAFC3\r
-:100D30002EAFAD97E9968EAD9FADE99780579F4F47\r
-:100D4000AF969FAF8EAFAF97E996AEADBFADE997C7\r
-:100D5000AC56BF4FE196BFAFAEAFE197E796AEADF1\r
-:100D6000BFADE7970D911D912D913C910D871E8789\r
-:100D70002F87388B6396EEADFFAD639700831183A9\r
-:100D80002283338361968CAD9DADAEADBFAD6197CF\r
-:100D900088589B45A549B842800F911FA21FB31FD9\r
-:100DA0004D815E816F81788509851A852B853C850B\r
-:100DB000402751276227732709811A812B813C81A3\r
-:100DC000402351236223732309851A852B853C8593\r
-:100DD0004027512762277327840F951FA61FB71F2F\r
-:100DE0008C019D0147E0000F111F221F331F4A9500\r
-:100DF000D1F7AC01BD01E9E1769567955795479527\r
-:100E0000EA95D1F7402B512B622B732B89819A8164\r
-:100E1000AB81BC81480F591F6A1F7B1FE796AEAD9F\r
-:100E2000BFADE79714960D911D912D913C911797A9\r
-:100E3000098B1A8B2B8B3C8B6596EEADFFAD6597BE\r
-:100E4000008311832283338389859A85AB85BC8592\r
-:100E50008A5A9844A843B741800F911FA21FB31F1D\r
-:100E6000C980DA80EB80FC800D811E812F8138855E\r
-:100E7000C026D126E226F326C422D522E622F72276\r
-:100E8000C026D126E226F3268C0D9D1DAE1DBF1D6A\r
-:100E90008C019D01FCE0000F111F221F331FFA95EA\r
-:100EA000D1F76C017D01A4E1F694E794D794C7943F\r
-:100EB000AA95D1F7C02AD12AE22AF32AC40ED51E58\r
-:100EC000E61EF71EE796AEADBFADE79718968D917B\r
-:100ED0009D910D90BC91A02D8D8B9E8BAF8BB88F6B\r
-:100EE0006796EEADFFAD679780839183A283B3834E\r
-:100EF0008D819E81AF81B88585529F48AF4DBB4D96\r
-:100F00000D891E892F89388D800F911FA21FB31F55\r
-:100F100049805A806B807C80442655266626772639\r
-:100F20004C205D206E207F2009811A812B813C811D\r
-:100F30004026512662267326840D951DA61DB71DD9\r
-:100F40008C019D01EFE03695279517950795EA9559\r
-:100F5000D1F72C013D01F1E1440C551C661C771CB6\r
-:100F6000FA95D1F7402A512A622A732A4C0C5D1C4B\r
-:100F70006E1C7F1CE796AEADBFADE7971C968D91BA\r
-:100F80009D910D90BC91A02D898F9A8FAB8FBC8FB6\r
-:100F90006996EEADFFAD699780839183A283B38399\r
-:100FA00089819A81AB81BC8182519143A244BE4325\r
-:100FB000098D1A8D2B8D3C8D800F911FA21FB31FA1\r
-:100FC0008A019B010C251D252E253F250421152175\r
-:100FD000262137210427152726273727800F911F21\r
-:100FE000A21FB31F4C015D011AE0B694A794979419\r
-:100FF00087941A95D1F78C019D0186E1000F111F8E\r
-:10100000221F331F8A95D1F7082919292A293B293C\r
-:10101000040D151D261D371DE796AEADBFADE79734\r
-:1010200050968D919D910D90BC91A02D8D8F9E8F8E\r
-:10103000AF8FB8A36B96EEADFFAD6B9780839183B6\r
-:10104000A283B3838155904FA348BA40840F951F64\r
-:10105000A61FB71FB701A60144255525662577258C\r
-:1010600040235123622373234C255D256E257F2564\r
-:10107000840F951FA61FB71F4C015D0147E0880C28\r
-:10108000991CAA1CBB1C4A95D1F7AC01BD01E9E132\r
-:101090007695679557954795EA95D1F74829592947\r
-:1010A0006A297B29400F511F621F731FE796AEAD5F\r
-:1010B000BFADE79754968D919D910D90BC91A02D59\r
-:1010C00089A39AA3ABA3BCA36D96EEADFFAD6D97BC\r
-:1010D00080839183A283B383865D9943A847B84BED\r
-:1010E0008C0D9D1DAE1DBF1D73016201C026D12652\r
-:1010F000E226F326C422D522E622F722C424D524F0\r
-:10110000E624F7248C0D9D1DAE1DBF1D4C015D0115\r
-:10111000FCE0880C991CAA1CBB1CFA95D1F76C0149\r
-:101120007D01A4E1F694E794D794C794AA95D1F7EA\r
-:10113000C828D928EA28FB28C40ED51EE61EF71EAB\r
-:10114000E796AEADBFADE79758968D919D910D9006\r
-:10115000BC91A02D8DA39EA3AFA3B8A76F96EEADB3\r
-:10116000FFAD6F9780839183A283B3838D5E994B8C\r
-:10117000AF4CB745840D951DA61DB71D280139013B\r
-:1011800044265526662677264C205D206E207F203B\r
-:101190004026512662267326840D951DA61DB71D77\r
-:1011A0004C015D01EFE0B694A79497948794EA957B\r
-:1011B000D1F72C013D01F1E1440C551C661C771C54\r
-:1011C000FA95D1F7482859286A287B284C0C5D1CD1\r
-:1011D0006E1C7F1CE796AEADBFADE7975C968D9118\r
-:1011E0009D910D90BC91A02D89A79AA7ABA7BCA7F4\r
-:1011F000A196EEADFFADA19780839183A283B383C7\r
-:101200008F5F9A46A94BB240800F911FA21FB31F58\r
-:101210008A019B010C251D252E253F250421152122\r
-:10122000262137210427152726273727800F911FCE\r
-:10123000A21FB31F4C015D011AE0B694A7949794C6\r
-:1012400087941A95D1F78C019D0186E1000F111F3B\r
-:10125000221F331F8A95D1F7082919292A293B29EA\r
-:10126000040D151D261D371DE796AEADBFADE797E2\r
-:1012700090968D919D910D90BC91A02D8DA79EA7CC\r
-:10128000AFA7B8ABA396EEADFFADA39780839183D4\r
-:10129000A283B38388529746AF47B649840F951F00\r
-:1012A000A61FB71FB701A60144255525662577253A\r
-:1012B00040235123622373234C255D256E257F2512\r
-:1012C000840F951FA61FB71F4C015D0147E0880CD6\r
-:1012D000991CAA1CBB1C4A95D1F7AC01BD01E9E1E0\r
-:1012E0007695679557954795EA95D1F748295929F5\r
-:1012F0006A297B29400F511F621F731FE796AEAD0D\r
-:10130000BFADE79794968D919D910D90BC91A02DC6\r
-:1013100089AB9AABABABBCABA596EEADFFADA597D9\r
-:1013200080839183A283B38381559840AB4BB447AC\r
-:101330008C0D9D1DAE1DBF1D73016201C026D126FF\r
-:10134000E226F326C422D522E622F722C424D5249D\r
-:10135000E624F7248C0D9D1DAE1DBF1D4C015D01C3\r
-:10136000FCE0880C991CAA1CBB1CFA95D1F76C01F7\r
-:101370007D01A4E1F694E794D794C794AA95D1F798\r
-:10138000C828D928EA28FB28C40ED51EE61EF71E59\r
-:10139000E796AEADBFADE79798968D919D910D9074\r
-:1013A000BC91A02D8DAB9EABAFABB8AFA796EEAD09\r
-:1013B000FFADA79780839183A283B3838F54944A10\r
-:1013C000A109B109840D951DA61DB71D280139017C\r
-:1013D00044265526662677264C205D206E207F20E9\r
-:1013E0004026512662267326840D951DA61DB71D25\r
-:1013F0004C015D01EFE0B694A79497948794EA9529\r
-:10140000D1F72C013D01F1E1440C551C661C771C01\r
-:10141000FA95D1F7482859286A287B284C0C5D1C7E\r
-:101420006E1C7F1CE796AEADBFADE7979C968D9185\r
-:101430009D910D90BC91A02D89AF9AAFABAFBCAF81\r
-:10144000A996EEADFFADA99780839183A283B38364\r
-:1014500082549842A34AB647800F911FA21FB31F20\r
-:101460008A019B010C251D252E253F2504211521D0\r
-:10147000262137210427152726273727800F911F7C\r
-:10148000A21FB31F4C015D011AE0B694A794979474\r
-:1014900087941A95D1F78C019D0186E1000F111FE9\r
-:1014A000221F331F8A95D1F7082919292A293B2998\r
-:1014B000040D151D261D371DE796AEADBFADE79790\r
-:1014C000D0968D919D910D90BC91A02D21968CAFC1\r
-:1014D0009DAFAEAFBFAF2197AB96EEADFFADAB9773\r
-:1014E00080839183A283B3834C015D01FEED8F1A4B\r
-:1014F000FEEE9F0AFFE6AF0AF4E9BF0A840E951ECE\r
-:10150000A61EB71EB701A6014425552566257725D9\r
-:1015100040235123622373234C255D256E257F25AF\r
-:10152000480D591D6A1D7B1D4A015B0197E0880C1F\r
-:10153000991CAA1CBB1C9A95D1F7E9E17695679591\r
-:1015400057954795EA95D1F7482959296A297B2962\r
-:10155000400F511F621F731FE796AEADBFADE797F7\r
-:10156000D4968D919D910D90BC91A02D25968CAF18\r
-:101570009DAFAEAFBFAF2597AD96EEADFFADAD97CA\r
-:1015800080839183A283B3834C015D01FDE68F1AB2\r
-:10159000FEE89F0AF7E6AF0AF2E0BF0A8C0C9D1C3A\r
-:1015A000AE1CBF1C73016201C026D126E226F326C1\r
-:1015B000C422D522E622F722C424D524E624F72427\r
-:1015C000C80CD91CEA1CFB1C46015701FCE0880C26\r
-:1015D000991CAA1CBB1CFA95D1F7A4E1F694E794D8\r
-:1015E000D794C794AA95D1F7C828D928EA28FB2808\r
-:1015F000C40ED51EE61EF71EE796AEADBFADE7974B\r
-:10160000D8968D919D910D90BC91A02D29968CAF6F\r
-:101610009DAFAEAFBFAF2997AF96EEADFFADAF9721\r
-:1016200080839183A283B3834C015D01F2E78F1A1B\r
-:10163000FCEB9F0AF6E8AF0AF9E5BF0A840C951C9B\r
-:10164000A61CB71C28013901442655266626772694\r
-:101650004C205D206E207F20402651266226732676\r
-:10166000480C591C6A1C7B1C53014201BFE0B69414\r
-:10167000A79497948794BA95D1F781E1440C551CAF\r
-:10168000661C771C8A95D1F7482859286A287B2838\r
-:101690004C0C5D1C6E1C7F1CE796AEADBFADE79792\r
-:1016A000DC968D919D910D90BC91A02D2D968CAFC7\r
-:1016B0009DAFAEAFBFAF2D97E196EEADFFADE19719\r
-:1016C00080839183A283B3834C015D01FFED8F1A68\r
-:1016D000F7EF9F0AFBE4AF0AF6EBBF0A800E911EFC\r
-:1016E000A21EB31E8A019B010C251D252E253F2518\r
-:1016F00004211521262137210427152726273727DE\r
-:10170000080D191D2A1D3B1D480159019AE0B69488\r
-:10171000A794979487949A95D1F7E6E1000F111F4B\r
-:10172000221F331FEA95D1F7082919292A293B29B5\r
-:10173000040D151D261D371D89889A88AB88BC8825\r
-:101740008EE9881A8AED980A81EEA80A89E0B80A1B\r
-:10175000840E951EA61EB71EB301A2014027512775\r
-:10176000622773274C215D216E217F214425552559\r
-:1017700066257725480D591D6A1D7B1D4A015B01B1\r
-:10178000F5E0880C991CAA1CBB1CFA95D1F7ABE1BB\r
-:101790007695679557954795AA95D1F74829592980\r
-:1017A0006A297B29400F511F621F731F8DA09EA0C5\r
-:1017B000AFA0B8A490EC891A9CE4990A9FEBA90AFF\r
-:1017C0009FE3B90A8C0C9D1CAE1CBF1C68017901FB\r
-:1017D000C426D526E626F726C420D520E620F72005\r
-:1017E000C026D126E226F326C80CD91CEA1CFB1C15\r
-:1017F00046015701B9E0880C991CAA1CBB1CBA957C\r
-:10180000D1F787E1F694E794D794C7948A95D1F7F6\r
-:10181000C828D928EA28FB28C40ED51EE61EF71EC4\r
-:1018200089AC9AACABACBCACAFEA8A1AA5EA9A0A0E\r
-:10183000A1EAAA0AA9EDBA0A840C951CA61CB71C39\r
-:101840002A013B014C245D246E247F244022512236\r
-:10185000622273224426552666267726480C591C98\r
-:101860006A1C7B1C530142019EE0880C991CAA1C37\r
-:10187000BB1C9A95D1F7E2E176946794579447940C\r
-:10188000EA95D1F7482859286A287B284C0C5D1C1A\r
-:101890006E1C7F1C8D849E84AF84B888B6E58B1A3D\r
-:1018A000B8E39B0AB9E4AB0AB6E1BB0A800E911E0D\r
-:1018B000A21EB31E9701860104251525262537256E\r
-:1018C00004231523262337230C251D252E253F25EC\r
-:1018D000080D191D2A1D3B1D48015901FCE0B69455\r
-:1018E000A79497948794FA95D1F7A4E1000F111F5C\r
-:1018F000221F331FAA95D1F7082919292A293B2924\r
-:10190000040D151D261D371D89A09AA0ABA0BCA0F3\r
-:10191000E3EA8E1AEFEE9E0AE0EDAE0AE9E2BE0AB5\r
-:10192000840E951EA61EB71EB301A20140275127A3\r
-:10193000622773274C215D216E217F214425552587\r
-:1019400066257725480D591D6A1D7B1D4A015B01DF\r
-:10195000B5E0880C991CAA1CBB1CBA95D1F78BE189\r
-:1019600076956795579547958A95D1F748295929CE\r
-:101970006A297B29400F511F621F731F8DA89EA8E3\r
-:10198000AFA8B8ACFDEA8F1AFBEE9F0AFBEBAF0ADB\r
-:10199000FDEFBF0A8C0C9D1CAE1CBF1C68017901B9\r
-:1019A000C426D526E626F726C420D520E620F72033\r
-:1019B000C026D126E226F326C80CD91CEA1CFB1C43\r
-:1019C0004601570199E0880C991CAA1CBB1C9A95EA\r
-:1019D000D1F7E7E1F694E794D794C794EA95D1F765\r
-:1019E000C828D928EA28FB28C40ED51EE61EF71EF3\r
-:1019F0002D968CAC9DACAEACBFAC2D978FE7881A02\r
-:101A000089E1980A8EE5A80A87E2B80A840C951C39\r
-:101A1000A61CB71C2A013B014C245D246E247F24A4\r
-:101A200040225122622273224426552666267726BA\r
-:101A3000480C591C6A1C7B1C53014201FEE0880CB7\r
-:101A4000991CAA1CBB1CFA95D1F7A2E17694679465\r
-:101A500057944794AA95D1F7482859286A287B2893\r
-:101A60004C0C5D1C6E1C7F1C8D8C9E8CAF8CB8A0AA\r
-:101A700098E3891A94E0990A9CE2A90A98E1B90AC4\r
-:101A8000800E911EA21EB31E970186010425152506\r
-:101A90002625372504231523262337230C251D252A\r
-:101AA0002E253F25080D191D2A1D3B1D48015901F2\r
-:101AB000BCE0B694A79497948794BA95D1F784E143\r
-:101AC000000F111F221F331F8A95D1F708291929EA\r
-:101AD0002A293B29040D151D261D371D89A89AA802\r
-:101AE000ABA8BCA8AAE18A1AA2E39A0AAEE1AA0AA4\r
-:101AF000AEEDBA0A840E951EA61EB71EB301A20152\r
-:101B000040275127622773274C215D216E217F21B9\r
-:101B10004425552566257725480D591D6A1D7B1DD1\r
-:101B20004A015B0195E0880C991CAA1CBB1C9A9584\r
-:101B3000D1F7EBE17695679557954795EA95D1F7FB\r
-:101B4000482959296A297B29400F511F621F731F99\r
-:101B500029968CAC9DACAEACBFAC2997BAE28B1A7F\r
-:101B6000B8EF9B0AB8ECAB0ABCE3BB0A8C0C9D1C1B\r
-:101B7000AE1CBF1C68017901C426D526E626F726CF\r
-:101B8000C420D520E620F720C026D126E226F32661\r
-:101B9000C80CD91CEA1CFB1C46015701F9E0880C53\r
-:101BA000991CAA1CBB1CFA95D1F7A7E1F694E794FF\r
-:101BB000D794C794AA95D1F7C828D928EA28FB2832\r
-:101BC000C40ED51EE61EF71E898C9A8CAB8CBC8C7D\r
-:101BD000E9E78E1AE2EF9E0AEAE2AE0AEBE0BE0AFD\r
-:101BE000840C951CA61CB71C2A013B014C245D24C7\r
-:101BF0006E247F24402251226222732244265526DD\r
-:101C000066267726480C591C6A1C7B1C530142012E\r
-:101C1000BEE0880C991CAA1CBB1CBA95D1F782E1C6\r
-:101C200076946794579447948A95D1F74828592811\r
-:101C30006A287B284C0C5D1C6E1C7F1C8DA49EA406\r
-:101C4000AFA4B8A8F3E18F1AFBEE9F0AF5EAAF0A3A\r
-:101C5000FAEBBF0A800E911EA21EB31E97018601E9\r
-:101C60000425152526253725042315232623372368\r
-:101C70000C251D252E253F25080D191D2A1D3B1D50\r
-:101C8000480159019CE0B694A794979487949A953B\r
-:101C9000D1F7E4E1000F111F221F331FEA95D1F79E\r
-:101CA000082919292A293B29040D151D261D371D30\r
-:101CB00025968CAC9DACAEACBFAC25978BEF881A4B\r
-:101CC00086E1980A8CE1A80A86E5B80A840E951E7A\r
-:101CD000A61EB71EB301A201402751276227732712\r
-:101CE0004C215D216E217F214425552566257725D0\r
-:101CF000480D591D6A1D7B1D4A015B01F5E0880CEA\r
-:101D0000991CAA1CBB1CFA95D1F7ABE17695679597\r
-:101D100057954795AA95D1F7482959296A297B29CA\r
-:101D2000400F511F621F731F8D889E88AF88B88C2B\r
-:101D300098E0891A9CE5990A90E1A90A93E0B90A0A\r
-:101D40008C0C9D1CAE1CBF1C68017901C426D526D5\r
-:101D5000E626F726C420D520E620F720C026D12687\r
-:101D6000E226F326C80CD91CEA1CFB1C46015701CD\r
-:101D7000B9E0880C991CAA1CBB1CBA95D1F787E165\r
-:101D8000F694E794D794C7948A95D1F7C828D928B0\r
-:101D9000EA28FB28C40ED51EE61EF71E89A49AA4C5\r
-:101DA000ABA4BCA4A7E28A1AADEF9A0AA0E9AA0ADA\r
-:101DB000A8E9BA0A840C951CA61CB71C2A013B0191\r
-:101DC0004C245D246E247F244022512262227322FF\r
-:101DD0004426552666267726480C591C6A1C7B1C0F\r
-:101DE000530142019EE0880C991CAA1CBB1C9A95C9\r
-:101DF000D1F7E2E17694679457944794EA95D1F746\r
-:101E0000482859286A287B284C0C5D1C6E1C7F1CB6\r
-:101E10004601570184249524A624B72421968CAD2D\r
-:101E20009DADAEADBFAD21978657934BA54DB24743\r
-:101E3000800F911FA21FB31F950184010423152356\r
-:101E4000262337230C251D252E253F25800F911F86\r
-:101E5000A21FB31F8C019D01FCE0369527951795B5\r
-:101E60000795FA95D1F7E5960CAF1DAF2EAF3FAFB2\r
-:101E7000E5978C019D01A4E1000F111F221F331F64\r
-:101E8000AA95D1F7E5968CAD9DADAEADBFADE5970A\r
-:101E9000082B192B2A2B3B2B040D151D261D371D36\r
-:101EA00089A19AA1ABA1BCA18E5B964CA540B109BA\r
-:101EB000840F951FA61FB71F80269126A226B32642\r
-:101EC000880E991EAA1EBB1ED501C40144E0880FCE\r
-:101ED000991FAA1FBB1F4A95D1F75CE1B694A7943E\r
-:101EE000979487945A95D1F7882A992AAA2ABB2AC7\r
-:101EF000800E911EA21EB31E4DA55EA56FA578A9EA\r
-:101F00004F5759406E4878474C0D5D1D6E1D7F1D23\r
-:101F100073016201C026D126E226F326C824D92403\r
-:101F2000EA24FB24C40ED51EE61EF71EB701A60147\r
-:101F3000EBE0440F551F661F771FEA95D1F7F5E1D7\r
-:101F4000F694E794D794C794FA95D1F7C42AD52A82\r
-:101F5000E62AF72AC80CD91CEA1CFB1C49AD5AAD6D\r
-:101F60006BAD7CAD4E5D5E4962467249440E551EB6\r
-:101F7000661E771EB901A801482559256A257B25CB\r
-:101F80004C255D256E257F25440E551E661E771E49\r
-:101F9000D20123013D014C0C5D1C6E1C7F1C299657\r
-:101FA0004CAD5DAD6EAD7FAD2997445F574C6A4136\r
-:101FB0007240400F511F621F731F950184010C2551\r
-:101FC0001D252E253F250425152526253725040FFB\r
-:101FD000151F261F371FB901A801A9E0769567953F\r
-:101FE00057954795AA95D1F7B7E1000F111F221F0A\r
-:101FF000331FBA95D1F7042B152B262B372B040D45\r
-:10200000151D261D371D49895A896B897C894C5BB2\r
-:10201000554161447B45480D591D6A1D7B1D460194\r
-:10202000570184249524A624B72480269126A2262D\r
-:10203000B326840E951EA61EB71EB501A40184E02A\r
-:10204000440F551F661F771F8A95D1F79CE1B69400\r
-:10205000A794979487949A95D1F7842A952AA62ACB\r
-:10206000B72A800E911EA21EB31E4D8D5E8D6F8D00\r
-:1020700078A1475550436142744BC40ED51EE61EED\r
-:10208000F71EB301A2014027512762277327482575\r
-:1020900059256A257B254C0D5D1D6E1D7F1DDB01BD\r
-:1020A000CA01EBE0880F991FAA1FBB1FEA95D1F761\r
-:1020B000F5E17695679557954795FA95D1F7482BB1\r
-:1020C000592B6A2B7B2B480D591D6A1D7B1DC9A4FA\r
-:1020D000DAA4EBA4FCA4B0EACB1AB4EBDB0AB4E4B8\r
-:1020E000EB0AB9E0FB0A4C0C5D1C6E1C7F1C6801FE\r
-:1020F0007901C824D924EA24FB24C426D526E6265F\r
-:10210000F7264C0C5D1C6E1C7F1CF20123013F0165\r
-:10211000440E551E661E771ECDA8DEA8EFA8F8ACAB\r
-:10212000F0E9CF1AF3E4DF0AF0E4EF0AF1E4FF0A82\r
-:10213000C00ED11EE21EF31E95018401042715274F\r
-:102140002627372704251525262537250C0D1D1D87\r
-:102150002E1D3F1D68017901A9E0F694E794D794FC\r
-:10216000C794AA95D1F7B7E1000F111F221F331FA3\r
-:10217000BA95D1F70C291D292E293F29040D151DCB\r
-:10218000261D371D2596CCACDDACEEACFFAC2597FB\r
-:102190008AE3C81A81E8D80A84E6E80A87EDF80AD3\r
-:1021A000C80CD91CEA1CFB1C4A015B018424952441\r
-:1021B000A624B72480269126A226B3268C0C9D1C2B\r
-:1021C000AE1CBF1C7501640184E0CC0CDD1CEE1C50\r
-:1021D000FF1C8A95D1F79CE1B694A79497948794B5\r
-:1021E0009A95D1F78C289D28AE28BF28800E911E85\r
-:1021F000A21EB31ECD84DE84EF84F88896E0C91A4F\r
-:1022000098EDD90A9EE5E90A95E1F90AC40ED51EB2\r
-:10221000E61EF71EB301A20140275127622773274C\r
-:10222000482559256A257B254C0D5D1D6E1D7F1D9A\r
-:102230006A017B01EBE0CC0CDD1CEE1CFF1CEA9577\r
-:10224000D1F7F5E17695679557954795FA95D1F7CA\r
-:102250004C295D296E297F29480D591D6A1D7B1D5A\r
-:10226000C98CDA8CEB8CFC8CABE7CA1AAFECDA0ABF\r
-:10227000A0E1EA0AABE2FA0A4C0C5D1C6E1C7F1C62\r
-:1022800068017901C824D924EA24FB24C426D52670\r
-:10229000E626F7264C0C5D1C6E1C7F1CF201230108\r
-:1022A0003F01440E551E661E771ECDA0DEA0EFA096\r
-:1022B000F8A4FBEFCF1AF2EEDF0AF7E7EF0AFBEF25\r
-:1022C000FF0AC00ED11EE21EF31E950184010427F1\r
-:1022D00015272627372704251525262537250C0DF4\r
-:1022E0001D1D2E1D3F1D68017901A9E0F694E7949C\r
-:1022F000D794C794AA95D1F7B7E1000F111F221FF9\r
-:10230000331FBA95D1F70C291D292E293F29040D19\r
-:10231000151D261D371DC9A8DAA8EBA8FCA887EC57\r
-:10232000C81A8FE2D80A8BE2E80A86E2F80AC80CDB\r
-:10233000D91CEA1CFB1C4A015B0184249524A624B9\r
-:10234000B72480269126A226B3268C0C9D1CAE1C99\r
-:10235000BF1C7501640184E0CC0CDD1CEE1CFF1C6D\r
-:102360008A95D1F79CE1B694A794979487949A950F\r
-:10237000D1F78C289D28AE28BF28800E911EA21E62\r
-:10238000B31E2196CCACDDACEEACFFAC21979BE14B\r
-:10239000C91A96E6D90A94E2E90A99E1F90AC40E43\r
-:1023A000D51EE61EF71EB301A20140275127622762\r
-:1023B0007327482559256A257B254C0D5D1D6E1D0B\r
-:1023C0007F1D6A017B01EBE0CC0CDD1CEE1CFF1CC9\r
-:1023D000EA95D1F7F5E17695679557954795FA9582\r
-:1023E000D1F74C295D296E297F29480D591D6A1D99\r
-:1023F0007B1D2D96CCACDDACEEACFFAC2D97A8E0F0\r
-:10240000CA1AA3E8DA0AADE5EA0AA0EEFA0A4C0C09\r
-:102410005D1C6E1C7F1C68017901C824D924EA2444\r
-:10242000FB24C426D526E626F7264C0C5D1C6E1C24\r
-:102430007F1CF20123013F01440E551E661E771ECC\r
-:10244000CD88DE88EF88F88CFBE9CF1AF9EADF0A3D\r
-:10245000F3E5EF0AFBE3FF0AC00ED11EE21EF31EF6\r
-:1024600095018401042715272627372704251525DC\r
-:10247000262537250C0D1D1D2E1D3F1D68017901D8\r
-:10248000A9E0F694E794D794C794AA95D1F7B7E159\r
-:10249000000F111F221F331FBA95D1F70C291D29D8\r
-:1024A0002E293F29040D151D261D371DCD84DE84E0\r
-:1024B000EF84F8888CEBC81A8DEDD80A86EDE80A0F\r
-:1024C0008BE0F80AC80CD91CEA1CFB1C4A015B0112\r
-:1024D00080949094A094B094802A912AA22AB32A3E\r
-:1024E00084249524A624B7248C0C9D1CAE1CBF1CF0\r
-:1024F0007501640186E0CC0CDD1CEE1CFF1C8A9586\r
-:10250000D1F79AE1B694A794979487949A95D1F7C6\r
-:102510008C289D28AE28BF28800E911EA21EB31EB7\r
-:10252000C9A4DAA4EBA4FCA499E6C91AD10895EDD4\r
-:10253000E90A9CEBF90AC40ED51EE61EF71EB3018C\r
-:10254000A2014095509560957095482959296A29AE\r
-:102550007B2940275127622773274C0D5D1D6E1D77\r
-:102560007F1D6A017B01EAE0CC0CDD1CEE1CFF1C28\r
-:10257000EA95D1F7F6E17695679557954795FA95DF\r
-:10258000D1F74C295D296E297F29480D591D6A1DF7\r
-:102590007B1D2996CCACDDACEEACFFAC2997A9E550\r
-:1025A000CA1AACEDDA0AABE6EA0AA4E5FA0AC40CE8\r
-:1025B000D51CE61CF71C2801390140945094609406\r
-:1025C0007094442A552A662A772A482459246A2472\r
-:1025D0007B244C0C5D1C6E1C7F1C73016201AFE000\r
-:1025E000CC0CDD1CEE1CFF1CAA95D1F7B1E1769452\r
-:1025F000679457944794BA95D1F74C285D286E2874\r
-:102600007F28440E551E661E771EC9A0DAA0EBA0D7\r
-:10261000FCA0B7ECCB1ABFE5DB0ABCE6EB0AB3E0E3\r
-:10262000FB0AC00ED11EE21EF31E95018401009527\r
-:102630001095209530950429152926293729042736\r
-:102640001527262737270C0D1D1D2E1D3F1D680140\r
-:1026500079018BE0F694E794D794C7948A95D1F7E3\r
-:1026600095E1000F111F221F331F9A95D1F70C29F6\r
-:102670001D292E293F29040D151D261D371D2196C4\r
-:10268000CCACDDACEEACFFAC2197EDE3CE1AE6EAC4\r
-:10269000DE0AE4EAEE0AEAE9FE0AC80CD91CEA1CE2\r
-:1026A000FB1C4A015B0180949094A094B094802A12\r
-:1026B000912AA22AB32A84249524A624B7248C0C18\r
-:1026C0009D1CAE1CBF1C75016401E6E0CC0CDD1C3A\r
-:1026D000EE1CFF1CEA95D1F7FAE1B694A794979403\r
-:1026E0008794FA95D1F78C289D28AE28BF28800EB4\r
-:1026F000911EA21EB31EC98CDA8CEB8CFC8CFEE6FC\r
-:10270000CF1AF3E3DF0AF3EFEF0AF0E7FF0AC40E94\r
-:10271000D51EE61EF71EB301A201409550956095A7\r
-:102720007095482959296A297B2940275127622712\r
-:1027300073274C0D5D1D6E1D7F1D6A017B01AAE094\r
-:10274000CC0CDD1CEE1CFF1CAA95D1F7B6E17695EA\r
-:10275000679557954795BA95D1F74C295D296E290C\r
-:102760007F29480D591D6A1D7B1DCDA8DEA8EFA845\r
-:10277000F8AC83E8C81A8BE0D80A80E1E80AF108CF\r
-:10278000C40CD51CE61CF71C280139014094509458\r
-:1027900060947094442A552A662A772A482459243A\r
-:1027A0006A247B244C0C5D1C6E1C7F1C730162012F\r
-:1027B0008FE0CC0CDD1CEE1CFF1C8A95D1F791E15B\r
-:1027C00076946794579447949A95D1F74C285D284E\r
-:1027D0006E287F28440E551E661E771EC988DA882B\r
-:1027E000EB88FC889FE2C91A92EAD90A9BE7E90ABA\r
-:1027F0009AE7F90AC00ED11EE21EF31E950184016C\r
-:1028000000951095209530950429152926293729FA\r
-:1028100004271527262737270C0D1D1D2E1D3F1DAC\r
-:1028200068017901EBE0F694E794D794C794EA95B0\r
-:10283000D1F7F5E1000F111F221F331FFA95D1F7D1\r
-:102840000C291D292E293F29040D151D261D371D74\r
-:10285000CDA4DEA4EFA4F8A8A1EBCA1AA1E8DA0A75\r
-:10286000A7E5EA0AA0E9FA0AC80CD91CEA1CFB1C75\r
-:102870004A015B0180949094A094B094802A912A9C\r
-:10288000A22AB32A84249524A624B7248C0C9D1C48\r
-:10289000AE1CBF1C75016401A6E0CC0CDD1CEE1C57\r
-:1028A000FF1CAA95D1F7BAE1B694A79497948794A0\r
-:1028B000BA95D1F78C289D28AE28BF28800E911E8E\r
-:1028C000A21EB31E2D968CAD9DADAEADBFAD2D97A6\r
-:1028D00080529941A34DB140840F951FA61FB71F89\r
-:1028E000B301A201409550956095709548295929EA\r
-:1028F0006A297B294027512762277327840F951F58\r
-:10290000A61FB71F6C017D014AE0CC0CDD1CEE1C3C\r
-:10291000FF1C4A95D1F7AC01BD01E6E176956795BC\r
-:1029200057954795EA95D1F74C295D296E297F295E\r
-:10293000480D591D6A1D7B1D8DA19EA1AFA1B8A593\r
-:102940008C5E9C4BAE4FBC45840D951DA61DB71DDE\r
-:10295000280139014094509460947094442A552A77\r
-:10296000662A772A482459246A247B24840D951DDD\r
-:10297000A61DB71D6C017D01FFE0CC0CDD1CEE1C1B\r
-:10298000FF1CFA95D1F72C013D01A1E176946794E3\r
-:1029900057944794AA95D1F74C285D286E287F2834\r
-:1029A000440E551E661E771E25968CAD9DADAEADB0\r
-:1029B000BFAD25978F559E4EA74FB14B800F911FEE\r
-:1029C000A21FB31F950184010095109520953095A5\r
-:1029D00004291529262937290427152726273727CB\r
-:1029E000800F911FA21FB31F6C017D011BE0F694A5\r
-:1029F000E794D794C7941A95D1F78C019D0185E18E\r
-:102A0000000F111F221F331F8A95D1F70C291D2992\r
-:102A10002E293F29040D151D261D371D8D8D9E8DD8\r
-:102A2000AF8DB8A18E579148AC4AB840880D991D1A\r
-:102A3000AA1DBB1D4A015B0180949094A094B094A0\r
-:102A4000802A912AA22AB32A84249524A624B72472\r
-:102A5000880D991DAA1DBB1D6C017D01E6E0CC0C03\r
-:102A6000DD1CEE1CFF1CEA95D1F74C015D01FAE17B\r
-:102A7000B694A79497948794FA95D1F78C289D28BB\r
-:102A8000AE28BF28800E911EA21EB31E89AD9AAD3E\r
-:102A9000ABADBCAD8B5C9D40A54CB244840F951F83\r
-:102AA000A61FB71FB301A201409550956095709580\r
-:102AB000482959296A297B294027512762277327EA\r
-:102AC000840F951FA61FB71F6C017D014AE0CC0C37\r
-:102AD000DD1CEE1CFF1C4A95D1F7AC01BD01E6E1FF\r
-:102AE0007695679557954795EA95D1F74C295D29D5\r
-:102AF0006E297F29480D591D6A1D7B1D8D899E8970\r
-:102B0000AF89B88D85549D42A842B54D840D951D61\r
-:102B1000A61DB71D2801390140945094609470940B\r
-:102B2000442A552A662A772A482459246A247B2471\r
-:102B3000840D951DA61DB71D6C017D01FFE0CC0C19\r
-:102B4000DD1CEE1CFF1CFA95D1F72C013D01A1E123\r
-:102B50007694679457944794AA95D1F74C285D28AA\r
-:102B60006E287F28440E551E661E771E89A99AA9D5\r
-:102B7000ABA9BCA98F569C42A947B441800F911FB5\r
-:102B8000A21FB31F950184010095109520953095E3\r
-:102B90000429152926293729042715272627372709\r
-:102BA000800F911FA21FB31F6C017D011BE0F694E3\r
-:102BB000E794D794C7941A95D1F78C019D0185E1CC\r
-:102BC000000F111F221F331F8A95D1F70C291D29D1\r
-:102BD0002E293F29040D151D261D371D61968CAD2C\r
-:102BE0009DADAEADBFAD6197880D991DAA1DBB1DF2\r
-:102BF00061968CAF9DAFAEAFBFAF619789819A816F\r
-:102C0000AB81BC81800F911FA21FB31F89839A8360\r
-:102C1000AB83BC830D811E812F813885040D151D6A\r
-:102C2000261D371D0D831E832F83388789859A853E\r
-:102C3000AB85BC85840F951FA61FB71F89879A8710\r
-:102C4000AB87BC87E796AEADBFADE797A05CBF4F43\r
-:102C5000E796BFAFAEAFE797B0E42B1A310811F09B\r
-:102C60000C94AE0661960CAD1DAD2EAD3FAD6197D7\r
-:102C7000E996AEADBFADE99718960D931D932D93D0\r
-:102C80003C931B9709811A812B813C811C960D93E3\r
-:102C90001D932D933C931F970D811E812F813885A5\r
-:102CA00050960D931D932D933C93539709851A8548\r
-:102CB0002B853C8554960D931D932D933C935797EC\r
-:102CC000E7968EAD9FADE797C858DF4F0FB6F894E3\r
-:102CD000DEBF0FBECDBFDF91CF911F910F91FF904F\r
-:102CE000EF90DF90CF90BF90AF909F908F907F90AC\r
-:102CF0006F905F904F903F902F900895CF93DF9308\r
-:102D0000FC0181E093E2A5E4B7E680879187A28782\r
-:102D1000B38789E89BEAADECBFEE84879587A687E9\r
-:102D2000B7878EEF9CEDAAEBB8E9808B918BA28BD5\r
-:102D3000B38B86E794E5A2E3B0E1848B958BA68BF9\r
-:102D4000B78B10821182128213821482158216822E\r
-:102D50001782DF01A85ABF4F80E4ED01982F199226\r
-:102D60009A95E9F77896DF011D928A95E9F7DF9148\r
-:102D7000CF9108958F929F92AF92BF92EF92FF9260\r
-:102D80000F931F93CF93DF93EC017B018A0188811E\r
-:102D90009981AA81BB8170E060E04A015B01880EE5\r
-:102DA000991EAA1EBB1E2FE1B22288829982AA8296\r
-:102DB000BB8288169906AA06BB0668F48C809D80A3\r
-:102DC000AE80BF802FEF821A920AA20AB20A8C82CA\r
-:102DD0009D82AE82BF828F739927AA27BB27009757\r
-:102DE000A105B10579F120E4822E912CA12CB12C02\r
-:102DF000881A990AAA0ABB0A48968C0F9D1F481583\r
-:102E000059056A057B0570F4A801B701DF91CF91E0\r
-:102E10001F910F91FF90EF90BF90AF909F908F9078\r
-:102E20000C940530A401B7010E940530E80CF91C90\r
-:102E30000819190940E450E0BE01685E7F4FCE01D9\r
-:102E40000E94CE050034110548F0A801407CB7016E\r
-:102E5000CE010E94CE057C010F731127A801B70196\r
-:102E6000CE014896D3CFEF92FF920F931F93CF934B\r
-:102E7000DF938C01EB01488159816A817B814F731B\r
-:102E8000552766277727DB01CA010196A11DB11DD1\r
-:102E90004C0F5D1F20E8FA01208F40E450E060E015\r
-:102EA00070E0481B590B6A0B7B0B7E01F8E1EF0EBB\r
-:102EB000F11C4830510561057105A0F4489670E099\r
-:102EC00060E08C0F9D1F0E940E3040E450E0B7017F\r
-:102ED000CE010E94CE0540E450E060E070E080E06A\r
-:102EE00090E0DC0148505109489670E060E08C0F9A\r
-:102EF0009D1F0E940E3088819981AA81BB8123E0A9\r
-:102F0000880F991FAA1FBB1F2A95D1F78883998321\r
-:102F1000AA83BB83FE01E05BFF4F808331969083E1\r
-:102F20003196A0833196B0833C812D819E818F8123\r
-:102F30003196308331962083319690833196808309\r
-:102F400040E450E0B701CE010E94CE058885F8012B\r
-:102F50008083898581838A8582838B8583838C8521\r
-:102F600084838D8585838E8586838F8587838889F5\r
-:102F70008087898981878A8982878B8983878C89E1\r
-:102F800084878D8985878E8986878F89878788E959\r
-:102F9000FE0111928A95E9F7DF91CF911F910F9170\r
-:102FA000FF90EF900895CF92DF92EF92FF920F93F0\r
-:102FB0001F93CF93DF93CDB7DEB7C859D1090FB6B2\r
-:102FC000F894DEBF0FBECDBF7C016B0180E190E0C5\r
-:102FD0000E9436278C01CE0101960E947E16A60122\r
-:102FE000B701CE0101960E94BA16BE016F5F7F4FF6\r
-:102FF000C8010E943317C801C856DF4F0FB6F894B6\r
-:10300000DEBF0FBECDBFDF91CF911F910F91FF901B\r
-:10301000EF90DF90CF9008951F93CF93DF93EC0153\r
-:10302000162F0E941530181739F0612FCE01DF914D\r
-:10303000CF911F910C941D30DF91CF911F91089576\r
-:103040000C941530DC01ED91FC910190F081E02DA4\r
-:10305000099461E08CE00C94622A60E08CE00C94AE\r
-:10306000622A61E08DE00C94622A60E08DE00C94AD\r
-:10307000622A88E198E00FB6F894A89580936000E2\r
-:103080000FBE909360000E9431180E942D18FBCF54\r
-:103090000F931F93CF93DF938C01EC01892B11F4D5\r
-:1030A000C1E0D0E01C161D060CF561E08CE00E942A\r
-:1030B000622A60E08DE00E94622A64E670E080E0AF\r
-:1030C00090E00E94D72860E08CE00E94622A61E0D4\r
-:1030D0008DE00E94622A64E670E080E090E00E9449\r
-:1030E000D72801151105F1F22197DCCF0E942D1888\r
-:1030F000DF91CF911F910F910C9435180E943118D8\r
-:103100000E942D186AE070E080E090E00E94D728CD\r
-:103110000E9429180E94351865E070E080E090E078\r
-:103120000E94D728EBCF0F931F93CF93DF938C018F\r
-:10313000EC01892B11F4C1E0D0E061E08DE00E9448\r
-:10314000622A1C161D06ACF40E94351864E670E075\r
-:1031500080E090E00E94D7280E94311864E670E079\r
-:1031600080E090E00E94D7280115110551F32197C6\r
-:10317000E8CFDF91CF911F910F910C9435180F93E9\r
-:103180001F93CF93DF938C01EC01892B11F4C1E0E5\r
-:10319000D0E01C161D06ACF40E942D1864E670E009\r
-:1031A00080E090E00E94D7280E94291864E670E031\r
-:1031B00080E090E00E94D7280115110551F3219776\r
-:1031C000E8CFDF91CF911F910F910C942D18809132\r
-:1031D000113590911235A0911335B09114350196A7\r
-:1031E000A11DB11D8093113590931235A093133515\r
-:1031F000B09314354091040150910501609106018E\r
-:1032000070910701481759076A077B0710F510925C\r
-:1032100011351092123510921335109214356091B9\r
-:10322000103580910901861770F081E080930F3589\r
-:1032300080910F35680F6093103570E08CE00E942C\r
-:1032400076290C94351880910801681788F38FEF60\r
-:10325000EDCF08958091113590911235A0911335DD\r
-:10326000B09114350196A11DB11D80931135909335\r
-:103270001235A0931335B093143540910401509149\r
-:1032800005016091060170910701481759076A0707\r
-:103290007B0710F5109211351092123510921335EC\r
-:1032A000109214356091103580910901861770F0E5\r
-:1032B00081E080930F3580910F35680F6093103552\r
-:1032C00070E08DE00E9476290C942D188091080101\r
-:1032D000681788F38FEFEDCF089560EC70E08BE313\r
-:1032E00099E30E94221866E070E08BE399E30E9464\r
-:1032F00022186091233970E08BE399E30E94221831\r
-:1033000060EC70E08BE399E30C94221860EC70E0C1\r
-:103310008BE399E30E94221867E070E08BE399E366\r
-:103320000E9422186091150170E08BE399E30E94DE\r
-:10333000221860EC70E08BE399E30C94221860ECA7\r
-:1033400070E08BE399E30E9422186FE070E08BE35A\r
-:1033500099E30E94221861E070E08BE399E30E94F8\r
-:10336000221860EC70E08BE399E30C94221860EC77\r
-:1033700070E08BE399E30E9422186FE070E08BE32A\r
-:1033800099E30E94221870E060E08BE399E30E94C9\r
-:10339000221860EC70E08BE399E30C94221860EC47\r
-:1033A00070E08BE399E30E9422186EE070E08BE3FB\r
-:1033B00099E30E9422188091213961E070E0811127\r
-:1033C00002C070E060E08BE399E30E94221860EC99\r
-:1033D00070E08BE399E30C94221860EC70E08BE3CF\r
-:1033E00099E30E94221868E070E08BE399E30E9461\r
-:1033F000221866E470E08BE399E30E94221860ECE7\r
-:1034000070E08BE399E30C94221860EC70E08BE39E\r
-:1034100099E30E94221860E570E08BE399E30E9433\r
-:10342000221861E070E08BE399E30E9422186AE1C0\r
-:1034300070E08BE399E30E94221860EC70E08BE36C\r
-:1034400099E30C94221860EC70E08BE399E30E94FE\r
-:10345000221868E470E08BE399E30E94221860E987\r
-:1034600070E08BE399E30E94221860EC70E08BE33C\r
-:1034700099E30C94221860EC70E08BE399E30E94CE\r
-:10348000221867E470E08BE399E30E94221861E35D\r
-:1034900070E08BE399E30E94221860EC70E08BE30C\r
-:1034A00099E30C94221860EC70E08BE399E30E949E\r
-:1034B000221869E470E08BE399E30E94221861E925\r
-:1034C00070E08BE399E30E94221860EC70E08BE3DC\r
-:1034D00099E30C94221880912339882341F060915C\r
-:1034E0002C3970912D398AEA91E00C9407040895E3\r
-:1034F00080912339882341F060911801709119015E\r
-:103500008AEA91E00C94E004089580931F3988239F\r
-:1035100021F081E080932039089510922039089598\r
-:10352000809123398823E1F080911E39843A51F447\r
-:10353000609116017091170150E040E08AEA91E035\r
-:103540000E94AE0580911E39893A51F460911601AE\r
-:103550007091170141E050E08AEA91E00C94AE05C9\r
-:10356000089580912339882361F08AEA91E00E94CE\r
-:103570000A02609328397093293980932A399093ED\r
-:103580002B39089580912339882371F0409128398F\r
-:103590005091293960912A3970912B398AEA91E04A\r
-:1035A0000E9446040C94B11A08958091233988230F\r
-:1035B00061F08AEA91E00E94DA0160932439709305\r
-:1035C000253980932639909327390895809123399E\r
-:1035D000882371F040912439509125396091263922\r
-:1035E000709127398AEA91E00E9415030C94D51A4C\r
-:1035F000089580912339882321F08AEA91E00C9480\r
-:10360000280280E0089583ED9FE00E94201891E059\r
-:10361000833709F090E0892F0895C8E3DFE06FEF6A\r
-:10362000CE010E940C182196C11580E1D807B9F788\r
-:103630000E94391888E39FE00E942018982F81E0AB\r
-:10364000933021F0903F11F080E00895089589E3D0\r
-:103650009FE00E942018982F80931E3981E0943AB1\r
-:1036600021F0993A11F080E0089508958AE39FE0EF\r
-:103670000E94201880931D399FEF980F81E09E3F94\r
-:1036800008F080E00895BF92CF92DF92EF92FF9210\r
-:103690000F931F93CF93DF938BE090E00E94362728\r
-:1036A0007C018C01C8E3DFE0CE010E942018F80104\r
-:1036B00081938F012196C334FFE0DF07A9F76BE008\r
-:1036C00070E0C7010E94D317B82EC92E082F192FFA\r
-:1036D000DD24D394CE010E942018F80191918F012E\r
-:1036E0008913D12C2196C335FFE0DF0799F78B2D85\r
-:1036F0009C2D0E94D227C7010E94D2278D2DDF91D9\r
-:10370000CF911F910F91FF90EF90DF90CF90BF90DE\r
-:1037100008958FED9FE00E94201891E0833709F013\r
-:1037200090E0892F0895CF92DF92EF92FF92CF938E\r
-:103730000E94891B882309F461C084ED9FE00E94E8\r
-:10374000201890E090932D3980932C3985ED9FE0DF\r
-:103750000E94201890E0909319018093180186ED43\r
-:103760009FE00E94201890E090931701809316012B\r
-:103770008BED9FE00E942018C82F8CED9FE00E94E7\r
-:10378000201890E0B0E0A0E07C01DD24CC24FC2AED\r
-:103790008DED9FE00E942018C82F8EED9FE00E94C3\r
-:1037A0002018C82ADC2AC0922439D0922539E09208\r
-:1037B0002639F092273987ED9FE00E942018C82F04\r
-:1037C00088ED9FE00E94201890E0B0E0A0E07C012E\r
-:1037D000DD24CC24FC2A89ED9FE00E942018C82F0C\r
-:1037E0008AED9FE00E942018C82ADC2AC09228395E\r
-:1037F000D0922939E0922A39F0922B39CF91FF905B\r
-:10380000EF90DF90CF90089580912239882309F4BA\r
-:103810008AC080912339882309F485C060912C39AE\r
-:1038200084ED9FE00E940C186091180185ED9FE0E7\r
-:103830000E940C186091160186ED9FE00E940C1802\r
-:10384000609128397091293980912A3990912B39CA\r
-:10385000692F77278827992787ED9FE00E940C180A\r
-:10386000609128397091293980912A3990912B39AA\r
-:10387000BC018827992788ED9FE00E940C18609171\r
-:1038800028397091293980912A3990912B39672FE5\r
-:10389000782F892F992789ED9FE00E940C1860915D\r
-:1038A00028398AED9FE00E940C18609124397091AC\r
-:1038B00025398091263990912739692F77278827D4\r
-:1038C00099278BED9FE00E940C186091243970912C\r
-:1038D00025398091263990912739BC01882799276D\r
-:1038E0008CED9FE00E940C1860912439709125396D\r
-:1038F0008091263990912739672F782F892F992722\r
-:103900008DED9FE00E940C18609124398EED9FE0B0\r
-:103910000E940C1863E78FED9FE00E940C188AE06C\r
-:1039200090E00C94BF188AE090E00C94931860E04B\r
-:103930008FED9FE00C940C1882E390E00E944818F1\r
-:103940000E940D1B8091203970E060E0882319F0FF\r
-:1039500060911F3970E08AEA91E00C9454058AEA7C\r
-:1039600091E00E9411031092233908958091243927\r
-:1039700090912539A0912639B0912739892B8A2B2E\r
-:103980008B2BE1F08091283990912939A0912A3927\r
-:10399000B0912B39892B8A2B8B2B81F080911601CA\r
-:1039A000909117018F3F910549F080912C399091AA\r
-:1039B0002D39892B19F010921501089581E080931B\r
-:1039C000150108958091D101883C50F42091CF01D8\r
-:1039D0003091D00181E02115384118F480E008953C\r
-:1039E00081E00895CF938AEA91E00E944F01C82FA9\r
-:1039F0000E94A62860931A3570931B3580931C355E\r
-:103A000090931D35C0FF2DC081E0809325358093B4\r
-:103A100024358093233580911E3590911F35209158\r
-:103A20002535211108C020912435211104C0209191\r
-:103A30002335222319F120910F01309110018217B3\r
-:103A40009307B0F4019690931F3580931E3581E063\r
-:103A500080932235809121358823E9F0CF910C9411\r
-:103A60002918109225351092243510922335D3CF82\r
-:103A700081E08093223580932135ECCF009741F08F\r
-:103A8000019790931F3580931E3510922235E2CF17\r
-:103A900010922135FACFCF910C942D180F931F93CC\r
-:103AA0000E94A62800911A3510911B3520911C35D3\r
-:103AB00030911D35601B710B820B930B00910B0134\r
-:103AC00010910C0120910D0130910E0160177107CA\r
-:103AD0008207930720F01F910F910C94F21C1F9105\r
-:103AE0000F9108951092900089E18093910080E8F1\r
-:103AF0009EE3909397008093960080E280937100FC\r
-:103B0000089580910901809310350895803C61F4F7\r
-:103B10006BED70E08BE399E30E9422188CED682F27\r
-:103B20008BE399E30C941A2B8B3DC9F76BED70E096\r
-:103B30008BE399E30E9422188DEDF1CF60EC70E0E9\r
-:103B40008BE399E30E94221861E270E08BE399E332\r
-:103B50000E94221880912D350E94861D80912C355F\r
-:103B60000E94861D80912B350E94861D80912A35EA\r
-:103B70000E94861D60EC70E08BE399E30C942218A0\r
-:103B800060EC70E08BE399E30E94221862E270E03F\r
-:103B90008BE399E30E942218809129350E94861DAB\r
-:103BA000809128350E94861D809127350E94861DB0\r
-:103BB000809126350E94861D60EC70E08BE399E3CE\r
-:103BC0000C942218CF93C0911201C35660EC70E0A0\r
-:103BD0008BE399E30E94221863E270E08BE399E3A0\r
-:103BE0000E9422188C2F0E94861D60EC70E08BE3EF\r
-:103BF00099E3CF910C94221860EC70E08BE399E389\r
-:103C00000E94221864E270E08BE399E30E9422187C\r
-:103C100080911C390E94861D60EC70E08BE399E373\r
-:103C20000C94221860EC70E08BE399E30E94221858\r
-:103C300062E070E08BE399E30E94221880912B39B7\r
-:103C40000E94861D80912A390E94861D8091293903\r
-:103C50000E94861D809128390E94861D60EC70E0CC\r
-:103C60008BE399E30C94221860EC70E08BE399E30A\r
-:103C70000E94221861E070E08BE399E30E94221811\r
-:103C8000809127390E94861D809126390E94861DC9\r
-:103C9000809125390E94861D809124390E94861DBD\r
-:103CA00060EC70E08BE399E30C942218CF93DF93E0\r
-:103CB000C8E3DFE0CE010E9420180E94861D2196F5\r
-:103CC000C11580E1D807B1F7DF91CF91089560EC7D\r
-:103CD00070E08BE399E30E94221861E570E08BE3CA\r
-:103CE00099E30E9422180E94561E60EC70E08BE35C\r
-:103CF00099E30C942218CF93C82F60EC70E08BE30B\r
-:103D000099E30E94221860E970E08BE399E30E9436\r
-:103D100022186C2F8BE399E30E941A2B60EC70E061\r
-:103D20008BE399E3CF910C942218CF93DF93C82FA4\r
-:103D3000D62F0E94031B81110BC0C83C48F48C2F66\r
-:103D400090E06D2F885C904FDF91CF910C940C1810\r
-:103D500083E0DF91CF910C947B1ECF93DF930E9481\r
-:103D6000B61CC0912339C11149C080911501811140\r
-:103D70003EC0D0912239DD23D1F1409124395091B8\r
-:103D8000253960912639709127398AEA91E00E949D\r
-:103D90006403892B59F481E00E947B1E90E080E04F\r
-:103DA0000E9448188C2FDF91CF91089581E0809375\r
-:103DB00023390E94901A0E94C21A0E946B1A0E9414\r
-:103DC000781A0E94D51A8AEA91E00E94020568EAF0\r
-:103DD00072E28AEA91E00E94A6020E94A21C0E945E\r
-:103DE0006D1983E090E00E94BF18CD2FDBCF0E94B9\r
-:103DF0006D1983E090E00E949318D4CF0E946D1952\r
-:103E0000D1CF60EC70E08BE399E30E94221864E06C\r
-:103E100070E08BE399E30E94221860912C398BE3C8\r
-:103E200099E30E941A2B60EC70E08BE399E30C9409\r
-:103E3000221860EC70E08BE399E30E94221865E0A1\r
-:103E400070E08BE399E30E942218609118018BE3E4\r
-:103E500099E30E941A2B60EC70E08BE399E30C94D9\r
-:103E6000221860EC70E08BE399E30E94221869E06D\r
-:103E700070E08BE399E30E94221860911F398BE375\r
-:103E800099E30E941A2B60EC70E08BE399E30C94A9\r
-:103E9000221860EC70E08BE399E30E94221863E043\r
-:103EA00070E08BE399E30E942218609116018BE386\r
-:103EB00099E30E941A2B60EC70E08BE399E30C9479\r
-:103EC0002218CF93C82F60EC70E08BE399E30E9437\r
-:103ED000221860E470E08BE399E30E9422186C2FB3\r
-:103EE0008BE399E30E941A2B60EC70E08BE399E37B\r
-:103EF000CF910C94221890911735992309F48EC212\r
-:103F0000803C09F0ACC080910A0181119DC01092E3\r
-:103F100017358091021D9091031D6091FE1C7091D8\r
-:103F2000FF1C8617970761F42091041D3091051D31\r
-:103F30008091001D9091011D2817390709F471C265\r
-:103F40002091041D3091051D8091021D9091031D4B\r
-:103F500002972817390709F464C28091CF01909124\r
-:103F6000D0018115984108F05CC22091CB013091BD\r
-:103F7000CC018091CD019091CE01019710F48FEF8B\r
-:103F800097E18217930709F44BC02817390708F007\r
-:103F900042C0821B930B0196009709F442C24091E4\r
-:103FA000D1014F5F4093D101E091041DF091051DB7\r
-:103FB000318320834091041D5091051D2091001DE7\r
-:103FC0003091011D4217530771F57093051D6093E1\r
-:103FD000041DE0916A1BF0916B1B918380832091FB\r
-:103FE0006A1B30916B1B8091661B9091671B281791\r
-:103FF000390721F58091641B9091651B90936B1B91\r
-:1040000080936A1B8091CD019091CE019093CC0159\r
-:104010008093CB010895821B930B8F5F974EBCCF8B\r
-:1040200081E090E0BCCF2091041D3091051D2E5FF2\r
-:104030003F4F3093051D2093041DCBCF80916A1B09\r
-:1040400090916B1B0296DACF81E0809317358EEF4B\r
-:1040500080930A0110921935109218350895209115\r
-:10406000183530911935243F91E0390708F0D9C14E\r
-:1040700090910A012115310529F49E3F19F480938E\r
-:104080000A010895911146C08B3D21F481E080938F\r
-:104090001635089590911635992349F08C3D21F0FD\r
-:1040A0008D3D19F48BED01C080EC10921635909186\r
-:1040B000D101983C08F0B5C12091CF013091D001D9\r
-:1040C0002115384108F0ADC12091CF013091D001C8\r
-:1040D0002F5F3F4F3093D0012093CF012091CD012E\r
-:1040E0003091CE01A9014F5F5F4F5093CE014093B5\r
-:1040F000CD012E523E4FF90180838091CD019091E8\r
-:10410000CE018115984109F08CC11092CE01109218\r
-:10411000CD010895913009F044C08B3DD1F49093C6\r
-:1041200016358091183590911935049709F079C1A9\r
-:1041300060912237709123378091243790912537F1\r
-:104140000E94772E6115710581059105D9F40C94B3\r
-:10415000341E90911635992349F08C3D21F08D3D08\r
-:1041600019F48BED01C080EC10921635A9014F5F58\r
-:104170005F4F50931935409318352E5D384CF90137\r
-:104180008083CFCF6093243970932539809326396B\r
-:1041900090932739809114018131C9F60E94E61A63\r
-:1041A000D6CF923009F045C08B3DD9F481E08093A1\r
-:1041B00016358091183590911935049709F031C161\r
-:1041C0006091223770912337809124379091253761\r
-:1041D0000E94772E6115710581059105D9F40C9423\r
-:1041E000121E90911635992349F08C3D21F08D3D9A\r
-:1041F00019F48BED01C080EC10921635A9014F5FC8\r
-:104200005F4F50931935409318352E5D384CF901A6\r
-:104210008083CFCF609328397093293980932A39CE\r
-:1042200090932B39809114018131C9F60E94C21AF2\r
-:10423000D6CF933099F48F3F11F40C94491F8231FB\r
-:1042400008F081E190E0909317018093160180912E\r
-:104250001401813191F70E94901AEFCF9430D1F47C\r
-:104260008F3F11F40C94011F863088F090E08D3060\r
-:10427000910514F08CE090E090932D3980932C39C7\r
-:1042800080911401813171F70E946B1AEBCF86E0A7\r
-:1042900090E0F2CF9530D1F48F3F11F40C94191FB8\r
-:1042A000853088F090E08930910514F088E090E046\r
-:1042B000909319018093180180911401813171F755\r
-:1042C0000E94781AEBCF85E090E0F2CF993021F48C\r
-:1042D0000E94851A0C94311F963079F48F3F11F4A7\r
-:1042E0000C946D19811103C00E94AF1CF9CF81306D\r
-:1042F00009F097C00E94AD1EF3CF913211F40C94D7\r
-:104300009E1D923211F40C94C01D933211F40C9442\r
-:10431000E21D973021F40E94B61C0C94861990334C\r
-:1043200019F490E00C94BF18903421F40E94F91A0B\r
-:104330000C94611F983029F4833709F072C00C94F3\r
-:10434000ED199E3059F4813021F4809321390C9479\r
-:10435000CF198111FCCF10922139F9CF9F3041F450\r
-:104360000E94E21C811102C00C949F190C94B71991\r
-:10437000993529F4883F09F054C00E949C1C95355A\r
-:1043800029F4883F09F04DC00E943918913511F485\r
-:104390000C94671E923551F58B3D79F481E0809342\r
-:1043A000163580911835909119350297D1F56091A5\r
-:1043B0002337809122370C94951E909116359923BE\r
-:1043C00049F08C3D21F08D3D19F48BED01C080EC5E\r
-:1043D00010921635A9014F5F5F4F509319354093E6\r
-:1043E00018352E5D384CF9018083DBCF903511F400\r
-:1043F0000C94051A983411F40C94231A993411F47E\r
-:104400000C94531A973411F40C943B1A933511F40D\r
-:104410000C94041C943529F40C94971C803C09F4EA\r
-:1044200013CE089581E08093C8014FB7F89420918E\r
-:104430000B3530910C3580910D3590910E354FBF75\r
-:1044400028173907F1F0E0910B35F0910C35809188\r
-:10445000093590910A35E817F90761F4809107351D\r
-:104460009091083590930C3580930B3580810E9494\r
-:104470007B1FDBCFE0910B35F0910C35CF0101961E\r
-:10448000F1CF1092C8010895CF93DF9360EC70E0F4\r
-:104490008BE399E30E94221870E060E08BE399E3DC\r
-:1044A0000E942218C6E2D7E320911A3930911B39B5\r
-:1044B000CE018652974382179307C8F46991603CF6\r
-:1044C00061F46BED70E08BE399E30E9422186CEDD0\r
-:1044D0008BE399E30E941A2BE7CF6B3DC9F76BED95\r
-:1044E00070E08BE399E30E9422186DEDF1CF60EC50\r
-:1044F00070E08BE399E30E94221810921B3910920E\r
-:104500001A39DF91CF910895CF93DF93EC01209773\r
-:10451000E1F0219780911A3990911B39843F9140A5\r
-:10452000A0F48AEA91E00E94BF0120911A393091EB\r
-:104530001B39A9014F5F5F4F50931B3940931A39C4\r
-:104540002A5D384CF9018083E2CFDF91CF91089545\r
-:104550000F931F93CF93DF93EC0180912139811149\r
-:104560007BC08AEA91E00E94BF012197409111012E\r
-:1045700080FF57C09C0154E0359527955A95E1F787\r
-:104580002F7033274F3FD9F410921B3910921A39EC\r
-:10459000209311018AEA91E00E9466019093130131\r
-:1045A000809312018AEA91E00E94520180931C39A3\r
-:1045B000CE01DF91CF911F910F910C94842224138F\r
-:1045C000E3CF8AEA91E00E946601209112013091C6\r
-:1045D0001301820F931F97FD0196959587959093F0\r
-:1045E00013018093120100911C3910E08AEA91E0D6\r
-:1045F0000E945201800F912F911D95958795809370\r
-:104600001C39CE010E9484228FEF809311010E94F9\r
-:10461000E21D0E94FC1DDF91CF911F910F910C9420\r
-:1046200044224F3F39F010921B3910921A398FEF04\r
-:10463000809311018AEA91E00E9466019093130130\r
-:10464000809312018AEA91E00E94520180931C3902\r
-:10465000CE010E948422DBCF10921B3910921A39AE\r
-:10466000E9CFCF92DF92EF92FF920F931F93CF93F8\r
-:10467000DF93EC0180912339882309F479C080917C\r
-:104680002139811153C00E94311860E071E080E04F\r
-:1046900090E00E94952C162F107FCF3FD10508F097\r
-:1046A000116070E060E08AEA91E00E943305612FBA\r
-:1046B0008AEA91E00E94B1009EE2C92E95E3D92ECC\r
-:1046C000C25DDA4CEE24E394F12CCC16DD06F1F059\r
-:1046D000F60161916F018AEA91E00E94B100FFEF5B\r
-:1046E000EF1AFF0A8FEFE816F10479F78AEA91E0F2\r
-:1046F0000E942B0270E060E08AEA91E00E9433059C\r
-:10470000612F8AEA91E00E94B100DCCF8AEA91E051\r
-:104710000E942B020E943518DF91CF911F910F91BB\r
-:10472000FF90EF90DF90CF900C94A21C0E94311864\r
-:10473000CF3FD10519F010F0CFEFD0E080912039B4\r
-:1047400070E060E08111BE018AEA91E00E943305C9\r
-:104750000EE215E3C8018E5295438C179D07B0F603\r
-:10476000F80161918F018AEA91E00E94B100F2CFD5\r
-:1047700082E00E947B1E85E090E0DF91CF911F9147\r
-:104780000F91FF90EF90DF90CF900C9448180F930B\r
-:104790001F93CF93DF938091C90181116EC081E097\r
-:1047A0008093C901C0E0D8E14FB7F8942091021D71\r
-:1047B0003091031D8091041D9091051D4FBF281756\r
-:1047C000390709F45AC0E091021DF091031D809150\r
-:1047D000001D9091011DE817F90709F03FC0809175\r
-:1047E000FE1C9091FF1C9093031D8093021D00817D\r
-:1047F0001181E091681BF091691B2091661B30913B\r
-:10480000671BE217F30789F52091641B3091651B44\r
-:104810003093691B2093681B20813181A90141508D\r
-:104820005109443F514008F0BFCFEEE2F5E30E528C\r
-:104830001543C8018E0F9F1FBE010E94192EDC0177\r
-:10484000AE52BE4F8C918193CF018E529543821709\r
-:10485000930778F3C9010E943123A6CFE091021D8E\r
-:10486000F091031DCF010296BECFE091681BF0913D\r
-:10487000691B9F012E5F3F4FCBCF1092D101109249\r
-:10488000D0011092CF011092C901DF91CF911F91F9\r
-:104890000F910895809123398823E1F10E944E1DE4\r
-:1048A0008091D101882311F180912035811102C0BE\r
-:1048B0000E94F21C80912235811128C080912135FF\r
-:1048C000811124C080912035882331F062E370E0AB\r
-:1048D00080E090E00E94D7280E94F21C809122354F\r
-:1048E000811104C0109220350E94C7234FB7F8945D\r
-:1048F00020910B3530910C3580910D3590910E350E\r
-:104900004FBF2817390791F00C94122281E0809351\r
-:104910002035ECCF80912239882319F00E94E718C6\r
-:10492000E5CF0E942A190E94AF1CE0CF0895CF93D3\r
-:104930008091CA01811127C081E08093CA01C4E13E\r
-:104940008BE399E30E94902A892BD9F08FB7F894D2\r
-:1049500040910B3550910C35209107353091083539\r
-:104960004217530791F440910D3550910E35209127\r
-:10497000093530910A354217530739F48FBFC150BA\r
-:10498000F9F61092CA01CF91089540910D355091DA\r
-:104990000E3520910B3530910C35215031094217DD\r
-:1049A000530761F38FBF8BE399E30E94AA2A2FB7C5\r
-:1049B000F894E0910D35F0910E35808340910D35DE\r
-:1049C00050910E358091093590910A354817590755\r
-:1049D00051F4809107359091083590930E3580936E\r
-:1049E0000D352FBFCCCF80910D3590910E350196AE\r
-:1049F000F4CF1F920F920FB60F9211240BB60F92A5\r
-:104A00002F933F934F935F936F937F938F939F93D6\r
-:104A1000AF93BF93EF93FF930E949724FF91EF9181\r
-:104A2000BF91AF919F918F917F916F915F914F91C6\r
-:104A30003F912F910F900BBE0F900FBE0F901F90C4\r
-:104A400018958091DC3980FF2AC082E0809315356B\r
-:104A50008091153581508230B8F50E94031B882360\r
-:104A600081F10E941A1B882361F10E94271B882371\r
-:104A700041F10E94361B882321F10E94431B8823A9\r
-:104A800051F181E0809322390E94891B882319F11A\r
-:104A90000E94931B82E1809314010C94AD1E82FF4F\r
-:104AA00002C083E0D3CF83FF02C081E0CFCF42E2D8\r
-:104AB00050E065E571E08BE399E30E945A2C0E9477\r
-:104AC0007E18109222390895109222394EE150E05A\r
-:104AD00068E771E0F0CF089580E00E9458290E94B5\r
-:104AE0008D2CA6E0BDE181E098E1FD019C011192D1\r
-:104AF00021503040E1F7E7E0F5E3B183A083B783CD\r
-:104B0000A683B583A48386E095E39383828326E01E\r
-:104B100040E052EC61E070E08BE399E30E94612B8E\r
-:104B20000E94721D61E08CE00E94262A61E08DE007\r
-:104B30000E94262AE6E2F7E384EF91E0DF019C0180\r
-:104B40001D9221503040E1F7E2E2F7E384E0DF011B\r
-:104B50001D928A95E9F7E2EDF1E080E098E1DF014E\r
-:104B60009C011D9221503040E1F7ACE6BBE182E9A7\r
-:104B700091E0FD019C01119221503040E1F7EEEFF0\r
-:104B8000FCE1B183A083B783A683B583A4832CEF14\r
-:104B90003CE133832283A2EDB9E1FD019C01119236\r
-:104BA00021503040E1F7E4E6FBE1B183A083B78315\r
-:104BB000A683B583A48382E69BE19383828322E06C\r
-:104BC00030E043E050E064E070E08AEA91E00E9467\r
-:104BD000F7000C942125CF93CFB7F8948091313909\r
-:104BE000811127C0E0EDF0E08491E0EBF0E09491DA\r
-:104BF000E82FF0E0EE0FFF1FEA50FF4FA591B491B0\r
-:104C0000EC91E92321F461E084E00E94622A61E0F2\r
-:104C100084E00E94262A8CB580618CBD8CB58064AE\r
-:104C20008CBD61E087E00E94262A61E085E00E9459\r
-:104C3000262A809131398F5F80933139CFBFCF9150\r
-:104C400008959FB7F89480913139882319F08150E5\r
-:104C50008093313980913139811105C08CB58F7BBA\r
-:104C60008CBD109230399FBF08952FB7F8948130D2\r
-:104C700041F0C0F08230A1F082E08093303980E0D2\r
-:104C800001C082E090912F39892B80932F39809138\r
-:104C90003039811103C081E0809330392FBF0895EE\r
-:104CA00084E0F0CF81E0EECF909130399230A1F0E6\r
-:104CB0009FB7F894833088F4E82FF0E0E956FE4F70\r
-:104CC0008081809520912F39822380932F39811103\r
-:104CD00002C0109230399FBF089580E0F2CF08954E\r
-:104CE000833060F5E82FF0E0EE0FFF1FE65EFE4F29\r
-:104CF00071836083813051F08230A1F0809169002E\r
-:104D00008C7F482B40936900E89A08958091690050\r
-:104D1000837F440F551F440F551F482B4093690054\r
-:104D2000E99A0895809169008F7C94E0440F551FA3\r
-:104D30009A95E1F7482B40936900EA9A08958330E9\r
-:104D400078F4E82FF0E0813061F0823061F0E8988B\r
-:104D5000EE0FFF1FE65EFE4F8FE696E291838083A3\r
-:104D60000895E998F5CFEA98F3CF1F920F920FB606\r
-:104D70000F9211240BB60F922F933F934F935F9393\r
-:104D80006F937F938F939F93AF93BF93EF93FF9313\r
-:104D9000E0911A01F0911B010995FF91EF91BF91EC\r
-:104DA000AF919F918F917F916F915F914F913F91C3\r
-:104DB0002F910F900BBE0F900FBE0F901F90189564\r
-:104DC0001F920F920FB60F9211240BB60F922F93D2\r
-:104DD0003F934F935F936F937F938F939F93AF9383\r
-:104DE000BF93EF93FF93E0911C01F0911D01099592\r
-:104DF000FF91EF91BF91AF919F918F917F916F91B3\r
-:104E00005F914F913F912F910F900BBE0F900FBE6E\r
-:104E10000F901F9018951F920F920FB60F921124AA\r
-:104E20000BB60F922F933F934F935F936F937F93A4\r
-:104E30008F939F93AF93BF93EF93FF93E0911E01E6\r
-:104E4000F0911F010995FF91EF91BF91AF919F9153\r
-:104E50008F917F916F915F914F913F912F910F9023\r
-:104E60000BBE0F900FBE0F901F9018950F931F93BE\r
-:104E7000CF93DF938230910510F482E090E0E091CF\r
-:104E8000DA39F091DB3930E020E0B0E0A0E0309793\r
-:104E900099F42115310509F44AC0281B390B243037\r
-:104EA0003105D8F58A819B816115710589F1FB0176\r
-:104EB00093838283FE0111C040815181028113815D\r
-:104EC00048175907E0F04817590799F4109761F00F\r
-:104ED00012960C93129713961C933296CF01DF9182\r
-:104EE000CF911F910F9108950093DA391093DB3918\r
-:104EF000F4CF2115310551F04217530738F0A901BD\r
-:104F0000DB019A01BD01DF01F801C1CFEF01F9CF4B\r
-:104F10009093DB398093DA39CDCFFE01E20FF31F96\r
-:104F2000819391932250310939832883D7CF2091DF\r
-:104F3000D8393091D939232B41F420912201309175\r
-:104F400023013093D9392093D83920912001309111\r
-:104F500021012115310541F42DB73EB740912401BF\r
-:104F600050912501241B350BE091D839F091D939A6\r
-:104F7000E217F307A0F42E1B3F0B2817390778F030\r
-:104F8000AC014E5F5F4F2417350748F04E0F5F1F8F\r
-:104F90005093D9394093D839819391939FCFF0E0C2\r
-:104FA000E0E09CCFCF93DF930097E9F0FC013297CC\r
-:104FB00013821282A091DA39B091DB39ED0130E031\r
-:104FC00020E01097A1F420813181820F931F20915E\r
-:104FD000D8393091D9392817390709F061C0F093D1\r
-:104FE000D939E093D839DF91CF910895EA01CE17EE\r
-:104FF000DF07E8F54A815B819E0141155105B1F754\r
-:10500000E901FB83EA8349915991C40FD51FEC173D\r
-:10501000FD0761F4808191810296840F951FE9015B\r
-:1050200099838883828193819B838A83F0E0E0E087\r
-:1050300012968D919C9113970097B9F52D913C9103\r
-:105040001197CD010296820F931F2091D83930918C\r
-:10505000D9392817390739F6309751F51092DB39CD\r
-:105060001092DA39B093D939A093D839BCCFD38311\r
-:10507000C28340815181840F951FC817D90761F4FD\r
-:105080004E5F5F4F88819981480F591F518340833C\r
-:105090008A819B81938382832115310509F0B0CFEA\r
-:1050A000F093DB39E093DA399ECFFD01DC01C0CF0C\r
-:1050B00013821282D7CF08951F920F920FB60F92CC\r
-:1050C00011242F933F938F939F93AF93BF9380911E\r
-:1050D000333990913439A0913539B09136393091C6\r
-:1050E000323923E0230F2D3758F50196A11DB11D4C\r
-:1050F000209332398093333990933439A0933539E2\r
-:10510000B09336398091373990913839A091393937\r
-:10511000B0913A390196A11DB11D80933739909312\r
-:105120003839A0933939B0933A39BF91AF919F91F3\r
-:105130008F913F912F910F900FBE0F901F90189558\r
-:1051400026E8230F0296A11DB11DD2CF2FB7F894E8\r
-:105150006091333970913439809135399091363975\r
-:105160002FBF08953FB7F89480913739909138391F\r
-:10517000A0913939B0913A3926B5A89B05C02F3F87\r
-:1051800019F00196A11DB11D3FBFBA2FA92F982F6D\r
-:105190008827BC01CD01620F711D811D911D42E068\r
-:1051A000660F771F881F991F4A95D1F708958F9230\r
-:1051B0009F92AF92BF92CF92DF92EF92FF926B01DC\r
-:1051C0007C010E94B2284B015C01C114D104E104AE\r
-:1051D000F104E9F00E945B280E94B228681979095D\r
-:1051E0008A099B09683E73408105910570F321E0AF\r
-:1051F000C21AD108E108F10888EE880E83E0981EF3\r
-:10520000A11CB11CC114D104E104F10429F7FF90E1\r
-:10521000EF90DF90CF90BF90AF909F908F900895C8\r
-:10522000789484B5826084BD84B5816084BD85B581\r
-:10523000826085BD85B5816085BDEEE6F0E0808148\r
-:1052400081608083E1E8F0E0108280818260808369\r
-:10525000808181608083E0E8F0E0808181608083EC\r
-:10526000E1EBF0E0808184608083E0EBF0E080811E\r
-:1052700081608083E1E9F0E08081826080838081C9\r
-:1052800081608083E0E9F0E0808181608083EAE7EB\r
-:10529000F0E080818460808380818260808380816F\r
-:1052A000816080838081806880831092C10008952E\r
-:1052B000883020F0873108F487E188512091260159\r
-:1052C00090E4299F900111248770822B80937C00A9\r
-:1052D00080917A00806480937A0080917A0086FDC4\r
-:1052E000FCCF809178009091790008951F93CF931F\r
-:1052F000DF93182FEB0161E00E94262A209739F4F2\r
-:1053000060E0812FDF91CF911F910C94622ACF3FF3\r
-:10531000D10511F461E0F5CFE12FF0E0E457FF4F44\r
-:10532000E491E150EA3008F054C0F0E0E656F64D62\r
-:105330000C946E2EA429AC29B129BB29E929E929AD\r
-:10534000C529CD29D529DF2984B5806884BDC7BD8D\r
-:10535000DF91CF911F91089584B5806284BDC8BD4F\r
-:10536000F7CF80918000806880938000D09389007F\r
-:10537000C0938800EDCF8091800080628093800090\r
-:10538000D0938B00C0938A00E3CF8091B0008068F7\r
-:105390008093B000C093B300DBCF8091B0008062F7\r
-:1053A0008093B000C093B400D3CF80919000806808\r
-:1053B00080939000D0939900C0939800C9CF8091BA\r
-:1053C0009000806280939000D0939B00C0939A00DD\r
-:1053D000BFCFC038D1050CF09DCF92CF81508A301D\r
-:1053E00080F5E82FF0E0E950F64D0C946E2E0B2A74\r
-:1053F0000F2A012A072A212A212A122A182A1C2ABE\r
-:10540000222A809180008F77809380000895809178\r
-:1054100080008F7DF9CF84B58F7784BD089584B5E2\r
-:105420008F7DFBCF8091B0008F778093B00008957F\r
-:105430008091B0008F7DF9CF809190008F7780931D\r
-:1054400090000895809190008F7DF9CFCF93DF93E6\r
-:1054500090E0FC01E455FF4F249184539F4FFC01E1\r
-:1054600084918823C9F090E0880F991FFC01EA50CD\r
-:10547000FF4FA591B491FC01E451FF4FC591D49128\r
-:1054800061110DC09FB7F8948C91209582238C9365\r
-:105490008881282328839FBFDF91CF9108956230B0\r
-:1054A00051F49FB7F8943C91822F809583238C937D\r
-:1054B000E8812E2BEFCF8FB7F894EC912E2B2C9305\r
-:1054C0008FBFEACF1F93CF93DF93282F30E0F901EE\r
-:1054D000E457FF4F8491F901E455FF4FD491F9014E\r
-:1054E000E453FF4FC491CC23A9F0162F81110E94E1\r
-:1054F000EE29EC2FF0E0EE0FFF1FE451FF4FA591D6\r
-:10550000B4918FB7F894EC91111108C0D095DE23B7\r
-:10551000DC938FBFDF91CF911F910895DE2BF8CFE1\r
-:10552000FC01918D228D892F90E0805C9F4F821B22\r
-:1055300091098F7399270895FC01918D828D981799\r
-:1055400031F0828DE80FF11D858D90E008958FEF89\r
-:105550009FEF0895FC01918D828D981761F0A28DC7\r
-:10556000AE0FBF2FB11D5D968C91928D9F5F9F7383\r
-:10557000928F90E008958FEF9FEF089583E29CE271\r
-:10558000892B49F080E090E0892B29F00E94232CA0\r
-:1055900081110E94000080E090E0892B49F080E0BA\r
-:1055A00090E0892B29F00E94000081110C940000EA\r
-:1055B0000895FC01A48DA80FB92FB11DA35ABF4FA8\r
-:1055C0002C91848D90E001968F739927848FA68902\r
-:1055D000B7892C93A089B1898C9180648C93938D29\r
-:1055E000848D981306C00288F389E02D80818F7D19\r
-:1055F00080830895CF93DF93EC01888D8823B9F0E1\r
-:10560000AA89BB89E889F9898C9185FD03C08081CD\r
-:1056100086FD0DC00FB607FCF7CF8C9185FFF2CF4A\r
-:10562000808185FFEDCFCE010E94D92AE9CFDF919D\r
-:10563000CF910895EF92FF920F931F93CF93DF9333\r
-:10564000EC0181E0888F9B8D8C8D981316C0E889C2\r
-:10565000F989808185FF11C0EE89FF896083E8891F\r
-:10566000F98980818064808381E090E0DF91CF912F\r
-:105670001F910F91FF90EF900895F62E0B8D10E083\r
-:105680000F5F1F4F0F731127E02E8C8D8E110CC0F2\r
-:105690000FB607FCFACFE889F989808185FFF5CF3D\r
-:1056A000CE010E94D92AF1CFEB8DEC0FFD2FF11D19\r
-:1056B000E35AFF4FF0820B8FEA89FB898081806279\r
-:1056C000D2CFCF92DF92EF92FF921F93CF93DF93CF\r
-:1056D000EC016A017B01122FE889F98982E080835D\r
-:1056E000C11481EED806E104F104A1F060E079E094\r
-:1056F0008DE390E0A70196010E942D2E21503109E3\r
-:10570000410951095695479537952795211580E10F\r
-:10571000380798F0E889F989108260E874E88EE12A\r
-:1057200090E0A70196010E942D2E215031094109D8\r
-:1057300051095695479537952795EC85FD8530831A\r
-:10574000EE85FF852083188EEC89FD891083EA8918\r
-:10575000FB89808180618083EA89FB898081886000\r
-:105760008083EA89FB89808180688083EA89FB895C\r
-:1057700080818F7D8083DF91CF911F91FF90EF908B\r
-:10578000DF90CF9008951F920F920FB60F921124C1\r
-:105790000BB60F922F938F939F93EF93FF93E0910C\r
-:1057A0004B39F0914C398081E0915139F091523967\r
-:1057B00082FD1DC09081809154398F5F8F7320913D\r
-:1057C0005539821741F0E0915439F0E0E55CF64C30\r
-:1057D000958F80935439FF91EF919F918F912F91E5\r
-:1057E0000F900BBE0F900FBE0F901F9018958081E9\r
-:1057F000F2CF1F920F920FB60F9211240BB60F9299\r
-:105800002F933F934F935F936F937F938F939F93C8\r
-:10581000AF93BF93EF93FF938BE399E30E94D92A51\r
-:10582000FF91EF91BF91AF919F918F917F916F9178\r
-:105830005F914F913F912F910F900BBE0F900FBE34\r
-:105840000F901F9018958BE399E30E94902A21E016\r
-:10585000892B09F420E0822F0895EBE3F9E313820A\r
-:10586000128288EE93E0A0E0B0E084839583A68363\r
-:10587000B7838EE991E09183808385EC90E09587F2\r
-:10588000848784EC90E09787868780EC90E0918B0A\r
-:10589000808B81EC90E0938B828B82EC90E0958BF7\r
-:1058A000848B86EC90E0978B868B118E128E138EF4\r
-:1058B000148E0895AF92BF92CF92DF92EF92FF9233\r
-:1058C0000F931F93CF93DF936C017B018B01040F28\r
-:1058D000151FEB015E01AE18BF08C017D10759F0C4\r
-:1058E0006991D601ED91FC910190F081E02DC60106\r
-:1058F0000995892B79F7C501DF91CF911F910F9100\r
-:10590000FF90EF90DF90CF90BF90AF900895FC0193\r
-:1059100044835583668377830895009729F0BC01FB\r
-:1059200090E080E00C94FC2F0895CF92DF92EF92EC\r
-:10593000FF926B017C01C114D104E104F10441F038\r
-:105940000E94AD2FA70196010E944F2E6B017C0192\r
-:10595000C701B601FF90EF90DF90CF9008950895B2\r
-:105960000E9410290E94AF2C0E946C25CEEBDAE237\r
-:105970000E944A242097E1F30E94BE2AF9CF0E9498\r
-:10598000372D08F481E008950E94CB2C6894B11162\r
-:105990000C94A62D08950E948B2D88F09F5798F0A7\r
-:1059A000B92F9927B751B0F0E1F0660F771F881F24\r
-:1059B000991F1AF0BA95C9F714C0B13091F00E943E\r
-:1059C000A52DB1E008950C94A52D672F782F882779\r
-:1059D000B85F39F0B93FCCF3869577956795B39565\r
-:1059E000D9F73EF490958095709561957F4F8F4FD4\r
-:1059F0009F4F0895E89409C097FB3EF490958095D9\r
-:105A0000709561957F4F8F4F9F4F9923A9F0F92F84\r
-:105A100096E9BB279395F695879577956795B79502\r
-:105A2000F111F8CFFAF4BB0F11F460FF1BC06F5FE8\r
-:105A30007F4F8F4F9F4F16C0882311F096E911C0FA\r
-:105A4000772321F09EE8872F762F05C0662371F01B\r
-:105A500096E8862F70E060E02AF09A95660F771F2F\r
-:105A6000881FDAF7880F9695879597F90895990F0B\r
-:105A70000008550FAA0BE0E8FEEF16161706E80718\r
-:105A8000F907C0F012161306E407F50798F0621B39\r
-:105A9000730B840B950B39F40A2661F0232B242B0E\r
-:105AA000252B21F408950A2609F4A140A6958FEF2D\r
-:105AB000811D811D089597F99F6780E870E060E07F\r
-:105AC00008959FEF80EC089500240A941616170697\r
-:105AD00018060906089500240A94121613061406DF\r
-:105AE00005060895092E0394000C11F4882352F042\r
-:105AF000BB0F40F4BF2B11F460FF04C06F5F7F4FFA\r
-:105B00008F4F9F4F089557FD9058440F551F59F0E0\r
-:105B10005F3F71F04795880F97FB991F61F09F3F9A\r
-:105B200079F087950895121613061406551FF2CFC3\r
-:105B30004695F1DF08C0161617061806991FF1CF13\r
-:105B400086957105610508940895E894BB2766273A\r
-:105B50007727CB0197F908950E94BF2D0C94722DE1\r
-:105B60000E94642D38F00E946B2D20F0952311F0D7\r
-:105B70000C945B2D0C94612D11240C94A62D0E9485\r
-:105B8000832D70F3959FC1F3950F50E0551F629FD1\r
-:105B9000F001729FBB27F00DB11D639FAA27F00D86\r
-:105BA000B11DAA1F649F6627B00DA11D661F829FAD\r
-:105BB0002227B00DA11D621F739FB00DA11D621F92\r
-:105BC000839FA00D611D221F749F3327A00D611DAF\r
-:105BD000231F849F600D211D822F762F6A2F112491\r
-:105BE0009F5750409AF0F1F088234AF0EE0FFF1FC4\r
-:105BF000BB1F661F771F881F91505040A9F79E3F1B\r
-:105C0000510580F00C945B2D0C94A62D5F3FE4F3BE\r
-:105C1000983ED4F3869577956795B795F795E79570\r
-:105C20009F5FC1F7FE2B880F911D9695879597F979\r
-:105C30000895AA1BBB1B51E107C0AA1FBB1FA617D3\r
-:105C4000B70710F0A61BB70B881F991F5A95A9F725\r
-:105C500080959095BC01CD010895A1E21A2EAA1B52\r
-:105C6000BB1BFD010DC0AA1FBB1FEE1FFF1FA2170C\r
-:105C7000B307E407F50720F0A21BB30BE40BF50B09\r
-:105C8000661F771F881F991F1A9469F76095709592\r
-:105C9000809590959B01AC01BD01CF010895052E23\r
-:105CA00097FB1EF400940E94662E57FD07D00E94B9\r
-:105CB0002D2E07FC03D04EF40C94662E5095409583\r
-:105CC000309521953F4F4F4F5F4F089590958095A8\r
-:105CD000709561957F4F8F4F9F4F0895EE0FFF1F77\r
-:105CE0000024001C0BBE0790F691E02D0994692753\r
-:105CF0009627692778278727782708950E94492FB4\r
-:105D0000A59F900DB49F900DA49F800D911D11240F\r
-:105D10000895E894DF93CF93FC01DB010E94492FA3\r
-:105D20007F936F93E9019A01AC01BF93AF933F93C7\r
-:105D30002F93DF010E94492F26F46C1B7D0B820BF1\r
-:105D4000930B9E01EB01FC010E94582FAF91BF9174\r
-:105D50002F913F910E94582FBE01CF01F9012F9141\r
-:105D60003F91CF91DF910895689401C0E8948F929C\r
-:105D70009F92CF93DF930E94C22EDF91CF919F908D\r
-:105D80008F90089588249924F401E401B0E49F934E\r
-:105D9000AA279A158B049C04ED05FE05CF05D007B4\r
-:105DA000A10798F4AD2FDC2FCF2FFE2FE92D982CD3\r
-:105DB000892E982F872F762F652F542F432F322F20\r
-:105DC0002227B85031F7BF9127C01B2EBF91BB27A8\r
-:105DD000220F331F441F551F661F771F881F991FEF\r
-:105DE000881C991CEE1FFF1FCC1FDD1FAA1FBB1FA5\r
-:105DF0008A149B04EC05FD05CE05DF05A007B1075D\r
-:105E000048F08A189B08EC09FD09CE09DF09A00BB0\r
-:105E1000B10B21601A94E1F62EF49401AF01BE019A\r
-:105E2000CD01000C0895002E083090F0982F872F98\r
-:105E3000762F652F542F432F322F22270850F4CF6F\r
-:105E4000220F331F441F551F661F771F881F991F7E\r
-:105E50000A95B2F7002D089597FD1094002E083092\r
-:105E600098F00850232F342F452F562F672F782F67\r
-:105E7000892F912DF4CF15949795879577956795F0\r
-:105E800057954795379527950A95AAF71124002D20\r
-:105E90000895A29FB001B39FC001A39F700D811D03\r
-:105EA0001124911DB29F700D811D1124911D089523\r
-:105EB0000E94492F460F571FC81FD91F08F431965B\r
-:105EC00008958F929F92AF92BF92CF92DF92EF92FE\r
-:105ED000FF92CF93DF93EC01688179818A819B8166\r
-:105EE000611571058105910521F464E279ED8BE579\r
-:105EF00097E02DE133EF41E050E00E944F2E490141\r
-:105F00005A019B01AC01A7EAB1E40E947E2E6B010D\r
-:105F10007C01ACEEB4EFA50194010E942E30C60EB8\r
-:105F2000D71EE81EF91EF7FE06C081E0C81AD10888\r
-:105F3000E10880E8F80AC882D982EA82FB82C701B8\r
-:105F4000B6019F77DF91CF91FF90EF90DF90CF90D8\r
-:105F5000BF90AF909F908F9008958F929F92AF9235\r
-:105F6000BF92CF92DF92EF92FF9260910001709109\r
-:105F70000101809102019091030161157105810574\r
-:105F8000910521F464E279ED8BE597E02DE133EFA3\r
-:105F900041E050E00E944F2E49015A019B01AC01A3\r
-:105FA000A7EAB1E40E947E2E6B017C01ACEEB4EF57\r
-:105FB000A50194010E942E30C60ED71EE81EF91EC0\r
-:105FC000F7FE06C081E0C81AD108E10880E8F80AA7\r
-:105FD000C0920001D0920101E0920201F09203010F\r
-:105FE000C701B6019F77FF90EF90DF90CF90BF90F1\r
-:105FF000AF909F908F90089560930001709301017E\r
-:1060000080930201909303010895FB01DC0102C01B\r
-:1060100001900D9241505040D8F70895DC0101C025\r
-:106020006D9341505040E0F70895F999FECF92BD2D\r
-:1060300081BDF89A992780B50895262FF999FECF4A\r
-:106040001FBA92BD81BD20BD0FB6F894FA9AF99A95\r
-:106050000FBE01960895B7FF0C947E2E0E947E2EEF\r
-:0A606000821B930B0895F894FFCF04\r
-:10606A0001000000F82A00002801FE030000000FCA\r
-:10607A0000FFDCFE1101FF0005006F266F266F2668\r
-:10608A000000DD39800001781E00A02800F03C00E5\r
-:10609A00405100127A00E4A20024F40048E801907A\r
-:1060AA00D00320A10700000000B1004502B101BFE2\r
-:1060BA00018002C7004572726F722C20696E646596\r
-:1060CA007465726D696E61746520626F6F74207693\r
-:1060DA006563746F720D0A004572726F722C2069C3\r
-:1060EA006E636F727265637420626F6F7420766577\r
-:1060FA0063746F720D0A00010204000000001A2B7B\r
-:0A610A005A2C902AAA2A9C2AFA2A8D\r
-:00000001FF\r
diff --git a/Precompiled/rnode_firmware_latest_esp32_generic.zip b/Precompiled/rnode_firmware_latest_esp32_generic.zip
deleted file mode 100644 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
index 92413e0..0000000
+++ /dev/null
@@ -1,1505 +0,0 @@
-:100000000C945E010C94D91F0C94AE1F0C94831FAA\r
-:100010000C94581F0C942D1F0C94021F0C94D71E87\r
-:100020000C94AC1E0C9491010C9491010C949101D0\r
-:100030000C9491010C9491010C9491010C949101F8\r
-:100040000C9491010C9491010C9491010C949101E8\r
-:100050000C9491010C9491010C9491010C94621EEA\r
-:100060000C9491010C942C1E0C94021E0C94910182\r
-:100070000C9491010C9491010C9491010C94042026\r
-:100080000C9491010C9491010C9491010C949101A8\r
-:100090000C9491010C9491010C9491010C94910198\r
-:1000A0000C9491010C9491010C9491010C94910188\r
-:1000B0000C9491010C9491010C9491010C94910178\r
-:1000C0000C9491010C9491010C9491010C94910168\r
-:1000D0000C9491010C9491010C9491010C94910158\r
-:1000E0000C9491010C9448030C94E2030C94360395\r
-:1000F0000C944E030C945B040C94F81A0C94290293\r
-:100100000C94C9030C9433040C9452030C94130301\r
-:100110000C9444030C94BF200C94F1010C94061C25\r
-:100120000C94BD1B0C9451040C94D11A0C94320302\r
-:100130000C941A040C94F4030C941C1C0C94CB1B0C\r
-:100140000C94C0010C94CF010C94C1030C94E301F6\r
-:100150000C94B7040C947B020C9417030C94FC02CF\r
-:100160000C943D040C94371B0C94911A0C9440038E\r
-:100170000C94D8030C941B030C94FC030C940604FD\r
-:100180000C9410040C943A030C9428030C9422034E\r
-:100190000C9401030C9493010C94CE030C942C0347\r
-:1001A0000C94EC030C941F030C94D21A0C94470487\r
-:1001B0000C94290400002100240027002A002D00AF\r
-:1001C0003000330001010000040107010A010000B2\r
-:1001D0002200250028002B002E00310034000201EF\r
-:1001E0000000050108010B010505050507050808C4\r
-:1001F0000808020202020A0A0808040404040101B1\r
-:1002000001010101010103030303030303030407C5\r
-:1002100007070C0C0C0C0C0C0C0C0202020206065C\r
-:100220000606060606060B0B0B0B0B0B0B0B01024F\r
-:1002300010202008081020401020408002010201F8\r
-:1002400008040201010204081020408080402010B0\r
-:100250000804020180040201804020100804020109\r
-:100260000804020101020408102040800102040871\r
-:100270001020408000000A0B02090C0D0E08070335\r
-:100280000401000000000000000000000000000069\r
-:10029000000000000000000000000000000000005E\r
-:1002A000121110000000000000000000000000001B\r
-:1002B00000000000000000000000BF202092111F7D\r
-:1002C00011241FBECFEFD1E2DEBFCDBF00E00CBFD7\r
-:1002D00012E0A0E0B2E0E2E4FDE500E00BBF02C006\r
-:1002E00007900D92A83AB107D9F72FE1A8EAB2E03A\r
-:1002F00001C01D92A131B207E1F711E0CEE5D1E0D6\r
-:1003000000E006C021970109802FFE010E94A22C67\r
-:10031000CD35D10780E00807A9F70E9414210C947D\r
-:100320009F2E0C940000AF92BF92CF92DF92EF927B\r
-:10033000FF920F931F93CF93DF936C017B018B018F\r
-:10034000040F151FEB015E01AE18BF08C017D107DF\r
-:1003500059F06991D601ED91FC910190F081E02D69\r
-:10036000C6011995892B79F7C501DF91CF911F91AE\r
-:100370000F91FF90EF90DF90CF90BF90AF900895D6\r
-:10038000FC01538D448D252F30E0842F90E0821B9B\r
-:10039000930B541710F0CF96089501970895FC0120\r
-:1003A000918D828D981761F0A28DAE0FBF2FB11D78\r
-:1003B0005D968C91928D9F5F9F73928F90E00895D0\r
-:1003C0008FEF9FEF0895FC01918D828D981731F08A\r
-:1003D000828DE80FF11D858D90E008958FEF9FEFDE\r
-:1003E0000895FC01918D228D892F90E0805C9F4FB4\r
-:1003F000821B91098F739927089581E59EE10E94E0\r
-:10040000F10121E0892B09F420E0822F0895FC01FD\r
-:10041000A48DA80FB92FB11DA35ABF4F2C91848D65\r
-:1004200090E001968F739927848FA689B7892C93C2\r
-:10043000A089B1898C91837080648C93938D848D15\r
-:10044000981306C00288F389E02D80818F7D808318\r
-:100450000895EF92FF920F931F93CF93DF93EC01D8\r
-:1004600081E0888F9B8D8C8D98131AC0E889F9895B\r
-:10047000808185FF15C09FB7F894EE89FF8960835E\r
-:10048000E889F98980818370806480839FBF81E0DF\r
-:1004900090E0DF91CF911F910F91FF90EF90089521\r
-:1004A000F62E0B8D10E00F5F1F4F0F731127E02EFC\r
-:1004B0008C8D8E110CC00FB607FCFACFE889F98934\r
-:1004C000808185FFF5CFCE010E940702F1CFEB8D31\r
-:1004D000EC0FFD2FF11DE35AFF4FF0829FB7F89408\r
-:1004E0000B8FEA89FB8980818062CFCF682F81E5FD\r
-:1004F0009EE10C942902CF93DF93EC01888D882331\r
-:10050000B9F0AA89BB89E889F9898C9185FD03C076\r
-:10051000808186FD0DC00FB607FCF7CF8C9185FF5B\r
-:10052000F2CF808185FFEDCFCE010E940702E9CF97\r
-:10053000DF91CF91089580E090E0892B29F00E940F\r
-:10054000FD0181110E94000080E090E0892B49F0BC\r
-:1005500080E090E0892B29F00E94000081110E9428\r
-:10056000000080E090E0892B49F080E090E0892B4A\r
-:1005700029F00E94000081110E94000080E090E0BC\r
-:10058000892B49F080E090E0892B29F00E9400003F\r
-:1005900081110C9400000895282F30E0F901EC58E7\r
-:1005A000FD4F9491F901E25DFD4F449128513E4F7A\r
-:1005B000F9012491222309F435C0992311F19150B6\r
-:1005C0009231F8F4E92FF0E08827E651FD4F8F4F84\r
-:1005D0000C94A22C1B031F03FC021303170301033B\r
-:1005E000220328032C03320336033A034003440357\r
-:1005F000010348034E035203809180008F7780935C\r
-:100600008000E22FF0E0EE0FFF1FE253FE4FA591B6\r
-:10061000B4918FB7F894EC91611148C040954E2386\r
-:100620004C938FBF0895809180008F7DE8CF80919B\r
-:100630008000877FE4CF84B58F7784BDE2CF84B517\r
-:100640008F7DFBCF8091B0008F778093B000D9CFA2\r
-:100650008091B0008F7DF9CF809190008F7780934B\r
-:100660009000CFCF809190008F7DF9CF8091900046\r
-:10067000877FF5CF8091A0008F778093A000C1CFB6\r
-:100680008091A0008F7DF9CF8091A000877FF5CF6A\r
-:10069000809120018F7780932001B3CF809120013A\r
-:1006A0008F7DF9CF80912001877FF5CF4E2BB8CF7A\r
-:1006B000CF93DF9390E0FC01E25DFD4F24918851E0\r
-:1006C0009E4FFC0184918823D1F090E0880F991F00\r
-:1006D000FC01EC54FE4FA591B491FC01E253FE4F96\r
-:1006E000C591D49161110EC09FB7F8948C91E22FFF\r
-:1006F000E0958E238C932881E223E8839FBFDF91CE\r
-:10070000CF9108958FB7F894EC91E22BEC938FBFC3\r
-:10071000F6CF1F93CF93DF93182FEB0161E00E9478\r
-:100720005803209739F460E0812FDF91CF911F911A\r
-:100730000C94CC02CF3FD10511F461E0F5CFE12F4D\r
-:10074000F0E0EC58FD4FE491E150E23108F0B3C025\r
-:10075000F0E08827E155FC4F8F4F0C94A22CC10389\r
-:10076000C903CE03D803E2035B04EC03F403FC03E8\r
-:10077000060410041A04290433045B043D044704EE\r
-:10078000510484B5806884BDC7BDDF91CF911F91AE\r
-:10079000089584B5806284BDC8BDF7CF8091800084\r
-:1007A000806880938000D0938900C0938800EDCF4B\r
-:1007B00080918000806280938000D0938B00C093F2\r
-:1007C0008A00E3CF80918000886080938000D0937E\r
-:1007D0008D00C0938C00D9CF8091B0008068809349\r
-:1007E000B000C093B300D1CF8091B00080628093FD\r
-:1007F000B000C093B400C9CF80919000806880930E\r
-:100800009000D0939900C0939800BFCF8091900042\r
-:10081000806280939000D0939B00C0939A00B5CFE4\r
-:1008200080919000886080939000D0939D00C09349\r
-:100830009C00ABCF8091A00080688093A000809145\r
-:10084000A0008F7B8093A000D093A900C093A80044\r
-:100850009CCF8091A00080628093A000D093AB00D9\r
-:10086000C093AA0092CF8091A00088608093A000DE\r
-:10087000D093AD00C093AC0088CF809120018068F8\r
-:1008800080932001D0932901C09328017ECF8091CD\r
-:100890002001806280932001D0932B01C0932A0114\r
-:1008A00074CF80912001886080932001D0932D0126\r
-:1008B000C0932C016ACFC038D1050CF03ECF33CFA6\r
-:1008C0003FB7F8948091B4029091B502A091B6021E\r
-:1008D000B091B70226B5A89B05C02F3F19F001962D\r
-:1008E000A11DB11D3FBFBA2FA92F982F8827BC018A\r
-:1008F000CD01620F711D811D911D42E0660F771FB2\r
-:10090000881F991F4A95D1F708958F929F92AF92B1\r
-:10091000BF92CF92DF92EF92FF924B015C010E9457\r
-:1009200060046B017C010E9460046C197D098E09D2\r
-:100930009F09683E734081059105A8F321E0821A62\r
-:100940009108A108B10888EEC80E83E0D81EE11C0A\r
-:10095000F11C81149104A104B10429F7FF90EF90D8\r
-:10096000DF90CF90BF90AF909F908F9008950895A3\r
-:100970008091200A9091210AA091220AB091230A25\r
-:10098000892B8A2B8B2BE1F080911C0A90911D0AF8\r
-:10099000A0911E0AB0911F0A892B8A2B8B2B81F004\r
-:1009A00080911602909117028F3F910549F0809136\r
-:1009B0001A0A90911B0A892B19F0109213020895BC\r
-:1009C00081E0809313020895CF93DF9380EC90E051\r
-:1009D0000E94760290E080E00E947602CEE1D3E0B1\r
-:1009E0002091AA023091AB02CE018E519340821722\r
-:1009F0009307A8F46991603C51F48BED90E00E945C\r
-:100A000076026CED81E59EE10E942902E9CF6B3D03\r
-:100A1000C9F78BED90E00E9476026DEDF3CF80EC8C\r
-:100A200090E00E9476021092AB021092AA02DF912F\r
-:100A3000CF91089580EC90E00E94760283E090E0F0\r
-:100A40000E9476026091160281E59EE10E942902D1\r
-:100A500080EC90E00C94760280EC90E00E947602AC\r
-:100A600085E090E00E9476026091140281E59EE1AB\r
-:100A70000E94290280EC90E00C94760280EC90E0D9\r
-:100A80000E94760284E090E00E94760260911A0A49\r
-:100A900081E59EE10E94290280EC90E00C947602B0\r
-:100AA000CF93C82F80EC90E00E94760280E990E01E\r
-:100AB0000E9476026C2F81E59EE10E94290280EC63\r
-:100AC00090E0CF910C947602803C51F48BED90E055\r
-:100AD0000E9476028CED682F81E59EE10C9429023C\r
-:100AE0008B3DC9F78BED90E00E9476028DEDF3CF40\r
-:100AF0001F93CF93DF9380EC90E00E94760281E019\r
-:100B000090E00E947602C091200AD091210A1091B3\r
-:100B1000220A8091230A0E946405812F0E946405A5\r
-:100B20008D2F0E9464058C2F0E94640580EC90E05C\r
-:100B3000DF91CF911F910C9476021F93CF93DF9397\r
-:100B400080EC90E00E94760282E090E00E947602C3\r
-:100B5000C0911C0AD0911D0A10911E0A80911F0A93\r
-:100B60000E946405812F0E9464058D2F0E946405F8\r
-:100B70008C2F0E94640580EC90E0DF91CF911F9153\r
-:100B80000C94760280EC90E00E94760284E290E081\r
-:100B90000E9476028091A9020E94640580EC90E098\r
-:100BA0000C947602CF93C0910102C35680EC90E082\r
-:100BB0000E94760283E290E00E9476028C2F0E94CF\r
-:100BC000640580EC90E0CF910C94760260E08DE0BB\r
-:100BD0000C94CC0261E08DE00C94CC02CF93DF93B7\r
-:100BE000EC0161E08DE00E94CC020E94E60564E623\r
-:100BF00070E080E090E00E9485040E94EA0564E6CF\r
-:100C000070E080E090E00E948504219771F7DF9109\r
-:100C1000CF910C94E60560E08CE00C94CC020F932D\r
-:100C20001F93CF93DF938C01EC01892B11F4C1E06A\r
-:100C3000D0E00E940B0664E670E080E090E00E9445\r
-:100C4000850461E08CE00E94CC0264E670E080E004\r
-:100C500090E00E9485040115110559F3219749F789\r
-:100C6000DF91CF911F910F910C940B060E94EA0522\r
-:100C70000E940B066AE070E080E090E00E9485042C\r
-:100C800061E08CE00E94CC020E94E60565E070E025\r
-:100C900080E090E00E948504E9CF0F931F93CF93EB\r
-:100CA000DF938C01EC01892B11F4C1E0D0E061E00D\r
-:100CB0008CE00E94CC0260E08DE00E94CC0264E6F1\r
-:100CC00070E080E090E00E94850460E08CE00E948B\r
-:100CD000CC0261E08DE00E94CC0264E670E080E02E\r
-:100CE00090E00E9485040115110509F32197F9F69A\r
-:100CF0000E940B06DF91CF911F910F910C94E60596\r
-:100D00002F923F924F925F926F927F928F929F921B\r
-:100D1000AF92BF92CF92DF92EF92FF920F931F9309\r
-:100D2000CF93DF93CDB7DEB7C857D1090FB6F8948C\r
-:100D3000DEBF0FBECDBFE9969FAF8EAFE9971A0118\r
-:100D4000DC0118960D911D912D913C911B97619698\r
-:100D50000CAF1DAF2EAF3FAF61971C960D911D914B\r
-:100D60002D913C911F9709831A832B833C835096C6\r
-:100D70000D911D912D913C9153970D831E832F83CF\r
-:100D8000388754960D911D912D913C915797098765\r
-:100D90001A872B873C87E6966FAFE697E7967FAF7B\r
-:100DA000E797A85ABF4F6396BFAFAEAF6397FC01FA\r
-:100DB000E45AFF4F6596FFAFEEAF65978C01005A7E\r
-:100DC0001F4F67961FAF0EAF67979C012C593F4F7F\r
-:100DD00069963FAF2EAF699788599F4F6B969FAF2B\r
-:100DE0008EAF6B97E996AEADBFADE997A459BF4FF3\r
-:100DF0006D96BFAFAEAF6D97E996EEADFFADE997DB\r
-:100E0000E059FF4F6F96FFAFEEAF6F97E9960EADCB\r
-:100E10001FADE9970C581F4FA1961FAF0EAFA197BA\r
-:100E2000E9962EAD3FADE99728583F4FA3963FAFC7\r
-:100E30002EAFA397E9968EAD9FADE99784589F4F4B\r
-:100E4000A5969FAF8EAFA597E996AEADBFADE997DA\r
-:100E5000A058BF4FA796BFAFAEAFA797E996EEAD2C\r
-:100E6000FFADE997EC57FF4FA996FFAFEEAFA997FB\r
-:100E7000E9960EAD1FADE99708571F4FAB961FAF10\r
-:100E80000EAFAB97E9962EAD3FADE99724573F4F94\r
-:100E9000AD963FAF2EAFAD97E9968EAD9FADE9977A\r
-:100EA00080579F4FAF969FAF8EAFAF97E996AEAD8D\r
-:100EB000BFADE997AC56BF4FE196BFAFAEAFE1977C\r
-:100EC000E796AEADBFADE7970D911D912D913C9189\r
-:100ED0000D871E872F87388B6396EEADFFAD639726\r
-:100EE000008311832283338361968CAD9DADAEADBB\r
-:100EF000BFAD619788589B45A549B842800F911FA7\r
-:100F0000A21FB31F4D815E816F81788509851A8587\r
-:100F10002B853C85402751276227732709811A8139\r
-:100F20002B813C81402351236223732309851A8539\r
-:100F30002B853C854027512762277327840F951FF7\r
-:100F4000A61FB71F8C019D0147E0000F111F221F34\r
-:100F5000331F4A95D1F7AC01BD01E9E1769567955C\r
-:100F600057954795EA95D1F7402B512B622B732B60\r
-:100F700089819A81AB81BC81480F591F6A1F7B1FF1\r
-:100F8000E796AEADBFADE79714960D911D912D91EB\r
-:100F90003C911797098B1A8B2B8B3C8B6596EEAD8A\r
-:100FA000FFAD6597008311832283338389859A85FA\r
-:100FB000AB85BC858A5A9844A843B741800F911FDE\r
-:100FC000A21FB31FC980DA80EB80FC800D811E81D7\r
-:100FD0002F813885C026D126E226F326C422D522C9\r
-:100FE000E622F722C026D126E226F3268C0D9D1D8F\r
-:100FF000AE1DBF1D8C019D01FCE0000F111F221FC3\r
-:10100000331FFA95D1F76C017D01A4E1F694E794C2\r
-:10101000D794C794AA95D1F7C02AD12AE22AF32AF5\r
-:10102000C40ED51EE61EF71EE796AEADBFADE79720\r
-:1010300018968D919D910D90BC91A02D8D8B9E8BBE\r
-:10104000AF8BB88F6796EEADFFAD679780839183C6\r
-:10105000A283B3838D819E81AF81B88585529F48DD\r
-:10106000AF4DBB4D0D891E892F89388D800F911F83\r
-:10107000A21FB31F49805A806B807C80442655266E\r
-:10108000662677264C205D206E207F2009811A81FC\r
-:101090002B813C814026512662267326840D951DA6\r
-:1010A000A61DB71D8C019D01EFE03695279517957C\r
-:1010B0000795EA95D1F72C013D01F1E1440C551C4F\r
-:1010C000661C771CFA95D1F7402A512A622A732AA6\r
-:1010D0004C0C5D1C6E1C7F1CE796AEADBFADE79758\r
-:1010E0001C968D919D910D90BC91A02D898F9A8F0A\r
-:1010F000AB8FBC8F6996EEADFFAD6997808391830E\r
-:10110000A283B38389819A81AB81BC81825191434F\r
-:10111000A244BE43098D1A8D2B8D3C8D800F911FEB\r
-:10112000A21FB31F8A019B010C251D252E253F25DB\r
-:1011300004211521262137210427152726273727A3\r
-:10114000800F911FA21FB31F4C015D011AE0B694DE\r
-:10115000A794979487941A95D1F78C019D0186E105\r
-:10116000000F111F221F331F8A95D1F70829192953\r
-:101170002A293B29040D151D261D371DE796AEAD06\r
-:10118000BFADE79750968D919D910D90BC91A02D8C\r
-:101190008D8F9E8FAF8FB8A36B96EEADFFAD6B9723\r
-:1011A00080839183A283B3838155904FA348BA4033\r
-:1011B000840F951FA61FB71FB701A601442555250B\r
-:1011C0006625772540235123622373234C255D2513\r
-:1011D0006E257F25840F951FA61FB71F4C015D014B\r
-:1011E00047E0880C991CAA1CBB1C4A95D1F7AC019E\r
-:1011F000BD01E9E17695679557954795EA95D1F751\r
-:10120000482959296A297B29400F511F621F731FE2\r
-:10121000E796AEADBFADE79754968D919D910D9039\r
-:10122000BC91A02D89A39AA3ABA3BCA36D96EEADF0\r
-:10123000FFAD6D9780839183A283B383865D9943CD\r
-:10124000A847B84B8C0D9D1DAE1DBF1D73016201DB\r
-:10125000C026D126E226F326C422D522E622F72292\r
-:10126000C424D524E624F7248C0D9D1DAE1DBF1D7E\r
-:101270004C015D01FCE0880C991CAA1CBB1CFA9572\r
-:10128000D1F76C017D01A4E1F694E794D794C7945B\r
-:10129000AA95D1F7C828D928EA28FB28C40ED51E5C\r
-:1012A000E61EF71EE796AEADBFADE79758968D9157\r
-:1012B0009D910D90BC91A02D8DA39EA3AFA3B8A727\r
-:1012C0006F96EEADFFAD6F9780839183A283B3835A\r
-:1012D0008D5E994BAF4CB745840D951DA61DB71D6E\r
-:1012E0002801390144265526662677264C205D20A4\r
-:1012F0006E207F204026512662267326840D951D80\r
-:10130000A61DB71D4C015D01EFE0B694A79497941C\r
-:101310008794EA95D1F72C013D01F1E1440C551C6D\r
-:10132000661C771CFA95D1F7482859286A287B282B\r
-:101330004C0C5D1C6E1C7F1CE796AEADBFADE797F5\r
-:101340005C968D919D910D90BC91A02D89A79AA737\r
-:10135000ABA7BCA7A196EEADFFADA197808391830B\r
-:10136000A283B3838F5F9A46A94BB240800F911F2F\r
-:10137000A21FB31F8A019B010C251D252E253F2589\r
-:101380000421152126213721042715272627372751\r
-:10139000800F911FA21FB31F4C015D011AE0B6948C\r
-:1013A000A794979487941A95D1F78C019D0186E1B3\r
-:1013B000000F111F221F331F8A95D1F70829192901\r
-:1013C0002A293B29040D151D261D371DE796AEADB4\r
-:1013D000BFADE79790968D919D910D90BC91A02DFA\r
-:1013E0008DA79EA7AFA7B8ABA396EEADFFADA39711\r
-:1013F00080839183A283B38388529746AF47B649CF\r
-:10140000840F951FA61FB71FB701A60144255525B8\r
-:101410006625772540235123622373234C255D25C0\r
-:101420006E257F25840F951FA61FB71F4C015D01F8\r
-:1014300047E0880C991CAA1CBB1C4A95D1F7AC014B\r
-:10144000BD01E9E17695679557954795EA95D1F7FE\r
-:10145000482959296A297B29400F511F621F731F90\r
-:10146000E796AEADBFADE79794968D919D910D90A7\r
-:10147000BC91A02D89AB9AABABABBCABA596EEAD46\r
-:10148000FFADA59780839183A283B3838155984054\r
-:10149000AB4BB4478C0D9D1DAE1DBF1D730162018A\r
-:1014A000C026D126E226F326C422D522E622F72240\r
-:1014B000C424D524E624F7248C0D9D1DAE1DBF1D2C\r
-:1014C0004C015D01FCE0880C991CAA1CBB1CFA9520\r
-:1014D000D1F76C017D01A4E1F694E794D794C79409\r
-:1014E000AA95D1F7C828D928EA28FB28C40ED51E0A\r
-:1014F000E61EF71EE796AEADBFADE79798968D91C5\r
-:101500009D910D90BC91A02D8DAB9EABAFABB8AFB4\r
-:10151000A796EEADFFADA79780839183A283B38397\r
-:101520008F54944AA109B109840D951DA61DB71DBC\r
-:101530002801390144265526662677264C205D2051\r
-:101540006E207F204026512662267326840D951D2D\r
-:10155000A61DB71D4C015D01EFE0B694A7949794CA\r
-:101560008794EA95D1F72C013D01F1E1440C551C1B\r
-:10157000661C771CFA95D1F7482859286A287B28D9\r
-:101580004C0C5D1C6E1C7F1CE796AEADBFADE797A3\r
-:101590009C968D919D910D90BC91A02D89AF9AAF95\r
-:1015A000ABAFBCAFA996EEADFFADA9978083918399\r
-:1015B000A283B38382549842A34AB647800F911FF7\r
-:1015C000A21FB31F8A019B010C251D252E253F2537\r
-:1015D00004211521262137210427152726273727FF\r
-:1015E000800F911FA21FB31F4C015D011AE0B6943A\r
-:1015F000A794979487941A95D1F78C019D0186E161\r
-:10160000000F111F221F331F8A95D1F708291929AE\r
-:101610002A293B29040D151D261D371DE796AEAD61\r
-:10162000BFADE797D0968D919D910D90BC91A02D67\r
-:1016300021968CAF9DAFAEAFBFAF2197AB96EEAD0D\r
-:10164000FFADAB9780839183A283B3834C015D018F\r
-:10165000FEED8F1AFEEE9F0AFFE6AF0AF4E9BF0A1D\r
-:10166000840E951EA61EB71EB701A601442555255A\r
-:101670006625772540235123622373234C255D255E\r
-:101680006E257F25480D591D6A1D7B1D4A015B0192\r
-:1016900097E0880C991CAA1CBB1C9A95D1F7E9E12C\r
-:1016A0007695679557954795EA95D1F74829592931\r
-:1016B0006A297B29400F511F621F731FE796AEAD49\r
-:1016C000BFADE797D4968D919D910D90BC91A02DC3\r
-:1016D00025968CAF9DAFAEAFBFAF2597AD96EEAD63\r
-:1016E000FFADAD9780839183A283B3834C015D01ED\r
-:1016F000FDE68F1AFEE89F0AF7E6AF0AF2E0BF0A9E\r
-:101700008C0C9D1CAE1CBF1C73016201C026D1262F\r
-:10171000E226F326C422D522E622F722C424D524C9\r
-:10172000E624F724C80CD91CEA1CFB1C460157010F\r
-:10173000FCE0880C991CAA1CBB1CFA95D1F7A4E10B\r
-:10174000F694E794D794C794AA95D1F7C828D928D6\r
-:10175000EA28FB28C40ED51EE61EF71EE796AEAD9E\r
-:10176000BFADE797D8968D919D910D90BC91A02D1E\r
-:1017700029968CAF9DAFAEAFBFAF2997AF96EEADB8\r
-:10178000FFADAF9780839183A283B3834C015D014A\r
-:10179000F2E78F1AFCEB9F0AF6E8AF0AF9E5BF0AF9\r
-:1017A000840C951CA61CB71C28013901442655261B\r
-:1017B000662677264C205D206E207F20402651260D\r
-:1017C00062267326480C591C6A1C7B1C530142017B\r
-:1017D000BFE0B694A79497948794BA95D1F781E126\r
-:1017E000440C551C661C771C8A95D1F7482859284B\r
-:1017F0006A287B284C0C5D1C6E1C7F1CE796AEADE6\r
-:10180000BFADE797DC968D919D910D90BC91A02D79\r
-:101810002D968CAF9DAFAEAFBFAF2D97E196EEADDD\r
-:10182000FFADE19780839183A283B3834C015D0177\r
-:10183000FFED8F1AF7EF9F0AFBE4AF0AF6EBBF0A42\r
-:10184000800E911EA21EB31E8A019B010C251D2530\r
-:101850002E253F2504211521262137210427152770\r
-:1018600026273727080D191D2A1D3B1D4801590140\r
-:101870009AE0B694A794979487949A95D1F7E6E165\r
-:10188000000F111F221F331FEA95D1F708291929CC\r
-:101890002A293B29040D151D261D371D89889A8884\r
-:1018A000AB88BC888EE9881A8AED980A81EEA80A6E\r
-:1018B00089E0B80A840E951EA61EB71EB301A201C8\r
-:1018C00040275127622773274C215D216E217F21FC\r
-:1018D0004425552566257725480D591D6A1D7B1D14\r
-:1018E0004A015B01F5E0880C991CAA1CBB1CFA9507\r
-:1018F000D1F7ABE17695679557954795AA95D1F7BE\r
-:10190000482959296A297B29400F511F621F731FDB\r
-:101910008DA09EA0AFA0B8A490EC891A9CE4990A6F\r
-:101920009FEBA90A9FE3B90A8C0C9D1CAE1CBF1C3F\r
-:1019300068017901C426D526E626F726C420D520DD\r
-:10194000E620F720C026D126E226F326C80CD91CB3\r
-:10195000EA1CFB1C46015701B9E0880C991CAA1C23\r
-:10196000BB1CBA95D1F787E1F694E794D794C79456\r
-:101970008A95D1F7C828D928EA28FB28C40ED51E95\r
-:10198000E61EF71E89AC9AACABACBCACAFEA8A1AC7\r
-:10199000A5EA9A0AA1EAAA0AA9EDBA0A840C951C3A\r
-:1019A000A61CB71C2A013B014C245D246E247F2415\r
-:1019B000402251226222732244265526662677262B\r
-:1019C000480C591C6A1C7B1C530142019EE0880C88\r
-:1019D000991CAA1CBB1C9A95D1F7E2E176946794F6\r
-:1019E00057944794EA95D1F7482859286A287B28C4\r
-:1019F0004C0C5D1C6E1C7F1C8D849E84AF84B8884B\r
-:101A0000B6E58B1AB8E39B0AB9E4AB0AB6E1BB0AA8\r
-:101A1000800E911EA21EB31E970186010425152576\r
-:101A20002625372504231523262337230C251D259A\r
-:101A30002E253F25080D191D2A1D3B1D4801590162\r
-:101A4000FCE0B694A79497948794FA95D1F7A4E113\r
-:101A5000000F111F221F331FAA95D1F7082919293A\r
-:101A60002A293B29040D151D261D371D89A09AA082\r
-:101A7000ABA0BCA0E3EA8E1AEFEE9E0AE0EDAE0A40\r
-:101A8000E9E2BE0A840E951EA61EB71EB301A2018E\r
-:101A900040275127622773274C215D216E217F212A\r
-:101AA0004425552566257725480D591D6A1D7B1D42\r
-:101AB0004A015B01B5E0880C991CAA1CBB1CBA95B5\r
-:101AC000D1F78BE176956795579547958A95D1F72C\r
-:101AD000482959296A297B29400F511F621F731F0A\r
-:101AE0008DA89EA8AFA8B8ACFDEA8F1AFBEE9F0A9E\r
-:101AF000FBEBAF0AFDEFBF0A8C0C9D1CAE1CBF1C9C\r
-:101B000068017901C426D526E626F726C420D5200B\r
-:101B1000E620F720C026D126E226F326C80CD91CE1\r
-:101B2000EA1CFB1C4601570199E0880C991CAA1C71\r
-:101B3000BB1C9A95D1F7E7E1F694E794D794C79444\r
-:101B4000EA95D1F7C828D928EA28FB28C40ED51E63\r
-:101B5000E61EF71E2D968CAC9DACAEACBFAC2D979F\r
-:101B60008FE7881A89E1980A8EE5A80A87E2B80A01\r
-:101B7000840C951CA61CB71C2A013B014C245D2437\r
-:101B80006E247F244022512262227322442655264D\r
-:101B900066267726480C591C6A1C7B1C530142019F\r
-:101BA000FEE0880C991CAA1CBB1CFA95D1F7A2E197\r
-:101BB0007694679457944794AA95D1F74828592862\r
-:101BC0006A287B284C0C5D1C6E1C7F1C8D8C9E8CA7\r
-:101BD000AF8CB8A098E3891A94E0990A9CE2A90A0C\r
-:101BE00098E1B90A800E911EA21EB31E97018601CC\r
-:101BF00004251525262537250423152326233723D9\r
-:101C00000C251D252E253F25080D191D2A1D3B1DC0\r
-:101C100048015901BCE0B694A79497948794BA956B\r
-:101C2000D1F784E1000F111F221F331F8A95D1F7CE\r
-:101C3000082919292A293B29040D151D261D371DA0\r
-:101C400089A89AA8ABA8BCA8AAE18A1AA2E39A0A12\r
-:101C5000AEE1AA0AAEEDBA0A840E951EA61EB71E04\r
-:101C6000B301A20140275127622773274C215D2130\r
-:101C70006E217F214425552566257725480D591D60\r
-:101C80006A1D7B1D4A015B0195E0880C991CAA1C0A\r
-:101C9000BB1C9A95D1F7EBE17695679557954795DB\r
-:101CA000EA95D1F7482959296A297B29400F511F04\r
-:101CB000621F731F29968CAC9DACAEACBFAC29974C\r
-:101CC000BAE28B1AB8EF9B0AB8ECAB0ABCE3BB0ACA\r
-:101CD0008C0C9D1CAE1CBF1C68017901C426D52646\r
-:101CE000E626F726C420D520E620F720C026D126F8\r
-:101CF000E226F326C80CD91CEA1CFB1C460157013E\r
-:101D0000F9E0880C991CAA1CBB1CFA95D1F7A7E135\r
-:101D1000F694E794D794C794AA95D1F7C828D92800\r
-:101D2000EA28FB28C40ED51EE61EF71E898C9A8C65\r
-:101D3000AB8CBC8CE9E78E1AE2EF9E0AEAE2AE0AAF\r
-:101D4000EBE0BE0A840C951CA61CB71C2A013B01C3\r
-:101D50004C245D246E247F2440225122622273226F\r
-:101D60004426552666267726480C591C6A1C7B1C7F\r
-:101D700053014201BEE0880C991CAA1CBB1CBA95F9\r
-:101D8000D1F782E176946794579447948A95D1F776\r
-:101D9000482859286A287B284C0C5D1C6E1C7F1C27\r
-:101DA0008DA49EA4AFA4B8A8F3E18F1AFBEE9F0AFE\r
-:101DB000F5EAAF0AFAEBBF0A800E911EA21EB31E0F\r
-:101DC000970186010425152526253725042315238B\r
-:101DD000262337230C251D252E253F25080D191DEB\r
-:101DE0002A1D3B1D480159019CE0B694A794979485\r
-:101DF00087949A95D1F7E4E1000F111F221F331F3A\r
-:101E0000EA95D1F7082919292A293B29040D151D1E\r
-:101E1000261D371D25968CAC9DACAEACBFAC25976E\r
-:101E20008BEF881A86E1980A8CE1A80A86E5B80A41\r
-:101E3000840E951EA61EB71EB301A201402751278E\r
-:101E4000622773274C215D216E217F214425552572\r
-:101E500066257725480D591D6A1D7B1D4A015B01CA\r
-:101E6000F5E0880C991CAA1CBB1CFA95D1F7ABE1D4\r
-:101E70007695679557954795AA95D1F74829592999\r
-:101E80006A297B29400F511F621F731F8D889E880E\r
-:101E9000AF88B88C98E0891A9CE5990A90E1A90A64\r
-:101EA00093E0B90A8C0C9D1CAE1CBF1C6801790123\r
-:101EB000C426D526E626F726C420D520E620F7201E\r
-:101EC000C026D126E226F326C80CD91CEA1CFB1C2E\r
-:101ED00046015701B9E0880C991CAA1CBB1CBA9595\r
-:101EE000D1F787E1F694E794D794C7948A95D1F710\r
-:101EF000C828D928EA28FB28C40ED51EE61EF71EDE\r
-:101F000089A49AA4ABA4BCA4A7E28A1AADEF9A0A4A\r
-:101F1000A0E9AA0AA8E9BA0A840C951CA61CB71C59\r
-:101F20002A013B014C245D246E247F24402251224F\r
-:101F3000622273224426552666267726480C591CB1\r
-:101F40006A1C7B1C530142019EE0880C991CAA1C50\r
-:101F5000BB1C9A95D1F7E2E1769467945794479425\r
-:101F6000EA95D1F7482859286A287B284C0C5D1C33\r
-:101F70006E1C7F1C4601570184249524A624B72497\r
-:101F800021968CAD9DADAEADBFAD21978657934BDD\r
-:101F9000A54DB247800F911FA21FB31F9501840169\r
-:101FA00004231523262337230C251D252E253F2505\r
-:101FB000800F911FA21FB31F8C019D01FCE036957D\r
-:101FC000279517950795FA95D1F7E5960CAF1DAFB4\r
-:101FD0002EAF3FAFE5978C019D01A4E1000F111FCB\r
-:101FE000221F331FAA95D1F7E5968CAD9DADAEADFE\r
-:101FF000BFADE597082B192B2A2B3B2B040D151D84\r
-:10200000261D371D89A19AA1ABA1BCA18E5B964C60\r
-:10201000A540B109840F951FA61FB71F80269126E2\r
-:10202000A226B326880E991EAA1EBB1ED501C40186\r
-:1020300044E0880F991FAA1FBB1F4A95D1F75CE1A6\r
-:10204000B694A794979487945A95D1F7882A992A99\r
-:10205000AA2ABB2A800E911EA21EB31E4DA55EA504\r
-:102060006FA578A94F5759406E4878474C0D5D1DB4\r
-:102070006E1D7F1D73016201C026D126E226F32664\r
-:10208000C824D924EA24FB24C40ED51EE61EF71E5C\r
-:10209000B701A601EBE0440F551F661F771FEA95B5\r
-:1020A000D1F7F5E1F694E794D794C794FA95D1F770\r
-:1020B000C42AD52AE62AF72AC80CD91CEA1CFB1C1C\r
-:1020C00049AD5AAD6BAD7CAD4E5D5E49624672491D\r
-:1020D000440E551E661E771EB901A80148255925D4\r
-:1020E0006A257B254C255D256E257F25440E551ED2\r
-:1020F000661E771ED20123013D014C0C5D1C6E1C37\r
-:102100007F1C29964CAD5DAD6EAD7FAD2997445FC8\r
-:10211000574C6A417240400F511F621F731F950157\r
-:1021200084010C251D252E253F2504251525262552\r
-:102130003725040F151F261F371FB901A801A9E075\r
-:102140007695679557954795AA95D1F7B7E1000F12\r
-:10215000111F221F331FBA95D1F7042B152B262BE5\r
-:10216000372B040D151D261D371D49895A896B898A\r
-:102170007C894C5B554161447B45480D591D6A1D66\r
-:102180007B1D4601570184249524A624B72480266C\r
-:102190009126A226B326840E951EA61EB71EB50153\r
-:1021A000A40184E0440F551F661F771F8A95D1F75D\r
-:1021B0009CE1B694A794979487949A95D1F7842A32\r
-:1021C000952AA62AB72A800E911EA21EB31E4D8DF7\r
-:1021D0005E8D6F8D78A1475550436142744BC40E9C\r
-:1021E000D51EE61EF71EB301A20140275127622724\r
-:1021F0007327482559256A257B254C0D5D1D6E1DCD\r
-:102200007F1DDB01CA01EBE0880F991FAA1FBB1FCE\r
-:10221000EA95D1F7F5E17695679557954795FA9543\r
-:10222000D1F7482B592B6A2B7B2B480D591D6A1D62\r
-:102230007B1DC9A4DAA4EBA4FCA4B0EACB1AB4EBCE\r
-:10224000DB0AB4E4EB0AB9E0FB0A4C0C5D1C6E1C23\r
-:102250007F1C68017901C824D924EA24FB24C42600\r
-:10226000D526E626F7264C0C5D1C6E1C7F1CF20161\r
-:1022700023013F01440E551E661E771ECDA8DEA821\r
-:10228000EFA8F8ACF0E9CF1AF3E4DF0AF0E4EF0AC4\r
-:10229000F1E4FF0AC00ED11EE21EF31E9501840177\r
-:1022A0000427152726273727042515252625372512\r
-:1022B0000C0D1D1D2E1D3F1D68017901A9E0F6942E\r
-:1022C000E794D794C794AA95D1F7B7E1000F111FEF\r
-:1022D000221F331FBA95D1F70C291D292E293F291A\r
-:1022E000040D151D261D371D2596CCACDDACEEACBE\r
-:1022F000FFAC25978AE3C81A81E8D80A84E6E80A81\r
-:1023000087EDF80AC80CD91CEA1CFB1C4A015B01CA\r
-:1023100084249524A624B72480269126A226B326B9\r
-:102320008C0C9D1CAE1CBF1C7501640184E0CC0CA0\r
-:10233000DD1CEE1CFF1C8A95D1F79CE1B694A79496\r
-:10234000979487949A95D1F78C289D28AE28BF281A\r
-:10235000800E911EA21EB31ECD84DE84EF84F88809\r
-:1023600096E0C91A98EDD90A9EE5E90A95E1F90ABD\r
-:10237000C40ED51EE61EF71EB301A2014027512749\r
-:1023800062277327482559256A257B254C0D5D1D3D\r
-:102390006E1D7F1D6A017B01EBE0CC0CDD1CEE1C89\r
-:1023A000FF1CEA95D1F7F5E1769567955795479526\r
-:1023B000FA95D1F74C295D296E297F29480D591DC1\r
-:1023C0006A1D7B1DC98CDA8CEB8CFC8CABE7CA1ABE\r
-:1023D000AFECDA0AA0E1EA0AABE2FA0A4C0C5D1CA7\r
-:1023E0006E1C7F1C68017901C824D924EA24FB24CF\r
-:1023F000C426D526E626F7264C0C5D1C6E1C7F1CD9\r
-:10240000F20123013F01440E551E661E771ECDA02A\r
-:10241000DEA0EFA0F8A4FBEFCF1AF2EEDF0AF7E799\r
-:10242000EF0AFBEFFF0AC00ED11EE21EF31E95015C\r
-:102430008401042715272627372704251525262557\r
-:1024400037250C0D1D1D2E1D3F1D68017901A9E0CA\r
-:10245000F694E794D794C794AA95D1F7B7E1000F03\r
-:10246000111F221F331FBA95D1F70C291D292E29C0\r
-:102470003F29040D151D261D371DC9A8DAA8EBA894\r
-:10248000FCA887ECC81A8FE2D80A8BE2E80A86E239\r
-:10249000F80AC80CD91CEA1CFB1C4A015B01842405\r
-:1024A0009524A624B72480269126A226B3268C0C38\r
-:1024B0009D1CAE1CBF1C7501640184E0CC0CDD1CAE\r
-:1024C000EE1CFF1C8A95D1F79CE1B694A7949794D3\r
-:1024D00087949A95D1F78C289D28AE28BF28800E26\r
-:1024E000911EA21EB31E2196CCACDDACEEACFFACAF\r
-:1024F00021979BE1C91A96E6D90A94E2E90A99E183\r
-:10250000F90AC40ED51EE61EF71EB301A20140272C\r
-:10251000512762277327482559256A257B254C0DAD\r
-:102520005D1D6E1D7F1D6A017B01EBE0CC0CDD1C87\r
-:10253000EE1CFF1CEA95D1F7F5E176956795579566\r
-:102540004795FA95D1F74C295D296E297F29480DC9\r
-:10255000591D6A1D7B1D2D96CCACDDACEEACFFACDD\r
-:102560002D97A8E0CA1AA3E8DA0AADE5EA0AA0EEB8\r
-:10257000FA0A4C0C5D1C6E1C7F1C68017901C82492\r
-:10258000D924EA24FB24C426D526E626F7264C0CBB\r
-:102590005D1C6E1C7F1CF20123013F01440E551E81\r
-:1025A000661E771ECD88DE88EF88F88CFBE9CF1A8F\r
-:1025B000F9EADF0AF3E5EF0AFBE3FF0AC00ED11EDA\r
-:1025C000E21EF31E950184010427152726273727CD\r
-:1025D00004251525262537250C0D1D1D2E1D3F1DF7\r
-:1025E00068017901A9E0F694E794D794C794AA9575\r
-:1025F000D1F7B7E1000F111F221F331FBA95D1F792\r
-:102600000C291D292E293F29040D151D261D371DB6\r
-:10261000CD84DE84EF84F8888CEBC81A8DEDD80A5F\r
-:1026200086EDE80A8BE0F80AC80CD91CEA1CFB1CF2\r
-:102630004A015B0180949094A094B094802A912ADE\r
-:10264000A22AB32A84249524A624B7248C0C9D1C8A\r
-:10265000AE1CBF1C7501640186E0CC0CDD1CEE1CB9\r
-:10266000FF1C8A95D1F79AE1B694A7949794879422\r
-:102670009A95D1F78C289D28AE28BF28800E911EF0\r
-:10268000A21EB31EC9A4DAA4EBA4FCA499E6C91A3D\r
-:10269000D10895EDE90A9CEBF90AC40ED51EE61E99\r
-:1026A000F71EB301A2014095509560957095482999\r
-:1026B00059296A297B2940275127622773274C0D06\r
-:1026C0005D1D6E1D7F1D6A017B01EAE0CC0CDD1CE7\r
-:1026D000EE1CFF1CEA95D1F7F6E1769567955795C4\r
-:1026E0004795FA95D1F74C295D296E297F29480D28\r
-:1026F000591D6A1D7B1D2996CCACDDACEEACFFAC40\r
-:102700002997A9E5CA1AACEDDA0AABE6EA0AA4E50C\r
-:10271000FA0AC40CD51CE61CF71C280139014094A8\r
-:10272000509460947094442A552A662A772A482443\r
-:1027300059246A247B244C0C5D1C6E1C7F1C730185\r
-:102740006201AFE0CC0CDD1CEE1CFF1CAA95D1F79A\r
-:10275000B1E17694679457944794BA95D1F74C2891\r
-:102760005D286E287F28440E551E661E771EC9A060\r
-:10277000DAA0EBA0FCA0B7ECCB1ABFE5DB0ABCE605\r
-:10278000EB0AB3E0FB0AC00ED11EE21EF31E950158\r
-:102790008401009510952095309504291529262946\r
-:1027A000372904271527262737270C0D1D1D2E1D19\r
-:1027B0003F1D680179018BE0F694E794D794C794A4\r
-:1027C0008A95D1F795E1000F111F221F331F9A95AB\r
-:1027D000D1F70C291D292E293F29040D151D261D71\r
-:1027E000371D2196CCACDDACEEACFFAC2197EDE310\r
-:1027F000CE1AE6EADE0AE4EAEE0AEAE9FE0AC80CC4\r
-:10280000D91CEA1CFB1C4A015B0180949094A094A3\r
-:10281000B094802A912AA22AB32A84249524A6243B\r
-:10282000B7248C0C9D1CAE1CBF1C75016401E6E036\r
-:10283000CC0CDD1CEE1CFF1CEA95D1F7FAE1B69436\r
-:10284000A79497948794FA95D1F78C289D28AE2861\r
-:10285000BF28800E911EA21EB31EC98CDA8CEB8C91\r
-:10286000FC8CFEE6CF1AF3E3DF0AF3EFEF0AF0E7A2\r
-:10287000FF0AC40ED51EE61EF71EB301A201409545\r
-:10288000509560957095482959296A297B294027D8\r
-:102890005127622773274C0D5D1D6E1D7F1D6A0138\r
-:1028A0007B01AAE0CC0CDD1CEE1CFF1CAA95D1F725\r
-:1028B000B6E17695679557954795BA95D1F74C2926\r
-:1028C0005D296E297F29480D591D6A1D7B1DCDA8E4\r
-:1028D000DEA8EFA8F8AC83E8C81A8BE0D80A80E13C\r
-:1028E000E80AF108C40CD51CE61CF71C28013901C4\r
-:1028F0004094509460947094442A552A662A772A0A\r
-:10290000482459246A247B244C0C5D1C6E1C7F1CBB\r
-:10291000730162018FE0CC0CDD1CEE1CFF1C8A955C\r
-:10292000D1F791E176946794579447949A95D1F7AB\r
-:102930004C285D286E287F28440E551E661E771E83\r
-:10294000C988DA88EB88FC889FE2C91A92EAD90A1A\r
-:102950009BE7E90A9AE7F90AC00ED11EE21EF31EB0\r
-:10296000950184010095109520953095042915292D\r
-:102970002629372904271527262737270C0D1D1D43\r
-:102980002E1D3F1D68017901EBE0F694E794D79482\r
-:10299000C794EA95D1F7F5E1000F111F221F331FED\r
-:1029A000FA95D1F70C291D292E293F29040D151D53\r
-:1029B000261D371DCDA4DEA4EFA4F8A8A1EBCA1AEA\r
-:1029C000A1E8DA0AA7E5EA0AA0E9FA0AC80CD91CC4\r
-:1029D000EA1CFB1C4A015B0180949094A094B09483\r
-:1029E000802A912AA22AB32A84249524A624B724D3\r
-:1029F0008C0C9D1CAE1CBF1C75016401A6E0CC0CA8\r
-:102A0000DD1CEE1CFF1CAA95D1F7BAE1B694A79481\r
-:102A100097948794BA95D1F78C289D28AE28BF2823\r
-:102A2000800E911EA21EB31E2D968CAD9DADAEAD37\r
-:102A3000BFAD2D9780529941A34DB140840F951F92\r
-:102A4000A61FB71FB301A2014095509560957095E0\r
-:102A5000482959296A297B2940275127622773274A\r
-:102A6000840F951FA61FB71F6C017D014AE0CC0C97\r
-:102A7000DD1CEE1CFF1C4A95D1F7AC01BD01E6E15F\r
-:102A80007695679557954795EA95D1F74C295D2935\r
-:102A90006E297F29480D591D6A1D7B1D8DA19EA1A0\r
-:102AA000AFA1B8A58C5E9C4BAE4FBC45840D951D67\r
-:102AB000A61DB71D2801390140945094609470946C\r
-:102AC000442A552A662A772A482459246A247B24D2\r
-:102AD000840D951DA61DB71D6C017D01FFE0CC0C7A\r
-:102AE000DD1CEE1CFF1CFA95D1F72C013D01A1E184\r
-:102AF0007694679457944794AA95D1F74C285D280B\r
-:102B00006E287F28440E551E661E771E25968CADB6\r
-:102B10009DADAEADBFAD25978F559E4EA74FB14B26\r
-:102B2000800F911FA21FB31F95018401009510957E\r
-:102B3000209530950429152926293729042715279A\r
-:102B400026273727800F911FA21FB31F6C017D011D\r
-:102B50001BE0F694E794D794C7941A95D1F78C01AB\r
-:102B60009D0185E1000F111F221F331F8A95D1F7A8\r
-:102B70000C291D292E293F29040D151D261D371D41\r
-:102B80008D8D9E8DAF8DB8A18E579148AC4AB840BF\r
-:102B9000880D991DAA1DBB1D4A015B01809490946C\r
-:102BA000A094B094802A912AA22AB32A842495243E\r
-:102BB000A624B724880D991DAA1DBB1D6C017D019B\r
-:102BC000E6E0CC0CDD1CEE1CFF1CEA95D1F74C01B5\r
-:102BD0005D01FAE1B694A79497948794FA95D1F79A\r
-:102BE0008C289D28AE28BF28800E911EA21EB31EE1\r
-:102BF00089AD9AADABADBCAD8B5C9D40A54CB244EC\r
-:102C0000840F951FA61FB71FB301A20140955095D1\r
-:102C100060957095482959296A297B2940275127B1\r
-:102C200062277327840F951FA61FB71F6C017D01B4\r
-:102C30004AE0CC0CDD1CEE1CFF1C4A95D1F7AC0120\r
-:102C4000BD01E6E17695679557954795EA95D1F7E9\r
-:102C50004C295D296E297F29480D591D6A1D7B1D50\r
-:102C60008D899E89AF89B88D85549D42A842B54D06\r
-:102C7000840D951DA61DB71D28013901409450945F\r
-:102C800060947094442A552A662A772A4824592445\r
-:102C90006A247B24840D951DA61DB71D6C017D0142\r
-:102CA000FFE0CC0CDD1CEE1CFF1CFA95D1F72C01CB\r
-:102CB0003D01A1E17694679457944794AA95D1F782\r
-:102CC0004C285D286E287F28440E551E661E771EF0\r
-:102CD00089A99AA9ABA9BCA98F569C42A947B4411E\r
-:102CE000800F911FA21FB31F9501840100951095BD\r
-:102CF00020953095042915292629372904271527D9\r
-:102D000026273727800F911FA21FB31F6C017D015B\r
-:102D10001BE0F694E794D794C7941A95D1F78C01E9\r
-:102D20009D0185E1000F111F221F331F8A95D1F7E6\r
-:102D30000C291D292E293F29040D151D261D371D7F\r
-:102D400061968CAD9DADAEADBFAD6197880D991DFF\r
-:102D5000AA1DBB1D61968CAF9DAFAEAFBFAF619793\r
-:102D600089819A81AB81BC81800F911FA21FB31F03\r
-:102D700089839A83AB83BC830D811E812F81388523\r
-:102D8000040D151D261D371D0D831E832F833887C7\r
-:102D900089859A85AB85BC85840F951FA61FB71FB3\r
-:102DA00089879A87AB87BC87E796AEADBFADE797BB\r
-:102DB000A05CBF4FE796BFAFAEAFE797B0E42B1A6A\r
-:102DC000310811F00C94600761960CAD1DAD2EAD6D\r
-:102DD0003FAD6197E996AEADBFADE99718960D93FB\r
-:102DE0001D932D933C931B9709811A812B813C8164\r
-:102DF0001C960D931D932D933C931F970D811E815F\r
-:102E00002F81388550960D931D932D933C935397A6\r
-:102E100009851A852B853C8554960D931D932D931A\r
-:102E20003C935797E7968EAD9FADE797C858DF4F15\r
-:102E30000FB6F894DEBF0FBECDBFDF91CF911F91CB\r
-:102E40000F91FF90EF90DF90CF90BF90AF909F9049\r
-:102E50008F907F906F905F904F903F902F9008954C\r
-:102E60000F931F93CF93DF93EC01062F142F60E095\r
-:102E70008E850E94CC023C852D858091170A88237F\r
-:102E800081F09FB7F8948091170A813081F58DB356\r
-:102E90008093A8024DB38091AC02809584238DBBB2\r
-:102EA0009FBF3CBD2DBD0EBD00000DB407FEFDCF84\r
-:102EB0008EB51EBD00000DB407FEFDCF1EB580917E\r
-:102EC000170A882351F09FB7F8942091170A809130\r
-:102ED000A802213079F48DBB9FBF61E08E850E94EE\r
-:102EE000CC02812FDF91CF911F910F910895909384\r
-:102EF000A802D7CF8FBFF1CFAF92BF92CF92DF9210\r
-:102F0000EF92FF920F931F93CF936093031F7093E1\r
-:102F1000041F8093051F9093061F6B017C01FF0C1B\r
-:102F2000CC08DC2C76019B01AC016C2D7C2D8C2D0A\r
-:102F30009C2D03E10E943B2DA12CE8E4BE2EF8EE6F\r
-:102F4000CF2EDD24D394E12CF12C00E010E00E9480\r
-:102F5000DE2CC22FC22ED32E7A01A7016627772737\r
-:102F600066E88FEE9EE10E94301777276F2D5E2D69\r
-:102F70004D2D67E88FEE9EE10E9430174C2F68E8D8\r
-:102F80008FEE9EE1CF911F910F91FF90EF90DF9018\r
-:102F9000CF90BF90AF900C94301740E00C94301756\r
-:102FA000CF92DF92EF92FF920F931F93CF93DF9315\r
-:102FB0006AE18FEE9EE10E94CD17C82FD0E069E153\r
-:102FC0008FEE9EE10E94CD17682F880F770B880B3C\r
-:102FD000990B0E94C22A20E030E040E85EE30E94A4\r
-:102FE000212B0E94582C6B018B016091031F709163\r
-:102FF000041F8091051F9091061F0E94C22A24ED94\r
-:1030000030E843E45EE40E94BB2ABE0187FD10C0A5\r
-:103010006D59710917FF0FC06C0D7D1DCB01DF913C\r
-:10302000CF911F910F91FF90EF90DF90CF90089577\r
-:10303000645A7109EFCF072E000C880B990B0E9480\r
-:10304000C22A20EB32E748E85FE30E94212B0E946E\r
-:10305000582CE4CF43E861E88FEE9EE10E943017E0\r
-:1030600062E18FEE9EE10E94CD1783FFF9CF48E029\r
-:1030700062E98FEE9EE10E94301781E090E00895B2\r
-:10308000CF92DF92EF92FF926EE18FEE9EE10E946F\r
-:10309000CD1782958F70CC24C394D12C02C0CC0C58\r
-:1030A000DD1C8A95E2F70D2C000CEE08FF086DE19F\r
-:1030B0008FEE9EE10E94CD1790E044E095958795B4\r
-:1030C0004A95E1F78A30910534F5FC01EE0FFF1FB8\r
-:1030D000E80FF91FEC5DFD4F608171818281082E40\r
-:1030E000000C990B28EE33E040E050E00E94F52BF5\r
-:1030F000C701B6010E94F52B21313105410551056B\r
-:1031000074F04CE066EA8FEE9EE1FF90EF90DF9066\r
-:10311000CF900C94301760E070E0CB01E3CF44E037\r
-:10312000F1CFCF93DF93EC01863091052CF08D30F9\r
-:10313000910534F5069731F545EC61EB8FEE9EE194\r
-:103140000E9430174CE067EB8FEE9EE10E94301733\r
-:10315000C6E0D0E06EE18FEE9EE10E94CD178F7049\r
-:1031600094E0CC0FDD1F9A95E1F74C2F482B6EE9C8\r
-:103170008FEE9EE10E943017DF91CF910C944018A2\r
-:10318000CCE0D0E043EC61EB8FEE9EE10E94301783\r
-:103190004AE067EB8FEE9EE10E943017DBCFCF93C2\r
-:1031A000DF93EC01099714F0C8E0D0E06DE18FEEF9\r
-:1031B0009EE10E94CD17C530D10514F4C5E0D0E0E2\r
-:1031C0002497CC0FDD1F482F417F4C2B6DE98FEEEC\r
-:1031D0009EE1DF91CF910C94301710920A1F10924C\r
-:1031E000091F6DE18FEE9EE10E94CD17482F4E7FA3\r
-:1031F0006DE98FEE9EE10C94301781E090E09093A2\r
-:103200000A1F8093091F6DE18FEE9EE10E94CD178A\r
-:10321000482F41606DE98FEE9EE10C943017CF93FB\r
-:10322000DF93EC0141E861E88FEE9EE10E943017E8\r
-:10323000CD2B99F00E94FD1840E06DE88FEE9EE1E5\r
-:103240000E94301740E062EA8FEE9EE10E94301744\r
-:1032500081E090E0DF91CF9108950E94ED18ECCFCE\r
-:103260000F931F93CF93DF9366E08FEE9EE10E9452\r
-:10327000CD17D82F67E08FEE9EE10E94CD17C82FA3\r
-:1032800068E08FEE9EE10E94CD174C2F50E070E079\r
-:1032900060E0762F652F542F44276D2B8A019B0108\r
-:1032A000082BC901B80120E038E448EE51E00E9443\r
-:1032B000B12C03E10E94562D2093200A3093210A5D\r
-:1032C0004093220A5093230ADF91CF911F910F91CF\r
-:1032D0000895CF92DF92EF92FF92CF93DF936091A8\r
-:1032E0001C0A70911D0A80911E0A90911F0A0E946B\r
-:1032F000C22A6B017C0120E030EC43EF55E40E94D0\r
-:10330000BB2AD0E0C0E018160CF065C020E030E821\r
-:1033100042E256E4C701B6010E94BB2AC1E0D0E0F8\r
-:1033200018160CF058C020E030EC43E756E4C70113\r
-:10333000B6010E94BB2AC2E0D0E018160CF04BC0C8\r
-:1033400020E030E842EA56E4C701B6010E94BB2AF9\r
-:10335000C3E0D0E018160CF03EC020E034E244EFA9\r
-:1033600056E4C701B6010E94BB2AC4E0D0E018169B\r
-:1033700094F520E034EE42E257E4C701B6010E9422\r
-:10338000BB2AC5E0D0E0181634F520E034E244E76B\r
-:1033900057E4C701B6010E94BB2AC6E0D0E0181668\r
-:1033A000D4F420E034E244EF57E4C701B6010E94B0\r
-:1033B000BB2AC7E0D0E0181674F420E034E244E7FA\r
-:1033C00058E4C701B6010E94BB2AC9E0D0E0181634\r
-:1033D00014F0C8E0D0E06DE18FEE9EE10E94CD17C1\r
-:1033E0008F7044E0CC0FDD1F4A95E1F74C2F482B3E\r
-:1033F0006DE98FEE9EE10E9430170E944018809187\r
-:10340000501E882319F16DE18FEE9EE10E94CD17C9\r
-:1034100090E034E0959587953A95E1F78A309105EB\r
-:10342000E4F4FC01EE0FFF1FE80FF91FEC5DFD4F08\r
-:1034300080819181A2810A2E000CBB0B80931C0A13\r
-:1034400090931D0AA0931E0AB0931F0ADF91CF919B\r
-:10345000FF90EF90DF90CF90089580E090E0DC0146\r
-:10346000EDCF68E18FEE9EE10E94CD179FB7F894F3\r
-:103470004091130850911408609115087091160836\r
-:103480009FBF4093170850931808609319087093D2\r
-:103490001A0880FF2AC081E08093B1028093B002B5\r
-:1034A0008093AF028091AD029091AE022091B10263\r
-:1034B000211108C02091B002211104C02091AF0257\r
-:1034C000222301F18F309105B8F401969093AE025A\r
-:1034D0008093AD0281E080934D1E80914C1E882325\r
-:1034E000F1F061E08CE00C94CC021092B1021092E9\r
-:1034F000B0021092AF02D6CF81E080934D1E809330\r
-:103500004C1EEBCF009741F001979093AE02809351\r
-:10351000AD0210924D1EE1CF10924C1EFACF0C94CA\r
-:103520000B06CF93DF9362E18FEE9EE10E94CD17F1\r
-:10353000C82F482F62E98FEE9EE10E943017C5FD2B\r
-:103540002DC01092081F1092071F8091091F9091A3\r
-:103550000A1F62E2892B09F463E18FEE9EE10E946B\r
-:10356000CD17C82FD0E060E18FEE9EE10E94CD170D\r
-:10357000482F6DE88FEE9EE10E943017E0910B1FFF\r
-:10358000F0910C1F309711F0CE01199540E06DE8D5\r
-:103590008FEE9EE1DF91CF910C943017DF91CF91A8\r
-:1035A000089508950F931F93CF93DF93EC01E88163\r
-:1035B000F9810084F185E02D1995892BB1F06DE03A\r
-:1035C000CE010E94CD17082F60E0CE010E94CD17DA\r
-:1035D000182F402F6DE8CE010E943017812F90E008\r
-:1035E000DF91CF911F910F9108958FEF9FEFF8CF4B\r
-:1035F000CF93DF93EC01E881F9810084F185E02D20\r
-:103600001995892B69F0888D998D0196998F888FEE\r
-:1036100060E0CE010E94CD1790E0DF91CF91089538\r
-:103620008FEF9FEFFACFCF93DF93EC012097E1F07C\r
-:1036300021978091AA029091AB02843F9140A0F41F\r
-:103640008FEE9EE10E94F81A2091AA023091AB02FF\r
-:10365000A9014F5F5F4F5093AB024093AA02225ED5\r
-:103660003C4FF9018083E2CFDF91CF9108950F9312\r
-:103670001F93CF93DF93EC018091190A811178C0D9\r
-:103680008FEE9EE10E94F81A21974091000280FF80\r
-:1036900055C09C0154E0359527955A95E1F72F7058\r
-:1036A00033274F3FD1F41092AB021092AA0220931D\r
-:1036B00000020E94D017909302028093010269E1F8\r
-:1036C0008FEE9EE10E94CD178093A902CE01DF917B\r
-:1036D000CF911F910F910C94131B2413E4CF0E94E0\r
-:1036E000D0172091010230910202820F931F97FDA3\r
-:1036F000019695958795909302028093010200911F\r
-:10370000A90210E069E18FEE9EE10E94CD17800FC3\r
-:10371000912F911D959587958093A902CE010E94C6\r
-:10372000131B8FEF809300020E94D2050E94C205F6\r
-:10373000DF91CF911F910F910C94E4044F3F39F02A\r
-:103740001092AB021092AA028FEF809300020E94A7\r
-:10375000D017909302028093010269E18FEE9EE1FF\r
-:103760000E94CD178093A902CE010E94131BDCCFCB\r
-:103770001092AB021092AA02EACFCF93DF93EC0132\r
-:1037800063E10E94CD1790E0288D398D821B930B49\r
-:10379000DF91CF910895AF92BF92CF92DF92FF92C7\r
-:1037A0000F931F93CF93DF936C015B01EA0162E2F9\r
-:1037B0000E94CD17F82E90E09C012C0F3D1F2F3F4B\r
-:1037C000310529F020F0CFEFD0E0C81BD90B8501DF\r
-:1037D000AC0EBD1E0A151B0541F0F80141918F0189\r
-:1037E00060E8C6010E943017F5CF4C2F4F0D62EAFA\r
-:1037F000C6010E943017CE01DF91CF911F910F912A\r
-:10380000FF90DF90CF90BF90AF900895CF93DF935C\r
-:103810001F92CDB7DEB76983DC01ED91FC91028088\r
-:10382000F381E02D41E050E0BE016F5F7F4F1995BD\r
-:103830000F90DF91CF91089590E080E008950C946F\r
-:10384000DF2BCF938091150A811103C00E94ED18E0\r
-:103850000CC0C091160ACC23C9F30E94FD184C2F4E\r
-:1038600062EA8FEE9EE10E94301745E861E88FEE34\r
-:103870009EE1CF910C9430178091501E882341F126\r
-:103880008091B802843A89F440911602509117024F\r
-:103890004F30510514F04EE050E057FD40E04067D6\r
-:1038A00069E88FEE9EE10C943017893A89F44091D3\r
-:1038B0001602509117024231510514F041E150E0D7\r
-:1038C0004230510514F442E050E042504068E8CFE5\r
-:1038D0000895CF92DF92EF92FF92CF93DF930E94F1\r
-:1038E000B804D091501ED1117DC080911302811176\r
-:1038F00084C1C0914B1ECC2309F47FC1C090200A23\r
-:10390000D090210AE090220AF090230A61E0809191\r
-:10391000FD1E0E94580361E08091FD1E0E94CC02B2\r
-:103920008091FF1E9091001F8F3F9807C9F061E0C2\r
-:103930000E94580360E08091FF1E0E94CC026AE062\r
-:1039400070E080E090E00E94850461E08091FF1EBD\r
-:103950000E94CC026AE070E080E090E00E94850462\r
-:10396000DFB7F8948091180A811127C0EDE1F2E0E9\r
-:103970008491E3E6F2E09491E82FF0E0EE0FFF1F70\r
-:10398000EC54FE4FA591B491EC91E92321F461E050\r
-:1039900085E30E94CC0261E085E30E9458038CB568\r
-:1039A00080618CBD8CB580648CBD61E084E30E9435\r
-:1039B000580361E083E30E9458038091180A8F5FE7\r
-:1039C0008093180ADFBF62E48FEE9EE10E94CD175C\r
-:1039D000823181F081E00E94500590E080E00E94F9\r
-:1039E0004D06D0E08D2FDF91CF91FF90EF90DF90CB\r
-:1039F000CF90089540E861E88FEE9EE10E94301775\r
-:103A0000C701B6010E947C1740E06EE88FEE9EE190\r
-:103A10000E94301740E06FE88FEE9EE10E94301761\r
-:103A20006CE08FEE9EE10E94CD17482F43606CE85A\r
-:103A30008FEE9EE10E94301744E066EA8FEE9EE131\r
-:103A40000E94301740E869E88FEE9EE10E9430172F\r
-:103A500041E861E88FEE9EE10E94301781E080939B\r
-:103A6000501E0E943C1C0E94691980911A0A909174\r
-:103A70001B0A0E94911880911402909115020E94D5\r
-:103A8000CF180E9430196EE18FEE9EE10E94CD1793\r
-:103A9000482F44606EE98FEE9EE10E94301787E365\r
-:103AA0009BE190930C1F80930B1F60E08091011F9E\r
-:103AB0000E94580340E060EC8FEE9EE10E943017B8\r
-:103AC0002091011F3091021F80E02230310551F01A\r
-:103AD00081E02330310531F0C9014297049788F421\r
-:103AE00087E1821B2FB7F8948230E9F160F48823D4\r
-:103AF00009F43FC0813071F082E08093170A80E0C2\r
-:103B00000AC08FEFEFCF843091F178F18530A1F7C3\r
-:103B100088E001C080E29091AC02892B8093AC02D6\r
-:103B20008091170A811103C081E08093170A2FBF8B\r
-:103B30002091011F3091021F22303105E1F481E90B\r
-:103B40009AE1909304028093030280916A0083605B\r
-:103B500080936A00EC9A0E94211C83E090E00E940E\r
-:103B60000F06DC2F3FCF81E0D6CF82E0D4CF84E0B8\r
-:103B7000D2CF80E1D0CF2330310569F481E99AE1D9\r
-:103B8000909306028093050280916A008C60809376\r
-:103B90006A00ED9AE0CFC90142970497E0F687E109\r
-:103BA000821BE82FF0E0EE0FFF1FED5FFD4F21E9D4\r
-:103BB0003AE131832083843091F08530B9F083304D\r
-:103BC00039F080916900836080936900E89AC3CFDF\r
-:103BD000809169008C6080936900E99ABCCF8091E4\r
-:103BE0006900806380936900EA9AB5CF809169008B\r
-:103BF000806C80936900EB9AAECF83E090E00E94E6\r
-:103C0000EE05F0CE1F920F920FB60F9211240BB655\r
-:103C10000F922F933F934F935F936F937F938F9365\r
-:103C20009F93AF93BF93EF93FF9381E59EE10E9433\r
-:103C30000702FF91EF91BF91AF919F918F917F917B\r
-:103C40006F915F914F913F912F910F900BBE0F900D\r
-:103C50000FBE0F901F9018951F920F920FB60F92E4\r
-:103C600011240BB60F922F938F939F93EF93FF9393\r
-:103C7000E091611EF091621E8081E091671EF091DB\r
-:103C8000681E82FD1DC0908180916A1E8F5F8F73B8\r
-:103C900020916B1E821741F0E0916A1EF0E0EF5A0E\r
-:103CA000F14E958F80936A1EFF91EF919F918F91B6\r
-:103CB0002F910F900BBE0F900FBE0F901F90189575\r
-:103CC0008081F2CF1F920F920FB60F9211242F9383\r
-:103CD0003F938F939F93AF93BF93809113089091DD\r
-:103CE0001408A0911508B09116083091B30223E092\r
-:103CF000230F2D3758F50196A11DB11D2093B30256\r
-:103D00008093130890931408A0931508B093160895\r
-:103D10008091B4029091B502A091B602B091B70221\r
-:103D20000196A11DB11D8093B4029093B502A0939A\r
-:103D3000B602B093B702BF91AF919F918F913F911F\r
-:103D40002F910F900FBE0F901F90189526E8230F0C\r
-:103D50000296A11DB11DD2CF1F920F920FB60F92E6\r
-:103D600011240BB60F922F933F934F935F936F9352\r
-:103D70007F938F939F93AF93BF93EF93FF93E091C4\r
-:103D80001102F09112021995FF91EF91BF91AF913D\r
-:103D90009F918F917F916F915F914F913F912F9163\r
-:103DA0000F900BBE0F900FBE0F901F9018951F9293\r
-:103DB0000F920FB60F9211240BB60F922F933F93D1\r
-:103DC0004F935F936F937F938F939F93AF93BF9323\r
-:103DD000EF93FF93E0910F02F09110021995FF917C\r
-:103DE000EF91BF91AF919F918F917F916F915F9173\r
-:103DF0004F913F912F910F900BBE0F900FBE0F90E0\r
-:103E00001F9018951F920F920FB60F9211240BB6A8\r
-:103E10000F922F933F934F935F936F937F938F9363\r
-:103E20009F93AF93BF93EF93FF93E0910502F091BF\r
-:103E300006021995FF91EF91BF91AF919F918F91DC\r
-:103E40007F916F915F914F913F912F910F900BBE9A\r
-:103E50000F900FBE0F901F9018951F920F920FB6E4\r
-:103E60000F9211240BB60F922F933F934F935F93B2\r
-:103E70006F937F938F939F93AF93BF93EF93FF9332\r
-:103E8000E0910302F09104021995FF91EF91BF9127\r
-:103E9000AF919F918F917F916F915F914F913F91E2\r
-:103EA0002F910F900BBE0F900FBE0F901F90189583\r
-:103EB0001F920F920FB60F9211240BB60F922F93F1\r
-:103EC0003F934F935F936F937F938F939F93AF93A2\r
-:103ED000BF93EF93FF93E0910D02F0910E021995BD\r
-:103EE000FF91EF91BF91AF919F918F917F916F91D2\r
-:103EF0005F914F913F912F910F900BBE0F900FBE8E\r
-:103F00000F901F9018951F920F920FB60F921124C9\r
-:103F10000BB60F922F933F934F935F936F937F93C3\r
-:103F20008F939F93AF93BF93EF93FF93E0910B0217\r
-:103F3000F0910C021995FF91EF91BF91AF919F9174\r
-:103F40008F917F916F915F914F913F912F910F9042\r
-:103F50000BBE0F900FBE0F901F9018951F920F92DF\r
-:103F60000FB60F9211240BB60F922F933F934F93DE\r
-:103F70005F936F937F938F939F93AF93BF93EF93D1\r
-:103F8000FF93E0910902F0910A021995FF91EF91D8\r
-:103F9000BF91AF919F918F917F916F915F914F9161\r
-:103FA0003F912F910F900BBE0F900FBE0F901F905F\r
-:103FB00018951F920F920FB60F9211240BB60F9205\r
-:103FC0002F933F934F935F936F937F938F939F9321\r
-:103FD000AF93BF93EF93FF93E0910702F091080234\r
-:103FE0001995FF91EF91BF91AF919F918F917F9123\r
-:103FF0006F915F914F913F912F910F900BBE0F905A\r
-:104000000FBE0F901F9018951F920F920FB60F9230\r
-:1040100011240BB60F922F933F934F935F936F939F\r
-:104020007F938F939F93AF93BF93CF93EF93FF9320\r
-:104030008091B202811127C081E08093B202C4E175\r
-:1040400081E59EE10E94F101892BD9F08FB7F894A8\r
-:104050004091471E5091481E2091431E3091441EAE\r
-:104060004217530721F54091491E50914A1E209155\r
-:10407000451E3091461E42175307C9F48FBFC150E9\r
-:10408000F9F61092B202FF91EF91CF91BF91AF91EB\r
-:104090009F918F917F916F915F914F913F912F9160\r
-:1040A0000F900BBE0F900FBE0F901F901895409170\r
-:1040B000491E50914A1E2091471E3091481E2150A2\r
-:1040C000310942175307D1F28FBF81E59EE10E946B\r
-:1040D000CF012FB7F894E091491EF0914A1E8083DA\r
-:1040E0004091491E50914A1E8091451E9091461E56\r
-:1040F0004817590751F48091431E9091441E9093A4\r
-:104100004A1E8093491E2FBFBACF8091491E9091BD\r
-:104110004A1E0196F4CF80EC90E00E94760286E081\r
-:1041200090E00E9476028091501E90E00E947602FC\r
-:1041300080EC90E00C94760288E198E00FB6F89459\r
-:10414000A895809360000FBE909360000E94EA05DE\r
-:104150000E940B06FBCF1F93CF93DF93EC01162F2A\r
-:104160000E94DF2B181739F0612FCE01DF91CF911C\r
-:104170001F910C94E72BDF91CF911F910895E1E5FA\r
-:10418000FEE11382128288EE93E0A0E0B0E0848327\r
-:104190009583A683B78386E492E09183808385EC40\r
-:1041A00090E09587848784EC90E09787868780EC01\r
-:1041B00090E0918B808B81EC90E0938B828B82ECF2\r
-:1041C00090E0958B848B86EC90E0978B868B118E9C\r
-:1041D000128E138E148E81E08093EE1EEFEEFEE1C0\r
-:1041E0001382128228E532E03183208390E59487A0\r
-:1041F00085878AE090E09787868789E090E0918BB9\r
-:10420000808B82E090E0938B828B148A158A168AC9\r
-:10421000178A118E108E138E128E158E148E1482A4\r
-:104220001582168217820895CF93DF93CDB7DEB73C\r
-:10423000C859D1090FB6F894DEBF0FBECDBF789430\r
-:1042400084B5826084BD84B5816084BD85B582609B\r
-:1042500085BD85B5816085BD80916E00816080934C\r
-:104260006E001092810080918100826080938100B5\r
-:1042700080918100816080938100809180008160C5\r
-:10428000809380008091B10084608093B100809120\r
-:10429000B00081608093B000809191008260809333\r
-:1042A00091008091910081608093910080919000B5\r
-:1042B0008160809390008091A10082608093A10032\r
-:1042C0008091A10081608093A1008091A000816015\r
-:1042D0008093A000809121018260809321018091D0\r
-:1042E0002101816080932101809120018160809370\r
-:1042F000200180917A00846080937A0080917A0016\r
-:10430000826080937A0080917A00816080937A0045\r
-:1043100080917A00806880937A001092C100809129\r
-:104320007B00877F80937B0080E480937C0080917A\r
-:104330007A00806480937A0080917A0086FDFCCFB9\r
-:104340008091780060917900762F682F072E000CFD\r
-:10435000880B990B611571058105910511F00E947B\r
-:10436000B22AE2E1F5E081E093E0DF019C011D92D9\r
-:1043700021503040E1F7F093441EE093431EF09348\r
-:104380004A1EE093491EF093481EE093471E82E1C7\r
-:1043900098E09093461E8093451EE091611EF09137\r
-:1043A000621E82E08083E0915D1EF0915E1E1082AD\r
-:1043B000E0915F1EF091601E80E180831092691E83\r
-:1043C000E091651EF091661E86E08083E091631E99\r
-:1043D000F091641E808180618083E091631EF09182\r
-:1043E000641E808188608083E091631EF091641E6A\r
-:1043F000808180688083E091631EF091641E8081DB\r
-:104400008F7D80831092900089E18093910080E8F5\r
-:104410009EE3909397008093960080E280937100D2\r
-:1044200061E08CE00E94580361E08DE00E94580337\r
-:10443000EEE1F3E084EF91E0DF019C011D92215059\r
-:104440003040E1F7E4E2FAE084E0DF011D928A9572\r
-:10445000E9F7E8E2FAE080E094E1DF019C011D92D7\r
-:1044600021503040E1F7ECEEF2E082E3DF01282F4B\r
-:104470001D922A95E9F7F0933A1EE093391EF093C6\r
-:10448000401EE0933F1EF0933E1EE0933D1E2CE144\r
-:1044900033E030933C1E20933B1EEAEBF2E0DF0159\r
-:1044A0001D928A95E9F7F0932C1EE0932B1EF09352\r
-:1044B000321EE093311EF093301EE0932F1E8AEEE1\r
-:1044C00092E090932E1E80932D1E85E090E09093B5\r
-:1044D000FE1E8093FD1E84E090E09093001F809369\r
-:1044E000FF1E82E090E09093021F8093011F809155\r
-:1044F000111F811101C083E0282F80FF6FC182E06E\r
-:104500008093B9028091B9028150823008F07BC259\r
-:1045100083ED9FE00E941F1C833709F074C188E37C\r
-:104520009FE00E941F1C833009F069C189E39FE06E\r
-:104530000E941F1C8093B802982F9F7B943A29F009\r
-:10454000993A19F08F3F09F05EC18AE39FE00E941B\r
-:104550001F1C81508E3F08F056C18BE090E00E94F6\r
-:104560007A2D6C018C01E8E3EE2EEFE0FE2EC70100\r
-:104570000E941F1CF80181938F01FFEFEF1AFF0AC1\r
-:1045800023E4E2162FE0F20691F780E190E00E942A\r
-:104590007A2D8C0181E093E2A5E4B7E689879A87BA\r
-:1045A000AB87BC8789E89BEAADECBFEE8D879E8721\r
-:1045B000AF87B88B8EEF9CEDAAEBB8E9898B9A8B0D\r
-:1045C000AB8BBC8B86E794E5A2E3B0E18D8B9E8B31\r
-:1045D000AF8BB88F1D821E821F821886FE01E75A9C\r
-:1045E000FF4F80E4DF011D928A95E9F7CE018F5AD3\r
-:1045F0009F4F28E0FC0111922A95E9F72BE0F60184\r
-:10460000DE01599601900D922A95E1F720E82CA33E\r
-:10461000FE01B5962CE2DF011D922A95E9F748E5E7\r
-:1046200050E060E070E049835A836B837C8328E527\r
-:10463000FC01208340E450E0BE01675E7F4F80555F\r
-:1046400091090E94800639852A859B858C85D80131\r
-:104650003C9311962C93119712969C931297139654\r
-:104660008C9313973D852E859F85888914963C935E\r
-:10467000149715962C93159716969C931697179644\r
-:104680008C93179739892A899B898C8918963C9332\r
-:10469000189719962C9319971A969C931A971B960C\r
-:1046A0008C931B973D892E899F89888D1C963C93FE\r
-:1046B0001C971D962C931D971E969C931E971F96D4\r
-:1046C0008C934801BB24B394C7010E941F1CF401C2\r
-:1046D00091914F018913B12CFFEFEF1AFF0A23E5E7\r
-:1046E000E2162FE0F20681F7C8010E94162EC601DD\r
-:1046F0000E94162EBB2009F488C081E080934B1ED7\r
-:104700008FED9FE00E941F1C833709F07EC084ED6F\r
-:104710009FE00E941F1C90E090931B0A80931A0A4E\r
-:1047200085ED9FE00E941F1C90E0909315028093FE\r
-:10473000140286ED9FE00E941F1C90E090931702E8\r
-:10474000809316028BED9FE00E941F1C182F8CEDAA\r
-:104750009FE00E941F1C90E0B0E0A0E07C01DD24FF\r
-:10476000CC24F12A8DED9FE00E941F1C182F8EEDA6\r
-:104770009FE00E941F1CC82AD12AC092200AD09212\r
-:10478000210AE092220AF092230A87ED9FE00E941C\r
-:104790001F1C182F88ED9FE00E941F1C90E0B0E0C6\r
-:1047A000A0E07C01DD24CC24F12A89ED9FE00E9469\r
-:1047B0001F1C182F8AED9FE00E941F1CC82AD12AB7\r
-:1047C000C0921C0AD0921D0AE0921E0AF0921F0AA3\r
-:1047D00082E1809318020E94691C17C083E022FDC9\r
-:1047E0008FCE23FF02C081E08BCE42E250E066E62E\r
-:1047F00072E081E59EE10E9493010E9436068F7E61\r
-:10480000803E09F493CE10924B1E812C34E1932EFE\r
-:10481000412C512C32015394AA24A39454E1B52E77\r
-:104820008091501E882309F407C22FB7F894809115\r
-:10483000130890911408A0911508B09116082FBF85\r
-:1048400040911708509118086091190870911A0842\r
-:10485000841B950BA60BB70B0397A105B10510F0B0\r
-:104860000E94311A80914F1E882309F415C11091BE\r
-:104870004E1E111102C00E94311A80914D1E8111ED\r
-:10488000D7C180914C1E8111D3C1112331F062E355\r
-:1048900070E080E090E00E9485040E94311A8091CF\r
-:1048A0004D1E8111F9C010924E1E80910F0A811188\r
-:1048B000EBC081E080930F0A4FB7F89420913D1E22\r
-:1048C00030913E1E80913F1E9091401E4FBF281791\r
-:1048D000390709F4D9C0E0913D1EF0913E1E809148\r
-:1048E0003B1E90913C1EE817F90709F093C0809198\r
-:1048F000391E90913A1E90933E1E80933D1E20815A\r
-:104900003181E0912F1EF091301E80912D1E9091EB\r
-:104910002E1EE817F90709F084C080912B1E909194\r
-:104920002C1E9093301E80932F1E00811181C80190\r
-:104930000197843F914008F0BFCFEBE1F8E02B51A5\r
-:104940003840C9018E0F9F1FB4010E948E2CDC01DC\r
-:10495000A85DB54F8C918193CF018B519840801702\r
-:10496000910778F38091501E882309F485C08091C7\r
-:10497000190A81115DC00E94EA050E94632AA30101\r
-:1049800092010E94F52B607F362E0F3F110510F02B\r
-:10499000689430F890E080E00E940F19632D8FEE4C\r
-:1049A0009EE10E94061C9BE1C92E98E0D92EEE24C0\r
-:1049B000E394F12CD6016D916D018FEE9EE10E9482\r
-:1049C000061CBFEFEB1AFB0AEFEFEE16F10471F4D1\r
-:1049D0000E942A1890E080E00E940F19632D8FEE4C\r
-:1049E0009EE10E94061CEE24E394F12CC6018B513B\r
-:1049F000984080179107F0F20E942A180E94E6055D\r
-:104A00000E94211C59CF10924B1E4EE150E069E8E4\r
-:104A100072E0EFCEE0913D1EF0913E1ECF01029676\r
-:104A20006ACFE0912F1EF091301ECF01029679CF10\r
-:104A30000E94EA050F3F110519F010F00FEF10E08A\r
-:104A40008091150A811116C090E080E00E940F1934\r
-:104A5000BBE1EB2EB8E0FB2ED7016D917D018FEE0F\r
-:104A60009EE10E94061CC7018B5198408017910758\r
-:104A700098F3C2CFC801EACF82E00E94500585E0DA\r
-:104A800090E00E944D0618CF10924F1E1092381ED3\r
-:104A90001092371E10920F0A4FB7F8942091471EBC\r
-:104AA0003091481E8091491E90914A1E4FBF281791\r
-:104AB000390709F4ADC481E08093421EFF24F394CA\r
-:104AC000AEEFEA2E4FB7F8942091471E3091481E62\r
-:104AD0008091491E90914A1E4FBF2817390709F44B\r
-:104AE00095C4E091471EF091481E8091451E90911B\r
-:104AF000461EE817F90709F0FFC08091431E909108\r
-:104B0000441E9093481E8093471E10818091411E41\r
-:104B1000882309F480C4103C09F00FC18091190268\r
-:104B200081117CC41092411E80913D1E90913E1EC9\r
-:104B30004091391E50913A1E8417950761F42091D7\r
-:104B40003F1E3091401E80913B1E90913C1E2817C5\r
-:104B5000390709F4B7CF20913F1E3091401E809154\r
-:104B60003D1E90913E1E02972817390709F4AACFDF\r
-:104B70008091371E9091381E8115944108F0A2CF84\r
-:104B80002091351E3091361E8091331E9091341E37\r
-:104B9000019710F48FEF93E12817390709F4B9C092\r
-:104BA0002817390708F0AFC0821B930B01960097B6\r
-:104BB00009F488CF60914F1E6F5F60934F1EE091A4\r
-:104BC0003F1EF091401E3183208360913F1E709103\r
-:104BD000401E20913B1E30913C1E6217730709F066\r
-:104BE0009BC05093401E40933F1EE091311EF091B8\r
-:104BF000321E918380832091311E3091321E80912C\r
-:104C00002D1E90912E1E2817390709F090C0809113\r
-:104C10002B1E90912C1E9093321E8093311E8091FA\r
-:104C2000331E9091341E9093361E8093351E4ACFCA\r
-:104C300081E080934E1E30CF8091110A9091120A2C\r
-:104C4000A091130AB091140A0196A11DB11D2091E3\r
-:104C50004B1E222369F1893FEAE29E07A105B105B7\r
-:104C600048F48093110A9093120AA093130AB09308\r
-:104C7000140A12CF1092110A1092120A1092130AFB\r
-:104C80001092140A6091EE1E81E0623018F06832D2\r
-:104C900018F08FEF8093100A8091100A680F6093CC\r
-:104CA000EE1E70E08CE00E9489030E94E605F4CEBF\r
-:104CB000893FFAE29F07A105B10598F21092110A07\r
-:104CC0001092120A1092130A1092140A6091EE1EAA\r
-:104CD00081E0623018F0683218F08FEF8093100A8C\r
-:104CE0008091100A680F6093EE1E70E08DE00E94C4\r
-:104CF00089030E940B06D0CEE091471EF091481E1A\r
-:104D0000CF010196FECEB501621B730B860F971F74\r
-:104D10004ECF81E090E04ECF20913F1E3091401E5B\r
-:104D20002E5F3F4F3093401E20933F1E5ECF8091F9\r
-:104D3000311E9091321E02966ECF2091291E309125\r
-:104D40002A1E243F81E0380708F0BCCE909119025A\r
-:104D50002115310529F49E3F19F410931902B2CEA2\r
-:104D6000911144C01B3D19F4F092281EABCE8091E6\r
-:104D7000281E882349F01C3D21F01D3D19F41BED30\r
-:104D800001C010EC1092281E80914F1E883108F04F\r
-:104D900099CE8091371E9091381E8115944108F06C\r
-:104DA00091CE8091371E9091381E01969093381EB7\r
-:104DB0008093371E8091331E9091341E9C012F5F8B\r
-:104DC0003F4F3093341E2093331E885D954FFC0176\r
-:104DD00010838091331E9091341E8115944109F007\r
-:104DE00071CE1092341E1092331E6CCE913009F0A9\r
-:104DF0004AC01B3DD9F49093281E8091291E9091A2\r
-:104E00002A1E049709F05ECE6091240A7091250A4B\r
-:104E10008091260A9091270A0E94AA2C611571059B\r
-:104E200081059105D9F40E9478054CCE8091281E09\r
-:104E3000882349F01C3D21F01D3D19F41BED01C0F4\r
-:104E400010EC1092281EC901019690932A1E80939F\r
-:104E5000291E2C5D354FD9011C93CFCF6093200ABA\r
-:104E60007093210A8093220A9093230A20911802BA\r
-:104E70002131C9F62091501E2223A9F20E947C17ED\r
-:104E80000E943019D0CF923009F048C01B3DD9F4B0\r
-:104E9000F092281E8091291E90912A1E049709F0F5\r
-:104EA00011CE6091240A7091250A8091260A909172\r
-:104EB000270A0E94AA2C6115710581059105D9F474\r
-:104EC0000E949D05FFCD8091281E882349F01C3D3E\r
-:104ED00021F01D3D19F41BED01C010EC1092281EAD\r
-:104EE000C901019690932A1E8093291E2C5D354F8F\r
-:104EF000F9011083CFCF60931C0A70931D0A809331\r
-:104F00001E0A90931F0A809118028131C9F6809180\r
-:104F1000501E8823A9F20E946919D2CF9330A9F4B8\r
-:104F20001F3F19F40E941A05CDCD123108F011E18E\r
-:104F3000812F90E0909317028093160280911802BF\r
-:104F4000813181F70E943C1CEDCF9430F9F41F3F72\r
-:104F500019F40E943E05B6CD86E090E0163038F098\r
-:104F6000812F90E08D30910514F08CE090E09093CB\r
-:104F70001B0A80931A0A20911802213151F72091BF\r
-:104F8000501E222331F30E949118E3CF9530F9F49B\r
-:104F90001F3F19F40E942C0595CD85E090E0153057\r
-:104FA00038F0812F90E08930910514F088E090E08E\r
-:104FB000909315028093140220911802213151F729\r
-:104FC0002091501E222331F30E94CF18E3CF993055\r
-:104FD000B9F41093160A112381F0F092150A80ECAF\r
-:104FE00090E00E94760289E090E00E947602612FB4\r
-:104FF00081E59EE10E94290244C01092150AEFCF7C\r
-:10500000963049F51F3F19F40E948B205BCD11119A\r
-:105010001CC040E861E88FEE9EE10E9430179FB708\r
-:10502000F8948091180A882319F081508093180A07\r
-:105030008091180A811105C08CB58F7B8CBD1092B0\r
-:10504000170A9FBF1092501EDFCF113009F03ACDE2\r
-:105050000E94691CD9CF9132C9F480EC90E00E9483\r
-:10506000760281E290E00E94760280E00E94640570\r
-:1050700080E00E94640580E00E94640580E00E9458\r
-:10508000640580EC90E00E9476021CCD923239F4E7\r
-:1050900080EC90E00E94760282E290E0E4CF9332CE\r
-:1050A00019F40E94D2050ECD973081F40E94B80405\r
-:1050B00080EC90E00E94760287E090E00E94760209\r
-:1050C0008091130290E00E947602DBCF903329F4A6\r
-:1050D000812F90E00E940F06F5CC903491F48091DE\r
-:1050E000501E10E0882331F06CE28FEE9EE10E94AA\r
-:1050F000CD17182F80EC90E00E94760280E490E0BB\r
-:1051000074CF983071F4133709F0DCCC80EC90E068\r
-:105110000E94760288E090E00E94760286E490E0A9\r
-:10512000D2CF9E30C1F4113089F41093190A80EC6B\r
-:1051300090E00E9476028EE090E00E9476028091DC\r
-:10514000190A882331F181E090E0BDCF1111EFCF32\r
-:105150001092190AECCF9F30F9F480914F1E8831DC\r
-:1051600080F48091371E9091381E8115944148F447\r
-:1051700080EC90E00E9476028FE090E00E94760240\r
-:10518000E2CF80EC90E00E9476028FE090E00E94F7\r
-:10519000760290E080E097CF9935A9F4183F09F0A6\r
-:1051A00091CC82E390E00E944D0608E31FE06FEF90\r
-:1051B000C8010E94AB200F5F1F4F011520E11207AD\r
-:1051C000B1F70E949C20953521F4183F09F07ACC64\r
-:1051D000F8CF9135B1F480EC90E00E94760281E541\r
-:1051E00090E00E94760208E31FE0C8010E941F1CA5\r
-:1051F0000E9464050F5F1F4F011530E11307A9F7E7\r
-:1052000040CF9235D9F51B3DE9F4F092281E8091EC\r
-:10521000291E90912A1E029709F054CC0091250A6C\r
-:105220001091240A83ED9FE00E941F1C833711F127\r
-:10523000183C00F5812F90E0602F885C904F0E9411\r
-:10524000AB2040CC8091281E882349F01C3D21F0E2\r
-:105250001D3D19F41BED01C010EC1092281EC90170\r
-:10526000019690932A1E8093291E2C5D354FD901FB\r
-:105270001C93CDCF83E00E94500524CC903579F467\r
-:1052800080EC90E00E94760280E590E00E94760239\r
-:1052900081E090E00E94760287E190E014CF98349C\r
-:1052A00059F480EC90E00E94760288E490E00E943D\r
-:1052B000760280E990E007CF993459F480EC90E0D1\r
-:1052C0000E94760289E490E00E94760282E990E0F2\r
-:1052D000FACE933509F093C080914B1E882309F4D0\r
-:1052E00089C08091501E882309F484C060911A0AF5\r
-:1052F00084ED9FE00E94AB206091140285ED9FE059\r
-:105300000E94AB206091160286ED9FE00E94AB20C8\r
-:1053100060911C0A70911D0A80911E0A90911F0ACB\r
-:10532000692F77278827992787ED9FE00E94AB2078\r
-:1053300060911C0A70911D0A80911E0A90911F0AAB\r
-:10534000BC018827992788ED9FE00E94AB206091DF\r
-:105350001C0A70911D0A80911E0A90911F0A672FE6\r
-:10536000782F892F992789ED9FE00E94AB206091CB\r
-:105370001C0A8AED9FE00E94AB206091200A709188\r
-:10538000210A8091220A9091230A692F7727882782\r
-:1053900099278BED9FE00E94AB206091200A7091CD\r
-:1053A000210A8091220A9091230ABC01882799271B\r
-:1053B0008CED9FE00E94AB206091200A7091210A41\r
-:1053C0008091220A9091230A672F782F892F99279D\r
-:1053D0008DED9FE00E94AB206091200A8EED9FE052\r
-:1053E0000E94AB2063E78FED9FE00E94AB208AE034\r
-:1053F00090E070CE8AE090E00E94EE0563CB943599\r
-:1054000009F060CB60E08FED9FE019CF1092421E53\r
-:105410000E949B0205CA103C09F054CBF092411E39\r
-:10542000E092190210922A1E1092291E4BCB8F92E5\r
-:105430009F92AF92BF92CF92DF92EF92FF92CF9363\r
-:10544000DF93EC01688179818A819B816115710507\r
-:105450008105910521F464E279ED8BE597E02DE17A\r
-:1054600033EF41E050E00E94F52B49015A019B01C6\r
-:10547000AC01A7EAB1E40E94142C6B017C01ACEEF4\r
-:10548000B4EFA50194010E94222CC60ED71EE81E7F\r
-:10549000F91EF7FE06C081E0C81AD108E10880E8CD\r
-:1054A000F80AC882D982EA82FB82C701B6019F77D7\r
-:1054B000DF91CF91FF90EF90DF90CF90BF90AF90B2\r
-:1054C0009F908F9008958F929F92AF92BF92CF92AC\r
-:1054D000DF92EF92FF9260911A0270911B0280910D\r
-:1054E0001C0290911D02611571058105910521F441\r
-:1054F00064E279ED8BE597E02DE133EF41E050E098\r
-:105500000E94F52B49015A019B01AC01A7EAB1E4C5\r
-:105510000E94142C6B017C01ACEEB4EFA501940148\r
-:105520000E94222CC60ED71EE81EF91EF7FE06C0EA\r
-:1055300081E0C81AD108E10880E8F80AC0921A028E\r
-:10554000D0921B02E0921C02F0921D02C701B6012C\r
-:105550009F77FF90EF90DF90CF90BF90AF909F909C\r
-:105560008F90089560931A0270931B0280931C021F\r
-:1055700090931D0208950E94FD2A08F481E0089589\r
-:10558000E89409C097FB3EF49095809570956195DD\r
-:105590007F4F8F4F9F4F9923A9F0F92F96E9BB2793\r
-:1055A0009395F695879577956795B795F111F8CF0F\r
-:1055B000FAF4BB0F11F460FF1BC06F5F7F4F8F4F7A\r
-:1055C0009F4F16C0882311F096E911C0772321F070\r
-:1055D0009EE8872F762F05C0662371F096E8862F08\r
-:1055E00070E060E02AF09A95660F771F881FDAF75F\r
-:1055F000880F9695879597F90895990F0008550F8C\r
-:10560000AA0BE0E8FEEF16161706E807F907C0F048\r
-:1056100012161306E407F50798F0621B730B840B50\r
-:10562000950B39F40A2661F0232B242B252B21F42A\r
-:1056300008950A2609F4A140A6958FEF811D811DCA\r
-:1056400008950E94342B0C94A52B0E94972B38F0C0\r
-:105650000E949E2B20F0952311F00C948E2B0C941D\r
-:10566000942B11240C94D92B0E94B62B70F3959F88\r
-:10567000C1F3950F50E0551F629FF001729FBB2749\r
-:10568000F00DB11D639FAA27F00DB11DAA1F649FE5\r
-:105690006627B00DA11D661F829F2227B00DA11D98\r
-:1056A000621F739FB00DA11D621F839FA00D611D1E\r
-:1056B000221F749F3327A00D611D231F849F600D3F\r
-:1056C000211D822F762F6A2F11249F5750409AF068\r
-:1056D000F1F088234AF0EE0FFF1FBB1F661F771FF4\r
-:1056E000881F91505040A9F79E3F510580F00C94BF\r
-:1056F0008E2B0C94D92B5F3FE4F3983ED4F3869520\r
-:1057000077956795B795F795E7959F5FC1F7FE2B5E\r
-:10571000880F911D9695879597F9089597F99F673A\r
-:1057200080E870E060E008959FEF80EC0895002429\r
-:105730000A941616170618060906089500240A94F6\r
-:1057400012161306140605060895092E0394000C7C\r
-:1057500011F4882352F0BB0F40F4BF2B11F460FF0B\r
-:1057600004C06F5F7F4F8F4F9F4F089557FD905834\r
-:10577000440F551F59F05F3F71F04795880F97FB15\r
-:10578000991F61F09F3F79F08795089512161306CF\r
-:105790001406551FF2CF4695F1DF08C016161706FE\r
-:1057A0001806991FF1CF8695710561050894089533\r
-:1057B000E894BB2766277727CB0197F90895F999D5\r
-:1057C000FECF92BD81BDF89A992780B50895262F06\r
-:1057D000F999FECF1FBA92BD81BD20BD0FB6F894D6\r
-:1057E000FA9AF99A0FBE01960895052E97FB1EF4BA\r
-:1057F00000940E940C2C57FD07D00E94272C07FC18\r
-:1058000003D04EF40C940C2C509540953095219576\r
-:105810003F4F4F4F5F4F08959095809570956195DC\r
-:105820007F4F8F4F9F4F08950E94492CA59F900D49\r
-:10583000B49F900DA49F800D911D11240895B7FF72\r
-:105840000C94142C0E94142C821B930B0895A1E23B\r
-:105850001A2EAA1BBB1BFD010DC0AA1FBB1FEE1FEA\r
-:10586000FF1FA217B307E407F50720F0A21BB30B35\r
-:10587000E40BF50B661F771F881F991F1A9469F7B1\r
-:1058800060957095809590959B01AC01BD01CF010D\r
-:105890000895A29FB001B39FC001A39F700D811D09\r
-:1058A0001124911DB29F700D811D1124911D089529\r
-:1058B0000E945F2C6894B1110C94D92B08950E941A\r
-:1058C000BE2B88F09F5798F0B92F9927B751B0F0A9\r
-:1058D000E1F0660F771F881F991F1AF0BA95C9F774\r
-:1058E00014C0B13091F00E94D82BB1E008950C940F\r
-:1058F000D82B672F782F8827B85F39F0B93FCCF3C2\r
-:10590000869577956795B395D9F73EF490958095F0\r
-:10591000709561957F4F8F4F9F4F0895AA1BBB1BBA\r
-:1059200051E107C0AA1FBB1FA617B70710F0A61B9F\r
-:10593000B70B881F991F5A95A9F780959095BC01C0\r
-:10594000CD010895EE0FFF1F881F8BBF0790F691C2\r
-:10595000E02D1994692796276927782787277827C4\r
-:105960000895E894DF93CF93FC01DB010E94492C5A\r
-:105970007F936F93E9019A01AC01BF93AF933F937B\r
-:105980002F93DF010E94492C26F46C1B7D0B820BA8\r
-:10599000930B9E01EB01FC010E94712DAF91BF9111\r
-:1059A0002F913F910E94712DBE01CF01F9012F91DE\r
-:1059B0003F91CF91DF910895689401C0E8948F9250\r
-:1059C0009F92CF93DF930E94EA2CDF91CF919F901B\r
-:1059D0008F90089588249924F401E401B0E49F9302\r
-:1059E000AA279A158B049C04ED05FE05CF05D00768\r
-:1059F000A10798F4AD2FDC2FCF2FFE2FE92D982C87\r
-:105A0000892E982F872F762F652F542F432F322FD3\r
-:105A10002227B85031F7BF9127C01B2EBF91BB275B\r
-:105A2000220F331F441F551F661F771F881F991FA2\r
-:105A3000881C991CEE1FFF1FCC1FDD1FAA1FBB1F58\r
-:105A40008A149B04EC05FD05CE05DF05A007B10710\r
-:105A500048F08A189B08EC09FD09CE09DF09A00B64\r
-:105A6000B10B21601A94E1F62EF49401AF01BE014E\r
-:105A7000CD01000C0895002E083090F0982F872F4C\r
-:105A8000762F652F542F432F322F22270850F4CF23\r
-:105A9000220F331F441F551F661F771F881F991F32\r
-:105AA0000A95B2F7002D089597FD1094002E083046\r
-:105AB00098F00850232F342F452F562F672F782F1B\r
-:105AC000892F912DF4CF15949795879577956795A4\r
-:105AD00057954795379527950A95AAF71124002DD4\r
-:105AE00008950E94492C460F571FC81FD91F08F45C\r
-:105AF000319608950F931F93CF93DF9382309105D2\r
-:105B000010F482E090E0E0910F1FF091101F30E060\r
-:105B100020E0B0E0A0E0309799F42115310509F4B8\r
-:105B20004AC0281B390B24303105D8F58A819B8166\r
-:105B30006115710589F1FB0193838283FE0111C018\r
-:105B4000408151810281138148175907E0F04817BD\r
-:105B5000590799F4109761F012960C9312971396C7\r
-:105B60001C933296CF01DF91CF911F910F91089531\r
-:105B700000930F1F1093101FF4CF2115310551F022\r
-:105B80004217530738F0A901DB019A01BD01DF017B\r
-:105B9000F801C1CFEF01F9CF9093101F80930F1F31\r
-:105BA000CDCFFE01E20FF31F819391932250310973\r
-:105BB00039832883D7CF20910D1F30910E1F232BBF\r
-:105BC00041F4209120023091210230930E1F209346\r
-:105BD0000D1F20911E0230911F022115310541F445\r
-:105BE0002DB73EB74091220250912302241B350B62\r
-:105BF000E0910D1FF0910E1FE217F307A0F42E1B8A\r
-:105C00003F0B2817390778F0AC014E5F5F4F241720\r
-:105C1000350748F04E0F5F1F50930E1F40930D1F26\r
-:105C2000819391939FCFF0E0E0E09CCFCF93DF93FF\r
-:105C30000097E9F0FC01329713821282A0910F1FA6\r
-:105C4000B091101FED0130E020E01097A1F4208109\r
-:105C50003181820F931F20910D1F30910E1F281745\r
-:105C6000390709F061C0F0930E1FE0930D1FDF911B\r
-:105C7000CF910895EA01CE17DF07E8F54A815B81ED\r
-:105C80009E0141155105B1F7E901FB83EA83499172\r
-:105C90005991C40FD51FEC17FD0761F480819181E4\r
-:105CA0000296840F951FE9019983888382819381ED\r
-:105CB0009B838A83F0E0E0E012968D919C9113978C\r
-:105CC0000097B9F52D913C911197CD010296820F65\r
-:105CD000931F20910D1F30910E1F2817390739F699\r
-:105CE000309751F51092101F10920F1FB0930E1F96\r
-:105CF000A0930D1FBCCFD383C28340815181840FF9\r
-:105D0000951FC817D90761F44E5F5F4F888199814D\r
-:105D1000480F591F518340838A819B8193838283DB\r
-:105D20002115310509F0B0CFF093101FE0930F1F3C\r
-:105D30009ECFFD01DC01C0CF13821282D7CFF89431\r
-:025D4000FFCF93\r
-:105D4200FFDCFEB704B704B704B704B704B704B75F\r
-:105D520004B704010500FF0011FE0100000000006D\r
-:105D6200121F8000781E00A02800F03C0040510065\r
-:105D7200127A00E4A20024F40048E80190D0032043\r
-:105D8200A1070000000029029301C0017B02F1017A\r
-:105D9200CF01E30100000000061CCB1B1C1CD11A22\r
-:105DA200BD1BF81AD21A4572726F722C20696E648A\r
-:105DB200657465726D696E61746520626F6F7420BF\r
-:105DC200766563746F720D0A004572726F722C20D1\r
-:105DD200696E636F727265637420626F6F7420768E\r
-:085DE2006563746F720D0A0085\r
-:00000001FF\r
diff --git a/Precompiled/rnode_firmware_latest_tbeam.zip b/Precompiled/rnode_firmware_latest_tbeam.zip
deleted file mode 100644 (file)
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 (file)
index 0000000..37384d1
--- /dev/null
@@ -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 (executable)
index 0000000..01176af
--- /dev/null
@@ -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'<BBHI', 0x00, op, len(data), chk) + data
+                self.write(pkt)
+
+            if not wait_response:
+                return
+
+            # tries to get a response until that response has the
+            # same operation as the request or a retries limit has
+            # exceeded. This is needed for some esp8266s that
+            # reply with more sync responses than expected.
+            for retry in range(100):
+                p = self.read()
+                if len(p) < 8:
+                    continue
+                (resp, op_ret, len_ret, val) = struct.unpack('<BBHI', p[:8])
+                if resp != 1:
+                    continue
+                data = p[8:]
+
+                if op is None or op_ret == op:
+                    return val, data
+                if byte(data, 0) != 0 and byte(data, 1) == self.ROM_INVALID_RECV_MSG:
+                    self.flush_input()  # Unsupported read_reg can result in more than one error response for some reason
+                    raise UnsupportedCommandError(self, op)
+
+        finally:
+            if new_timeout != saved_timeout:
+                self._port.timeout = saved_timeout
+
+        raise FatalError("Response doesn't match request")
+
+    def check_command(self, op_description, op=None, data=b'', chk=0, timeout=DEFAULT_TIMEOUT):
+        """
+        Execute a command with 'command', check the result code and throw an appropriate
+        FatalError if it fails.
+
+        Returns the "result" of a successful command.
+        """
+        val, data = self.command(op, data, chk, timeout=timeout)
+
+        # things are a bit weird here, bear with us
+
+        # the status bytes are the last 2/4 bytes in the data (depending on chip)
+        if len(data) < self.STATUS_BYTES_LENGTH:
+            raise FatalError("Failed to %s. Only got %d byte status response." % (op_description, len(data)))
+        status_bytes = data[-self.STATUS_BYTES_LENGTH:]
+        # we only care if the first one is non-zero. If it is, the second byte is a reason.
+        if byte(status_bytes, 0) != 0:
+            raise FatalError.WithResult('Failed to %s' % op_description, status_bytes)
+
+        # if we had more data than just the status bytes, return it as the result
+        # (this is used by the md5sum command, maybe other commands?)
+        if len(data) > 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('<I', addr), timeout=timeout)
+        if byte(data, 0) != 0:
+            raise FatalError.WithResult("Failed to read register address %08x" % addr, data)
+        return val
+
+    """ Write to memory address in target """
+    def write_reg(self, addr, value, mask=0xFFFFFFFF, delay_us=0, delay_after_us=0):
+        command = struct.pack('<IIII', addr, value, mask, delay_us)
+        if delay_after_us > 0:
+            # add a dummy write to a date register as an excuse to have a delay
+            command += struct.pack('<IIII', self.UART_DATE_REG_ADDR, 0, 0, delay_after_us)
+
+        return self.check_command("write target memory", self.ESP_WRITE_REG, command)
+
+    def update_reg(self, addr, mask, new_val):
+        """ Update register at 'addr', replace the bits masked out by 'mask'
+        with new_val. new_val is shifted left to match the LSB of 'mask'
+
+        Returns just-written value of register.
+        """
+        shift = _mask_to_shift(mask)
+        val = self.read_reg(addr)
+        val &= ~mask
+        val |= (new_val << shift) & mask
+        self.write_reg(addr, val)
+
+        return val
+
+    """ Start downloading an application image to RAM """
+    def mem_begin(self, size, blocks, blocksize, offset):
+        if self.IS_STUB:  # check we're not going to overwrite a running stub with this data
+            stub = self.STUB_CODE
+            load_start = offset
+            load_end = offset + size
+            for (start, end) in [(stub["data_start"], stub["data_start"] + len(stub["data"])),
+                                 (stub["text_start"], stub["text_start"] + len(stub["text"]))]:
+                if load_start < end and load_end > 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('<IIII', size, blocks, blocksize, offset))
+
+    """ Send a block of an image to RAM """
+    def mem_block(self, data, seq):
+        return self.check_command("write to target RAM", self.ESP_MEM_DATA,
+                                  struct.pack('<IIII', len(data), seq, 0, 0) + data,
+                                  self.checksum(data))
+
+    """ Leave download mode and run the application """
+    def mem_finish(self, entrypoint=0):
+        # Sending ESP_MEM_END usually sends a correct response back, however sometimes
+        # (with ROM loader) the executed code may reset the UART or change the baud rate
+        # before the transmit FIFO is empty. So in these cases we set a short timeout and
+        # ignore errors.
+        timeout = DEFAULT_TIMEOUT if self.IS_STUB else MEM_END_ROM_TIMEOUT
+        data = struct.pack('<II', int(entrypoint == 0), entrypoint)
+        try:
+            return self.check_command("leave RAM download mode", self.ESP_MEM_END,
+                                      data=data, timeout=timeout)
+        except FatalError:
+            if self.IS_STUB:
+                raise
+            pass
+
+    """ Start downloading to Flash (performs an erase)
+
+    Returns number of blocks (of size self.FLASH_WRITE_SIZE) to write.
+    """
+    def flash_begin(self, size, offset, begin_rom_encrypted=False):
+        num_blocks = (size + self.FLASH_WRITE_SIZE - 1) // self.FLASH_WRITE_SIZE
+        erase_size = self.get_erase_size(offset, size)
+
+        t = time.time()
+        if self.IS_STUB:
+            timeout = DEFAULT_TIMEOUT
+        else:
+            timeout = timeout_per_mb(ERASE_REGION_TIMEOUT_PER_MB, size)  # ROM performs the erase up front
+
+        params = struct.pack('<IIII', erase_size, num_blocks, self.FLASH_WRITE_SIZE, offset)
+        if isinstance(self, (ESP32S2ROM, ESP32S3BETA2ROM, ESP32S3BETA3ROM, ESP32C3ROM, ESP32C6BETAROM)) and not self.IS_STUB:
+            params += struct.pack('<I', 1 if begin_rom_encrypted else 0)
+        self.check_command("enter Flash download mode", self.ESP_FLASH_BEGIN,
+                           params, timeout=timeout)
+        if size != 0 and not self.IS_STUB:
+            print("Took %.2fs to erase flash block" % (time.time() - t))
+        return num_blocks
+
+    """ Write block to flash """
+    def flash_block(self, data, seq, timeout=DEFAULT_TIMEOUT):
+        self.check_command("write to target Flash after seq %d" % seq,
+                           self.ESP_FLASH_DATA,
+                           struct.pack('<IIII', len(data), seq, 0, 0) + data,
+                           self.checksum(data),
+                           timeout=timeout)
+
+    """ Encrypt before writing to flash """
+    def flash_encrypt_block(self, data, seq, timeout=DEFAULT_TIMEOUT):
+        if isinstance(self, (ESP32S2ROM, ESP32C3ROM)) and not self.IS_STUB:
+            # ROM support performs the encrypted writes via the normal write command,
+            # triggered by flash_begin(begin_rom_encrypted=True)
+            return self.flash_block(data, seq, timeout)
+
+        self.check_command("Write encrypted to target Flash after seq %d" % seq,
+                           self.ESP_FLASH_ENCRYPT_DATA,
+                           struct.pack('<IIII', len(data), seq, 0, 0) + data,
+                           self.checksum(data),
+                           timeout=timeout)
+
+    """ Leave flash mode and run/reboot """
+    def flash_finish(self, reboot=False):
+        pkt = struct.pack('<I', int(not reboot))
+        # stub sends a reply to this command
+        self.check_command("leave Flash mode", self.ESP_FLASH_END, pkt)
+
+    """ Run application code in flash """
+    def run(self, reboot=False):
+        # Fake flash begin immediately followed by flash end
+        self.flash_begin(0, 0)
+        self.flash_finish(reboot)
+
+    """ Read SPI flash manufacturer and device id """
+    def flash_id(self):
+        SPIFLASH_RDID = 0x9F
+        return self.run_spiflash_command(SPIFLASH_RDID, b"", 24)
+
+    def get_security_info(self):
+        # TODO: this only works on the ESP32S2 ROM code loader and needs to work in stub loader also
+        res = self.check_command('get security info', self.ESP_GET_SECURITY_INFO, b'')
+        res = struct.unpack("<IBBBBBBBB", res)
+        flags, flash_crypt_cnt, key_purposes = res[0], res[1], res[2:]
+        # TODO: pack this as some kind of better data type
+        return (flags, flash_crypt_cnt, key_purposes)
+
+    @classmethod
+    def parse_flash_size_arg(cls, arg):
+        try:
+            return cls.FLASH_SIZES[arg]
+        except KeyError:
+            raise FatalError("Flash size '%s' is not supported by this chip type. Supported sizes: %s"
+                             % (arg, ", ".join(cls.FLASH_SIZES.keys())))
+
+    def run_stub(self, stub=None):
+        if stub is None:
+            stub = self.STUB_CODE
+
+        if self.sync_stub_detected:
+            print("Stub is already running. No upload is necessary.")
+            return self.STUB_CLASS(self)
+
+        # Upload
+        print("Uploading stub...")
+        for field in ['text', 'data']:
+            if field in stub:
+                offs = stub[field + "_start"]
+                length = len(stub[field])
+                blocks = (length + self.ESP_RAM_BLOCK - 1) // self.ESP_RAM_BLOCK
+                self.mem_begin(length, blocks, self.ESP_RAM_BLOCK, offs)
+                for seq in range(blocks):
+                    from_offs = seq * self.ESP_RAM_BLOCK
+                    to_offs = from_offs + self.ESP_RAM_BLOCK
+                    self.mem_block(stub[field][from_offs:to_offs], seq)
+        print("Running stub...")
+        self.mem_finish(stub['entry'])
+
+        p = self.read()
+        if p != b'OHAI':
+            raise FatalError("Failed to start stub. Unexpected response: %s" % p)
+        print("Stub running...")
+        return self.STUB_CLASS(self)
+
+    @stub_and_esp32_function_only
+    def flash_defl_begin(self, size, compsize, offset):
+        """ Start downloading compressed data to Flash (performs an erase)
+
+        Returns number of blocks (size self.FLASH_WRITE_SIZE) to write.
+        """
+        num_blocks = (compsize + self.FLASH_WRITE_SIZE - 1) // self.FLASH_WRITE_SIZE
+        erase_blocks = (size + self.FLASH_WRITE_SIZE - 1) // self.FLASH_WRITE_SIZE
+
+        t = time.time()
+        if self.IS_STUB:
+            write_size = size  # stub expects number of bytes here, manages erasing internally
+            timeout = DEFAULT_TIMEOUT
+        else:
+            write_size = erase_blocks * self.FLASH_WRITE_SIZE  # ROM expects rounded up to erase block size
+            timeout = timeout_per_mb(ERASE_REGION_TIMEOUT_PER_MB, write_size)  # ROM performs the erase up front
+        print("Compressed %d bytes to %d..." % (size, compsize))
+        params = struct.pack('<IIII', write_size, num_blocks, self.FLASH_WRITE_SIZE, offset)
+        if isinstance(self, (ESP32S2ROM, ESP32S3BETA2ROM, ESP32S3BETA3ROM, ESP32C3ROM, ESP32C6BETAROM)) and not self.IS_STUB:
+            params += struct.pack('<I', 0)  # extra param is to enter encrypted flash mode via ROM (not supported currently)
+        self.check_command("enter compressed flash mode", self.ESP_FLASH_DEFL_BEGIN, params, timeout=timeout)
+        if size != 0 and not self.IS_STUB:
+            # (stub erases as it writes, but ROM loaders erase on begin)
+            print("Took %.2fs to erase flash block" % (time.time() - t))
+        return num_blocks
+
+    """ Write block to flash, send compressed """
+    @stub_and_esp32_function_only
+    def flash_defl_block(self, data, seq, timeout=DEFAULT_TIMEOUT):
+        self.check_command("write compressed data to flash after seq %d" % seq,
+                           self.ESP_FLASH_DEFL_DATA, struct.pack('<IIII', len(data), seq, 0, 0) + data, self.checksum(data), timeout=timeout)
+
+    """ Leave compressed flash mode and run/reboot """
+    @stub_and_esp32_function_only
+    def flash_defl_finish(self, reboot=False):
+        if not reboot and not self.IS_STUB:
+            # skip sending flash_finish to ROM loader, as this
+            # exits the bootloader. Stub doesn't do this.
+            return
+        pkt = struct.pack('<I', int(not reboot))
+        self.check_command("leave compressed flash mode", self.ESP_FLASH_DEFL_END, pkt)
+        self.in_bootloader = False
+
+    @stub_and_esp32_function_only
+    def flash_md5sum(self, addr, size):
+        # the MD5 command returns additional bytes in the standard
+        # command reply slot
+        timeout = timeout_per_mb(MD5_TIMEOUT_PER_MB, size)
+        res = self.check_command('calculate md5sum', self.ESP_SPI_FLASH_MD5, struct.pack('<IIII', addr, size, 0, 0),
+                                 timeout=timeout)
+
+        if len(res) == 32:
+            return res.decode("utf-8")  # already hex formatted
+        elif len(res) == 16:
+            return hexify(res).lower()
+        else:
+            raise FatalError("MD5Sum command returned unexpected result: %r" % res)
+
+    @stub_and_esp32_function_only
+    def change_baud(self, baud):
+        print("Changing baud rate to %d" % baud)
+        # stub takes the new baud rate and the old one
+        second_arg = self._port.baudrate if self.IS_STUB else 0
+        self.command(self.ESP_CHANGE_BAUDRATE, struct.pack('<II', baud, second_arg))
+        print("Changed.")
+        self._set_port_baudrate(baud)
+        time.sleep(0.05)  # get rid of crap sent during baud rate change
+        self.flush_input()
+
+    @stub_function_only
+    def erase_flash(self):
+        # depending on flash chip model the erase may take this long (maybe longer!)
+        self.check_command("erase flash", self.ESP_ERASE_FLASH,
+                           timeout=CHIP_ERASE_TIMEOUT)
+
+    @stub_function_only
+    def erase_region(self, offset, size):
+        if offset % self.FLASH_SECTOR_SIZE != 0:
+            raise FatalError("Offset to erase from must be a multiple of 4096")
+        if size % self.FLASH_SECTOR_SIZE != 0:
+            raise FatalError("Size of data to erase must be a multiple of 4096")
+        timeout = timeout_per_mb(ERASE_REGION_TIMEOUT_PER_MB, size)
+        self.check_command("erase region", self.ESP_ERASE_REGION, struct.pack('<II', offset, size), timeout=timeout)
+
+    def read_flash_slow(self, offset, length, progress_fn):
+        raise NotImplementedInROMError(self, self.read_flash_slow)
+
+    def read_flash(self, offset, length, progress_fn=None):
+        if not self.IS_STUB:
+            return self.read_flash_slow(offset, length, progress_fn)  # ROM-only routine
+
+        # issue a standard bootloader command to trigger the read
+        self.check_command("read flash", self.ESP_READ_FLASH,
+                           struct.pack('<IIII',
+                                       offset,
+                                       length,
+                                       self.FLASH_SECTOR_SIZE,
+                                       64))
+        # now we expect (length // block_size) SLIP frames with the data
+        data = b''
+        while len(data) < length:
+            p = self.read()
+            data += p
+            if len(data) < length and len(p) < self.FLASH_SECTOR_SIZE:
+                raise FatalError('Corrupt data, expected 0x%x bytes but received 0x%x bytes' % (self.FLASH_SECTOR_SIZE, len(p)))
+            self.write(struct.pack('<I', len(data)))
+            if progress_fn and (len(data) % 1024 == 0 or len(data) == length):
+                progress_fn(len(data), length)
+        if progress_fn:
+            progress_fn(len(data), length)
+        if len(data) > 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('<I', hspi_arg)
+        if not self.IS_STUB:
+            # ESP32 ROM loader takes additional 'is legacy' arg, which is not
+            # currently supported in the stub loader or esptool.py (as it's not usually needed.)
+            is_legacy = 0
+            arg += struct.pack('BBBB', is_legacy, 0, 0, 0)
+        self.check_command("configure SPI flash pins", ESP32ROM.ESP_SPI_ATTACH, arg)
+
+    def flash_set_parameters(self, size):
+        """Tell the ESP bootloader the parameters of the chip
+
+        Corresponds to the "flashchip" data structure that the ROM
+        has in RAM.
+
+        'size' is in bytes.
+
+        All other flash parameters are currently hardcoded (on ESP8266
+        these are mostly ignored by ROM code, on ESP32 I'm not sure.)
+        """
+        fl_id = 0
+        total_size = size
+        block_size = 64 * 1024
+        sector_size = 4 * 1024
+        page_size = 256
+        status_mask = 0xffff
+        self.check_command("set SPI params", ESP32ROM.ESP_SPI_SET_PARAMS,
+                           struct.pack('<IIIIII', fl_id, total_size, block_size, sector_size, page_size, status_mask))
+
+    def run_spiflash_command(self, spiflash_command, data=b"", read_bits=0):
+        """Run an arbitrary SPI flash command.
+
+        This function uses the "USR_COMMAND" functionality in the ESP
+        SPI hardware, rather than the precanned commands supported by
+        hardware. So the value of spiflash_command is an actual command
+        byte, sent over the wire.
+
+        After writing command byte, writes 'data' to MOSI and then
+        reads back 'read_bits' of reply on MISO. Result is a number.
+        """
+
+        # SPI_USR register flags
+        SPI_USR_COMMAND = (1 << 31)
+        SPI_USR_MISO    = (1 << 28)
+        SPI_USR_MOSI    = (1 << 27)
+
+        # SPI registers, base address differs ESP32* vs 8266
+        base = self.SPI_REG_BASE
+        SPI_CMD_REG       = base + 0x00
+        SPI_USR_REG       = base + self.SPI_USR_OFFS
+        SPI_USR1_REG      = base + self.SPI_USR1_OFFS
+        SPI_USR2_REG      = base + self.SPI_USR2_OFFS
+        SPI_W0_REG        = base + self.SPI_W0_OFFS
+
+        # following two registers are ESP32 & 32S2/32C3 only
+        if self.SPI_MOSI_DLEN_OFFS is not None:
+            # ESP32/32S2/32C3 has a more sophisticated way to set up "user" commands
+            def set_data_lengths(mosi_bits, miso_bits):
+                SPI_MOSI_DLEN_REG = base + self.SPI_MOSI_DLEN_OFFS
+                SPI_MISO_DLEN_REG = base + self.SPI_MISO_DLEN_OFFS
+                if mosi_bits > 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("<H", new_status))
+
+        # also try using individual commands (also not supported by all chips for num_bytes 2 & 3)
+        for cmd in [SPIFLASH_WRSR, SPIFLASH_WRSR2, SPIFLASH_WRSR3][0:num_bytes]:
+            self.run_spiflash_command(enable_cmd)
+            self.run_spiflash_command(cmd, struct.pack("B", new_status & 0xFF))
+            new_status >>= 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('<II', offset + len(data), block_len))
+            if len(r) < block_len:
+                raise FatalError("Expected %d byte block, got %d bytes. Serial errors?" % (block_len, len(r)))
+            data += r[:block_len]  # command always returns 64 byte buffer, regardless of how many bytes were actually read from flash
+            if progress_fn and (len(data) % 1024 == 0 or len(data) == length):
+                progress_fn(len(data), length)
+        return data
+
+
+class ESP32S2ROM(ESP32ROM):
+    CHIP_NAME = "ESP32-S2"
+    IMAGE_CHIP_ID = 2
+
+    IROM_MAP_START = 0x40080000
+    IROM_MAP_END   = 0x40b80000
+    DROM_MAP_START = 0x3F000000
+    DROM_MAP_END   = 0x3F3F0000
+
+    CHIP_DETECT_MAGIC_VALUE = [0x000007c6]
+
+    SPI_REG_BASE = 0x3f402000
+    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
+
+    MAC_EFUSE_REG = 0x3f41A044  # ESP32-S2 has special block for MAC efuses
+
+    UART_CLKDIV_REG = 0x3f400014
+
+    FLASH_ENCRYPTED_WRITE_ALIGN = 16
+
+    # todo: use espefuse APIs to get this info
+    EFUSE_BASE = 0x3f41A000
+    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 << 19
+
+    PURPOSE_VAL_XTS_AES256_KEY_1 = 2
+    PURPOSE_VAL_XTS_AES256_KEY_2 = 3
+    PURPOSE_VAL_XTS_AES128_KEY = 4
+
+    UARTDEV_BUF_NO = 0x3ffffd14  # Variable in ROM .bss which indicates the port in use
+    UARTDEV_BUF_NO_USB = 2  # Value of the above variable indicating that USB is in use
+
+    USB_RAM_BLOCK = 0x800  # Max block size USB CDC is used
+
+    GPIO_STRAP_REG = 0x3f404038
+    GPIO_STRAP_SPI_BOOT_MASK = 0x8   # Not download mode
+    RTC_CNTL_OPTION1_REG = 0x3f408128
+    RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK = 0x1  # Is download mode forced over USB?
+
+    MEMORY_MAP = [[0x00000000, 0x00010000, "PADDING"],
+                  [0x3F000000, 0x3FF80000, "DROM"],
+                  [0x3F500000, 0x3FF80000, "EXTRAM_DATA"],
+                  [0x3FF9E000, 0x3FFA0000, "RTC_DRAM"],
+                  [0x3FF9E000, 0x40000000, "BYTE_ACCESSIBLE"],
+                  [0x3FF9E000, 0x40072000, "MEM_INTERNAL"],
+                  [0x3FFB0000, 0x40000000, "DRAM"],
+                  [0x40000000, 0x4001A100, "IROM_MASK"],
+                  [0x40020000, 0x40070000, "IRAM"],
+                  [0x40070000, 0x40072000, "RTC_IRAM"],
+                  [0x40080000, 0x40800000, "IROM"],
+                  [0x50000000, 0x50002000, "RTC_DATA"]]
+
+    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_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('<BBBBI', load_file.read(8))
+
+        if magic != expected_magic:
+            raise FatalError('Invalid firmware image magic=0x%x' % (magic))
+        return segments
+
+    def verify(self):
+        if len(self.segments) > 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('<II', f.read(8))
+        self.warn_if_unusual_segment(offset, size, is_irom_segment)
+        segment_data = f.read(size)
+        if len(segment_data) < size:
+            raise FatalError('End of file reading segment 0x%x, length %d (actual length %d)' % (offset, size, len(segment_data)))
+        segment = ImageSegment(offset, segment_data, file_offs)
+        self.segments.append(segment)
+        return segment
+
+    def warn_if_unusual_segment(self, offset, size, is_irom_segment):
+        if not is_irom_segment:
+            if offset > 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('<II', segment.addr, len(segment_data)))
+        f.write(segment_data)
+        if checksum is not None:
+            return ESPLoader.checksum(segment_data, checksum)
+
+    def read_checksum(self, f):
+        """ Return ESPLoader checksum from end of just-read image """
+        # Skip the padding. The checksum is stored in the last byte so that the
+        # file is a multiple of 16 bytes.
+        align_file_position(f, 16)
+        return ord(f.read(1))
+
+    def calculate_checksum(self):
+        """ Calculate checksum of loaded image, based on segments in
+        segment array.
+        """
+        checksum = ESPLoader.ESP_CHECKSUM_MAGIC
+        for seg in self.segments:
+            if seg.include_in_checksum:
+                checksum = ESPLoader.checksum(seg.data, checksum)
+        return checksum
+
+    def append_checksum(self, f, checksum):
+        """ Append ESPLoader checksum to the just-written image """
+        align_file_position(f, 16)
+        f.write(struct.pack(b'B', checksum))
+
+    def write_common_header(self, f, segments):
+        f.write(struct.pack('<BBBBI', ESPLoader.ESP_IMAGE_MAGIC, len(segments),
+                            self.flash_mode, self.flash_size_freq, self.entrypoint))
+
+    def is_irom_addr(self, addr):
+        """ Returns True if an address starts in the irom region.
+        Valid for ESP8266 only.
+        """
+        return ESP8266ROM.IROM_MAP_START <= addr < ESP8266ROM.IROM_MAP_END
+
+    def get_irom_segment(self):
+        irom_segments = [s for s in self.segments if self.is_irom_addr(s.addr)]
+        if len(irom_segments) > 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'<BBBBI', ESPBOOTLOADER.IMAGE_V2_MAGIC, ESPBOOTLOADER.IMAGE_V2_SEGMENT,
+                                self.flash_mode, self.flash_size_freq, self.entrypoint))
+
+            irom_segment = self.get_irom_segment()
+            if irom_segment is not None:
+                # save irom0 segment, make sure it has load addr 0 in the file
+                irom_segment = irom_segment.copy_with_new_addr(0)
+                irom_segment.pad_to_alignment(16)  # irom_segment must end on a 16 byte boundary
+                self.save_segment(f, irom_segment)
+
+            # second header, matches V1 header and contains loadable segments
+            normal_segments = self.get_non_irom_segments()
+            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)
+
+        # calculate a crc32 of entire file and append
+        # (algorithm used by recent 8266 SDK bootloaders)
+        with open(filename, 'rb') as f:
+            crc = esp8266_crc32(f.read())
+        with open(filename, 'ab') as f:
+            f.write(struct.pack(b'<I', crc))
+
+
+# Backwards compatibility for previous API, remove in esptool.py V3
+ESPFirmwareImage = ESP8266ROMFirmwareImage
+OTAFirmwareImage = ESP8266V2FirmwareImage
+
+
+def esp8266_crc32(data):
+    """
+    CRC32 algorithm used by 8266 SDK bootloader (and gen_appbin.py).
+    """
+    crc = binascii.crc32(data, 0) & 0xFFFFFFFF
+    if crc & 0x80000000:
+        return crc ^ 0xFFFFFFFF
+    else:
+        return crc + 1
+
+
+class ESP32FirmwareImage(BaseFirmwareImage):
+    """ ESP32 firmware image is very similar to V1 ESP8266 image,
+    except with an additional 16 byte reserved header at top of image,
+    and because of new flash mapping capabilities the flash-mapped regions
+    can be placed in the normal image (just @ 64kB padded offsets).
+    """
+
+    ROM_LOADER = ESP32ROM
+
+    # ROM bootloader will read the wp_pin field if SPI flash
+    # pins are remapped via flash. IDF actually enables QIO only
+    # from software bootloader, so this can be ignored. But needs
+    # to be set to this value so ROM bootloader will skip it.
+    WP_PIN_DISABLED = 0xEE
+
+    EXTENDED_HEADER_STRUCT_FMT = "<BBBBHB" + ("B" * 8) + "B"
+
+    IROM_ALIGN = 65536
+
+    def __init__(self, load_file=None):
+        super(ESP32FirmwareImage, self).__init__()
+        self.secure_pad = None
+        self.flash_mode = 0
+        self.flash_size_freq = 0
+        self.version = 1
+        self.wp_pin = self.WP_PIN_DISABLED
+        # SPI pin drive levels
+        self.clk_drv = 0
+        self.q_drv = 0
+        self.d_drv = 0
+        self.cs_drv = 0
+        self.hd_drv = 0
+        self.wp_drv = 0
+        self.min_rev = 0
+
+        self.append_digest = True
+
+        if load_file is not None:
+            start = load_file.tell()
+
+            segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC)
+            self.load_extended_header(load_file)
+
+            for _ in range(segments):
+                self.load_segment(load_file)
+            self.checksum = self.read_checksum(load_file)
+
+            if self.append_digest:
+                end = load_file.tell()
+                self.stored_digest = load_file.read(32)
+                load_file.seek(start)
+                calc_digest = hashlib.sha256()
+                calc_digest.update(load_file.read(end - start))
+                self.calc_digest = calc_digest.digest()  # TODO: decide what to do here?
+
+            self.verify()
+
+    def is_flash_addr(self, addr):
+        return (self.ROM_LOADER.IROM_MAP_START <= addr < self.ROM_LOADER.IROM_MAP_END) \
+            or (self.ROM_LOADER.DROM_MAP_START <= addr < self.ROM_LOADER.DROM_MAP_END)
+
+    def default_output_name(self, input_file):
+        """ Derive a default output name from the ELF name. """
+        return "%s.bin" % (os.path.splitext(input_file)[0])
+
+    def warn_if_unusual_segment(self, offset, size, is_irom_segment):
+        pass  # TODO: add warnings for ESP32 segment offset/size combinations that are wrong
+
+    def save(self, filename):
+        total_segments = 0
+        with io.BytesIO() as f:  # write file to memory first
+            self.write_common_header(f, self.segments)
+
+            # first 4 bytes of header are read by ROM bootloader for SPI
+            # config, but currently unused
+            self.save_extended_header(f)
+
+            checksum = ESPLoader.ESP_CHECKSUM_MAGIC
+
+            # split segments into flash-mapped vs ram-loaded, and take copies so we can mutate them
+            flash_segments = [copy.deepcopy(s) for s in sorted(self.segments, key=lambda s:s.addr) if self.is_flash_addr(s.addr)]
+            ram_segments = [copy.deepcopy(s) for s in sorted(self.segments, key=lambda s:s.addr) if not self.is_flash_addr(s.addr)]
+
+            # check for multiple ELF sections that are mapped in the same flash mapping region.
+            # this is usually a sign of a broken linker script, but if you have a legitimate
+            # use case then let us know
+            if len(flash_segments) > 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("<LLLLLL", section_header[offs:])
+            return (name_offs, sec_type, lma, size, sec_offs)
+        all_sections = [read_section_header(offs) for offs in section_header_offsets]
+        prog_sections = [s for s in all_sections if s[1] == ELFFile.SEC_TYPE_PROGBITS]
+
+        # search for the string table section
+        if not (shstrndx * self.LEN_SEC_HEADER) in section_header_offsets:
+            raise FatalError("ELF file has no STRTAB section at shstrndx %d" % shstrndx)
+        _, sec_type, _, sec_size, sec_offs = read_section_header(shstrndx * self.LEN_SEC_HEADER)
+        if sec_type != ELFFile.SEC_TYPE_STRTAB:
+            print('WARNING: ELF file has incorrect STRTAB section type 0x%02x' % sec_type)
+        f.seek(sec_offs)
+        string_table = f.read(sec_size)
+
+        # build the real list of ELFSections by reading the actual section names from the
+        # string table section, and actual data for each section from the ELF file itself
+        def lookup_string(offs):
+            raw = string_table[offs:]
+            return raw[:raw.index(b'\x00')]
+
+        def read_data(offs, size):
+            f.seek(offs)
+            return f.read(size)
+
+        prog_sections = [ELFSection(lookup_string(n_offs), lma, read_data(offs, size)) for (n_offs, _type, lma, size, offs) in prog_sections
+                         if lma != 0 and size > 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("<LLLLLLLL", segment_header[offs:])
+            return (seg_type, lma, size, seg_offs)
+        all_segments = [read_segment_header(offs) for offs in segment_header_offsets]
+        prog_segments = [s for s in all_segments if s[0] == ELFFile.SEG_TYPE_LOAD]
+
+        def read_data(offs, size):
+            f.seek(offs)
+            return f.read(size)
+
+        prog_segments = [ELFSection(b'PHDR', lma, read_data(offs, size)) for (_type, lma, size, offs) in prog_segments
+                         if lma != 0 and size > 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 (<ESPLoader instance>, <args>) or a single <args>
+# 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'<I', d))
+            if f.tell() % 1024 == 0:
+                print_overwrite('%d bytes read... (%d %%)' % (f.tell(),
+                                                              f.tell() * 100 // args.size))
+            sys.stdout.flush()
+        print_overwrite("Read %d bytes" % f.tell(), last_line=True)
+    print('Done!')
+
+
+def detect_flash_size(esp, args):
+    if args.flash_size == 'detect':
+        if esp.secure_download_mode:
+            raise FatalError("Detecting flash size is not supported in secure download mode. Need to manually specify flash size.")
+        flash_id = esp.flash_id()
+        size_id = flash_id >> 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='<address> <filename>', 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='<address> <filename>',
+                                    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='<address> <filename>',
+                                  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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..09ddc19
Binary files /dev/null and b/Release/rnode_firmware_latest_tbeam.zip differ