diff --git a/Changelog.md b/Changelog.md index c6cb5e29a..e5871935f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,10 +3,11 @@ ### 3.2 -- Compiled against [2.6.4 Arduino Core](https://github.com/esp8266/Arduino/releases/tag/2.7.4) -- Updated to [AdruinoJson 6.16.1](https://github.com/bblanchon/ArduinoJson/releases/tag/v6.16.1) +- Compiled against [2.7.4 Arduino Core](https://github.com/esp8266/Arduino/releases/tag/2.7.4) +- Updated to [AdruinoJson 6.17.0](https://github.com/bblanchon/ArduinoJson/releases/tag/v6.17.0) - Updated to [async-mqtt-client 2019.12.08](https://github.com/marvinroger/async-mqtt-client/tree/7f1ba481a22d56ccf123e4b2f6e555d134c956d0) - Fix for Client Timeout not Changing [Issue #201](https://github.com/forkineye/ESPixelStick/issues/201) +- Updated the flash tool included in binary releases to fix issues flashing on newer releases of MacOS. ### 3.1 diff --git a/dist/ESPSFlashTool.jar b/dist/ESPSFlashTool.jar index 64a0964ac..a9e30d051 100644 Binary files a/dist/ESPSFlashTool.jar and b/dist/ESPSFlashTool.jar differ diff --git a/dist/bin/esptool/CONTRIBUTING.md b/dist/bin/esptool/CONTRIBUTING.md new file mode 100644 index 000000000..db0986cfb --- /dev/null +++ b/dist/bin/esptool/CONTRIBUTING.md @@ -0,0 +1,51 @@ +# Contributing to esptool.py + +## Reporting Issues + +Please report bugs in esptool.py if you find them. + +However, before reporting a bug please check through the following: + +* [Troubleshooting Section](https://github.com/espressif/esptool/#troubleshooting) - common problems and known issues + +* [Existing Open Issues](https://github.com/espressif/esptool/issues) - someone might have already encountered this. + +If you don't find anything, please [open a new issue](https://github.com/espressif/esptool/issues/new). + +## Sending Feature Requests + +Feel free to post feature requests. It's helpful if you can explain exactly why the feature would be useful. + +There are usually some outstanding feature requests in the [existing issues list](https://github.com/espressif/esptool/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement), feel free to add comments to them. + +## Sending Pull Requests + +Pull Requests with changes and fixes are also welcome! + +### Code Style & Static Analysis + +esptool.py complies with Flake 8 and is valid Python 2 & Python 3 code (in the same source file.) + +When you submit a Pull Request, the Travis automated build system will run automated checks for this, using the [flake8 tool](http://flake8.readthedocs.io/en/latest/). If you want to check your code locally before submitting, you can install flake8 and run `python setup.py flake8` to test it. + +### Automated Integration Tests + +The test directory contains an integration suite with some integration tests for esptool.py: + +* `test_imagegen.py` tests the elf2image command and is run automatically by Travis for each Pull Request. You can run this command locally to check for regressions in the elf2image functionality. + +* `test_esptool.py` is a [Python unittest](https://docs.python.org/3/library/unittest.html) file that contains integration tests to be run against real ESP8266 or ESP32 hardware. These tests need real hardware so are not run automatically by Travis, they need to be run locally: + +`test_esptool.py` takes a command line with the following format: + +`./test_esptool.py [optional test name(s)]` + +For example, to run all tests on an ESP32 board connected to /dev/ttyUSB0, at 230400bps: + +`./test_esptool.py /dev/ttyUSB0 esp32 230400` + +Or to run the TestFlashing suite only on an ESP8266 board connected to /dev/ttyUSB2` at 460800bps: + +`./test_esptool.py /dev/ttyUSB2 esp8266 460800 TestFlashing` + +(Note that some tests will fail at higher baud rates on some hardware.) diff --git a/dist/bin/esptool/LICENSE b/dist/bin/esptool/LICENSE new file mode 100644 index 000000000..d159169d1 --- /dev/null +++ b/dist/bin/esptool/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/dist/bin/esptool/MANIFEST.in b/dist/bin/esptool/MANIFEST.in new file mode 100644 index 000000000..724438d0d --- /dev/null +++ b/dist/bin/esptool/MANIFEST.in @@ -0,0 +1,14 @@ +include README.md +include LICENSE +# sdist includes test/test*.py by default, but esptool.py tests +# are so far only intended to run from the git repo itself +prune test +prune ecdsa +prune pyaes +prune flasher_stub +prune .github +exclude .git* +exclude .travis* +exclude MANIFEST* + + diff --git a/dist/bin/esptool/README.md b/dist/bin/esptool/README.md new file mode 100644 index 000000000..c5be2d1f3 --- /dev/null +++ b/dist/bin/esptool/README.md @@ -0,0 +1,446 @@ +# esptool.py + +A Python-based, open source, platform independent, utility to communicate with the ROM bootloader in Espressif ESP8266 & ESP32 chips. + +esptool.py was started by Fredrik Ahlberg (@[themadinventor](https://github.com/themadinventor/)) as an unofficial community project. It is now also supported by Espressif. Current primary maintainer is Angus Gratton (@[projectgus](https://github.com/projectgus/)). + +esptool.py is Free Software under a GPLv2 license. + +[![Build Status](https://travis-ci.org/espressif/esptool.svg?branch=master)](https://travis-ci.org/espressif/esptool) + +## Installation / dependencies + +### Easy Installation + +You will need [either Python 2.7 or Python 3.4 or newer](https://www.python.org/downloads/) installed on your system. + +The latest stable esptool.py release can be installed from [pypi](http://pypi.python.org/pypi/esptool) via pip: + +``` +$ pip install esptool +``` + +With some Python installations this may not work and you'll receive an error, try `python -m pip install esptool` or `pip2 install esptool`. + +After installing, you will have `esptool.py` installed into the default Python executables directory and you should be able to run it with the command `esptool.py`. + +### Manual Installation + +Manual installation allows you to run the latest development version from this repository. + +esptool.py depends on [pySerial](https://github.com/pyserial/pyserial#readme) version 3.0 or newer for serial communication with the target device. + +If you choose to install esptool.py system-wide by running `python setup.py install`, then this will be taken care of automatically. + +If not using `setup.py`, then you'll have to install pySerial manually by running something like `pip install pyserial`, `easy_install pyserial` or `apt-get install python-serial`, depending on your platform. (The official pySerial installation instructions are [here](https://pyserial.readthedocs.org/en/latest/pyserial.html#installation)). + +esptool.py also bundles the pyaes & ecdsa Python modules as "vendored" libraries. These modules are required when using the ESP32-only `espsecure.py` and `espefuse.py` tools. If you install esptool.py via `pip` or `setup.py` as shown above, then versions of these libraries will be installed from pypi. If you run esptool.py from the repository directory directly, it will use the "vendored" versions. + +## Usage + +Use `esptool.py -h` to see a summary of all available commands and command line options. + +To see all options for a particular command, append `-h` to the command name. ie `esptool.py write_flash -h`. + +## Common Options + +### Serial Port + +* The serial port is selected using the `-p` option, like `-p /dev/ttyUSB0` (Linux and macOS) or `-p COM1` (Windows). +* A default serial port can be specified by setting the `ESPTOOL_PORT` environment variable. +* If no `-p` option or `ESPTOOL_PORT` value is specified, `esptool.py` will enumerate all connected serial ports and try each one until it finds an Espressif device connected (new behaviour in v2.4.0). + +Note: Windows and macOS may require drivers to be installed for a particular USB/serial adapter, before a serial port is available. Consult the documentation for your particular device. On macOS, you can also consult [System Information](https://support.apple.com/en-us/HT203001)'s list of USB devices to identify the manufacturer or device ID when the adapter is plugged in. On Windows, you can use [Windows Update or Device Manager](https://support.microsoft.com/en-us/help/15048/windows-7-update-driver-hardware-not-working-properly) to find a driver. + +If using Cygwin or WSL on Windows, you have to convert the Windows-style name into an Unix-style path (`COM1` -> `/dev/ttyS0`, and so on). (This is not necessary if using esp-idf for ESP32 with the supplied Windows MSYS2 environment, this environment uses a native Windows Python which accepts COM ports as-is.) + +In Linux, the current user may not have access to serial ports and a "Permission Denied" error will appear. On most Linux distributions, the solution is to add the user to the `dialout` group with a command like `sudo usermod -a -G dialout `. Check your Linux distribution's documentation for more information. + +### Baud rate + +The default esptool.py baud rate is 115200bps. Different rates may be set using `-b 921600` (or another baudrate of your choice). A default baud rate can also be specified using the `ESPTOOL_BAUD` environment variable. This can speed up `write_flash` and `read_flash` operations. + +The baud rate is limited to 115200 when esptool.py establishes the initial connection, higher speeds are only used for data transfers. + +Most hardware configurations will work with `-b 230400`, some with `-b 460800`, `-b 921600` and/or `-b 1500000` or higher. + +If you have connectivity problems then you can also set baud rates below 115200. You can also choose 74880, which is the usual baud rate used by the ESP8266 to output [boot log](#boot-log) information. + +## Commands + +### Write binary data to flash: write_flash + +Binary data can be written to the ESP's flash chip via the serial `write_flash` command: + +``` +esptool.py --port COM4 write_flash 0x1000 my_app-0x01000.bin +``` + +Multiple flash addresses and file names can be given on the same command line: + +``` +esptool.py --port COM4 write_flash 0x00000 my_app.elf-0x00000.bin 0x40000 my_app.elf-0x40000.bin +``` + +The `--chip` argument is optional when writing to flash, esptool will detect the type of chip when it connects to the serial port. + +The `--port` argument is documented under [Serial Port](#serial-port). + +The next arguments to write_flash are one or more pairs of offset (address) and file name. When generating ESP8266 "version 1" images, the file names created by `elf2image` include the flash offsets as part of the file name. For other types of images, consult your SDK documentation to determine the files to flash at which offsets. + +Numeric values passed to write_flash (and other commands) can be specified either in hex (ie 0x1000), or in decimal (ie 4096). + +See the [Troubleshooting](#troubleshooting) section if the write_flash command is failing, or the flashed module fails to boot. + +#### Setting flash mode and size + +You may also need to specify arguments for [flash mode and flash size](#flash-modes), if you wish to override the defaults. For example: + +``` +esptool.py --port /dev/ttyUSB0 write_flash --flash_mode qio --flash_size 32m 0x0 bootloader.bin 0x1000 my_app.bin +``` + +Since esptool v2.0, these options are not often needed as the default is to keep the flash mode and size from the `.bin` image file, and to detect the flash size. See the [Flash Modes](#flash-modes) section for more details. + +#### Compression + +By default, the serial transfer data is compressed for better performance. The `-u/--no-compress` option disables this behaviour. + +### Read Flash Contents: read_flash + +The read_flash command allows reading back the contents of flash. The arguments to the command are an address, a size, and a filename to dump the output to. For example, to read a full 2MB of attached flash: + +``` +./esptool.py -p PORT -b 460800 read_flash 0 0x200000 flash_contents.bin +``` + +(Note that if `write_flash` updated the boot image's [flash mode and flash size](#flash-modes) during flashing then these bytes may be different when read back.) + +### Erase Flash: erase_flash & erase region + +To erase the entire flash chip (all data replaced with 0xFF bytes): + +``` +esptool.py erase_flash +``` + +To erase a region of the flash, starting at address 0x20000 with length 0x4000 bytes (16KB): + +``` +esptool.py erase_region 0x20000 0x4000 +``` + +The address and length must both be multiples of the SPI flash erase sector size. This is 0x1000 (4096) bytes for supported flash chips. + +### Read built-in MAC address: read_mac + +``` +esptool.py read_mac +``` + +### Read SPI flash id: flash_id + +``` +esptool.py flash_id +``` + +Example output: + +``` +Manufacturer: e0 +Device: 4016 +Detected flash size: 4MB +``` + +Refer to [flashrom source code](https://review.coreboot.org/cgit/flashrom.git/tree/flashchips.h) for flash chip manufacturer name and part number. + +### Convert ELF to Binary: elf2image + +The `elf2image` command converts an ELF file (from compiler/linker output) into the binary executable images which can be flashed and then booted into: +``` +esptool.py --chip esp8266 elf2image my_app.elf +``` + +This command does not require a serial connection. + +`elf2image` also accepts the [Flash Modes](#flash-modes) arguments `--flash_freq` and `--flash_mode`, which can be used to set the default values in the image header. This is important when generating any image which will be booted directly by the chip. These values can also be overwritten via the `write_flash` command, see the [write_flash command](#Writing-binaries-to-flash) for details. + +#### elf2image for ESP8266 + +The default command output is two binary files: `my_app.elf-0x00000.bin` and `my_app.elf-0x40000.bin`. You can alter the firmware file name prefix using the `--output/-o` option. + +`elf2image` can also produce a "version 2" image file suitable for use with a software bootloader stub such as [rboot](https://github.com/raburton/rboot) or the Espressif bootloader program. You can't flash a "version 2" image without also flashing a suitable bootloader. + +``` +esptool.py --chip esp8266 elf2image --version=2 -o my_app-ota.bin my_app.elf +``` + +#### elf2image for ESP32 + +For esp32, elf2image produces a single output binary "image file". By default this has the same name as the .elf file, with a .bin extension. ie: + +``` +esptool.py --chip esp32 elf2image my_esp32_app.elf +``` + +In the above example, the output image file would be called `my_esp32_app.bin`. + +### Output .bin image details: image_info + +The `image_info` command outputs some information (load addresses, sizes, etc) about a `.bin` file created by `elf2image`. + +``` +esptool.py --chip esp32 image_info my_esp32_app.bin +``` + +Note that `--chip esp32` is required when reading ESP32 images. Otherwise the default is `--chip esp8266` and the image will be interpreted as an invalid ESP8266 image. + +### Advanced Commands + +The following commands are less commonly used, or only of interest to advanced users. They are documented on the wiki: + +* [verify_flash](https://github.com/espressif/esptool/wiki/Advanced-Commands#verify_flash) +* [dump_mem](https://github.com/espressif/esptool/wiki/Advanced-Commands#dump_mem) +* [load_ram](https://github.com/espressif/esptool/wiki/Advanced-Commands#load_ram) +* [read_mem & write_mem](https://github.com/espressif/esptool/wiki/Advanced-Commands#read_mem--write_mem) +* [read_flash_status](https://github.com/espressif/esptool/wiki/Advanced-Commands#read_flash_status) +* [write_flash_status](https://github.com/espressif/esptool/wiki/Advanced-Commands#write_flash_status) +* [chip_id](https://github.com/espressif/esptool/wiki/Advanced-Commands#chip_id) +* [make_image](https://github.com/espressif/esptool/wiki/Advanced-Commands#make_image) +* [run](https://github.com/espressif/esptool/wiki/Advanced-Commands#run) + +## Additional ESP32 Tools + +The following tools for ESP32, bundled with esptool.py, are documented on the wiki: + +* [espefuse.py - for reading/writing ESP32 efuse region](https://github.com/espressif/esptool/wiki/espefuse) +* [espsecure.py - for working with ESP32 security features](https://github.com/espressif/esptool/wiki/espsecure) + +## Serial Connections + +The ESP8266 & ESP32 ROM serial bootloader uses a 3.3V UART serial connection. Many development boards make the serial connections for you onboard. + +However, if you are wiring the chip yourself to a USB/Serial adapter or similar then the following connections must be made: + +ESP32/ESP8266 Pin | Serial Port Pin +--------------------- | ---------------------------- +TX (aka GPIO1) | RX (receive) +RX (aka GPIO3) | TX (transmit) +Ground | Ground + +Note that TX (transmit) on the ESP8266 is connected to RX (receive) on the serial port connection, and vice versa. + +Do not connect the chip to 5V TTL serial adapters, and especially not to "standard" RS-232 adapters! 3.3V serial only! + +## Entering the Bootloader + +Both ESP8266 and ESP32 have to be reset in a certain way in order to launch the serial bootloader. + +On some development boards (including NodeMCU, WeMOS, HUZZAH Feather, Core Board, ESP32-WROVER-KIT), esptool.py can automatically trigger a reset into the serial bootloader - in which case you don't need to read this section. + +For everyone else, three things must happen to enter the serial bootloader - a reset, required pins set correctly, and GPIO0 pulled low: + +### Boot Mode + +Both ESP8266 and ESP32 chooses the boot mode each time it resets. A reset event can happen in one of several ways: + +* Power applied to chip. +* The nRESET pin was low and is pulled high (on ESP8266 only). +* The CH_PD/EN pin ("enable") pin was low and is pulled high. + +On ESP8266, both the nRESET and CH_PD pins must be pulled high for the chip to start operating. + +For more details on selecting the boot mode, see the following Wiki pages: + +* [ESP8266 Boot Mode Selection](https://github.com/espressif/esptool/wiki/ESP8266-Boot-Mode-Selection) +* [ESP32 Boot Mode Selection](https://github.com/espressif/esptool/wiki/ESP32-Boot-Mode-Selection) + +## Flash Modes + +`write_flash` and some other comands accept command line arguments to set bootloader flash mode, flash size and flash clock frequency. The chip needs correct mode, frequency and size settings in order to run correctly - although there is some flexibility. A header at the beginning of a bootable image contains these values. + +To override these values, the options `--flash_mode`, `--flash_size` and/or `--flash_freq` must appear after `write_flash` on the command line, for example: + +``` +esptool.py --port /dev/ttyUSB1 write_flash --flash_mode dio --flash_size 4MB 0x0 bootloader.bin +``` + +These options are only consulted when flashing a bootable image to an ESP8266 at offset 0x0, or an ESP32 at offset 0x1000. These are addresses used by the ROM bootloader to load from flash. When flashing at all other offsets, these arguments are not used. + +### Flash Mode (--flash_mode, -fm) + +These set Quad Flash I/O or Dual Flash I/O modes. Valid values are `keep`, `qio`, `qout`, `dio`, `dout`. The default is `keep`, which keeps whatever value is already in the image file. This parameter can also be specified using the environment variable `ESPTOOL_FM`. + +Most boards use `qio` mode. Some ESP8266 modules, including the ESP-12E modules on some (not all) NodeMCU boards, are dual I/O and the firmware will only boot when flashed with `--flash_mode dio`. Most ESP32 modules are also dual I/O. + +In `qio` mode, two additional GPIOs (9 and 10) are used for SPI flash communications. If flash mode is set to `dio` then these pins are available for other purposes. + +For a full explanation of these modes, see the [SPI Flash Modes wiki page](https://github.com/espressif/esptool/wiki/SPI-Flash-Modes). + +### Flash Frequency (--flash_freq, -ff) + +Clock frequency for SPI flash interactions. Valid values are `keep`, `40m`, `26m`, `20m`, `80m` (MHz). The default is `keep`, which keeps whatever value is already in the image file. This parameter can also be specified using the environment variable `ESPTOOL_FF`. + +The flash chip connected to most chips works with 40MHz clock speeds, but you can try lower values if the device won't boot. The highest 80MHz flash clock speed will give best performance, but may cause crashing if the flash or board designis not capable of this speed. + +### Flash Size (--flash_size, -fs) + +Size of the SPI flash, given in megabytes. Valid values vary by chip type: + +Chip | flash_size values +---------|--------------------------------------------------------------- +ESP32 | `detect`, `1MB`, `2MB`, `4MB`, `8MB`, `16MB` +ESP8266 | `detect`, `256KB`, `512KB`, `1MB`, `2MB`, `4MB`, `2MB-c1`, `4MB-c1`, `8MB`, `16MB` + +For ESP8266, some [additional sizes & layouts for OTA "firmware slots" are available](#esp8266-and-flash-size). + +The default `--flash_size` parameter is `detect`, which tries to autodetect size based on SPI flash ID. If detection fails, a warning is printed and a default value of of `4MB` (4 megabytes) is used. + +If flash size is not successfully detected, you can find the flash size by using the `flash_id` command and then looking up the ID from the output (see [Read SPI flash id](#read-spi-flash-id)). Alternatively, read off the silkscreen labelling of the flash chip and search for its datasheet. + +The default `flash_size` parameter can also be overriden using the environment variable `ESPTOOL_FS`. + +#### ESP8266 and Flash Size + +The ESP8266 SDK stores WiFi configuration at the "end" of flash, and it finds the end using this size. However there is no downside to specifying a smaller flash size than you really have, as long as you don't need to write an image larger than this size. + +ESP-12, ESP-12E and ESP-12F modules (and boards that use them such as NodeMCU, HUZZAH, etc.) usually have at least 4 megabyte / `4MB` (sometimes labelled 32 megabit) flash. + +If using OTA, some additional sizes & layouts for OTA "firmware slots" are available. If not using OTA updates then you can ignore these extra sizes: + +|flash_size arg | Number of OTA slots | OTA Slot Size | Non-OTA Space | +|---------------|---------------------|---------------|---------------| +|256KB | 1 (no OTA) | 256KB | N/A | +|512KB | 1 (no OTA) | 512KB | N/A | +|1MB | 2 | 512KB | 0KB | +|2MB | 2 | 512KB | 1024KB | +|4MB | 2 | 512KB | 3072KB | +|2MB-c1 | 2 | 1024KB | 0KB | +|4MB-c1 | 2 | 1024KB | 2048KB | +|8MB [^] | 2 | 1024KB | 6144KB | +|16MB [^] | 2 | 1024KB | 14336KB | + +* [^] Support for 8MB & 16MB flash size is not present in all ESP8266 SDKs. If your SDK doesn't support these flash sizes, use `--flash_size 4MB`. + +#### ESP32 and Flash Size + +The ESP32 esp-idf flashes a partition table to the flash at offset 0x8000. All of the partitions in this table must fit inside the configured flash size, otherwise the ESP32 will not work correctly. + +## Advanced Options + +See the [Advanced Options wiki page](https://github.com/espressif/esptool/wiki/Advanced-Options) for some of the more unusual esptool.py command line options. + +## Remote Serial Ports + +It is possible to connect to any networked remote serial port that supports [RFC2217](http://www.ietf.org/rfc/rfc2217.txt) (Telnet) protocol, or a plain TCP socket. See the [Remote Serial Ports wiki page](https://github.com/espressif/esptool/wiki/Remote-Serial-Ports) for details. + +## Troubleshooting + +Flashing problems can be fiddly to troubleshoot. Try the suggestions here if you're having problems: + +### Bootloader won't respond + +If you see errors like "Failed to connect" then your chip is probably not entering the bootloader properly: + +* Check you are passing the correct serial port on the command line. +* Check you have permissions to access the serial port, and other software (such as modem-manager on Linux) is not trying to interact with it. A common pitfall is leaving a serial terminal accessing this port open in another window and forgetting about it. +* Check the chip is receiving 3.3V from a stable power source (see [Insufficient Power](#insufficient-power) for more details.) +* Check that all pins are connected as described in [Entering the bootloader](#entering-the-bootloader). Check the voltages at each pin with a multimeter, "high" pins should be close to 3.3V and "low" pins should be close to 0V. +* If you have connected other devices to GPIO pins mentioned above section, try removing them and see if esptool.py starts working. +* Try using a slower baud rate (`-b 9600` is a very slow value that you can use to verify it's not a baud rate problem.) + + +### write_flash operation fails part way through + +If flashing fails with random errors part way through, retry with a lower baud rate. + +Power stability problems may also cause this (see [Insufficient Power](#insufficient-power).) + +### write_flash succeeds but program doesn't run + +If esptool.py can flash your module with `write_flash` but your program doesn't run, try the following: + +#### Wrong Flash Mode + +Some devices only support the `dio` flash mode. Writing to flash with `qio` mode will succeed but the chip can't read the flash back to run - so nothing happens on boot. Try passing the `-fm dio` option to write_flash. + +See the [SPI Flash Modes](https://github.com/espressif/esptool/wiki/SPI-Flash-Modes) wiki page for a full description of the flash modes and how to determine which ones are supported on your device. + +#### Insufficient Power + +The 3.3V power supply for the ESP8266 and ESP32 has to supply large amounts of current (up to 70mA continuous, 200-300mA peak, slightly higher for ESP32). You also need sufficient capacitance on the power circuit to meet large spikes of power demand. + +##### Insufficient Capacitance + +If you're using a pre-made development board or module then the built-in power regulator & capacitors are usually good enough, provided the input power supply is adequate. + +*This is not true for some very simple pin breakout modules - [similar to this](https://user-images.githubusercontent.com/205573/30140831-9da417a6-93ba-11e7-95c3-f422744967de.jpg). These breakouts do not integrate enough capacitance to work reliably without additional components.*. Surface mount OEM modules like ESP-WROOM02 and ESP-WROOM32 require an external bulk capacitor on the PCB to be reliable, consult the module datasheet. + +##### Power Supply Rating + +It is possible to have a power supply that supplies enough current for the serial bootloader stage with esptool.py, but not enough for normal firmware operation. You may see the 3.3V VCC voltage droop down if you measure it with a multimeter, but you can have problems even if this isn't happening. + +Try swapping in a 3.3V supply with a higher current rating, add capacitors to the power line, and/or shorten any 3.3V power wires. + +The 3.3V output from FTDI FT232R chips/adapters or Arduino boards *do not* supply sufficient current to power an ESP8266 or ESP32 (it may seem to work sometimes, but it won't work reliably). Other USB TTL/serial adapters may also be marginal. + +#### Missing bootloader + +Recent ESP8266 SDKs and the ESP32 esp-idf both use a small firmware bootloader program. The hardware bootloader in ROM loads this firmware bootloader from flash, and then it runs the program. On ESP8266. firmware bootloader image (with a filename like `boot_v1.x.bin`) has to be flashed at offset 0. If the firmware bootloader is missing then the ESP8266 will not boot. On ESP32, the bootloader image should be flashed by esp-idf at offset 0x1000. + +Refer to SDK or esp-idf documentation for details regarding which binaries need to be flashed at which offsets. + +#### SPI Pins which must be disconnected + +Compared to the ROM bootloader that esptool.py talks to, a running firmware uses more of the chip's pins to access the SPI flash. + +If you set "Quad I/O" mode (`-fm qio`, the esptool.py default) then GPIOs 7, 8, 9 & 10 are used for reading the SPI flash and must be otherwise disconnected. + +If you set "Dual I/O" mode (`-fm dio`) then GPIOs 7 & 8 are used for reading the SPI flash and must be otherwise disconnected. + +Try disconnecting anything from those pins (and/or swap to Dual I/O mode if you were previously using Quad I/O mode but want to attach things to GPIOs 9 & 10). Note that if GPIOs 9 & 10 are also connected to input pins on the SPI flash chip, they may still be unsuitable for use as general purpose I/O. + +In addition to these pins, GPIOs 6 & 11 are also used to access the SPI flash (in all modes). However flashing will usually fail completely if these pins are connected incorrectly. + +### Early stage crash + +Use a [serial terminal program](#serial-terminal-programs) to view the boot log. (ESP8266 baud rate is 74880bps, ESP32 is 115200bps). See if the program is crashing during early startup or outputting an error message. + +## Serial Terminal Programs + +There are many serial terminal programs suitable for debugging & serial interaction. The pyserial module (which is required for esptool.py) includes one such command line terminal program - miniterm.py. For more details [see this page](http://pyserial.readthedocs.org/en/latest/tools.html#module-serial.tools.miniterm) or run `miniterm -h`. + +Note that not every serial program supports the unusual ESP8266 74880bps "boot log" baud rate. Support is especially sparse on Linux. `miniterm.py` supports this baud rate on all platforms. ESP32 uses the more common 115200bps. + +## Tracing esptool.py interactions + +Running `esptool.py --trace` will dump all serial interactions to the standard output (this is *a lot* of output). This can be helpful when debugging issues with the serial connection, or when providing information for bug reports. + +## Using esptool.py from Python + +esptool.py can easily be integrated into Python applications or called from other Python scripts. + +While it currently does have a poor Python API, something which [#208](https://github.com/espressif/esptool/issues/208) will address, it allows for passing CLI +arguments to `esptool.main()`. This workaround makes integration very straightforward as you can pass exactly the +same arguments as you would on the CLI. + +```python +command = ['--baud', '460800', 'read_flash', '0', '0x200000', 'flash_contents.bin'] +print('Using command %s' % ' '.join(command)) +esptool.main(command) +``` + +## Internal Technical Documentation + +The [repository wiki](https://github.com/espressif/esptool/wiki) contains some technical documentation regarding the serial protocol and file format used by the ROM bootloader. This may be useful if you're developing esptool.py or hacking system internals: + +* [Firmware Image Format](https://github.com/espressif/esptool/wiki/Firmware-Image-Format) +* [Serial Protocol](https://github.com/espressif/esptool/wiki/Serial-Protocol) +* [ESP8266 Boot ROM Log](https://github.com/espressif/esptool/wiki/ESP8266-Boot-ROM-Log) + + +## About + +esptool.py was initially created by Fredrik Ahlberg (@themadinventor, @kongo), and is currently maintained by Angus Gratton (@projectgus). It has also received improvements from many members of the ESP8266 community - including @rojer, @jimparis, @jms19, @pfalcon, @tommie, @0ff, @george-hopkins and others. + +This document and the attached source code are released under GNU General Public License Version 2. See the accompanying file LICENSE for a copy. diff --git a/dist/bin/esptool/ecdsa/__init__.py b/dist/bin/esptool/ecdsa/__init__.py new file mode 100644 index 000000000..e834b3a85 --- /dev/null +++ b/dist/bin/esptool/ecdsa/__init__.py @@ -0,0 +1,14 @@ +__all__ = ["curves", "der", "ecdsa", "ellipticcurve", "keys", "numbertheory", + "test_pyecdsa", "util", "six"] +from .keys import SigningKey, VerifyingKey, BadSignatureError, BadDigestError +from .curves import NIST192p, NIST224p, NIST256p, NIST384p, NIST521p, SECP256k1 + +_hush_pyflakes = [SigningKey, VerifyingKey, BadSignatureError, BadDigestError, + NIST192p, NIST224p, NIST256p, NIST384p, NIST521p, SECP256k1] +del _hush_pyflakes + +# This code comes from http://github.com/warner/python-ecdsa + +from ._version import get_versions +__version__ = get_versions()['version'] +del get_versions diff --git a/dist/bin/esptool/ecdsa/_version.py b/dist/bin/esptool/ecdsa/_version.py new file mode 100644 index 000000000..3e833e64f --- /dev/null +++ b/dist/bin/esptool/ecdsa/_version.py @@ -0,0 +1,183 @@ + +# This file helps to compute a version number in source trees obtained from +# git-archive tarball (such as those provided by githubs download-from-tag +# feature). Distribution tarballs (built by setup.py sdist) and build +# directories (produced by setup.py build) will contain a much shorter file +# that just contains the computed version number. + +# This file is released into the public domain. Generated by +# versioneer-0.12 (https://github.com/warner/python-versioneer) + +# these strings will be replaced by git during git-archive +git_refnames = " (tag: python-ecdsa-0.13)" +git_full = "5a6fc047222cf21ad89f6cbf8782d0f1e3ddacda" + +# these strings are filled in when 'setup.py versioneer' creates _version.py +tag_prefix = "python-ecdsa-" +parentdir_prefix = "ecdsa-" +versionfile_source = "ecdsa/_version.py" + +import os, sys, re, subprocess, errno + +def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False): + assert isinstance(commands, list) + p = None + for c in commands: + try: + # remember shell=False, so use git.cmd on windows, not just git + p = subprocess.Popen([c] + args, cwd=cwd, stdout=subprocess.PIPE, + stderr=(subprocess.PIPE if hide_stderr + else None)) + break + except EnvironmentError: + e = sys.exc_info()[1] + if e.errno == errno.ENOENT: + continue + if verbose: + print("unable to run %s" % args[0]) + print(e) + return None + else: + if verbose: + print("unable to find command, tried %s" % (commands,)) + return None + stdout = p.communicate()[0].strip() + if sys.version >= '3': + stdout = stdout.decode() + if p.returncode != 0: + if verbose: + print("unable to run %s (error)" % args[0]) + return None + return stdout + + +def versions_from_parentdir(parentdir_prefix, root, verbose=False): + # Source tarballs conventionally unpack into a directory that includes + # both the project name and a version string. + dirname = os.path.basename(root) + if not dirname.startswith(parentdir_prefix): + if verbose: + print("guessing rootdir is '%s', but '%s' doesn't start with prefix '%s'" % + (root, dirname, parentdir_prefix)) + return None + return {"version": dirname[len(parentdir_prefix):], "full": ""} + +def git_get_keywords(versionfile_abs): + # the code embedded in _version.py can just fetch the value of these + # keywords. When used from setup.py, we don't want to import _version.py, + # so we do it with a regexp instead. This function is not used from + # _version.py. + keywords = {} + try: + f = open(versionfile_abs,"r") + for line in f.readlines(): + if line.strip().startswith("git_refnames ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["refnames"] = mo.group(1) + if line.strip().startswith("git_full ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["full"] = mo.group(1) + f.close() + except EnvironmentError: + pass + return keywords + +def git_versions_from_keywords(keywords, tag_prefix, verbose=False): + if not keywords: + return {} # keyword-finding function failed to find keywords + refnames = keywords["refnames"].strip() + if refnames.startswith("$Format"): + if verbose: + print("keywords are unexpanded, not using") + return {} # unexpanded, so not in an unpacked git-archive tarball + refs = set([r.strip() for r in refnames.strip("()").split(",")]) + # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of + # just "foo-1.0". If we see a "tag: " prefix, prefer those. + TAG = "tag: " + tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) + if not tags: + # Either we're using git < 1.8.3, or there really are no tags. We use + # a heuristic: assume all version tags have a digit. The old git %d + # expansion behaves like git log --decorate=short and strips out the + # refs/heads/ and refs/tags/ prefixes that would let us distinguish + # between branches and tags. By ignoring refnames without digits, we + # filter out many common branch names like "release" and + # "stabilization", as well as "HEAD" and "master". + tags = set([r for r in refs if re.search(r'\d', r)]) + if verbose: + print("discarding '%s', no digits" % ",".join(refs-tags)) + if verbose: + print("likely tags: %s" % ",".join(sorted(tags))) + for ref in sorted(tags): + # sorting will prefer e.g. "2.0" over "2.0rc1" + if ref.startswith(tag_prefix): + r = ref[len(tag_prefix):] + if verbose: + print("picking %s" % r) + return { "version": r, + "full": keywords["full"].strip() } + # no suitable tags, so we use the full revision id + if verbose: + print("no suitable tags, using full revision id") + return { "version": keywords["full"].strip(), + "full": keywords["full"].strip() } + + +def git_versions_from_vcs(tag_prefix, root, verbose=False): + # this runs 'git' from the root of the source tree. This only gets called + # if the git-archive 'subst' keywords were *not* expanded, and + # _version.py hasn't already been rewritten with a short version string, + # meaning we're inside a checked out source tree. + + if not os.path.exists(os.path.join(root, ".git")): + if verbose: + print("no .git in %s" % root) + return {} + + GITS = ["git"] + if sys.platform == "win32": + GITS = ["git.cmd", "git.exe"] + stdout = run_command(GITS, ["describe", "--tags", "--dirty", "--always"], + cwd=root) + if stdout is None: + return {} + if not stdout.startswith(tag_prefix): + if verbose: + print("tag '%s' doesn't start with prefix '%s'" % (stdout, tag_prefix)) + return {} + tag = stdout[len(tag_prefix):] + stdout = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) + if stdout is None: + return {} + full = stdout.strip() + if tag.endswith("-dirty"): + full += "-dirty" + return {"version": tag, "full": full} + + +def get_versions(default={"version": "unknown", "full": ""}, verbose=False): + # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have + # __file__, we can work backwards from there to the root. Some + # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which + # case we can only use expanded keywords. + + keywords = { "refnames": git_refnames, "full": git_full } + ver = git_versions_from_keywords(keywords, tag_prefix, verbose) + if ver: + return ver + + try: + root = os.path.abspath(__file__) + # versionfile_source is the relative path from the top of the source + # tree (where the .git directory might live) to this file. Invert + # this to find the root from __file__. + for i in range(len(versionfile_source.split(os.sep))): + root = os.path.dirname(root) + except NameError: + return default + + return (git_versions_from_vcs(tag_prefix, root, verbose) + or versions_from_parentdir(parentdir_prefix, root, verbose) + or default) diff --git a/dist/bin/esptool/ecdsa/curves.py b/dist/bin/esptool/ecdsa/curves.py new file mode 100644 index 000000000..a361da741 --- /dev/null +++ b/dist/bin/esptool/ecdsa/curves.py @@ -0,0 +1,46 @@ +from __future__ import division + +from . import der, ecdsa + +class UnknownCurveError(Exception): + pass + +def orderlen(order): + return (1+len("%x"%order))//2 # bytes + +# the NIST curves +class Curve: + def __init__(self, name, curve, generator, oid, openssl_name=None): + self.name = name + self.openssl_name = openssl_name # maybe None + self.curve = curve + self.generator = generator + self.order = generator.order() + self.baselen = orderlen(self.order) + self.verifying_key_length = 2*self.baselen + self.signature_length = 2*self.baselen + self.oid = oid + self.encoded_oid = der.encode_oid(*oid) + +NIST192p = Curve("NIST192p", ecdsa.curve_192, ecdsa.generator_192, + (1, 2, 840, 10045, 3, 1, 1), "prime192v1") +NIST224p = Curve("NIST224p", ecdsa.curve_224, ecdsa.generator_224, + (1, 3, 132, 0, 33), "secp224r1") +NIST256p = Curve("NIST256p", ecdsa.curve_256, ecdsa.generator_256, + (1, 2, 840, 10045, 3, 1, 7), "prime256v1") +NIST384p = Curve("NIST384p", ecdsa.curve_384, ecdsa.generator_384, + (1, 3, 132, 0, 34), "secp384r1") +NIST521p = Curve("NIST521p", ecdsa.curve_521, ecdsa.generator_521, + (1, 3, 132, 0, 35), "secp521r1") +SECP256k1 = Curve("SECP256k1", ecdsa.curve_secp256k1, ecdsa.generator_secp256k1, + (1, 3, 132, 0, 10), "secp256k1") + +curves = [NIST192p, NIST224p, NIST256p, NIST384p, NIST521p, SECP256k1] + +def find_curve(oid_curve): + for c in curves: + if c.oid == oid_curve: + return c + raise UnknownCurveError("I don't know about the curve with oid %s." + "I only know about these: %s" % + (oid_curve, [c.name for c in curves])) diff --git a/dist/bin/esptool/ecdsa/der.py b/dist/bin/esptool/ecdsa/der.py new file mode 100644 index 000000000..b045952f7 --- /dev/null +++ b/dist/bin/esptool/ecdsa/der.py @@ -0,0 +1,199 @@ +from __future__ import division + +import binascii +import base64 +from .six import int2byte, b, integer_types, text_type + +class UnexpectedDER(Exception): + pass + +def encode_constructed(tag, value): + return int2byte(0xa0+tag) + encode_length(len(value)) + value +def encode_integer(r): + assert r >= 0 # can't support negative numbers yet + h = ("%x" % r).encode() + if len(h) % 2: + h = b("0") + h + s = binascii.unhexlify(h) + num = s[0] if isinstance(s[0], integer_types) else ord(s[0]) + if num <= 0x7f: + return b("\x02") + int2byte(len(s)) + s + else: + # DER integers are two's complement, so if the first byte is + # 0x80-0xff then we need an extra 0x00 byte to prevent it from + # looking negative. + return b("\x02") + int2byte(len(s)+1) + b("\x00") + s + +def encode_bitstring(s): + return b("\x03") + encode_length(len(s)) + s +def encode_octet_string(s): + return b("\x04") + encode_length(len(s)) + s +def encode_oid(first, second, *pieces): + assert first <= 2 + assert second <= 39 + encoded_pieces = [int2byte(40*first+second)] + [encode_number(p) + for p in pieces] + body = b('').join(encoded_pieces) + return b('\x06') + encode_length(len(body)) + body +def encode_sequence(*encoded_pieces): + total_len = sum([len(p) for p in encoded_pieces]) + return b('\x30') + encode_length(total_len) + b('').join(encoded_pieces) +def encode_number(n): + b128_digits = [] + while n: + b128_digits.insert(0, (n & 0x7f) | 0x80) + n = n >> 7 + if not b128_digits: + b128_digits.append(0) + b128_digits[-1] &= 0x7f + return b('').join([int2byte(d) for d in b128_digits]) + +def remove_constructed(string): + s0 = string[0] if isinstance(string[0], integer_types) else ord(string[0]) + if (s0 & 0xe0) != 0xa0: + raise UnexpectedDER("wanted constructed tag (0xa0-0xbf), got 0x%02x" + % s0) + tag = s0 & 0x1f + length, llen = read_length(string[1:]) + body = string[1+llen:1+llen+length] + rest = string[1+llen+length:] + return tag, body, rest + +def remove_sequence(string): + if not string.startswith(b("\x30")): + n = string[0] if isinstance(string[0], integer_types) else ord(string[0]) + raise UnexpectedDER("wanted sequence (0x30), got 0x%02x" % n) + length, lengthlength = read_length(string[1:]) + endseq = 1+lengthlength+length + return string[1+lengthlength:endseq], string[endseq:] + +def remove_octet_string(string): + if not string.startswith(b("\x04")): + n = string[0] if isinstance(string[0], integer_types) else ord(string[0]) + raise UnexpectedDER("wanted octetstring (0x04), got 0x%02x" % n) + length, llen = read_length(string[1:]) + body = string[1+llen:1+llen+length] + rest = string[1+llen+length:] + return body, rest + +def remove_object(string): + if not string.startswith(b("\x06")): + n = string[0] if isinstance(string[0], integer_types) else ord(string[0]) + raise UnexpectedDER("wanted object (0x06), got 0x%02x" % n) + length, lengthlength = read_length(string[1:]) + body = string[1+lengthlength:1+lengthlength+length] + rest = string[1+lengthlength+length:] + numbers = [] + while body: + n, ll = read_number(body) + numbers.append(n) + body = body[ll:] + n0 = numbers.pop(0) + first = n0//40 + second = n0-(40*first) + numbers.insert(0, first) + numbers.insert(1, second) + return tuple(numbers), rest + +def remove_integer(string): + if not string.startswith(b("\x02")): + n = string[0] if isinstance(string[0], integer_types) else ord(string[0]) + raise UnexpectedDER("wanted integer (0x02), got 0x%02x" % n) + length, llen = read_length(string[1:]) + numberbytes = string[1+llen:1+llen+length] + rest = string[1+llen+length:] + nbytes = numberbytes[0] if isinstance(numberbytes[0], integer_types) else ord(numberbytes[0]) + assert nbytes < 0x80 # can't support negative numbers yet + return int(binascii.hexlify(numberbytes), 16), rest + +def read_number(string): + number = 0 + llen = 0 + # base-128 big endian, with b7 set in all but the last byte + while True: + if llen > len(string): + raise UnexpectedDER("ran out of length bytes") + number = number << 7 + d = string[llen] if isinstance(string[llen], integer_types) else ord(string[llen]) + number += (d & 0x7f) + llen += 1 + if not d & 0x80: + break + return number, llen + +def encode_length(l): + assert l >= 0 + if l < 0x80: + return int2byte(l) + s = ("%x" % l).encode() + if len(s)%2: + s = b("0")+s + s = binascii.unhexlify(s) + llen = len(s) + return int2byte(0x80|llen) + s + +def read_length(string): + num = string[0] if isinstance(string[0], integer_types) else ord(string[0]) + if not (num & 0x80): + # short form + return (num & 0x7f), 1 + # else long-form: b0&0x7f is number of additional base256 length bytes, + # big-endian + llen = num & 0x7f + if llen > len(string)-1: + raise UnexpectedDER("ran out of length bytes") + return int(binascii.hexlify(string[1:1+llen]), 16), 1+llen + +def remove_bitstring(string): + num = string[0] if isinstance(string[0], integer_types) else ord(string[0]) + if not string.startswith(b("\x03")): + raise UnexpectedDER("wanted bitstring (0x03), got 0x%02x" % num) + length, llen = read_length(string[1:]) + body = string[1+llen:1+llen+length] + rest = string[1+llen+length:] + return body, rest + +# SEQUENCE([1, STRING(secexp), cont[0], OBJECT(curvename), cont[1], BINTSTRING) + + +# signatures: (from RFC3279) +# ansi-X9-62 OBJECT IDENTIFIER ::= { +# iso(1) member-body(2) us(840) 10045 } +# +# id-ecSigType OBJECT IDENTIFIER ::= { +# ansi-X9-62 signatures(4) } +# ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { +# id-ecSigType 1 } +## so 1,2,840,10045,4,1 +## so 0x42, .. .. + +# Ecdsa-Sig-Value ::= SEQUENCE { +# r INTEGER, +# s INTEGER } + +# id-public-key-type OBJECT IDENTIFIER ::= { ansi-X9.62 2 } +# +# id-ecPublicKey OBJECT IDENTIFIER ::= { id-publicKeyType 1 } + +# I think the secp224r1 identifier is (t=06,l=05,v=2b81040021) +# secp224r1 OBJECT IDENTIFIER ::= { +# iso(1) identified-organization(3) certicom(132) curve(0) 33 } +# and the secp384r1 is (t=06,l=05,v=2b81040022) +# secp384r1 OBJECT IDENTIFIER ::= { +# iso(1) identified-organization(3) certicom(132) curve(0) 34 } + +def unpem(pem): + if isinstance(pem, text_type): + pem = pem.encode() + + d = b("").join([l.strip() for l in pem.split(b("\n")) + if l and not l.startswith(b("-----"))]) + return base64.b64decode(d) +def topem(der, name): + b64 = base64.b64encode(der) + lines = [("-----BEGIN %s-----\n" % name).encode()] + lines.extend([b64[start:start+64]+b("\n") + for start in range(0, len(b64), 64)]) + lines.append(("-----END %s-----\n" % name).encode()) + return b("").join(lines) + diff --git a/dist/bin/esptool/ecdsa/ecdsa.py b/dist/bin/esptool/ecdsa/ecdsa.py new file mode 100644 index 000000000..4eb2d3955 --- /dev/null +++ b/dist/bin/esptool/ecdsa/ecdsa.py @@ -0,0 +1,576 @@ +#! /usr/bin/env python + +""" +Implementation of Elliptic-Curve Digital Signatures. + +Classes and methods for elliptic-curve signatures: +private keys, public keys, signatures, +NIST prime-modulus curves with modulus lengths of +192, 224, 256, 384, and 521 bits. + +Example: + + # (In real-life applications, you would probably want to + # protect against defects in SystemRandom.) + from random import SystemRandom + randrange = SystemRandom().randrange + + # Generate a public/private key pair using the NIST Curve P-192: + + g = generator_192 + n = g.order() + secret = randrange( 1, n ) + pubkey = Public_key( g, g * secret ) + privkey = Private_key( pubkey, secret ) + + # Signing a hash value: + + hash = randrange( 1, n ) + signature = privkey.sign( hash, randrange( 1, n ) ) + + # Verifying a signature for a hash value: + + if pubkey.verifies( hash, signature ): + print_("Demo verification succeeded.") + else: + print_("*** Demo verification failed.") + + # Verification fails if the hash value is modified: + + if pubkey.verifies( hash-1, signature ): + print_("**** Demo verification failed to reject tampered hash.") + else: + print_("Demo verification correctly rejected tampered hash.") + +Version of 2009.05.16. + +Revision history: + 2005.12.31 - Initial version. + 2008.11.25 - Substantial revisions introducing new classes. + 2009.05.16 - Warn against using random.randrange in real applications. + 2009.05.17 - Use random.SystemRandom by default. + +Written in 2005 by Peter Pearson and placed in the public domain. +""" + +from .six import int2byte, b, print_ +from . import ellipticcurve +from . import numbertheory +import random + + + +class Signature( object ): + """ECDSA signature. + """ + def __init__( self, r, s ): + self.r = r + self.s = s + + + +class Public_key( object ): + """Public key for ECDSA. + """ + + def __init__( self, generator, point ): + """generator is the Point that generates the group, + point is the Point that defines the public key. + """ + + self.curve = generator.curve() + self.generator = generator + self.point = point + n = generator.order() + if not n: + raise RuntimeError("Generator point must have order.") + if not n * point == ellipticcurve.INFINITY: + raise RuntimeError("Generator point order is bad.") + if point.x() < 0 or n <= point.x() or point.y() < 0 or n <= point.y(): + raise RuntimeError("Generator point has x or y out of range.") + + + def verifies( self, hash, signature ): + """Verify that signature is a valid signature of hash. + Return True if the signature is valid. + """ + + # From X9.62 J.3.1. + + G = self.generator + n = G.order() + r = signature.r + s = signature.s + if r < 1 or r > n-1: return False + if s < 1 or s > n-1: return False + c = numbertheory.inverse_mod( s, n ) + u1 = ( hash * c ) % n + u2 = ( r * c ) % n + xy = u1 * G + u2 * self.point + v = xy.x() % n + return v == r + + + +class Private_key( object ): + """Private key for ECDSA. + """ + + def __init__( self, public_key, secret_multiplier ): + """public_key is of class Public_key; + secret_multiplier is a large integer. + """ + + self.public_key = public_key + self.secret_multiplier = secret_multiplier + + def sign( self, hash, random_k ): + """Return a signature for the provided hash, using the provided + random nonce. It is absolutely vital that random_k be an unpredictable + number in the range [1, self.public_key.point.order()-1]. If + an attacker can guess random_k, he can compute our private key from a + single signature. Also, if an attacker knows a few high-order + bits (or a few low-order bits) of random_k, he can compute our private + key from many signatures. The generation of nonces with adequate + cryptographic strength is very difficult and far beyond the scope + of this comment. + + May raise RuntimeError, in which case retrying with a new + random value k is in order. + """ + + G = self.public_key.generator + n = G.order() + k = random_k % n + p1 = k * G + r = p1.x() + if r == 0: raise RuntimeError("amazingly unlucky random number r") + s = ( numbertheory.inverse_mod( k, n ) * \ + ( hash + ( self.secret_multiplier * r ) % n ) ) % n + if s == 0: raise RuntimeError("amazingly unlucky random number s") + return Signature( r, s ) + + + +def int_to_string( x ): + """Convert integer x into a string of bytes, as per X9.62.""" + assert x >= 0 + if x == 0: return b('\0') + result = [] + while x: + ordinal = x & 0xFF + result.append(int2byte(ordinal)) + x >>= 8 + + result.reverse() + return b('').join(result) + + +def string_to_int( s ): + """Convert a string of bytes into an integer, as per X9.62.""" + result = 0 + for c in s: + if not isinstance(c, int): c = ord( c ) + result = 256 * result + c + return result + + +def digest_integer( m ): + """Convert an integer into a string of bytes, compute + its SHA-1 hash, and convert the result to an integer.""" + # + # I don't expect this function to be used much. I wrote + # it in order to be able to duplicate the examples + # in ECDSAVS. + # + from hashlib import sha1 + return string_to_int( sha1( int_to_string( m ) ).digest() ) + + +def point_is_valid( generator, x, y ): + """Is (x,y) a valid public key based on the specified generator?""" + + # These are the tests specified in X9.62. + + n = generator.order() + curve = generator.curve() + if x < 0 or n <= x or y < 0 or n <= y: + return False + if not curve.contains_point( x, y ): + return False + if not n*ellipticcurve.Point( curve, x, y ) == \ + ellipticcurve.INFINITY: + return False + return True + + + +# NIST Curve P-192: +_p = 6277101735386680763835789423207666416083908700390324961279 +_r = 6277101735386680763835789423176059013767194773182842284081 +# s = 0x3045ae6fc8422f64ed579528d38120eae12196d5L +# c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65L +_b = 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1 +_Gx = 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012 +_Gy = 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811 + +curve_192 = ellipticcurve.CurveFp( _p, -3, _b ) +generator_192 = ellipticcurve.Point( curve_192, _Gx, _Gy, _r ) + + +# NIST Curve P-224: +_p = 26959946667150639794667015087019630673557916260026308143510066298881 +_r = 26959946667150639794667015087019625940457807714424391721682722368061 +# s = 0xbd71344799d5c7fcdc45b59fa3b9ab8f6a948bc5L +# c = 0x5b056c7e11dd68f40469ee7f3c7a7d74f7d121116506d031218291fbL +_b = 0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4 +_Gx =0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21 +_Gy = 0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34 + +curve_224 = ellipticcurve.CurveFp( _p, -3, _b ) +generator_224 = ellipticcurve.Point( curve_224, _Gx, _Gy, _r ) + +# NIST Curve P-256: +_p = 115792089210356248762697446949407573530086143415290314195533631308867097853951 +_r = 115792089210356248762697446949407573529996955224135760342422259061068512044369 +# s = 0xc49d360886e704936a6678e1139d26b7819f7e90L +# c = 0x7efba1662985be9403cb055c75d4f7e0ce8d84a9c5114abcaf3177680104fa0dL +_b = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b +_Gx = 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296 +_Gy = 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5 + +curve_256 = ellipticcurve.CurveFp( _p, -3, _b ) +generator_256 = ellipticcurve.Point( curve_256, _Gx, _Gy, _r ) + +# NIST Curve P-384: +_p = 39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319 +_r = 39402006196394479212279040100143613805079739270465446667946905279627659399113263569398956308152294913554433653942643 +# s = 0xa335926aa319a27a1d00896a6773a4827acdac73L +# c = 0x79d1e655f868f02fff48dcdee14151ddb80643c1406d0ca10dfe6fc52009540a495e8042ea5f744f6e184667cc722483L +_b = 0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef +_Gx = 0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7 +_Gy = 0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f + +curve_384 = ellipticcurve.CurveFp( _p, -3, _b ) +generator_384 = ellipticcurve.Point( curve_384, _Gx, _Gy, _r ) + +# NIST Curve P-521: +_p = 6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151 +_r = 6864797660130609714981900799081393217269435300143305409394463459185543183397655394245057746333217197532963996371363321113864768612440380340372808892707005449 +# s = 0xd09e8800291cb85396cc6717393284aaa0da64baL +# c = 0x0b48bfa5f420a34949539d2bdfc264eeeeb077688e44fbf0ad8f6d0edb37bd6b533281000518e19f1b9ffbe0fe9ed8a3c2200b8f875e523868c70c1e5bf55bad637L +_b = 0x051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00 +_Gx = 0xc6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66 +_Gy = 0x11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650 + +curve_521 = ellipticcurve.CurveFp( _p, -3, _b ) +generator_521 = ellipticcurve.Point( curve_521, _Gx, _Gy, _r ) + +# Certicom secp256-k1 +_a = 0x0000000000000000000000000000000000000000000000000000000000000000 +_b = 0x0000000000000000000000000000000000000000000000000000000000000007 +_p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f +_Gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 +_Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 +_r = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 + +curve_secp256k1 = ellipticcurve.CurveFp( _p, _a, _b) +generator_secp256k1 = ellipticcurve.Point( curve_secp256k1, _Gx, _Gy, _r) + + + +def __main__(): + class TestFailure(Exception): pass + + def test_point_validity( generator, x, y, expected ): + """generator defines the curve; is (x,y) a point on + this curve? "expected" is True if the right answer is Yes.""" + if point_is_valid( generator, x, y ) == expected: + print_("Point validity tested as expected.") + else: + raise TestFailure("*** Point validity test gave wrong result.") + + def test_signature_validity( Msg, Qx, Qy, R, S, expected ): + """Msg = message, Qx and Qy represent the base point on + elliptic curve c192, R and S are the signature, and + "expected" is True iff the signature is expected to be valid.""" + pubk = Public_key( generator_192, + ellipticcurve.Point( curve_192, Qx, Qy ) ) + got = pubk.verifies( digest_integer( Msg ), Signature( R, S ) ) + if got == expected: + print_("Signature tested as expected: got %s, expected %s." % \ + ( got, expected )) + else: + raise TestFailure("*** Signature test failed: got %s, expected %s." % \ + ( got, expected )) + + print_("NIST Curve P-192:") + + p192 = generator_192 + + # From X9.62: + + d = 651056770906015076056810763456358567190100156695615665659 + Q = d * p192 + if Q.x() != 0x62B12D60690CDCF330BABAB6E69763B471F994DD702D16A5: + raise TestFailure("*** p192 * d came out wrong.") + else: + print_("p192 * d came out right.") + + k = 6140507067065001063065065565667405560006161556565665656654 + R = k * p192 + if R.x() != 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD \ + or R.y() != 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835: + raise TestFailure("*** k * p192 came out wrong.") + else: + print_("k * p192 came out right.") + + u1 = 2563697409189434185194736134579731015366492496392189760599 + u2 = 6266643813348617967186477710235785849136406323338782220568 + temp = u1 * p192 + u2 * Q + if temp.x() != 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD \ + or temp.y() != 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835: + raise TestFailure("*** u1 * p192 + u2 * Q came out wrong.") + else: + print_("u1 * p192 + u2 * Q came out right.") + + e = 968236873715988614170569073515315707566766479517 + pubk = Public_key( generator_192, generator_192 * d ) + privk = Private_key( pubk, d ) + sig = privk.sign( e, k ) + r, s = sig.r, sig.s + if r != 3342403536405981729393488334694600415596881826869351677613 \ + or s != 5735822328888155254683894997897571951568553642892029982342: + raise TestFailure("*** r or s came out wrong.") + else: + print_("r and s came out right.") + + valid = pubk.verifies( e, sig ) + if valid: print_("Signature verified OK.") + else: raise TestFailure("*** Signature failed verification.") + + valid = pubk.verifies( e-1, sig ) + if not valid: print_("Forgery was correctly rejected.") + else: raise TestFailure("*** Forgery was erroneously accepted.") + + print_("Testing point validity, as per ECDSAVS.pdf B.2.2:") + + test_point_validity( \ + p192, \ + 0xcd6d0f029a023e9aaca429615b8f577abee685d8257cc83a, \ + 0x00019c410987680e9fb6c0b6ecc01d9a2647c8bae27721bacdfc, \ + False ) + + test_point_validity( + p192, \ + 0x00017f2fce203639e9eaf9fb50b81fc32776b30e3b02af16c73b, \ + 0x95da95c5e72dd48e229d4748d4eee658a9a54111b23b2adb, \ + False ) + + test_point_validity( + p192, \ + 0x4f77f8bc7fccbadd5760f4938746d5f253ee2168c1cf2792, \ + 0x000147156ff824d131629739817edb197717c41aab5c2a70f0f6, \ + False ) + + test_point_validity( + p192, \ + 0xc58d61f88d905293bcd4cd0080bcb1b7f811f2ffa41979f6, \ + 0x8804dc7a7c4c7f8b5d437f5156f3312ca7d6de8a0e11867f, \ + True ) + + test_point_validity( + p192, \ + 0xcdf56c1aa3d8afc53c521adf3ffb96734a6a630a4a5b5a70, \ + 0x97c1c44a5fb229007b5ec5d25f7413d170068ffd023caa4e, \ + True ) + + test_point_validity( + p192, \ + 0x89009c0dc361c81e99280c8e91df578df88cdf4b0cdedced, \ + 0x27be44a529b7513e727251f128b34262a0fd4d8ec82377b9, \ + True ) + + test_point_validity( + p192, \ + 0x6a223d00bd22c52833409a163e057e5b5da1def2a197dd15, \ + 0x7b482604199367f1f303f9ef627f922f97023e90eae08abf, \ + True ) + + test_point_validity( + p192, \ + 0x6dccbde75c0948c98dab32ea0bc59fe125cf0fb1a3798eda, \ + 0x0001171a3e0fa60cf3096f4e116b556198de430e1fbd330c8835, \ + False ) + + test_point_validity( + p192, \ + 0xd266b39e1f491fc4acbbbc7d098430931cfa66d55015af12, \ + 0x193782eb909e391a3148b7764e6b234aa94e48d30a16dbb2, \ + False ) + + test_point_validity( + p192, \ + 0x9d6ddbcd439baa0c6b80a654091680e462a7d1d3f1ffeb43, \ + 0x6ad8efc4d133ccf167c44eb4691c80abffb9f82b932b8caa, \ + False ) + + test_point_validity( + p192, \ + 0x146479d944e6bda87e5b35818aa666a4c998a71f4e95edbc, \ + 0xa86d6fe62bc8fbd88139693f842635f687f132255858e7f6, \ + False ) + + test_point_validity( + p192, \ + 0xe594d4a598046f3598243f50fd2c7bd7d380edb055802253, \ + 0x509014c0c4d6b536e3ca750ec09066af39b4c8616a53a923, \ + False ) + + print_("Trying signature-verification tests from ECDSAVS.pdf B.2.4:") + print_("P-192:") + Msg = 0x84ce72aa8699df436059f052ac51b6398d2511e49631bcb7e71f89c499b9ee425dfbc13a5f6d408471b054f2655617cbbaf7937b7c80cd8865cf02c8487d30d2b0fbd8b2c4e102e16d828374bbc47b93852f212d5043c3ea720f086178ff798cc4f63f787b9c2e419efa033e7644ea7936f54462dc21a6c4580725f7f0e7d158 + Qx = 0xd9dbfb332aa8e5ff091e8ce535857c37c73f6250ffb2e7ac + Qy = 0x282102e364feded3ad15ddf968f88d8321aa268dd483ebc4 + R = 0x64dca58a20787c488d11d6dd96313f1b766f2d8efe122916 + S = 0x1ecba28141e84ab4ecad92f56720e2cc83eb3d22dec72479 + test_signature_validity( Msg, Qx, Qy, R, S, True ) + + Msg = 0x94bb5bacd5f8ea765810024db87f4224ad71362a3c28284b2b9f39fab86db12e8beb94aae899768229be8fdb6c4f12f28912bb604703a79ccff769c1607f5a91450f30ba0460d359d9126cbd6296be6d9c4bb96c0ee74cbb44197c207f6db326ab6f5a659113a9034e54be7b041ced9dcf6458d7fb9cbfb2744d999f7dfd63f4 + Qx = 0x3e53ef8d3112af3285c0e74842090712cd324832d4277ae7 + Qy = 0xcc75f8952d30aec2cbb719fc6aa9934590b5d0ff5a83adb7 + R = 0x8285261607283ba18f335026130bab31840dcfd9c3e555af + S = 0x356d89e1b04541afc9704a45e9c535ce4a50929e33d7e06c + test_signature_validity( Msg, Qx, Qy, R, S, True ) + + Msg = 0xf6227a8eeb34afed1621dcc89a91d72ea212cb2f476839d9b4243c66877911b37b4ad6f4448792a7bbba76c63bdd63414b6facab7dc71c3396a73bd7ee14cdd41a659c61c99b779cecf07bc51ab391aa3252386242b9853ea7da67fd768d303f1b9b513d401565b6f1eb722dfdb96b519fe4f9bd5de67ae131e64b40e78c42dd + Qx = 0x16335dbe95f8e8254a4e04575d736befb258b8657f773cb7 + Qy = 0x421b13379c59bc9dce38a1099ca79bbd06d647c7f6242336 + R = 0x4141bd5d64ea36c5b0bd21ef28c02da216ed9d04522b1e91 + S = 0x159a6aa852bcc579e821b7bb0994c0861fb08280c38daa09 + test_signature_validity( Msg, Qx, Qy, R, S, False ) + + Msg = 0x16b5f93afd0d02246f662761ed8e0dd9504681ed02a253006eb36736b563097ba39f81c8e1bce7a16c1339e345efabbc6baa3efb0612948ae51103382a8ee8bc448e3ef71e9f6f7a9676694831d7f5dd0db5446f179bcb737d4a526367a447bfe2c857521c7f40b6d7d7e01a180d92431fb0bbd29c04a0c420a57b3ed26ccd8a + Qx = 0xfd14cdf1607f5efb7b1793037b15bdf4baa6f7c16341ab0b + Qy = 0x83fa0795cc6c4795b9016dac928fd6bac32f3229a96312c4 + R = 0x8dfdb832951e0167c5d762a473c0416c5c15bc1195667dc1 + S = 0x1720288a2dc13fa1ec78f763f8fe2ff7354a7e6fdde44520 + test_signature_validity( Msg, Qx, Qy, R, S, False ) + + Msg = 0x08a2024b61b79d260e3bb43ef15659aec89e5b560199bc82cf7c65c77d39192e03b9a895d766655105edd9188242b91fbde4167f7862d4ddd61e5d4ab55196683d4f13ceb90d87aea6e07eb50a874e33086c4a7cb0273a8e1c4408f4b846bceae1ebaac1b2b2ea851a9b09de322efe34cebe601653efd6ddc876ce8c2f2072fb + Qx = 0x674f941dc1a1f8b763c9334d726172d527b90ca324db8828 + Qy = 0x65adfa32e8b236cb33a3e84cf59bfb9417ae7e8ede57a7ff + R = 0x9508b9fdd7daf0d8126f9e2bc5a35e4c6d800b5b804d7796 + S = 0x36f2bf6b21b987c77b53bb801b3435a577e3d493744bfab0 + test_signature_validity( Msg, Qx, Qy, R, S, False ) + + Msg = 0x1843aba74b0789d4ac6b0b8923848023a644a7b70afa23b1191829bbe4397ce15b629bf21a8838298653ed0c19222b95fa4f7390d1b4c844d96e645537e0aae98afb5c0ac3bd0e4c37f8daaff25556c64e98c319c52687c904c4de7240a1cc55cd9756b7edaef184e6e23b385726e9ffcba8001b8f574987c1a3fedaaa83ca6d + Qx = 0x10ecca1aad7220b56a62008b35170bfd5e35885c4014a19f + Qy = 0x04eb61984c6c12ade3bc47f3c629ece7aa0a033b9948d686 + R = 0x82bfa4e82c0dfe9274169b86694e76ce993fd83b5c60f325 + S = 0xa97685676c59a65dbde002fe9d613431fb183e8006d05633 + test_signature_validity( Msg, Qx, Qy, R, S, False ) + + Msg = 0x5a478f4084ddd1a7fea038aa9732a822106385797d02311aeef4d0264f824f698df7a48cfb6b578cf3da416bc0799425bb491be5b5ecc37995b85b03420a98f2c4dc5c31a69a379e9e322fbe706bbcaf0f77175e05cbb4fa162e0da82010a278461e3e974d137bc746d1880d6eb02aa95216014b37480d84b87f717bb13f76e1 + Qx = 0x6636653cb5b894ca65c448277b29da3ad101c4c2300f7c04 + Qy = 0xfdf1cbb3fc3fd6a4f890b59e554544175fa77dbdbeb656c1 + R = 0xeac2ddecddfb79931a9c3d49c08de0645c783a24cb365e1c + S = 0x3549fee3cfa7e5f93bc47d92d8ba100e881a2a93c22f8d50 + test_signature_validity( Msg, Qx, Qy, R, S, False ) + + Msg = 0xc598774259a058fa65212ac57eaa4f52240e629ef4c310722088292d1d4af6c39b49ce06ba77e4247b20637174d0bd67c9723feb57b5ead232b47ea452d5d7a089f17c00b8b6767e434a5e16c231ba0efa718a340bf41d67ea2d295812ff1b9277daacb8bc27b50ea5e6443bcf95ef4e9f5468fe78485236313d53d1c68f6ba2 + Qx = 0xa82bd718d01d354001148cd5f69b9ebf38ff6f21898f8aaa + Qy = 0xe67ceede07fc2ebfafd62462a51e4b6c6b3d5b537b7caf3e + R = 0x4d292486c620c3de20856e57d3bb72fcde4a73ad26376955 + S = 0xa85289591a6081d5728825520e62ff1c64f94235c04c7f95 + test_signature_validity( Msg, Qx, Qy, R, S, False ) + + Msg = 0xca98ed9db081a07b7557f24ced6c7b9891269a95d2026747add9e9eb80638a961cf9c71a1b9f2c29744180bd4c3d3db60f2243c5c0b7cc8a8d40a3f9a7fc910250f2187136ee6413ffc67f1a25e1c4c204fa9635312252ac0e0481d89b6d53808f0c496ba87631803f6c572c1f61fa049737fdacce4adff757afed4f05beb658 + Qx = 0x7d3b016b57758b160c4fca73d48df07ae3b6b30225126c2f + Qy = 0x4af3790d9775742bde46f8da876711be1b65244b2b39e7ec + R = 0x95f778f5f656511a5ab49a5d69ddd0929563c29cbc3a9e62 + S = 0x75c87fc358c251b4c83d2dd979faad496b539f9f2ee7a289 + test_signature_validity( Msg, Qx, Qy, R, S, False ) + + Msg = 0x31dd9a54c8338bea06b87eca813d555ad1850fac9742ef0bbe40dad400e10288acc9c11ea7dac79eb16378ebea9490e09536099f1b993e2653cd50240014c90a9c987f64545abc6a536b9bd2435eb5e911fdfde2f13be96ea36ad38df4ae9ea387b29cced599af777338af2794820c9cce43b51d2112380a35802ab7e396c97a + Qx = 0x9362f28c4ef96453d8a2f849f21e881cd7566887da8beb4a + Qy = 0xe64d26d8d74c48a024ae85d982ee74cd16046f4ee5333905 + R = 0xf3923476a296c88287e8de914b0b324ad5a963319a4fe73b + S = 0xf0baeed7624ed00d15244d8ba2aede085517dbdec8ac65f5 + test_signature_validity( Msg, Qx, Qy, R, S, True ) + + Msg = 0xb2b94e4432267c92f9fdb9dc6040c95ffa477652761290d3c7de312283f6450d89cc4aabe748554dfb6056b2d8e99c7aeaad9cdddebdee9dbc099839562d9064e68e7bb5f3a6bba0749ca9a538181fc785553a4000785d73cc207922f63e8ce1112768cb1de7b673aed83a1e4a74592f1268d8e2a4e9e63d414b5d442bd0456d + Qx = 0xcc6fc032a846aaac25533eb033522824f94e670fa997ecef + Qy = 0xe25463ef77a029eccda8b294fd63dd694e38d223d30862f1 + R = 0x066b1d07f3a40e679b620eda7f550842a35c18b80c5ebe06 + S = 0xa0b0fb201e8f2df65e2c4508ef303bdc90d934016f16b2dc + test_signature_validity( Msg, Qx, Qy, R, S, False ) + + Msg = 0x4366fcadf10d30d086911de30143da6f579527036937007b337f7282460eae5678b15cccda853193ea5fc4bc0a6b9d7a31128f27e1214988592827520b214eed5052f7775b750b0c6b15f145453ba3fee24a085d65287e10509eb5d5f602c440341376b95c24e5c4727d4b859bfe1483d20538acdd92c7997fa9c614f0f839d7 + Qx = 0x955c908fe900a996f7e2089bee2f6376830f76a19135e753 + Qy = 0xba0c42a91d3847de4a592a46dc3fdaf45a7cc709b90de520 + R = 0x1f58ad77fc04c782815a1405b0925e72095d906cbf52a668 + S = 0xf2e93758b3af75edf784f05a6761c9b9a6043c66b845b599 + test_signature_validity( Msg, Qx, Qy, R, S, False ) + + Msg = 0x543f8af57d750e33aa8565e0cae92bfa7a1ff78833093421c2942cadf9986670a5ff3244c02a8225e790fbf30ea84c74720abf99cfd10d02d34377c3d3b41269bea763384f372bb786b5846f58932defa68023136cd571863b304886e95e52e7877f445b9364b3f06f3c28da12707673fecb4b8071de06b6e0a3c87da160cef3 + Qx = 0x31f7fa05576d78a949b24812d4383107a9a45bb5fccdd835 + Qy = 0x8dc0eb65994a90f02b5e19bd18b32d61150746c09107e76b + R = 0xbe26d59e4e883dde7c286614a767b31e49ad88789d3a78ff + S = 0x8762ca831c1ce42df77893c9b03119428e7a9b819b619068 + test_signature_validity( Msg, Qx, Qy, R, S, False ) + + Msg = 0xd2e8454143ce281e609a9d748014dcebb9d0bc53adb02443a6aac2ffe6cb009f387c346ecb051791404f79e902ee333ad65e5c8cb38dc0d1d39a8dc90add5023572720e5b94b190d43dd0d7873397504c0c7aef2727e628eb6a74411f2e400c65670716cb4a815dc91cbbfeb7cfe8c929e93184c938af2c078584da045e8f8d1 + Qx = 0x66aa8edbbdb5cf8e28ceb51b5bda891cae2df84819fe25c0 + Qy = 0x0c6bc2f69030a7ce58d4a00e3b3349844784a13b8936f8da + R = 0xa4661e69b1734f4a71b788410a464b71e7ffe42334484f23 + S = 0x738421cf5e049159d69c57a915143e226cac8355e149afe9 + test_signature_validity( Msg, Qx, Qy, R, S, False ) + + Msg = 0x6660717144040f3e2f95a4e25b08a7079c702a8b29babad5a19a87654bc5c5afa261512a11b998a4fb36b5d8fe8bd942792ff0324b108120de86d63f65855e5461184fc96a0a8ffd2ce6d5dfb0230cbbdd98f8543e361b3205f5da3d500fdc8bac6db377d75ebef3cb8f4d1ff738071ad0938917889250b41dd1d98896ca06fb + Qx = 0xbcfacf45139b6f5f690a4c35a5fffa498794136a2353fc77 + Qy = 0x6f4a6c906316a6afc6d98fe1f0399d056f128fe0270b0f22 + R = 0x9db679a3dafe48f7ccad122933acfe9da0970b71c94c21c1 + S = 0x984c2db99827576c0a41a5da41e07d8cc768bc82f18c9da9 + test_signature_validity( Msg, Qx, Qy, R, S, False ) + + + + print_("Testing the example code:") + + # Building a public/private key pair from the NIST Curve P-192: + + g = generator_192 + n = g.order() + + # (random.SystemRandom is supposed to provide + # crypto-quality random numbers, but as Debian recently + # illustrated, a systems programmer can accidentally + # demolish this security, so in serious applications + # further precautions are appropriate.) + + randrange = random.SystemRandom().randrange + + secret = randrange( 1, n ) + pubkey = Public_key( g, g * secret ) + privkey = Private_key( pubkey, secret ) + + # Signing a hash value: + + hash = randrange( 1, n ) + signature = privkey.sign( hash, randrange( 1, n ) ) + + # Verifying a signature for a hash value: + + if pubkey.verifies( hash, signature ): + print_("Demo verification succeeded.") + else: + raise TestFailure("*** Demo verification failed.") + + if pubkey.verifies( hash-1, signature ): + raise TestFailure( "**** Demo verification failed to reject tampered hash.") + else: + print_("Demo verification correctly rejected tampered hash.") + +if __name__ == "__main__": + __main__() diff --git a/dist/bin/esptool/ecdsa/ellipticcurve.py b/dist/bin/esptool/ecdsa/ellipticcurve.py new file mode 100644 index 000000000..390bb355d --- /dev/null +++ b/dist/bin/esptool/ecdsa/ellipticcurve.py @@ -0,0 +1,293 @@ +#! /usr/bin/env python +# +# Implementation of elliptic curves, for cryptographic applications. +# +# This module doesn't provide any way to choose a random elliptic +# curve, nor to verify that an elliptic curve was chosen randomly, +# because one can simply use NIST's standard curves. +# +# Notes from X9.62-1998 (draft): +# Nomenclature: +# - Q is a public key. +# The "Elliptic Curve Domain Parameters" include: +# - q is the "field size", which in our case equals p. +# - p is a big prime. +# - G is a point of prime order (5.1.1.1). +# - n is the order of G (5.1.1.1). +# Public-key validation (5.2.2): +# - Verify that Q is not the point at infinity. +# - Verify that X_Q and Y_Q are in [0,p-1]. +# - Verify that Q is on the curve. +# - Verify that nQ is the point at infinity. +# Signature generation (5.3): +# - Pick random k from [1,n-1]. +# Signature checking (5.4.2): +# - Verify that r and s are in [1,n-1]. +# +# Version of 2008.11.25. +# +# Revision history: +# 2005.12.31 - Initial version. +# 2008.11.25 - Change CurveFp.is_on to contains_point. +# +# Written in 2005 by Peter Pearson and placed in the public domain. + +from __future__ import division + +from .six import print_ +from . import numbertheory + +class CurveFp( object ): + """Elliptic Curve over the field of integers modulo a prime.""" + def __init__( self, p, a, b ): + """The curve of points satisfying y^2 = x^3 + a*x + b (mod p).""" + self.__p = p + self.__a = a + self.__b = b + + def p( self ): + return self.__p + + def a( self ): + return self.__a + + def b( self ): + return self.__b + + def contains_point( self, x, y ): + """Is the point (x,y) on this curve?""" + return ( y * y - ( x * x * x + self.__a * x + self.__b ) ) % self.__p == 0 + + + +class Point( object ): + """A point on an elliptic curve. Altering x and y is forbidding, + but they can be read by the x() and y() methods.""" + def __init__( self, curve, x, y, order = None ): + """curve, x, y, order; order (optional) is the order of this point.""" + self.__curve = curve + self.__x = x + self.__y = y + self.__order = order + # self.curve is allowed to be None only for INFINITY: + if self.__curve: assert self.__curve.contains_point( x, y ) + if order: assert self * order == INFINITY + + def __eq__( self, other ): + """Return True if the points are identical, False otherwise.""" + if self.__curve == other.__curve \ + and self.__x == other.__x \ + and self.__y == other.__y: + return True + else: + return False + + def __add__( self, other ): + """Add one point to another point.""" + + # X9.62 B.3: + + if other == INFINITY: return self + if self == INFINITY: return other + assert self.__curve == other.__curve + if self.__x == other.__x: + if ( self.__y + other.__y ) % self.__curve.p() == 0: + return INFINITY + else: + return self.double() + + p = self.__curve.p() + + l = ( ( other.__y - self.__y ) * \ + numbertheory.inverse_mod( other.__x - self.__x, p ) ) % p + + x3 = ( l * l - self.__x - other.__x ) % p + y3 = ( l * ( self.__x - x3 ) - self.__y ) % p + + return Point( self.__curve, x3, y3 ) + + def __mul__( self, other ): + """Multiply a point by an integer.""" + + def leftmost_bit( x ): + assert x > 0 + result = 1 + while result <= x: result = 2 * result + return result // 2 + + e = other + if self.__order: e = e % self.__order + if e == 0: return INFINITY + if self == INFINITY: return INFINITY + assert e > 0 + + # From X9.62 D.3.2: + + e3 = 3 * e + negative_self = Point( self.__curve, self.__x, -self.__y, self.__order ) + i = leftmost_bit( e3 ) // 2 + result = self + # print_("Multiplying %s by %d (e3 = %d):" % ( self, other, e3 )) + while i > 1: + result = result.double() + if ( e3 & i ) != 0 and ( e & i ) == 0: result = result + self + if ( e3 & i ) == 0 and ( e & i ) != 0: result = result + negative_self + # print_(". . . i = %d, result = %s" % ( i, result )) + i = i // 2 + + return result + + def __rmul__( self, other ): + """Multiply a point by an integer.""" + + return self * other + + def __str__( self ): + if self == INFINITY: return "infinity" + return "(%d,%d)" % ( self.__x, self.__y ) + + def double( self ): + """Return a new point that is twice the old.""" + + if self == INFINITY: + return INFINITY + + # X9.62 B.3: + + p = self.__curve.p() + a = self.__curve.a() + + l = ( ( 3 * self.__x * self.__x + a ) * \ + numbertheory.inverse_mod( 2 * self.__y, p ) ) % p + + x3 = ( l * l - 2 * self.__x ) % p + y3 = ( l * ( self.__x - x3 ) - self.__y ) % p + + return Point( self.__curve, x3, y3 ) + + def x( self ): + return self.__x + + def y( self ): + return self.__y + + def curve( self ): + return self.__curve + + def order( self ): + return self.__order + + +# This one point is the Point At Infinity for all purposes: +INFINITY = Point( None, None, None ) + +def __main__(): + + class FailedTest(Exception): pass + def test_add( c, x1, y1, x2, y2, x3, y3 ): + """We expect that on curve c, (x1,y1) + (x2, y2 ) = (x3, y3).""" + p1 = Point( c, x1, y1 ) + p2 = Point( c, x2, y2 ) + p3 = p1 + p2 + print_("%s + %s = %s" % ( p1, p2, p3 ), end=' ') + if p3.x() != x3 or p3.y() != y3: + raise FailedTest("Failure: should give (%d,%d)." % ( x3, y3 )) + else: + print_(" Good.") + + def test_double( c, x1, y1, x3, y3 ): + """We expect that on curve c, 2*(x1,y1) = (x3, y3).""" + p1 = Point( c, x1, y1 ) + p3 = p1.double() + print_("%s doubled = %s" % ( p1, p3 ), end=' ') + if p3.x() != x3 or p3.y() != y3: + raise FailedTest("Failure: should give (%d,%d)." % ( x3, y3 )) + else: + print_(" Good.") + + def test_double_infinity( c ): + """We expect that on curve c, 2*INFINITY = INFINITY.""" + p1 = INFINITY + p3 = p1.double() + print_("%s doubled = %s" % ( p1, p3 ), end=' ') + if p3.x() != INFINITY.x() or p3.y() != INFINITY.y(): + raise FailedTest("Failure: should give (%d,%d)." % ( INFINITY.x(), INFINITY.y() )) + else: + print_(" Good.") + + def test_multiply( c, x1, y1, m, x3, y3 ): + """We expect that on curve c, m*(x1,y1) = (x3,y3).""" + p1 = Point( c, x1, y1 ) + p3 = p1 * m + print_("%s * %d = %s" % ( p1, m, p3 ), end=' ') + if p3.x() != x3 or p3.y() != y3: + raise FailedTest("Failure: should give (%d,%d)." % ( x3, y3 )) + else: + print_(" Good.") + + + # A few tests from X9.62 B.3: + + c = CurveFp( 23, 1, 1 ) + test_add( c, 3, 10, 9, 7, 17, 20 ) + test_double( c, 3, 10, 7, 12 ) + test_add( c, 3, 10, 3, 10, 7, 12 ) # (Should just invoke double.) + test_multiply( c, 3, 10, 2, 7, 12 ) + + test_double_infinity(c) + + # From X9.62 I.1 (p. 96): + + g = Point( c, 13, 7, 7 ) + + check = INFINITY + for i in range( 7 + 1 ): + p = ( i % 7 ) * g + print_("%s * %d = %s, expected %s . . ." % ( g, i, p, check ), end=' ') + if p == check: + print_(" Good.") + else: + raise FailedTest("Bad.") + check = check + g + + # NIST Curve P-192: + p = 6277101735386680763835789423207666416083908700390324961279 + r = 6277101735386680763835789423176059013767194773182842284081 + #s = 0x3045ae6fc8422f64ed579528d38120eae12196d5L + c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65 + b = 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1 + Gx = 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012 + Gy = 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811 + + c192 = CurveFp( p, -3, b ) + p192 = Point( c192, Gx, Gy, r ) + + # Checking against some sample computations presented + # in X9.62: + + d = 651056770906015076056810763456358567190100156695615665659 + Q = d * p192 + if Q.x() != 0x62B12D60690CDCF330BABAB6E69763B471F994DD702D16A5: + raise FailedTest("p192 * d came out wrong.") + else: + print_("p192 * d came out right.") + + k = 6140507067065001063065065565667405560006161556565665656654 + R = k * p192 + if R.x() != 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD \ + or R.y() != 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835: + raise FailedTest("k * p192 came out wrong.") + else: + print_("k * p192 came out right.") + + u1 = 2563697409189434185194736134579731015366492496392189760599 + u2 = 6266643813348617967186477710235785849136406323338782220568 + temp = u1 * p192 + u2 * Q + if temp.x() != 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD \ + or temp.y() != 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835: + raise FailedTest("u1 * p192 + u2 * Q came out wrong.") + else: + print_("u1 * p192 + u2 * Q came out right.") + +if __name__ == "__main__": + __main__() diff --git a/dist/bin/esptool/ecdsa/keys.py b/dist/bin/esptool/ecdsa/keys.py new file mode 100644 index 000000000..5b5212548 --- /dev/null +++ b/dist/bin/esptool/ecdsa/keys.py @@ -0,0 +1,283 @@ +import binascii + +from . import ecdsa +from . import der +from . import rfc6979 +from .curves import NIST192p, find_curve +from .util import string_to_number, number_to_string, randrange +from .util import sigencode_string, sigdecode_string +from .util import oid_ecPublicKey, encoded_oid_ecPublicKey +from .six import PY3, b +from hashlib import sha1 + +class BadSignatureError(Exception): + pass +class BadDigestError(Exception): + pass + +class VerifyingKey: + def __init__(self, _error__please_use_generate=None): + if not _error__please_use_generate: + raise TypeError("Please use SigningKey.generate() to construct me") + + @classmethod + def from_public_point(klass, point, curve=NIST192p, hashfunc=sha1): + self = klass(_error__please_use_generate=True) + self.curve = curve + self.default_hashfunc = hashfunc + self.pubkey = ecdsa.Public_key(curve.generator, point) + self.pubkey.order = curve.order + return self + + @classmethod + def from_string(klass, string, curve=NIST192p, hashfunc=sha1, + validate_point=True): + order = curve.order + assert len(string) == curve.verifying_key_length, \ + (len(string), curve.verifying_key_length) + xs = string[:curve.baselen] + ys = string[curve.baselen:] + assert len(xs) == curve.baselen, (len(xs), curve.baselen) + assert len(ys) == curve.baselen, (len(ys), curve.baselen) + x = string_to_number(xs) + y = string_to_number(ys) + if validate_point: + assert ecdsa.point_is_valid(curve.generator, x, y) + from . import ellipticcurve + point = ellipticcurve.Point(curve.curve, x, y, order) + return klass.from_public_point(point, curve, hashfunc) + + @classmethod + def from_pem(klass, string): + return klass.from_der(der.unpem(string)) + + @classmethod + def from_der(klass, string): + # [[oid_ecPublicKey,oid_curve], point_str_bitstring] + s1,empty = der.remove_sequence(string) + if empty != b(""): + raise der.UnexpectedDER("trailing junk after DER pubkey: %s" % + binascii.hexlify(empty)) + s2,point_str_bitstring = der.remove_sequence(s1) + # s2 = oid_ecPublicKey,oid_curve + oid_pk, rest = der.remove_object(s2) + oid_curve, empty = der.remove_object(rest) + if empty != b(""): + raise der.UnexpectedDER("trailing junk after DER pubkey objects: %s" % + binascii.hexlify(empty)) + assert oid_pk == oid_ecPublicKey, (oid_pk, oid_ecPublicKey) + curve = find_curve(oid_curve) + point_str, empty = der.remove_bitstring(point_str_bitstring) + if empty != b(""): + raise der.UnexpectedDER("trailing junk after pubkey pointstring: %s" % + binascii.hexlify(empty)) + assert point_str.startswith(b("\x00\x04")) + return klass.from_string(point_str[2:], curve) + + def to_string(self): + # VerifyingKey.from_string(vk.to_string()) == vk as long as the + # curves are the same: the curve itself is not included in the + # serialized form + order = self.pubkey.order + x_str = number_to_string(self.pubkey.point.x(), order) + y_str = number_to_string(self.pubkey.point.y(), order) + return x_str + y_str + + def to_pem(self): + return der.topem(self.to_der(), "PUBLIC KEY") + + def to_der(self): + order = self.pubkey.order + x_str = number_to_string(self.pubkey.point.x(), order) + y_str = number_to_string(self.pubkey.point.y(), order) + point_str = b("\x00\x04") + x_str + y_str + return der.encode_sequence(der.encode_sequence(encoded_oid_ecPublicKey, + self.curve.encoded_oid), + der.encode_bitstring(point_str)) + + def verify(self, signature, data, hashfunc=None, sigdecode=sigdecode_string): + hashfunc = hashfunc or self.default_hashfunc + digest = hashfunc(data).digest() + return self.verify_digest(signature, digest, sigdecode) + + def verify_digest(self, signature, digest, sigdecode=sigdecode_string): + if len(digest) > self.curve.baselen: + raise BadDigestError("this curve (%s) is too short " + "for your digest (%d)" % (self.curve.name, + 8*len(digest))) + number = string_to_number(digest) + r, s = sigdecode(signature, self.pubkey.order) + sig = ecdsa.Signature(r, s) + if self.pubkey.verifies(number, sig): + return True + raise BadSignatureError + +class SigningKey: + def __init__(self, _error__please_use_generate=None): + if not _error__please_use_generate: + raise TypeError("Please use SigningKey.generate() to construct me") + + @classmethod + def generate(klass, curve=NIST192p, entropy=None, hashfunc=sha1): + secexp = randrange(curve.order, entropy) + return klass.from_secret_exponent(secexp, curve, hashfunc) + + # to create a signing key from a short (arbitrary-length) seed, convert + # that seed into an integer with something like + # secexp=util.randrange_from_seed__X(seed, curve.order), and then pass + # that integer into SigningKey.from_secret_exponent(secexp, curve) + + @classmethod + def from_secret_exponent(klass, secexp, curve=NIST192p, hashfunc=sha1): + self = klass(_error__please_use_generate=True) + self.curve = curve + self.default_hashfunc = hashfunc + self.baselen = curve.baselen + n = curve.order + assert 1 <= secexp < n + pubkey_point = curve.generator*secexp + pubkey = ecdsa.Public_key(curve.generator, pubkey_point) + pubkey.order = n + self.verifying_key = VerifyingKey.from_public_point(pubkey_point, curve, + hashfunc) + self.privkey = ecdsa.Private_key(pubkey, secexp) + self.privkey.order = n + return self + + @classmethod + def from_string(klass, string, curve=NIST192p, hashfunc=sha1): + assert len(string) == curve.baselen, (len(string), curve.baselen) + secexp = string_to_number(string) + return klass.from_secret_exponent(secexp, curve, hashfunc) + + @classmethod + def from_pem(klass, string, hashfunc=sha1): + # the privkey pem file has two sections: "EC PARAMETERS" and "EC + # PRIVATE KEY". The first is redundant. + if PY3 and isinstance(string, str): + string = string.encode() + privkey_pem = string[string.index(b("-----BEGIN EC PRIVATE KEY-----")):] + return klass.from_der(der.unpem(privkey_pem), hashfunc) + @classmethod + def from_der(klass, string, hashfunc=sha1): + # SEQ([int(1), octetstring(privkey),cont[0], oid(secp224r1), + # cont[1],bitstring]) + s, empty = der.remove_sequence(string) + if empty != b(""): + raise der.UnexpectedDER("trailing junk after DER privkey: %s" % + binascii.hexlify(empty)) + one, s = der.remove_integer(s) + if one != 1: + raise der.UnexpectedDER("expected '1' at start of DER privkey," + " got %d" % one) + privkey_str, s = der.remove_octet_string(s) + tag, curve_oid_str, s = der.remove_constructed(s) + if tag != 0: + raise der.UnexpectedDER("expected tag 0 in DER privkey," + " got %d" % tag) + curve_oid, empty = der.remove_object(curve_oid_str) + if empty != b(""): + raise der.UnexpectedDER("trailing junk after DER privkey " + "curve_oid: %s" % binascii.hexlify(empty)) + curve = find_curve(curve_oid) + + # we don't actually care about the following fields + # + #tag, pubkey_bitstring, s = der.remove_constructed(s) + #if tag != 1: + # raise der.UnexpectedDER("expected tag 1 in DER privkey, got %d" + # % tag) + #pubkey_str = der.remove_bitstring(pubkey_bitstring) + #if empty != "": + # raise der.UnexpectedDER("trailing junk after DER privkey " + # "pubkeystr: %s" % binascii.hexlify(empty)) + + # our from_string method likes fixed-length privkey strings + if len(privkey_str) < curve.baselen: + privkey_str = b("\x00")*(curve.baselen-len(privkey_str)) + privkey_str + return klass.from_string(privkey_str, curve, hashfunc) + + def to_string(self): + secexp = self.privkey.secret_multiplier + s = number_to_string(secexp, self.privkey.order) + return s + + def to_pem(self): + # TODO: "BEGIN ECPARAMETERS" + return der.topem(self.to_der(), "EC PRIVATE KEY") + + def to_der(self): + # SEQ([int(1), octetstring(privkey),cont[0], oid(secp224r1), + # cont[1],bitstring]) + encoded_vk = b("\x00\x04") + self.get_verifying_key().to_string() + return der.encode_sequence(der.encode_integer(1), + der.encode_octet_string(self.to_string()), + der.encode_constructed(0, self.curve.encoded_oid), + der.encode_constructed(1, der.encode_bitstring(encoded_vk)), + ) + + def get_verifying_key(self): + return self.verifying_key + + def sign_deterministic(self, data, hashfunc=None, sigencode=sigencode_string): + hashfunc = hashfunc or self.default_hashfunc + digest = hashfunc(data).digest() + + return self.sign_digest_deterministic(digest, hashfunc=hashfunc, sigencode=sigencode) + + def sign_digest_deterministic(self, digest, hashfunc=None, sigencode=sigencode_string): + """ + Calculates 'k' from data itself, removing the need for strong + random generator and producing deterministic (reproducible) signatures. + See RFC 6979 for more details. + """ + secexp = self.privkey.secret_multiplier + k = rfc6979.generate_k( + self.curve.generator.order(), secexp, hashfunc, digest) + + return self.sign_digest(digest, sigencode=sigencode, k=k) + + def sign(self, data, entropy=None, hashfunc=None, sigencode=sigencode_string, k=None): + """ + hashfunc= should behave like hashlib.sha1 . The output length of the + hash (in bytes) must not be longer than the length of the curve order + (rounded up to the nearest byte), so using SHA256 with nist256p is + ok, but SHA256 with nist192p is not. (In the 2**-96ish unlikely event + of a hash output larger than the curve order, the hash will + effectively be wrapped mod n). + + Use hashfunc=hashlib.sha1 to match openssl's -ecdsa-with-SHA1 mode, + or hashfunc=hashlib.sha256 for openssl-1.0.0's -ecdsa-with-SHA256. + """ + + hashfunc = hashfunc or self.default_hashfunc + h = hashfunc(data).digest() + return self.sign_digest(h, entropy, sigencode, k) + + def sign_digest(self, digest, entropy=None, sigencode=sigencode_string, k=None): + if len(digest) > self.curve.baselen: + raise BadDigestError("this curve (%s) is too short " + "for your digest (%d)" % (self.curve.name, + 8*len(digest))) + number = string_to_number(digest) + r, s = self.sign_number(number, entropy, k) + return sigencode(r, s, self.privkey.order) + + def sign_number(self, number, entropy=None, k=None): + # returns a pair of numbers + order = self.privkey.order + # privkey.sign() may raise RuntimeError in the amazingly unlikely + # (2**-192) event that r=0 or s=0, because that would leak the key. + # We could re-try with a different 'k', but we couldn't test that + # code, so I choose to allow the signature to fail instead. + + # If k is set, it is used directly. In other cases + # it is generated using entropy function + if k is not None: + _k = k + else: + _k = randrange(order, entropy) + + assert 1 <= _k < order + sig = self.privkey.sign(number, _k) + return sig.r, sig.s diff --git a/dist/bin/esptool/ecdsa/numbertheory.py b/dist/bin/esptool/ecdsa/numbertheory.py new file mode 100644 index 000000000..7ace0dc34 --- /dev/null +++ b/dist/bin/esptool/ecdsa/numbertheory.py @@ -0,0 +1,613 @@ +#! /usr/bin/env python +# +# Provide some simple capabilities from number theory. +# +# Version of 2008.11.14. +# +# Written in 2005 and 2006 by Peter Pearson and placed in the public domain. +# Revision history: +# 2008.11.14: Use pow( base, exponent, modulus ) for modular_exp. +# Make gcd and lcm accept arbitrarly many arguments. + +from __future__ import division + +from .six import print_, integer_types +from .six.moves import reduce + +import math + + +class Error( Exception ): + """Base class for exceptions in this module.""" + pass + +class SquareRootError( Error ): + pass + +class NegativeExponentError( Error ): + pass + + +def modular_exp( base, exponent, modulus ): + "Raise base to exponent, reducing by modulus" + if exponent < 0: + raise NegativeExponentError( "Negative exponents (%d) not allowed" \ + % exponent ) + return pow( base, exponent, modulus ) +# result = 1L +# x = exponent +# b = base + 0L +# while x > 0: +# if x % 2 > 0: result = (result * b) % modulus +# x = x // 2 +# b = ( b * b ) % modulus +# return result + + +def polynomial_reduce_mod( poly, polymod, p ): + """Reduce poly by polymod, integer arithmetic modulo p. + + Polynomials are represented as lists of coefficients + of increasing powers of x.""" + + # This module has been tested only by extensive use + # in calculating modular square roots. + + # Just to make this easy, require a monic polynomial: + assert polymod[-1] == 1 + + assert len( polymod ) > 1 + + while len( poly ) >= len( polymod ): + if poly[-1] != 0: + for i in range( 2, len( polymod ) + 1 ): + poly[-i] = ( poly[-i] - poly[-1] * polymod[-i] ) % p + poly = poly[0:-1] + + return poly + + + +def polynomial_multiply_mod( m1, m2, polymod, p ): + """Polynomial multiplication modulo a polynomial over ints mod p. + + Polynomials are represented as lists of coefficients + of increasing powers of x.""" + + # This is just a seat-of-the-pants implementation. + + # This module has been tested only by extensive use + # in calculating modular square roots. + + # Initialize the product to zero: + + prod = ( len( m1 ) + len( m2 ) - 1 ) * [0] + + # Add together all the cross-terms: + + for i in range( len( m1 ) ): + for j in range( len( m2 ) ): + prod[i+j] = ( prod[i+j] + m1[i] * m2[j] ) % p + + return polynomial_reduce_mod( prod, polymod, p ) + + +def polynomial_exp_mod( base, exponent, polymod, p ): + """Polynomial exponentiation modulo a polynomial over ints mod p. + + Polynomials are represented as lists of coefficients + of increasing powers of x.""" + + # Based on the Handbook of Applied Cryptography, algorithm 2.227. + + # This module has been tested only by extensive use + # in calculating modular square roots. + + assert exponent < p + + if exponent == 0: return [ 1 ] + + G = base + k = exponent + if k%2 == 1: s = G + else: s = [ 1 ] + + while k > 1: + k = k // 2 + G = polynomial_multiply_mod( G, G, polymod, p ) + if k%2 == 1: s = polynomial_multiply_mod( G, s, polymod, p ) + + return s + + + +def jacobi( a, n ): + """Jacobi symbol""" + + # Based on the Handbook of Applied Cryptography (HAC), algorithm 2.149. + + # This function has been tested by comparison with a small + # table printed in HAC, and by extensive use in calculating + # modular square roots. + + assert n >= 3 + assert n%2 == 1 + a = a % n + if a == 0: return 0 + if a == 1: return 1 + a1, e = a, 0 + while a1%2 == 0: + a1, e = a1//2, e+1 + if e%2 == 0 or n%8 == 1 or n%8 == 7: s = 1 + else: s = -1 + if a1 == 1: return s + if n%4 == 3 and a1%4 == 3: s = -s + return s * jacobi( n % a1, a1 ) + + + +def square_root_mod_prime( a, p ): + """Modular square root of a, mod p, p prime.""" + + # Based on the Handbook of Applied Cryptography, algorithms 3.34 to 3.39. + + # This module has been tested for all values in [0,p-1] for + # every prime p from 3 to 1229. + + assert 0 <= a < p + assert 1 < p + + if a == 0: return 0 + if p == 2: return a + + jac = jacobi( a, p ) + if jac == -1: raise SquareRootError( "%d has no square root modulo %d" \ + % ( a, p ) ) + + if p % 4 == 3: return modular_exp( a, (p+1)//4, p ) + + if p % 8 == 5: + d = modular_exp( a, (p-1)//4, p ) + if d == 1: return modular_exp( a, (p+3)//8, p ) + if d == p-1: return ( 2 * a * modular_exp( 4*a, (p-5)//8, p ) ) % p + raise RuntimeError("Shouldn't get here.") + + for b in range( 2, p ): + if jacobi( b*b-4*a, p ) == -1: + f = ( a, -b, 1 ) + ff = polynomial_exp_mod( ( 0, 1 ), (p+1)//2, f, p ) + assert ff[1] == 0 + return ff[0] + raise RuntimeError("No b found.") + + + +def inverse_mod( a, m ): + """Inverse of a mod m.""" + + if a < 0 or m <= a: a = a % m + + # From Ferguson and Schneier, roughly: + + c, d = a, m + uc, vc, ud, vd = 1, 0, 0, 1 + while c != 0: + q, c, d = divmod( d, c ) + ( c, ) + uc, vc, ud, vd = ud - q*uc, vd - q*vc, uc, vc + + # At this point, d is the GCD, and ud*a+vd*m = d. + # If d == 1, this means that ud is a inverse. + + assert d == 1 + if ud > 0: return ud + else: return ud + m + + +def gcd2(a, b): + """Greatest common divisor using Euclid's algorithm.""" + while a: + a, b = b%a, a + return b + + +def gcd( *a ): + """Greatest common divisor. + + Usage: gcd( [ 2, 4, 6 ] ) + or: gcd( 2, 4, 6 ) + """ + + if len( a ) > 1: return reduce( gcd2, a ) + if hasattr( a[0], "__iter__" ): return reduce( gcd2, a[0] ) + return a[0] + + +def lcm2(a,b): + """Least common multiple of two integers.""" + + return (a*b)//gcd(a,b) + + +def lcm( *a ): + """Least common multiple. + + Usage: lcm( [ 3, 4, 5 ] ) + or: lcm( 3, 4, 5 ) + """ + + if len( a ) > 1: return reduce( lcm2, a ) + if hasattr( a[0], "__iter__" ): return reduce( lcm2, a[0] ) + return a[0] + + + +def factorization( n ): + """Decompose n into a list of (prime,exponent) pairs.""" + + assert isinstance( n, integer_types ) + + if n < 2: return [] + + result = [] + d = 2 + + # Test the small primes: + + for d in smallprimes: + if d > n: break + q, r = divmod( n, d ) + if r == 0: + count = 1 + while d <= n: + n = q + q, r = divmod( n, d ) + if r != 0: break + count = count + 1 + result.append( ( d, count ) ) + + # If n is still greater than the last of our small primes, + # it may require further work: + + if n > smallprimes[-1]: + if is_prime( n ): # If what's left is prime, it's easy: + result.append( ( n, 1 ) ) + else: # Ugh. Search stupidly for a divisor: + d = smallprimes[-1] + while 1: + d = d + 2 # Try the next divisor. + q, r = divmod( n, d ) + if q < d: break # n < d*d means we're done, n = 1 or prime. + if r == 0: # d divides n. How many times? + count = 1 + n = q + while d <= n: # As long as d might still divide n, + q, r = divmod( n, d ) # see if it does. + if r != 0: break + n = q # It does. Reduce n, increase count. + count = count + 1 + result.append( ( d, count ) ) + if n > 1: result.append( ( n, 1 ) ) + + return result + + + +def phi( n ): + """Return the Euler totient function of n.""" + + assert isinstance( n, integer_types ) + + if n < 3: return 1 + + result = 1 + ff = factorization( n ) + for f in ff: + e = f[1] + if e > 1: + result = result * f[0] ** (e-1) * ( f[0] - 1 ) + else: + result = result * ( f[0] - 1 ) + return result + + +def carmichael( n ): + """Return Carmichael function of n. + + Carmichael(n) is the smallest integer x such that + m**x = 1 mod n for all m relatively prime to n. + """ + + return carmichael_of_factorized( factorization( n ) ) + + +def carmichael_of_factorized( f_list ): + """Return the Carmichael function of a number that is + represented as a list of (prime,exponent) pairs. + """ + + if len( f_list ) < 1: return 1 + + result = carmichael_of_ppower( f_list[0] ) + for i in range( 1, len( f_list ) ): + result = lcm( result, carmichael_of_ppower( f_list[i] ) ) + + return result + +def carmichael_of_ppower( pp ): + """Carmichael function of the given power of the given prime. + """ + + p, a = pp + if p == 2 and a > 2: return 2**(a-2) + else: return (p-1) * p**(a-1) + + + +def order_mod( x, m ): + """Return the order of x in the multiplicative group mod m. + """ + + # Warning: this implementation is not very clever, and will + # take a long time if m is very large. + + if m <= 1: return 0 + + assert gcd( x, m ) == 1 + + z = x + result = 1 + while z != 1: + z = ( z * x ) % m + result = result + 1 + return result + + +def largest_factor_relatively_prime( a, b ): + """Return the largest factor of a relatively prime to b. + """ + + while 1: + d = gcd( a, b ) + if d <= 1: break + b = d + while 1: + q, r = divmod( a, d ) + if r > 0: + break + a = q + return a + + +def kinda_order_mod( x, m ): + """Return the order of x in the multiplicative group mod m', + where m' is the largest factor of m relatively prime to x. + """ + + return order_mod( x, largest_factor_relatively_prime( m, x ) ) + + +def is_prime( n ): + """Return True if x is prime, False otherwise. + + We use the Miller-Rabin test, as given in Menezes et al. p. 138. + This test is not exact: there are composite values n for which + it returns True. + + In testing the odd numbers from 10000001 to 19999999, + about 66 composites got past the first test, + 5 got past the second test, and none got past the third. + Since factors of 2, 3, 5, 7, and 11 were detected during + preliminary screening, the number of numbers tested by + Miller-Rabin was (19999999 - 10000001)*(2/3)*(4/5)*(6/7) + = 4.57 million. + """ + + # (This is used to study the risk of false positives:) + global miller_rabin_test_count + + miller_rabin_test_count = 0 + + if n <= smallprimes[-1]: + if n in smallprimes: return True + else: return False + + if gcd( n, 2*3*5*7*11 ) != 1: return False + + # Choose a number of iterations sufficient to reduce the + # probability of accepting a composite below 2**-80 + # (from Menezes et al. Table 4.4): + + t = 40 + n_bits = 1 + int( math.log( n, 2 ) ) + for k, tt in ( ( 100, 27 ), + ( 150, 18 ), + ( 200, 15 ), + ( 250, 12 ), + ( 300, 9 ), + ( 350, 8 ), + ( 400, 7 ), + ( 450, 6 ), + ( 550, 5 ), + ( 650, 4 ), + ( 850, 3 ), + ( 1300, 2 ), + ): + if n_bits < k: break + t = tt + + # Run the test t times: + + s = 0 + r = n - 1 + while ( r % 2 ) == 0: + s = s + 1 + r = r // 2 + for i in range( t ): + a = smallprimes[ i ] + y = modular_exp( a, r, n ) + if y != 1 and y != n-1: + j = 1 + while j <= s - 1 and y != n - 1: + y = modular_exp( y, 2, n ) + if y == 1: + miller_rabin_test_count = i + 1 + return False + j = j + 1 + if y != n-1: + miller_rabin_test_count = i + 1 + return False + return True + + +def next_prime( starting_value ): + "Return the smallest prime larger than the starting value." + + if starting_value < 2: return 2 + result = ( starting_value + 1 ) | 1 + while not is_prime( result ): result = result + 2 + return result + + +smallprimes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, + 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, + 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, + 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, + 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, + 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, + 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, + 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, + 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, + 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, + 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, + 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, + 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, + 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, + 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, + 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, + 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, + 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, + 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, + 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229] + +miller_rabin_test_count = 0 + +def __main__(): + + # Making sure locally defined exceptions work: + # p = modular_exp( 2, -2, 3 ) + # p = square_root_mod_prime( 2, 3 ) + + + print_("Testing gcd...") + assert gcd( 3*5*7, 3*5*11, 3*5*13 ) == 3*5 + assert gcd( [ 3*5*7, 3*5*11, 3*5*13 ] ) == 3*5 + assert gcd( 3 ) == 3 + + print_("Testing lcm...") + assert lcm( 3, 5*3, 7*3 ) == 3*5*7 + assert lcm( [ 3, 5*3, 7*3 ] ) == 3*5*7 + assert lcm( 3 ) == 3 + + print_("Testing next_prime...") + bigprimes = ( 999671, + 999683, + 999721, + 999727, + 999749, + 999763, + 999769, + 999773, + 999809, + 999853, + 999863, + 999883, + 999907, + 999917, + 999931, + 999953, + 999959, + 999961, + 999979, + 999983 ) + + for i in range( len( bigprimes ) - 1 ): + assert next_prime( bigprimes[i] ) == bigprimes[ i+1 ] + + error_tally = 0 + + # Test the square_root_mod_prime function: + + for p in smallprimes: + print_("Testing square_root_mod_prime for modulus p = %d." % p) + squares = [] + + for root in range( 0, 1+p//2 ): + sq = ( root * root ) % p + squares.append( sq ) + calculated = square_root_mod_prime( sq, p ) + if ( calculated * calculated ) % p != sq: + error_tally = error_tally + 1 + print_("Failed to find %d as sqrt( %d ) mod %d. Said %d." % \ + ( root, sq, p, calculated )) + + for nonsquare in range( 0, p ): + if nonsquare not in squares: + try: + calculated = square_root_mod_prime( nonsquare, p ) + except SquareRootError: + pass + else: + error_tally = error_tally + 1 + print_("Failed to report no root for sqrt( %d ) mod %d." % \ + ( nonsquare, p )) + + # Test the jacobi function: + for m in range( 3, 400, 2 ): + print_("Testing jacobi for modulus m = %d." % m) + if is_prime( m ): + squares = [] + for root in range( 1, m ): + if jacobi( root * root, m ) != 1: + error_tally = error_tally + 1 + print_("jacobi( %d * %d, %d ) != 1" % ( root, root, m )) + squares.append( root * root % m ) + for i in range( 1, m ): + if not i in squares: + if jacobi( i, m ) != -1: + error_tally = error_tally + 1 + print_("jacobi( %d, %d ) != -1" % ( i, m )) + else: # m is not prime. + f = factorization( m ) + for a in range( 1, m ): + c = 1 + for i in f: + c = c * jacobi( a, i[0] ) ** i[1] + if c != jacobi( a, m ): + error_tally = error_tally + 1 + print_("%d != jacobi( %d, %d )" % ( c, a, m )) + + +# Test the inverse_mod function: + print_("Testing inverse_mod . . .") + import random + n_tests = 0 + for i in range( 100 ): + m = random.randint( 20, 10000 ) + for j in range( 100 ): + a = random.randint( 1, m-1 ) + if gcd( a, m ) == 1: + n_tests = n_tests + 1 + inv = inverse_mod( a, m ) + if inv <= 0 or inv >= m or ( a * inv ) % m != 1: + error_tally = error_tally + 1 + print_("%d = inverse_mod( %d, %d ) is wrong." % ( inv, a, m )) + assert n_tests > 1000 + print_(n_tests, " tests of inverse_mod completed.") + + class FailedTest(Exception): pass + print_(error_tally, "errors detected.") + if error_tally != 0: + raise FailedTest("%d errors detected" % error_tally) + +if __name__ == '__main__': + __main__() diff --git a/dist/bin/esptool/ecdsa/rfc6979.py b/dist/bin/esptool/ecdsa/rfc6979.py new file mode 100644 index 000000000..6190ebd4d --- /dev/null +++ b/dist/bin/esptool/ecdsa/rfc6979.py @@ -0,0 +1,103 @@ +''' +RFC 6979: + Deterministic Usage of the Digital Signature Algorithm (DSA) and + Elliptic Curve Digital Signature Algorithm (ECDSA) + + http://tools.ietf.org/html/rfc6979 + +Many thanks to Coda Hale for his implementation in Go language: + https://github.com/codahale/rfc6979 +''' + +import hmac +from binascii import hexlify +from .util import number_to_string, number_to_string_crop +from .six import b + +try: + bin(0) +except NameError: + binmap = {"0": "0000", "1": "0001", "2": "0010", "3": "0011", + "4": "0100", "5": "0101", "6": "0110", "7": "0111", + "8": "1000", "9": "1001", "a": "1010", "b": "1011", + "c": "1100", "d": "1101", "e": "1110", "f": "1111"} + def bin(value): # for python2.5 + v = "".join(binmap[x] for x in "%x"%abs(value)).lstrip("0") + if value < 0: + return "-0b" + v + return "0b" + v + +def bit_length(num): + # http://docs.python.org/dev/library/stdtypes.html#int.bit_length + s = bin(num) # binary representation: bin(-37) --> '-0b100101' + s = s.lstrip('-0b') # remove leading zeros and minus sign + return len(s) # len('100101') --> 6 + +def bits2int(data, qlen): + x = int(hexlify(data), 16) + l = len(data) * 8 + + if l > qlen: + return x >> (l-qlen) + return x + +def bits2octets(data, order): + z1 = bits2int(data, bit_length(order)) + z2 = z1 - order + + if z2 < 0: + z2 = z1 + + return number_to_string_crop(z2, order) + +# https://tools.ietf.org/html/rfc6979#section-3.2 +def generate_k(order, secexp, hash_func, data): + ''' + order - order of the DSA generator used in the signature + secexp - secure exponent (private key) in numeric form + hash_func - reference to the same hash function used for generating hash + data - hash in binary form of the signing data + ''' + + qlen = bit_length(order) + holen = hash_func().digest_size + rolen = (qlen + 7) / 8 + bx = number_to_string(secexp, order) + bits2octets(data, order) + + # Step B + v = b('\x01') * holen + + # Step C + k = b('\x00') * holen + + # Step D + + k = hmac.new(k, v+b('\x00')+bx, hash_func).digest() + + # Step E + v = hmac.new(k, v, hash_func).digest() + + # Step F + k = hmac.new(k, v+b('\x01')+bx, hash_func).digest() + + # Step G + v = hmac.new(k, v, hash_func).digest() + + # Step H + while True: + # Step H1 + t = b('') + + # Step H2 + while len(t) < rolen: + v = hmac.new(k, v, hash_func).digest() + t += v + + # Step H3 + secret = bits2int(t, qlen) + + if secret >= 1 and secret < order: + return secret + + k = hmac.new(k, v+b('\x00'), hash_func).digest() + v = hmac.new(k, v, hash_func).digest() diff --git a/dist/bin/esptool/ecdsa/six.py b/dist/bin/esptool/ecdsa/six.py new file mode 100644 index 000000000..3003f8f00 --- /dev/null +++ b/dist/bin/esptool/ecdsa/six.py @@ -0,0 +1,394 @@ +"""Utilities for writing code that runs on Python 2 and 3""" + +# Copyright (c) 2010-2012 Benjamin Peterson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +# the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import operator +import sys +import types + +__author__ = "Benjamin Peterson " +__version__ = "1.2.0" + + +# True if we are running on Python 3. +PY3 = sys.version_info[0] == 3 + +if PY3: + string_types = str, + integer_types = int, + class_types = type, + text_type = str + binary_type = bytes + + MAXSIZE = sys.maxsize +else: + string_types = basestring, + integer_types = (int, long) + class_types = (type, types.ClassType) + text_type = unicode + binary_type = str + + if sys.platform.startswith("java"): + # Jython always uses 32 bits. + MAXSIZE = int((1 << 31) - 1) + else: + # It's possible to have sizeof(long) != sizeof(Py_ssize_t). + class X(object): + def __len__(self): + return 1 << 31 + try: + len(X()) + except OverflowError: + # 32-bit + MAXSIZE = int((1 << 31) - 1) + else: + # 64-bit + MAXSIZE = int((1 << 63) - 1) + del X + + +def _add_doc(func, doc): + """Add documentation to a function.""" + func.__doc__ = doc + + +def _import_module(name): + """Import module, returning the module after the last dot.""" + __import__(name) + return sys.modules[name] + + +class _LazyDescr(object): + + def __init__(self, name): + self.name = name + + def __get__(self, obj, tp): + result = self._resolve() + setattr(obj, self.name, result) + # This is a bit ugly, but it avoids running this again. + delattr(tp, self.name) + return result + + +class MovedModule(_LazyDescr): + + def __init__(self, name, old, new=None): + super(MovedModule, self).__init__(name) + if PY3: + if new is None: + new = name + self.mod = new + else: + self.mod = old + + def _resolve(self): + return _import_module(self.mod) + + +class MovedAttribute(_LazyDescr): + + def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): + super(MovedAttribute, self).__init__(name) + if PY3: + if new_mod is None: + new_mod = name + self.mod = new_mod + if new_attr is None: + if old_attr is None: + new_attr = name + else: + new_attr = old_attr + self.attr = new_attr + else: + self.mod = old_mod + if old_attr is None: + old_attr = name + self.attr = old_attr + + def _resolve(self): + module = _import_module(self.mod) + return getattr(module, self.attr) + + + +class _MovedItems(types.ModuleType): + """Lazy loading of moved objects""" + + +_moved_attributes = [ + MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), + MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), + MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), + MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("reload_module", "__builtin__", "imp", "reload"), + MovedAttribute("reduce", "__builtin__", "functools"), + MovedAttribute("StringIO", "StringIO", "io"), + MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), + + MovedModule("builtins", "__builtin__"), + MovedModule("configparser", "ConfigParser"), + MovedModule("copyreg", "copy_reg"), + MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), + MovedModule("http_cookies", "Cookie", "http.cookies"), + MovedModule("html_entities", "htmlentitydefs", "html.entities"), + MovedModule("html_parser", "HTMLParser", "html.parser"), + MovedModule("http_client", "httplib", "http.client"), + MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), + MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), + MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), + MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), + MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), + MovedModule("cPickle", "cPickle", "pickle"), + MovedModule("queue", "Queue"), + MovedModule("reprlib", "repr"), + MovedModule("socketserver", "SocketServer"), + MovedModule("tkinter", "Tkinter"), + MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), + MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), + MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), + MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), + MovedModule("tkinter_tix", "Tix", "tkinter.tix"), + MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), + MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), + MovedModule("tkinter_colorchooser", "tkColorChooser", + "tkinter.colorchooser"), + MovedModule("tkinter_commondialog", "tkCommonDialog", + "tkinter.commondialog"), + MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), + MovedModule("tkinter_font", "tkFont", "tkinter.font"), + MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), + MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", + "tkinter.simpledialog"), + MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), + MovedModule("winreg", "_winreg"), +] +for attr in _moved_attributes: + setattr(_MovedItems, attr.name, attr) +del attr + +moves = sys.modules[__name__ + ".moves"] = _MovedItems("moves") + + +def add_move(move): + """Add an item to six.moves.""" + setattr(_MovedItems, move.name, move) + + +def remove_move(name): + """Remove item from six.moves.""" + try: + delattr(_MovedItems, name) + except AttributeError: + try: + del moves.__dict__[name] + except KeyError: + raise AttributeError("no such move, %r" % (name,)) + + +if PY3: + _meth_func = "__func__" + _meth_self = "__self__" + + _func_code = "__code__" + _func_defaults = "__defaults__" + + _iterkeys = "keys" + _itervalues = "values" + _iteritems = "items" +else: + _meth_func = "im_func" + _meth_self = "im_self" + + _func_code = "func_code" + _func_defaults = "func_defaults" + + _iterkeys = "iterkeys" + _itervalues = "itervalues" + _iteritems = "iteritems" + + +try: + advance_iterator = next +except NameError: + def advance_iterator(it): + return it.next() +next = advance_iterator + + +try: + callable = callable +except NameError: + def callable(obj): + return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) + + +if PY3: + def get_unbound_function(unbound): + return unbound + + Iterator = object +else: + def get_unbound_function(unbound): + return unbound.im_func + + class Iterator(object): + + def next(self): + return type(self).__next__(self) + + callable = callable +_add_doc(get_unbound_function, + """Get the function out of a possibly unbound function""") + + +get_method_function = operator.attrgetter(_meth_func) +get_method_self = operator.attrgetter(_meth_self) +get_function_code = operator.attrgetter(_func_code) +get_function_defaults = operator.attrgetter(_func_defaults) + + +def iterkeys(d): + """Return an iterator over the keys of a dictionary.""" + return iter(getattr(d, _iterkeys)()) + +def itervalues(d): + """Return an iterator over the values of a dictionary.""" + return iter(getattr(d, _itervalues)()) + +def iteritems(d): + """Return an iterator over the (key, value) pairs of a dictionary.""" + return iter(getattr(d, _iteritems)()) + + +if PY3: + def b(s): + return s.encode("latin-1") + def u(s): + return s + if sys.version_info[1] <= 1: + def int2byte(i): + return bytes((i,)) + else: + # This is about 2x faster than the implementation above on 3.2+ + int2byte = operator.methodcaller("to_bytes", 1, "big") + import io + StringIO = io.StringIO + BytesIO = io.BytesIO +else: + def b(s): + return s + def u(s): + if isinstance(s, unicode): + return s + return unicode(s, "unicode_escape") + int2byte = chr + import StringIO + StringIO = BytesIO = StringIO.StringIO +_add_doc(b, """Byte literal""") +_add_doc(u, """Text literal""") + + +if PY3: + import builtins + exec_ = getattr(builtins, "exec") + + + def reraise(tp, value, tb=None): + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + + + print_ = getattr(builtins, "print") + del builtins + +else: + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") + + + exec_("""def reraise(tp, value, tb=None): + raise tp, value, tb +""") + + + def print_(*args, **kwargs): + """The new-style print function.""" + fp = kwargs.pop("file", sys.stdout) + if fp is None: + return + def write(data): + if not isinstance(data, basestring): + data = str(data) + fp.write(data) + want_unicode = False + sep = kwargs.pop("sep", None) + if sep is not None: + if isinstance(sep, unicode): + want_unicode = True + elif not isinstance(sep, str): + raise TypeError("sep must be None or a string") + end = kwargs.pop("end", None) + if end is not None: + if isinstance(end, unicode): + want_unicode = True + elif not isinstance(end, str): + raise TypeError("end must be None or a string") + if kwargs: + raise TypeError("invalid keyword arguments to print()") + if not want_unicode: + for arg in args: + if isinstance(arg, unicode): + want_unicode = True + break + if want_unicode: + newline = unicode("\n") + space = unicode(" ") + else: + newline = "\n" + space = " " + if sep is None: + sep = space + if end is None: + end = newline + for i, arg in enumerate(args): + if i: + write(sep) + write(arg) + write(end) + +_add_doc(reraise, """Reraise an exception.""") + + +def with_metaclass(meta, base=object): + """Create a base class with a metaclass.""" + return meta("NewBase", (base,), {}) diff --git a/dist/bin/esptool/ecdsa/test_pyecdsa.py b/dist/bin/esptool/ecdsa/test_pyecdsa.py new file mode 100644 index 000000000..326cac4aa --- /dev/null +++ b/dist/bin/esptool/ecdsa/test_pyecdsa.py @@ -0,0 +1,663 @@ +from __future__ import with_statement, division + +import unittest +import os +import time +import shutil +import subprocess +from binascii import hexlify, unhexlify +from hashlib import sha1, sha256, sha512 + +from .six import b, print_, binary_type +from .keys import SigningKey, VerifyingKey +from .keys import BadSignatureError +from . import util +from .util import sigencode_der, sigencode_strings +from .util import sigdecode_der, sigdecode_strings +from .curves import Curve, UnknownCurveError +from .curves import NIST192p, NIST224p, NIST256p, NIST384p, NIST521p, SECP256k1 +from .ellipticcurve import Point +from . import der +from . import rfc6979 + +class SubprocessError(Exception): + pass + +def run_openssl(cmd): + OPENSSL = "openssl" + p = subprocess.Popen([OPENSSL] + cmd.split(), + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + stdout, ignored = p.communicate() + if p.returncode != 0: + raise SubprocessError("cmd '%s %s' failed: rc=%s, stdout/err was %s" % + (OPENSSL, cmd, p.returncode, stdout)) + return stdout.decode() + +BENCH = False + +class ECDSA(unittest.TestCase): + def test_basic(self): + priv = SigningKey.generate() + pub = priv.get_verifying_key() + + data = b("blahblah") + sig = priv.sign(data) + + self.assertTrue(pub.verify(sig, data)) + self.assertRaises(BadSignatureError, pub.verify, sig, data+b("bad")) + + pub2 = VerifyingKey.from_string(pub.to_string()) + self.assertTrue(pub2.verify(sig, data)) + + def test_deterministic(self): + data = b("blahblah") + secexp = int("9d0219792467d7d37b4d43298a7d0c05", 16) + + priv = SigningKey.from_secret_exponent(secexp, SECP256k1, sha256) + pub = priv.get_verifying_key() + + k = rfc6979.generate_k( + SECP256k1.generator.order(), secexp, sha256, sha256(data).digest()) + + sig1 = priv.sign(data, k=k) + self.assertTrue(pub.verify(sig1, data)) + + sig2 = priv.sign(data, k=k) + self.assertTrue(pub.verify(sig2, data)) + + sig3 = priv.sign_deterministic(data, sha256) + self.assertTrue(pub.verify(sig3, data)) + + self.assertEqual(sig1, sig2) + self.assertEqual(sig1, sig3) + + def test_bad_usage(self): + # sk=SigningKey() is wrong + self.assertRaises(TypeError, SigningKey) + self.assertRaises(TypeError, VerifyingKey) + + def test_lengths(self): + default = NIST192p + priv = SigningKey.generate() + pub = priv.get_verifying_key() + self.assertEqual(len(pub.to_string()), default.verifying_key_length) + sig = priv.sign(b("data")) + self.assertEqual(len(sig), default.signature_length) + if BENCH: + print_() + for curve in (NIST192p, NIST224p, NIST256p, NIST384p, NIST521p): + start = time.time() + priv = SigningKey.generate(curve=curve) + pub1 = priv.get_verifying_key() + keygen_time = time.time() - start + pub2 = VerifyingKey.from_string(pub1.to_string(), curve) + self.assertEqual(pub1.to_string(), pub2.to_string()) + self.assertEqual(len(pub1.to_string()), + curve.verifying_key_length) + start = time.time() + sig = priv.sign(b("data")) + sign_time = time.time() - start + self.assertEqual(len(sig), curve.signature_length) + if BENCH: + start = time.time() + pub1.verify(sig, b("data")) + verify_time = time.time() - start + print_("%s: siglen=%d, keygen=%0.3fs, sign=%0.3f, verify=%0.3f" \ + % (curve.name, curve.signature_length, + keygen_time, sign_time, verify_time)) + + def test_serialize(self): + seed = b("secret") + curve = NIST192p + secexp1 = util.randrange_from_seed__trytryagain(seed, curve.order) + secexp2 = util.randrange_from_seed__trytryagain(seed, curve.order) + self.assertEqual(secexp1, secexp2) + priv1 = SigningKey.from_secret_exponent(secexp1, curve) + priv2 = SigningKey.from_secret_exponent(secexp2, curve) + self.assertEqual(hexlify(priv1.to_string()), + hexlify(priv2.to_string())) + self.assertEqual(priv1.to_pem(), priv2.to_pem()) + pub1 = priv1.get_verifying_key() + pub2 = priv2.get_verifying_key() + data = b("data") + sig1 = priv1.sign(data) + sig2 = priv2.sign(data) + self.assertTrue(pub1.verify(sig1, data)) + self.assertTrue(pub2.verify(sig1, data)) + self.assertTrue(pub1.verify(sig2, data)) + self.assertTrue(pub2.verify(sig2, data)) + self.assertEqual(hexlify(pub1.to_string()), + hexlify(pub2.to_string())) + + def test_nonrandom(self): + s = b("all the entropy in the entire world, compressed into one line") + def not_much_entropy(numbytes): + return s[:numbytes] + # we control the entropy source, these two keys should be identical: + priv1 = SigningKey.generate(entropy=not_much_entropy) + priv2 = SigningKey.generate(entropy=not_much_entropy) + self.assertEqual(hexlify(priv1.get_verifying_key().to_string()), + hexlify(priv2.get_verifying_key().to_string())) + # likewise, signatures should be identical. Obviously you'd never + # want to do this with keys you care about, because the secrecy of + # the private key depends upon using different random numbers for + # each signature + sig1 = priv1.sign(b("data"), entropy=not_much_entropy) + sig2 = priv2.sign(b("data"), entropy=not_much_entropy) + self.assertEqual(hexlify(sig1), hexlify(sig2)) + + def assertTruePrivkeysEqual(self, priv1, priv2): + self.assertEqual(priv1.privkey.secret_multiplier, + priv2.privkey.secret_multiplier) + self.assertEqual(priv1.privkey.public_key.generator, + priv2.privkey.public_key.generator) + + def failIfPrivkeysEqual(self, priv1, priv2): + self.failIfEqual(priv1.privkey.secret_multiplier, + priv2.privkey.secret_multiplier) + + def test_privkey_creation(self): + s = b("all the entropy in the entire world, compressed into one line") + def not_much_entropy(numbytes): + return s[:numbytes] + priv1 = SigningKey.generate() + self.assertEqual(priv1.baselen, NIST192p.baselen) + + priv1 = SigningKey.generate(curve=NIST224p) + self.assertEqual(priv1.baselen, NIST224p.baselen) + + priv1 = SigningKey.generate(entropy=not_much_entropy) + self.assertEqual(priv1.baselen, NIST192p.baselen) + priv2 = SigningKey.generate(entropy=not_much_entropy) + self.assertEqual(priv2.baselen, NIST192p.baselen) + self.assertTruePrivkeysEqual(priv1, priv2) + + priv1 = SigningKey.from_secret_exponent(secexp=3) + self.assertEqual(priv1.baselen, NIST192p.baselen) + priv2 = SigningKey.from_secret_exponent(secexp=3) + self.assertTruePrivkeysEqual(priv1, priv2) + + priv1 = SigningKey.from_secret_exponent(secexp=4, curve=NIST224p) + self.assertEqual(priv1.baselen, NIST224p.baselen) + + def test_privkey_strings(self): + priv1 = SigningKey.generate() + s1 = priv1.to_string() + self.assertEqual(type(s1), binary_type) + self.assertEqual(len(s1), NIST192p.baselen) + priv2 = SigningKey.from_string(s1) + self.assertTruePrivkeysEqual(priv1, priv2) + + s1 = priv1.to_pem() + self.assertEqual(type(s1), binary_type) + self.assertTrue(s1.startswith(b("-----BEGIN EC PRIVATE KEY-----"))) + self.assertTrue(s1.strip().endswith(b("-----END EC PRIVATE KEY-----"))) + priv2 = SigningKey.from_pem(s1) + self.assertTruePrivkeysEqual(priv1, priv2) + + s1 = priv1.to_der() + self.assertEqual(type(s1), binary_type) + priv2 = SigningKey.from_der(s1) + self.assertTruePrivkeysEqual(priv1, priv2) + + priv1 = SigningKey.generate(curve=NIST256p) + s1 = priv1.to_pem() + self.assertEqual(type(s1), binary_type) + self.assertTrue(s1.startswith(b("-----BEGIN EC PRIVATE KEY-----"))) + self.assertTrue(s1.strip().endswith(b("-----END EC PRIVATE KEY-----"))) + priv2 = SigningKey.from_pem(s1) + self.assertTruePrivkeysEqual(priv1, priv2) + + s1 = priv1.to_der() + self.assertEqual(type(s1), binary_type) + priv2 = SigningKey.from_der(s1) + self.assertTruePrivkeysEqual(priv1, priv2) + + def assertTruePubkeysEqual(self, pub1, pub2): + self.assertEqual(pub1.pubkey.point, pub2.pubkey.point) + self.assertEqual(pub1.pubkey.generator, pub2.pubkey.generator) + self.assertEqual(pub1.curve, pub2.curve) + + def test_pubkey_strings(self): + priv1 = SigningKey.generate() + pub1 = priv1.get_verifying_key() + s1 = pub1.to_string() + self.assertEqual(type(s1), binary_type) + self.assertEqual(len(s1), NIST192p.verifying_key_length) + pub2 = VerifyingKey.from_string(s1) + self.assertTruePubkeysEqual(pub1, pub2) + + priv1 = SigningKey.generate(curve=NIST256p) + pub1 = priv1.get_verifying_key() + s1 = pub1.to_string() + self.assertEqual(type(s1), binary_type) + self.assertEqual(len(s1), NIST256p.verifying_key_length) + pub2 = VerifyingKey.from_string(s1, curve=NIST256p) + self.assertTruePubkeysEqual(pub1, pub2) + + pub1_der = pub1.to_der() + self.assertEqual(type(pub1_der), binary_type) + pub2 = VerifyingKey.from_der(pub1_der) + self.assertTruePubkeysEqual(pub1, pub2) + + self.assertRaises(der.UnexpectedDER, + VerifyingKey.from_der, pub1_der+b("junk")) + badpub = VerifyingKey.from_der(pub1_der) + class FakeGenerator: + def order(self): return 123456789 + badcurve = Curve("unknown", None, FakeGenerator(), (1,2,3,4,5,6), None) + badpub.curve = badcurve + badder = badpub.to_der() + self.assertRaises(UnknownCurveError, VerifyingKey.from_der, badder) + + pem = pub1.to_pem() + self.assertEqual(type(pem), binary_type) + self.assertTrue(pem.startswith(b("-----BEGIN PUBLIC KEY-----")), pem) + self.assertTrue(pem.strip().endswith(b("-----END PUBLIC KEY-----")), pem) + pub2 = VerifyingKey.from_pem(pem) + self.assertTruePubkeysEqual(pub1, pub2) + + def test_signature_strings(self): + priv1 = SigningKey.generate() + pub1 = priv1.get_verifying_key() + data = b("data") + + sig = priv1.sign(data) + self.assertEqual(type(sig), binary_type) + self.assertEqual(len(sig), NIST192p.signature_length) + self.assertTrue(pub1.verify(sig, data)) + + sig = priv1.sign(data, sigencode=sigencode_strings) + self.assertEqual(type(sig), tuple) + self.assertEqual(len(sig), 2) + self.assertEqual(type(sig[0]), binary_type) + self.assertEqual(type(sig[1]), binary_type) + self.assertEqual(len(sig[0]), NIST192p.baselen) + self.assertEqual(len(sig[1]), NIST192p.baselen) + self.assertTrue(pub1.verify(sig, data, sigdecode=sigdecode_strings)) + + sig_der = priv1.sign(data, sigencode=sigencode_der) + self.assertEqual(type(sig_der), binary_type) + self.assertTrue(pub1.verify(sig_der, data, sigdecode=sigdecode_der)) + + def test_hashfunc(self): + sk = SigningKey.generate(curve=NIST256p, hashfunc=sha256) + data = b("security level is 128 bits") + sig = sk.sign(data) + vk = VerifyingKey.from_string(sk.get_verifying_key().to_string(), + curve=NIST256p, hashfunc=sha256) + self.assertTrue(vk.verify(sig, data)) + + sk2 = SigningKey.generate(curve=NIST256p) + sig2 = sk2.sign(data, hashfunc=sha256) + vk2 = VerifyingKey.from_string(sk2.get_verifying_key().to_string(), + curve=NIST256p, hashfunc=sha256) + self.assertTrue(vk2.verify(sig2, data)) + + vk3 = VerifyingKey.from_string(sk.get_verifying_key().to_string(), + curve=NIST256p) + self.assertTrue(vk3.verify(sig, data, hashfunc=sha256)) + + +class OpenSSL(unittest.TestCase): + # test interoperability with OpenSSL tools. Note that openssl's ECDSA + # sign/verify arguments changed between 0.9.8 and 1.0.0: the early + # versions require "-ecdsa-with-SHA1", the later versions want just + # "-SHA1" (or to leave out that argument entirely, which means the + # signature will use some default digest algorithm, probably determined + # by the key, probably always SHA1). + # + # openssl ecparam -name secp224r1 -genkey -out privkey.pem + # openssl ec -in privkey.pem -text -noout # get the priv/pub keys + # openssl dgst -ecdsa-with-SHA1 -sign privkey.pem -out data.sig data.txt + # openssl asn1parse -in data.sig -inform DER + # data.sig is 64 bytes, probably 56b plus ASN1 overhead + # openssl dgst -ecdsa-with-SHA1 -prverify privkey.pem -signature data.sig data.txt ; echo $? + # openssl ec -in privkey.pem -pubout -out pubkey.pem + # openssl ec -in privkey.pem -pubout -outform DER -out pubkey.der + + def get_openssl_messagedigest_arg(self): + v = run_openssl("version") + # e.g. "OpenSSL 1.0.0 29 Mar 2010", or "OpenSSL 1.0.0a 1 Jun 2010", + # or "OpenSSL 0.9.8o 01 Jun 2010" + vs = v.split()[1].split(".") + if vs >= ["1","0","0"]: + return "-SHA1" + else: + return "-ecdsa-with-SHA1" + + # sk: 1:OpenSSL->python 2:python->OpenSSL + # vk: 3:OpenSSL->python 4:python->OpenSSL + # sig: 5:OpenSSL->python 6:python->OpenSSL + + def test_from_openssl_nist192p(self): + return self.do_test_from_openssl(NIST192p) + def test_from_openssl_nist224p(self): + return self.do_test_from_openssl(NIST224p) + def test_from_openssl_nist256p(self): + return self.do_test_from_openssl(NIST256p) + def test_from_openssl_nist384p(self): + return self.do_test_from_openssl(NIST384p) + def test_from_openssl_nist521p(self): + return self.do_test_from_openssl(NIST521p) + def test_from_openssl_secp256k1(self): + return self.do_test_from_openssl(SECP256k1) + + def do_test_from_openssl(self, curve): + curvename = curve.openssl_name + assert curvename + # OpenSSL: create sk, vk, sign. + # Python: read vk(3), checksig(5), read sk(1), sign, check + mdarg = self.get_openssl_messagedigest_arg() + if os.path.isdir("t"): + shutil.rmtree("t") + os.mkdir("t") + run_openssl("ecparam -name %s -genkey -out t/privkey.pem" % curvename) + run_openssl("ec -in t/privkey.pem -pubout -out t/pubkey.pem") + data = b("data") + with open("t/data.txt","wb") as e: e.write(data) + run_openssl("dgst %s -sign t/privkey.pem -out t/data.sig t/data.txt" % mdarg) + run_openssl("dgst %s -verify t/pubkey.pem -signature t/data.sig t/data.txt" % mdarg) + with open("t/pubkey.pem","rb") as e: pubkey_pem = e.read() + vk = VerifyingKey.from_pem(pubkey_pem) # 3 + with open("t/data.sig","rb") as e: sig_der = e.read() + self.assertTrue(vk.verify(sig_der, data, # 5 + hashfunc=sha1, sigdecode=sigdecode_der)) + + with open("t/privkey.pem") as e: fp = e.read() + sk = SigningKey.from_pem(fp) # 1 + sig = sk.sign(data) + self.assertTrue(vk.verify(sig, data)) + + def test_to_openssl_nist192p(self): + self.do_test_to_openssl(NIST192p) + def test_to_openssl_nist224p(self): + self.do_test_to_openssl(NIST224p) + def test_to_openssl_nist256p(self): + self.do_test_to_openssl(NIST256p) + def test_to_openssl_nist384p(self): + self.do_test_to_openssl(NIST384p) + def test_to_openssl_nist521p(self): + self.do_test_to_openssl(NIST521p) + def test_to_openssl_secp256k1(self): + self.do_test_to_openssl(SECP256k1) + + def do_test_to_openssl(self, curve): + curvename = curve.openssl_name + assert curvename + # Python: create sk, vk, sign. + # OpenSSL: read vk(4), checksig(6), read sk(2), sign, check + mdarg = self.get_openssl_messagedigest_arg() + if os.path.isdir("t"): + shutil.rmtree("t") + os.mkdir("t") + sk = SigningKey.generate(curve=curve) + vk = sk.get_verifying_key() + data = b("data") + with open("t/pubkey.der","wb") as e: e.write(vk.to_der()) # 4 + with open("t/pubkey.pem","wb") as e: e.write(vk.to_pem()) # 4 + sig_der = sk.sign(data, hashfunc=sha1, sigencode=sigencode_der) + + with open("t/data.sig","wb") as e: e.write(sig_der) # 6 + with open("t/data.txt","wb") as e: e.write(data) + with open("t/baddata.txt","wb") as e: e.write(data+b("corrupt")) + + self.assertRaises(SubprocessError, run_openssl, + "dgst %s -verify t/pubkey.der -keyform DER -signature t/data.sig t/baddata.txt" % mdarg) + run_openssl("dgst %s -verify t/pubkey.der -keyform DER -signature t/data.sig t/data.txt" % mdarg) + + with open("t/privkey.pem","wb") as e: e.write(sk.to_pem()) # 2 + run_openssl("dgst %s -sign t/privkey.pem -out t/data.sig2 t/data.txt" % mdarg) + run_openssl("dgst %s -verify t/pubkey.pem -signature t/data.sig2 t/data.txt" % mdarg) + +class DER(unittest.TestCase): + def test_oids(self): + oid_ecPublicKey = der.encode_oid(1, 2, 840, 10045, 2, 1) + self.assertEqual(hexlify(oid_ecPublicKey), b("06072a8648ce3d0201")) + self.assertEqual(hexlify(NIST224p.encoded_oid), b("06052b81040021")) + self.assertEqual(hexlify(NIST256p.encoded_oid), + b("06082a8648ce3d030107")) + x = oid_ecPublicKey + b("more") + x1, rest = der.remove_object(x) + self.assertEqual(x1, (1, 2, 840, 10045, 2, 1)) + self.assertEqual(rest, b("more")) + + def test_integer(self): + self.assertEqual(der.encode_integer(0), b("\x02\x01\x00")) + self.assertEqual(der.encode_integer(1), b("\x02\x01\x01")) + self.assertEqual(der.encode_integer(127), b("\x02\x01\x7f")) + self.assertEqual(der.encode_integer(128), b("\x02\x02\x00\x80")) + self.assertEqual(der.encode_integer(256), b("\x02\x02\x01\x00")) + #self.assertEqual(der.encode_integer(-1), b("\x02\x01\xff")) + + def s(n): return der.remove_integer(der.encode_integer(n) + b("junk")) + self.assertEqual(s(0), (0, b("junk"))) + self.assertEqual(s(1), (1, b("junk"))) + self.assertEqual(s(127), (127, b("junk"))) + self.assertEqual(s(128), (128, b("junk"))) + self.assertEqual(s(256), (256, b("junk"))) + self.assertEqual(s(1234567890123456789012345678901234567890), + (1234567890123456789012345678901234567890,b("junk"))) + + def test_number(self): + self.assertEqual(der.encode_number(0), b("\x00")) + self.assertEqual(der.encode_number(127), b("\x7f")) + self.assertEqual(der.encode_number(128), b("\x81\x00")) + self.assertEqual(der.encode_number(3*128+7), b("\x83\x07")) + #self.assertEqual(der.read_number("\x81\x9b"+"more"), (155, 2)) + #self.assertEqual(der.encode_number(155), b("\x81\x9b")) + for n in (0, 1, 2, 127, 128, 3*128+7, 840, 10045): #, 155): + x = der.encode_number(n) + b("more") + n1, llen = der.read_number(x) + self.assertEqual(n1, n) + self.assertEqual(x[llen:], b("more")) + + def test_length(self): + self.assertEqual(der.encode_length(0), b("\x00")) + self.assertEqual(der.encode_length(127), b("\x7f")) + self.assertEqual(der.encode_length(128), b("\x81\x80")) + self.assertEqual(der.encode_length(255), b("\x81\xff")) + self.assertEqual(der.encode_length(256), b("\x82\x01\x00")) + self.assertEqual(der.encode_length(3*256+7), b("\x82\x03\x07")) + self.assertEqual(der.read_length(b("\x81\x9b")+b("more")), (155, 2)) + self.assertEqual(der.encode_length(155), b("\x81\x9b")) + for n in (0, 1, 2, 127, 128, 255, 256, 3*256+7, 155): + x = der.encode_length(n) + b("more") + n1, llen = der.read_length(x) + self.assertEqual(n1, n) + self.assertEqual(x[llen:], b("more")) + + def test_sequence(self): + x = der.encode_sequence(b("ABC"), b("DEF")) + b("GHI") + self.assertEqual(x, b("\x30\x06ABCDEFGHI")) + x1, rest = der.remove_sequence(x) + self.assertEqual(x1, b("ABCDEF")) + self.assertEqual(rest, b("GHI")) + + def test_constructed(self): + x = der.encode_constructed(0, NIST224p.encoded_oid) + self.assertEqual(hexlify(x), b("a007") + b("06052b81040021")) + x = der.encode_constructed(1, unhexlify(b("0102030a0b0c"))) + self.assertEqual(hexlify(x), b("a106") + b("0102030a0b0c")) + +class Util(unittest.TestCase): + def test_trytryagain(self): + tta = util.randrange_from_seed__trytryagain + for i in range(1000): + seed = "seed-%d" % i + for order in (2**8-2, 2**8-1, 2**8, 2**8+1, 2**8+2, + 2**16-1, 2**16+1): + n = tta(seed, order) + self.assertTrue(1 <= n < order, (1, n, order)) + # this trytryagain *does* provide long-term stability + self.assertEqual(("%x"%(tta("seed", NIST224p.order))).encode(), + b("6fa59d73bf0446ae8743cf748fc5ac11d5585a90356417e97155c3bc")) + + def test_randrange(self): + # util.randrange does not provide long-term stability: we might + # change the algorithm in the future. + for i in range(1000): + entropy = util.PRNG("seed-%d" % i) + for order in (2**8-2, 2**8-1, 2**8, + 2**16-1, 2**16+1, + ): + # that oddball 2**16+1 takes half our runtime + n = util.randrange(order, entropy=entropy) + self.assertTrue(1 <= n < order, (1, n, order)) + + def OFF_test_prove_uniformity(self): + order = 2**8-2 + counts = dict([(i, 0) for i in range(1, order)]) + assert 0 not in counts + assert order not in counts + for i in range(1000000): + seed = "seed-%d" % i + n = util.randrange_from_seed__trytryagain(seed, order) + counts[n] += 1 + # this technique should use the full range + self.assertTrue(counts[order-1]) + for i in range(1, order): + print_("%3d: %s" % (i, "*"*(counts[i]//100))) + +class RFC6979(unittest.TestCase): + # https://tools.ietf.org/html/rfc6979#appendix-A.1 + def _do(self, generator, secexp, hsh, hash_func, expected): + actual = rfc6979.generate_k(generator.order(), secexp, hash_func, hsh) + self.assertEqual(expected, actual) + + def test_SECP256k1(self): + '''RFC doesn't contain test vectors for SECP256k1 used in bitcoin. + This vector has been computed by Golang reference implementation instead.''' + self._do( + generator = SECP256k1.generator, + secexp = int("9d0219792467d7d37b4d43298a7d0c05", 16), + hsh = sha256(b("sample")).digest(), + hash_func = sha256, + expected = int("8fa1f95d514760e498f28957b824ee6ec39ed64826ff4fecc2b5739ec45b91cd", 16)) + + def test_SECP256k1_2(self): + self._do( + generator=SECP256k1.generator, + secexp=int("cca9fbcc1b41e5a95d369eaa6ddcff73b61a4efaa279cfc6567e8daa39cbaf50", 16), + hsh=sha256(b("sample")).digest(), + hash_func=sha256, + expected=int("2df40ca70e639d89528a6b670d9d48d9165fdc0febc0974056bdce192b8e16a3", 16)) + + def test_SECP256k1_3(self): + self._do( + generator=SECP256k1.generator, + secexp=0x1, + hsh=sha256(b("Satoshi Nakamoto")).digest(), + hash_func=sha256, + expected=0x8F8A276C19F4149656B280621E358CCE24F5F52542772691EE69063B74F15D15) + + def test_SECP256k1_4(self): + self._do( + generator=SECP256k1.generator, + secexp=0x1, + hsh=sha256(b("All those moments will be lost in time, like tears in rain. Time to die...")).digest(), + hash_func=sha256, + expected=0x38AA22D72376B4DBC472E06C3BA403EE0A394DA63FC58D88686C611ABA98D6B3) + + def test_SECP256k1_5(self): + self._do( + generator=SECP256k1.generator, + secexp=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140, + hsh=sha256(b("Satoshi Nakamoto")).digest(), + hash_func=sha256, + expected=0x33A19B60E25FB6F4435AF53A3D42D493644827367E6453928554F43E49AA6F90) + + def test_SECP256k1_6(self): + self._do( + generator=SECP256k1.generator, + secexp=0xf8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181, + hsh=sha256(b("Alan Turing")).digest(), + hash_func=sha256, + expected=0x525A82B70E67874398067543FD84C83D30C175FDC45FDEEE082FE13B1D7CFDF1) + + def test_1(self): + # Basic example of the RFC, it also tests 'try-try-again' from Step H of rfc6979 + self._do( + generator = Point(None, 0, 0, int("4000000000000000000020108A2E0CC0D99F8A5EF", 16)), + secexp = int("09A4D6792295A7F730FC3F2B49CBC0F62E862272F", 16), + hsh = unhexlify(b("AF2BDBE1AA9B6EC1E2ADE1D694F41FC71A831D0268E9891562113D8A62ADD1BF")), + hash_func = sha256, + expected = int("23AF4074C90A02B3FE61D286D5C87F425E6BDD81B", 16)) + + def test_2(self): + self._do( + generator=NIST192p.generator, + secexp = int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), + hsh = sha1(b("sample")).digest(), + hash_func = sha1, + expected = int("37D7CA00D2C7B0E5E412AC03BD44BA837FDD5B28CD3B0021", 16)) + + def test_3(self): + self._do( + generator=NIST192p.generator, + secexp = int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), + hsh = sha256(b("sample")).digest(), + hash_func = sha256, + expected = int("32B1B6D7D42A05CB449065727A84804FB1A3E34D8F261496", 16)) + + def test_4(self): + self._do( + generator=NIST192p.generator, + secexp = int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), + hsh = sha512(b("sample")).digest(), + hash_func = sha512, + expected = int("A2AC7AB055E4F20692D49209544C203A7D1F2C0BFBC75DB1", 16)) + + def test_5(self): + self._do( + generator=NIST192p.generator, + secexp = int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), + hsh = sha1(b("test")).digest(), + hash_func = sha1, + expected = int("D9CF9C3D3297D3260773A1DA7418DB5537AB8DD93DE7FA25", 16)) + + def test_6(self): + self._do( + generator=NIST192p.generator, + secexp = int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), + hsh = sha256(b("test")).digest(), + hash_func = sha256, + expected = int("5C4CE89CF56D9E7C77C8585339B006B97B5F0680B4306C6C", 16)) + + def test_7(self): + self._do( + generator=NIST192p.generator, + secexp = int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), + hsh = sha512(b("test")).digest(), + hash_func = sha512, + expected = int("0758753A5254759C7CFBAD2E2D9B0792EEE44136C9480527", 16)) + + def test_8(self): + self._do( + generator=NIST521p.generator, + secexp = int("0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", 16), + hsh = sha1(b("sample")).digest(), + hash_func = sha1, + expected = int("089C071B419E1C2820962321787258469511958E80582E95D8378E0C2CCDB3CB42BEDE42F50E3FA3C71F5A76724281D31D9C89F0F91FC1BE4918DB1C03A5838D0F9", 16)) + + def test_9(self): + self._do( + generator=NIST521p.generator, + secexp = int("0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", 16), + hsh = sha256(b("sample")).digest(), + hash_func = sha256, + expected = int("0EDF38AFCAAECAB4383358B34D67C9F2216C8382AAEA44A3DAD5FDC9C32575761793FEF24EB0FC276DFC4F6E3EC476752F043CF01415387470BCBD8678ED2C7E1A0", 16)) + + def test_10(self): + self._do( + generator=NIST521p.generator, + secexp = int("0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", 16), + hsh = sha512(b("test")).digest(), + hash_func = sha512, + expected = int("16200813020EC986863BEDFC1B121F605C1215645018AEA1A7B215A564DE9EB1B38A67AA1128B80CE391C4FB71187654AAA3431027BFC7F395766CA988C964DC56D", 16)) + +def __main__(): + unittest.main() +if __name__ == "__main__": + __main__() diff --git a/dist/bin/esptool/ecdsa/util.py b/dist/bin/esptool/ecdsa/util.py new file mode 100644 index 000000000..c51ed3dd8 --- /dev/null +++ b/dist/bin/esptool/ecdsa/util.py @@ -0,0 +1,247 @@ +from __future__ import division + +import os +import math +import binascii +from hashlib import sha256 +from . import der +from .curves import orderlen +from .six import PY3, int2byte, b, next + +# RFC5480: +# The "unrestricted" algorithm identifier is: +# id-ecPublicKey OBJECT IDENTIFIER ::= { +# iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 } + +oid_ecPublicKey = (1, 2, 840, 10045, 2, 1) +encoded_oid_ecPublicKey = der.encode_oid(*oid_ecPublicKey) + +def randrange(order, entropy=None): + """Return a random integer k such that 1 <= k < order, uniformly + distributed across that range. For simplicity, this only behaves well if + 'order' is fairly close (but below) a power of 256. The try-try-again + algorithm we use takes longer and longer time (on average) to complete as + 'order' falls, rising to a maximum of avg=512 loops for the worst-case + (256**k)+1 . All of the standard curves behave well. There is a cutoff at + 10k loops (which raises RuntimeError) to prevent an infinite loop when + something is really broken like the entropy function not working. + + Note that this function is not declared to be forwards-compatible: we may + change the behavior in future releases. The entropy= argument (which + should get a callable that behaves like os.urandom) can be used to + achieve stability within a given release (for repeatable unit tests), but + should not be used as a long-term-compatible key generation algorithm. + """ + # we could handle arbitrary orders (even 256**k+1) better if we created + # candidates bit-wise instead of byte-wise, which would reduce the + # worst-case behavior to avg=2 loops, but that would be more complex. The + # change would be to round the order up to a power of 256, subtract one + # (to get 0xffff..), use that to get a byte-long mask for the top byte, + # generate the len-1 entropy bytes, generate one extra byte and mask off + # the top bits, then combine it with the rest. Requires jumping back and + # forth between strings and integers a lot. + + if entropy is None: + entropy = os.urandom + assert order > 1 + bytes = orderlen(order) + dont_try_forever = 10000 # gives about 2**-60 failures for worst case + while dont_try_forever > 0: + dont_try_forever -= 1 + candidate = string_to_number(entropy(bytes)) + 1 + if 1 <= candidate < order: + return candidate + continue + raise RuntimeError("randrange() tried hard but gave up, either something" + " is very wrong or you got realllly unlucky. Order was" + " %x" % order) + +class PRNG: + # this returns a callable which, when invoked with an integer N, will + # return N pseudorandom bytes. Note: this is a short-term PRNG, meant + # primarily for the needs of randrange_from_seed__trytryagain(), which + # only needs to run it a few times per seed. It does not provide + # protection against state compromise (forward security). + def __init__(self, seed): + self.generator = self.block_generator(seed) + + def __call__(self, numbytes): + a = [next(self.generator) for i in range(numbytes)] + + if PY3: + return bytes(a) + else: + return "".join(a) + + + def block_generator(self, seed): + counter = 0 + while True: + for byte in sha256(("prng-%d-%s" % (counter, seed)).encode()).digest(): + yield byte + counter += 1 + +def randrange_from_seed__overshoot_modulo(seed, order): + # hash the data, then turn the digest into a number in [1,order). + # + # We use David-Sarah Hopwood's suggestion: turn it into a number that's + # sufficiently larger than the group order, then modulo it down to fit. + # This should give adequate (but not perfect) uniformity, and simple + # code. There are other choices: try-try-again is the main one. + base = PRNG(seed)(2*orderlen(order)) + number = (int(binascii.hexlify(base), 16) % (order-1)) + 1 + assert 1 <= number < order, (1, number, order) + return number + +def lsb_of_ones(numbits): + return (1 << numbits) - 1 +def bits_and_bytes(order): + bits = int(math.log(order-1, 2)+1) + bytes = bits // 8 + extrabits = bits % 8 + return bits, bytes, extrabits + +# the following randrange_from_seed__METHOD() functions take an +# arbitrarily-sized secret seed and turn it into a number that obeys the same +# range limits as randrange() above. They are meant for deriving consistent +# signing keys from a secret rather than generating them randomly, for +# example a protocol in which three signing keys are derived from a master +# secret. You should use a uniformly-distributed unguessable seed with about +# curve.baselen bytes of entropy. To use one, do this: +# seed = os.urandom(curve.baselen) # or other starting point +# secexp = ecdsa.util.randrange_from_seed__trytryagain(sed, curve.order) +# sk = SigningKey.from_secret_exponent(secexp, curve) + +def randrange_from_seed__truncate_bytes(seed, order, hashmod=sha256): + # hash the seed, then turn the digest into a number in [1,order), but + # don't worry about trying to uniformly fill the range. This will lose, + # on average, four bits of entropy. + bits, bytes, extrabits = bits_and_bytes(order) + if extrabits: + bytes += 1 + base = hashmod(seed).digest()[:bytes] + base = "\x00"*(bytes-len(base)) + base + number = 1+int(binascii.hexlify(base), 16) + assert 1 <= number < order + return number + +def randrange_from_seed__truncate_bits(seed, order, hashmod=sha256): + # like string_to_randrange_truncate_bytes, but only lose an average of + # half a bit + bits = int(math.log(order-1, 2)+1) + maxbytes = (bits+7) // 8 + base = hashmod(seed).digest()[:maxbytes] + base = "\x00"*(maxbytes-len(base)) + base + topbits = 8*maxbytes - bits + if topbits: + base = int2byte(ord(base[0]) & lsb_of_ones(topbits)) + base[1:] + number = 1+int(binascii.hexlify(base), 16) + assert 1 <= number < order + return number + +def randrange_from_seed__trytryagain(seed, order): + # figure out exactly how many bits we need (rounded up to the nearest + # bit), so we can reduce the chance of looping to less than 0.5 . This is + # specified to feed from a byte-oriented PRNG, and discards the + # high-order bits of the first byte as necessary to get the right number + # of bits. The average number of loops will range from 1.0 (when + # order=2**k-1) to 2.0 (when order=2**k+1). + assert order > 1 + bits, bytes, extrabits = bits_and_bytes(order) + generate = PRNG(seed) + while True: + extrabyte = b("") + if extrabits: + extrabyte = int2byte(ord(generate(1)) & lsb_of_ones(extrabits)) + guess = string_to_number(extrabyte + generate(bytes)) + 1 + if 1 <= guess < order: + return guess + + +def number_to_string(num, order): + l = orderlen(order) + fmt_str = "%0" + str(2*l) + "x" + string = binascii.unhexlify((fmt_str % num).encode()) + assert len(string) == l, (len(string), l) + return string + +def number_to_string_crop(num, order): + l = orderlen(order) + fmt_str = "%0" + str(2*l) + "x" + string = binascii.unhexlify((fmt_str % num).encode()) + return string[:l] + +def string_to_number(string): + return int(binascii.hexlify(string), 16) + +def string_to_number_fixedlen(string, order): + l = orderlen(order) + assert len(string) == l, (len(string), l) + return int(binascii.hexlify(string), 16) + +# these methods are useful for the sigencode= argument to SK.sign() and the +# sigdecode= argument to VK.verify(), and control how the signature is packed +# or unpacked. + +def sigencode_strings(r, s, order): + r_str = number_to_string(r, order) + s_str = number_to_string(s, order) + return (r_str, s_str) + +def sigencode_string(r, s, order): + # for any given curve, the size of the signature numbers is + # fixed, so just use simple concatenation + r_str, s_str = sigencode_strings(r, s, order) + return r_str + s_str + +def sigencode_der(r, s, order): + return der.encode_sequence(der.encode_integer(r), der.encode_integer(s)) + +# canonical versions of sigencode methods +# these enforce low S values, by negating the value (modulo the order) if above order/2 +# see CECKey::Sign() https://github.com/bitcoin/bitcoin/blob/master/src/key.cpp#L214 +def sigencode_strings_canonize(r, s, order): + if s > order / 2: + s = order - s + return sigencode_strings(r, s, order) + +def sigencode_string_canonize(r, s, order): + if s > order / 2: + s = order - s + return sigencode_string(r, s, order) + +def sigencode_der_canonize(r, s, order): + if s > order / 2: + s = order - s + return sigencode_der(r, s, order) + + +def sigdecode_string(signature, order): + l = orderlen(order) + assert len(signature) == 2*l, (len(signature), 2*l) + r = string_to_number_fixedlen(signature[:l], order) + s = string_to_number_fixedlen(signature[l:], order) + return r, s + +def sigdecode_strings(rs_strings, order): + (r_str, s_str) = rs_strings + l = orderlen(order) + assert len(r_str) == l, (len(r_str), l) + assert len(s_str) == l, (len(s_str), l) + r = string_to_number_fixedlen(r_str, order) + s = string_to_number_fixedlen(s_str, order) + return r, s + +def sigdecode_der(sig_der, order): + #return der.encode_sequence(der.encode_integer(r), der.encode_integer(s)) + rs_strings, empty = der.remove_sequence(sig_der) + if empty != b(""): + raise der.UnexpectedDER("trailing junk after DER sig: %s" % + binascii.hexlify(empty)) + r, rest = der.remove_integer(rs_strings) + s, empty = der.remove_integer(rest) + if empty != b(""): + raise der.UnexpectedDER("trailing junk after DER numbers: %s" % + binascii.hexlify(empty)) + return r, s + diff --git a/dist/bin/esptool/espefuse.py b/dist/bin/esptool/espefuse.py new file mode 100644 index 000000000..81d679692 --- /dev/null +++ b/dist/bin/esptool/espefuse.py @@ -0,0 +1,974 @@ +#!/usr/bin/env python +# ESP32 efuse get/set utility +# https://github.com/themadinventor/esptool +# +# Copyright (C) 2016 Espressif Systems (Shanghai) PTE LTD +# +# 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 esptool +import io +import json +import os +import struct +import sys +import time + +# Table of efuse values - (category, block, word in block, mask, write disable bit, read disable bit, type, description) +# Match values in efuse_reg.h & Efuse technical reference chapter +EFUSES = [ + ('WR_DIS', "efuse", 0, 0, 0x0000FFFF, 1, None, "int", "Efuse write disable mask"), + ('RD_DIS', "efuse", 0, 0, 0x000F0000, 0, None, "int", "Efuse read disablemask"), + ('FLASH_CRYPT_CNT', "security", 0, 0, 0x07F00000, 2, None, "bitcount", "Flash encryption mode counter"), + ('MAC', "identity", 0, 1, 0xFFFFFFFF, 3, None, "mac", "Factory MAC Address"), + ('XPD_SDIO_FORCE', "config", 0, 4, 1 << 16, 5, None, "flag", "Ignore MTDI pin (GPIO12) for VDD_SDIO on reset"), + ('XPD_SDIO_REG', "config", 0, 4, 1 << 14, 5, None, "flag", "If XPD_SDIO_FORCE, enable VDD_SDIO reg on reset"), + ('XPD_SDIO_TIEH', "config", 0, 4, 1 << 15, 5, None, "flag", "If XPD_SDIO_FORCE & XPD_SDIO_REG, 1=3.3V 0=1.8V"), + ('CLK8M_FREQ', "config", 0, 4, 0xFF, None, None, "int", "8MHz clock freq override"), + ('SPI_PAD_CONFIG_CLK', "config", 0, 5, 0x1F << 0, 6, None, "spipin", "Override SD_CLK pad (GPIO6/SPICLK)"), + ('SPI_PAD_CONFIG_Q', "config", 0, 5, 0x1F << 5, 6, None, "spipin", "Override SD_DATA_0 pad (GPIO7/SPIQ)"), + ('SPI_PAD_CONFIG_D', "config", 0, 5, 0x1F << 10, 6, None, "spipin", "Override SD_DATA_1 pad (GPIO8/SPID)"), + ('SPI_PAD_CONFIG_HD', "config", 0, 3, 0x1F << 4, 6, None, "spipin", "Override SD_DATA_2 pad (GPIO9/SPIHD)"), + ('SPI_PAD_CONFIG_CS0', "config", 0, 5, 0x1F << 15, 6, None, "spipin", "Override SD_CMD pad (GPIO11/SPICS0)"), + ('FLASH_CRYPT_CONFIG', "security", 0, 5, 0x0F << 28, 10, 3, "int", "Flash encryption config (key tweak bits)"), + ('CHIP_VER_REV1', "identity", 0, 3, 1 << 15, 3, None, "flag", "Silicon Revision 1"), + ('CHIP_VER_REV2', "identity", 0, 5, 1 << 20, 6, None, "flag", "Silicon Revision 2"), + ('BLK3_PART_RESERVE', "calibration", 0, 3, 1 << 14, 10, 3, "flag", "BLOCK3 partially served for ADC calibration data"), + ('CHIP_VERSION', "identity", 0, 3, 0x03 << 12, 3, None, "int", "Reserved for future chip versions"), + ('CHIP_PACKAGE', "identity", 0, 3, 0x07 << 9, 3, None, "int", "Chip package identifier"), + ('CODING_SCHEME', "efuse", 0, 6, 0x3, 10, 3, "int", "Efuse variable block length scheme"), + ('CONSOLE_DEBUG_DISABLE',"security", 0, 6, 1 << 2, 15, None, "flag", "Disable ROM BASIC interpreter fallback"), + ('DISABLE_SDIO_HOST', "config", 0, 6, 1 << 3, None, None, "flag", "Disable SDIO host"), + ('ABS_DONE_0', "security", 0, 6, 1 << 4, 12, None, "flag", "secure boot enabled for bootloader"), + ('ABS_DONE_1', "security", 0, 6, 1 << 5, 13, None, "flag", "secure boot abstract 1 locked"), + ('JTAG_DISABLE', "security", 0, 6, 1 << 6, 14, None, "flag", "Disable JTAG"), + ('DISABLE_DL_ENCRYPT', "security", 0, 6, 1 << 7, 15, None, "flag", "Disable flash encryption in UART bootloader"), + ('DISABLE_DL_DECRYPT', "security", 0, 6, 1 << 8, 15, None, "flag", "Disable flash decryption in UART bootloader"), + ('DISABLE_DL_CACHE', "security", 0, 6, 1 << 9, 15, None, "flag", "Disable flash cache in UART bootloader"), + ('KEY_STATUS', "efuse", 0, 6, 1 << 10, 10, 3, "flag", "Usage of efuse block 3 (reserved)"), + ('ADC_VREF', "calibration", 0, 4,0x1F << 8,0, None, "vref", "Voltage reference calibration"), + ('BLK1', "security", 1, 0, 0xFFFFFFFF, 7, 0, "keyblock", "Flash encryption key"), + ('BLK2', "security", 2, 0, 0xFFFFFFFF, 8, 1, "keyblock", "Secure boot key"), + ('BLK3', "security", 3, 0, 0xFFFFFFFF, 9, 2, "keyblock", "Variable Block 3"), +] + +# if BLK3_PART_RESERVE is set, these efuse fields are in BLK3: +BLK3_PART_EFUSES = [ + ('ADC1_TP_LOW', "calibration", 3, 3, 0x7F << 0, 9, 2, "adc_tp", "ADC1 150mV reading"), + ('ADC1_TP_HIGH', "calibration", 3, 3, 0x1FF << 7, 9, 2, "adc_tp", "ADC1 850mV reading"), + ('ADC2_TP_LOW', "calibration", 3, 3, 0x7F << 16, 9, 2, "adc_tp", "ADC2 150mV reading"), + ('ADC2_TP_HIGH', "calibration", 3, 3, 0x1FF << 23, 9, 2, "adc_tp", "ADC2 850mV reading"), +] + +# Offsets and lengths of each of the 4 efuse blocks in register space +# +# These offsets/lens are for esptool.read_efuse(X) which takes +# a word offset (into registers) not a byte offset. +EFUSE_BLOCK_OFFS = [0, 14, 22, 30] +EFUSE_BLOCK_LEN = [7, 8, 8, 8] + +# EFUSE registers & command/conf values +EFUSE_REG_CONF = 0x3FF5A0FC +EFUSE_CONF_WRITE = 0x5A5A +EFUSE_CONF_READ = 0x5AA5 +EFUSE_REG_CMD = 0x3FF5A104 +EFUSE_CMD_WRITE = 0x2 +EFUSE_CMD_READ = 0x1 +# address of first word of write registers for each efuse +EFUSE_REG_WRITE = [0x3FF5A01C, 0x3FF5A098, 0x3FF5A0B8, 0x3FF5A0D8] +# 3/4 Coding scheme warnings registers +EFUSE_REG_DEC_STATUS = 0x3FF5A11C +EFUSE_REG_DEC_STATUS_MASK = 0xFFF + +# Efuse clock control +EFUSE_DAC_CONF_REG = 0x3FF5A118 +EFUSE_CLK_REG = 0x3FF5A0F8 + +EFUSE_DAC_CLK_DIV_MASK = 0xFF +EFUSE_CLK_SEL0_MASK = 0x00FF +EFUSE_CLK_SEL1_MASK = 0xFF00 + +EFUSE_CLK_SETTINGS = { + # APB freq: clk_sel0, clk_sel1, dac_clk_div + # Taken from TRM chapter "eFuse Controller": Timing Configuration + 26: (250, 255, 52), + 40: (160, 255, 80), + 80: (80, 128, 100), # this is here for completeness only as esptool never sets an 80MHz APB clock +} + +EFUSE_BURN_TIMEOUT = 0.250 # seconds + + +# Coding Scheme values +CODING_SCHEME_NONE = 0 +CODING_SCHEME_34 = 1 + + +def confirm(action, args): + print("%s%sThis is an irreversible operation." % (action, "" if action.endswith("\n") else ". ")) + if not args.do_not_confirm: + print("Type 'BURN' (all capitals) to continue.") + sys.stdout.flush() # required for Pythons which disable line buffering, ie mingw in mintty + try: + yes = raw_input() # raw_input renamed to input in Python 3 + except NameError: + yes = input() + if yes != "BURN": + print("Aborting.") + sys.exit(0) + + +def efuse_write_reg_addr(block, word): + """ + Return the physical address of the efuse write data register + block X word X. + """ + return EFUSE_REG_WRITE[block] + (4 * word) + + +class EspEfuses(object): + """ + Wrapper object to manage the efuse fields in a connected ESP bootloader + """ + def __init__(self, esp): + self._esp = esp + self._efuses = [EfuseField.from_tuple(self, efuse) for efuse in EFUSES] + if self["BLK3_PART_RESERVE"].get(): + # add these BLK3 efuses, if the BLK3_PART_RESERVE flag is set... + self._efuses += [EfuseField.from_tuple(self, efuse) for efuse in BLK3_PART_EFUSES] + + self.coding_scheme = self["CODING_SCHEME"].get() + + def __getitem__(self, efuse_name): + """ Return the efuse field with the given name """ + for e in self._efuses: + if efuse_name == e.register_name: + return e + raise KeyError + + def __iter__(self): + return self._efuses.__iter__() + + def write_efuses(self): + """ Write the values in the efuse write registers to + the efuse hardware, then refresh the efuse read registers. + """ + + # Configure clock + apb_freq = self._esp.get_crystal_freq() + clk_sel0, clk_sel1, dac_clk_div = EFUSE_CLK_SETTINGS[apb_freq] + + self.update_reg(EFUSE_DAC_CONF_REG, EFUSE_DAC_CLK_DIV_MASK, dac_clk_div) + self.update_reg(EFUSE_CLK_REG, EFUSE_CLK_SEL0_MASK, clk_sel0) + self.update_reg(EFUSE_CLK_REG, EFUSE_CLK_SEL1_MASK, clk_sel1) + + self.write_reg(EFUSE_REG_CONF, EFUSE_CONF_WRITE) + self.write_reg(EFUSE_REG_CMD, EFUSE_CMD_WRITE) + + def wait_idle(): + deadline = time.time() + EFUSE_BURN_TIMEOUT + while time.time() < deadline: + if self._esp.read_reg(EFUSE_REG_CMD) == 0: + return + raise esptool.FatalError("Timed out waiting for Efuse controller command to complete") + wait_idle() + self.write_reg(EFUSE_REG_CONF, EFUSE_CONF_READ) + self.write_reg(EFUSE_REG_CMD, EFUSE_CMD_READ) + wait_idle() + + def read_efuse(self, addr): + return self._esp.read_efuse(addr) + + def read_reg(self, addr): + return self._esp.read_reg(addr) + + def write_reg(self, addr, value): + return self._esp.write_reg(addr, value) + + def update_reg(self, addr, mask, new_val): + return self._esp.update_reg(addr, mask, new_val) + + def get_coding_scheme_warnings(self): + """ Check if the coding scheme has detected any errors. + Meaningless for default coding scheme (0) + """ + return self.read_reg(EFUSE_REG_DEC_STATUS) & EFUSE_REG_DEC_STATUS_MASK + + def get_block_len(self): + """ Return the length of BLK1, BLK2, BLK3 in bytes """ + return 24 if self.coding_scheme == CODING_SCHEME_34 else 32 + + +class EfuseField(object): + @staticmethod + def from_tuple(parent, efuse_tuple): + category = efuse_tuple[7] + return { + "mac": EfuseMacField, + "keyblock": EfuseKeyblockField, + "spipin": EfuseSpiPinField, + "vref": EfuseVRefField, + "adc_tp": EfuseAdcPointCalibration, + }.get(category, EfuseField)(parent, *efuse_tuple) + + def __init__(self, parent, register_name, category, block, word, mask, write_disable_bit, read_disable_bit, efuse_type, description): + self.category = category + self.parent = parent + self.block = block + self.word = word + self.data_reg_offs = EFUSE_BLOCK_OFFS[self.block] + self.word + self.mask = mask + self.shift = esptool._mask_to_shift(mask) + self.write_disable_bit = write_disable_bit + self.read_disable_bit = read_disable_bit + self.register_name = register_name + self.efuse_type = efuse_type + self.description = description + + def get_raw(self): + """ Return the raw (unformatted) numeric value of the efuse bits + + Returns a simple integer or (for some subclasses) a bitstring. + """ + value = self.parent.read_efuse(self.data_reg_offs) + return (value & self.mask) >> self.shift + + def get(self): + """ Get a formatted version of the efuse value, suitable for display """ + return self.get_raw() + + def is_readable(self): + """ Return true if the efuse is readable by software """ + if self.read_disable_bit is None: + return True # read cannot be disabled + value = (self.parent.read_efuse(0) >> 16) & 0xF # RD_DIS values + return (value & (1 << self.read_disable_bit)) == 0 + + def disable_read(self): + if self.read_disable_bit is None: + raise esptool.FatalError("This efuse cannot be read-disabled") + rddis_reg_addr = efuse_write_reg_addr(0, 0) + self.parent.write_reg(rddis_reg_addr, 1 << (16 + self.read_disable_bit)) + self.parent.write_efuses() + return self.get() + + def is_writeable(self): + if self.write_disable_bit is None: + return True # write cannot be disabled + value = self.parent.read_efuse(0) & 0xFFFF # WR_DIS values + return (value & (1 << self.write_disable_bit)) == 0 + + def disable_write(self): + wrdis_reg_addr = efuse_write_reg_addr(0, 0) + self.parent.write_reg(wrdis_reg_addr, 1 << self.write_disable_bit) + self.parent.write_efuses() + return self.get() + + def burn(self, new_value): + raw_value = (new_value << self.shift) & self.mask + # don't both reading old value as we can only set bits 0->1 + write_reg_addr = efuse_write_reg_addr(self.block, self.word) + self.parent.write_reg(write_reg_addr, raw_value) + self.parent.write_efuses() + return self.get() + + +class EfuseMacField(EfuseField): + def get_raw(self): + # MAC values are high half of second efuse word, then first efuse word + words = [self.parent.read_efuse(self.data_reg_offs + word) for word in [1,0]] + # endian-swap into a bitstring + bitstring = struct.pack(">II", *words) + return bitstring[2:] # trim 2 byte CRC from the beginning + + @staticmethod + def get_and_check(raw_mac, stored_crc): + computed_crc = EfuseMacField.calc_crc(raw_mac) + if computed_crc == stored_crc: + valid_msg = "(CRC 0x%02x OK)" % stored_crc + else: + valid_msg = "(CRC 0x%02x invalid - calculated 0x%02x)" % (stored_crc, computed_crc) + return "%s %s" % (hexify(raw_mac, ":"), valid_msg) + + def get(self): + stored_crc = self.get_stored_crc() + return EfuseMacField.get_and_check(self.get_raw(), stored_crc) + + def burn(self, new_value): + # Writing the BLK0 default MAC is not sensible, as it's written in the factory. + raise esptool.FatalError("Writing Factory MAC address is not supported") + + def get_stored_crc(self): + return (self.parent.read_efuse(self.data_reg_offs + 1) >> 16) & 0xFF + + @staticmethod + def calc_crc(raw_mac): + """ + This algorithm is the equivalent of esp_crc8() in ESP32 ROM code + + This is CRC-8 w/ inverted polynomial value 0x8C & initial value 0x00. + """ + result = 0x00 + for b in struct.unpack("B" * 6, raw_mac): + result ^= b + for _ in range(8): + lsb = result & 1 + result >>= 1 + if lsb != 0: + result ^= 0x8c + return result + + +class EfuseKeyblockField(EfuseField): + def get_raw(self): + words = self.get_words() + return struct.pack("<" + ("I" * len(words)), *words) + + def get_key(self): + # Keys are stored in reverse byte order + result = self.get_raw() + result = result[::-1] + return result + + def get_words(self): + num_words = self.parent.get_block_len() // 4 + return [self.parent.read_efuse(self.data_reg_offs + word) for word in range(num_words)] + + def get(self): + return hexify(self.get_raw(), " ") + + def apply_34_encoding(self, inbits): + """ Takes 24 byte sequence to be represented in 3/4 encoding, + returns 8 words suitable for writing "encoded" to an efuse block + """ + def popcnt(b): + """ Return number of "1" bits set in 'b' """ + return len([x for x in bin(b) if x == "1"]) + + outbits = b"" + while len(inbits) > 0: # process in chunks of 6 bytes + bits = inbits[0:6] + inbits = inbits[6:] + xor_res = 0 + mul_res = 0 + index = 1 + for b in struct.unpack("B" * 6, bits): + xor_res ^= b + mul_res += index * popcnt(b) + index += 1 + outbits += bits + outbits += struct.pack("BB", xor_res, mul_res) + return struct.unpack("<" + "I" * (len(outbits) // 4), outbits) + + def burn_key(self, new_value): + new_value = new_value[::-1] # AES keys are stored in reverse order in efuse + return self.burn(new_value) + + def burn(self, new_value): + key_len = self.parent.get_block_len() + if len(new_value) != key_len: + raise RuntimeError("Invalid new value length for key block (%d), %d is required" % len(new_value), key_len) + + if self.parent.coding_scheme == CODING_SCHEME_34: + words = self.apply_34_encoding(new_value) + else: + words = struct.unpack("<" + ("I" * 8), new_value) + return self.burn_words(words) + + def burn_words(self, words, word_offset=0): + write_reg_addr = efuse_write_reg_addr(self.block, self.word + word_offset) + for word in words: + self.parent.write_reg(write_reg_addr, word) + write_reg_addr += 4 + warnings_before = self.parent.get_coding_scheme_warnings() + self.parent.write_efuses() + warnings_after = self.parent.get_coding_scheme_warnings() + if warnings_after & ~warnings_before != 0: + print("WARNING: Burning efuse block added coding scheme warnings 0x%x -> 0x%x. Encoding bug?" % (warnings_before, warnings_after)) + return self.get() + + +class EfuseSpiPinField(EfuseField): + def get(self): + val = self.get_raw() + if val >= 30: + val += 2 # values 30,31 map to 32, 33 + return val + + def burn(self, new_value): + if new_value in [30, 31]: + raise esptool.FatalError("IO pins 30 & 31 cannot be set for SPI flash. 0-29, 32 & 33 only.") + if new_value > 33: + raise esptool.FatalError("IO pin %d cannot be set for SPI flash. 0-29, 32 & 33 only." % new_value) + if new_value > 30: + new_value -= 2 # values 32,33 map to 30, 31 + return super(EfuseSpiPinField, self).burn(new_value) + + +class EfuseVRefField(EfuseField): + VREF_OFFSET = 1100 # ideal efuse value in mV + VREF_STEP_SIZE = 7 # 1 count in efuse == 7mV + VREF_SIGN_BIT = 0x10 + VREF_MAG_BITS = 0x0F + + def get(self): + val = self.get_raw() + # sign-magnitude format + if (val & self.VREF_SIGN_BIT): + val = -(val & self.VREF_MAG_BITS) + else: + val = (val & self.VREF_MAG_BITS) + val *= self.VREF_STEP_SIZE + return self.VREF_OFFSET + val + + def burn(self, new_value): + raise RuntimeError("Writing to VRef is not supported.") + + +class EfuseAdcPointCalibration(EfuseField): + TP_OFFSET = { # See TP_xxxx_OFFSET in esp_adc_cal.c in ESP-IDF + "ADC1_TP_LOW": 278, + "ADC2_TP_LOW": 421, + "ADC1_TP_HIGH": 3265, + "ADC2_TP_HIGH": 3406, + } + SIGN_BIT = (0x40, 0x100) # LOW, HIGH (2s complement format) + STEP_SIZE = 4 + + def get(self): + idx = 0 if self.register_name.endswith("LOW") else 1 + sign_bit = self.SIGN_BIT[idx] + offset = self.TP_OFFSET[self.register_name] + raw = self.get_raw() + delta = (raw & (sign_bit - 1)) - (raw & sign_bit) + return offset + (delta * self.STEP_SIZE) + + +def dump(esp, _efuses, args): + """ Dump raw efuse data registers """ + for block in range(len(EFUSE_BLOCK_OFFS)): + print("EFUSE block %d:" % block) + offsets = [x + EFUSE_BLOCK_OFFS[block] for x in range(EFUSE_BLOCK_LEN[block])] + print(" ".join(["%08x" % esp.read_efuse(offs) for offs in offsets])) + + +def summary(esp, efuses, args): + """ Print a human-readable summary of efuse contents """ + ROW_FORMAT = "%-22s %-50s%s= %s %s %s" + human_output = (args.format == 'summary') + json_efuse = {} + if args.file != sys.stdout: + print("Saving efuse values to " + args.file.name) + if human_output: + print(ROW_FORMAT.replace("-50", "-12") % ("EFUSE_NAME", "Description", "", "[Meaningful Value]", "[Readable/Writeable]", "(Hex Value)"),file=args.file) + print("-" * 88,file=args.file) + for category in set(e.category for e in efuses): + if human_output: + print("%s fuses:" % category.title(),file=args.file) + for e in (e for e in efuses if e.category == category): + raw = e.get_raw() + try: + raw = "(0x%x)" % raw + except TypeError: + raw = "" + (readable, writeable) = (e.is_readable(), e.is_writeable()) + if readable and writeable: + perms = "R/W" + elif readable: + perms = "R/-" + elif writeable: + perms = "-/W" + else: + perms = "-/-" + base_value = e.get() + value = str(base_value) + if not readable: + value = value.replace("0", "?") + if human_output: + print(ROW_FORMAT % (e.register_name, e.description, "\n " if len(value) > 20 else "", value, perms, raw),file=args.file) + if args.format == 'json': + json_efuse[e.register_name] = { + 'value': base_value if readable else value, + 'readable':readable, + 'writeable':writeable} + if human_output: + print("",file=args.file) + if human_output: + sdio_force = efuses["XPD_SDIO_FORCE"] + sdio_tieh = efuses["XPD_SDIO_TIEH"] + sdio_reg = efuses["XPD_SDIO_REG"] + if sdio_force.get() == 0: + print("Flash voltage (VDD_SDIO) determined by GPIO12 on reset (High for 1.8V, Low/NC for 3.3V).",file=args.file) + elif sdio_reg.get() == 0: + print("Flash voltage (VDD_SDIO) internal regulator disabled by efuse.",file=args.file) + elif sdio_tieh.get() == 0: + print("Flash voltage (VDD_SDIO) set to 1.8V by efuse.",file=args.file) + else: + print("Flash voltage (VDD_SDIO) set to 3.3V by efuse.",file=args.file) + warnings = efuses.get_coding_scheme_warnings() + if warnings: + print("WARNING: Coding scheme has encoding bit error warnings (0x%x)" % warnings,file=args.file) + if args.file != sys.stdout: + args.file.close() + print("Done") + if args.format == 'json': + json.dump(json_efuse,args.file,sort_keys=True,indent=4) + print("") + + +def burn_efuse(esp, efuses, args): + efuse = efuses[args.efuse_name] + old_value = efuse.get() + if efuse.efuse_type == "flag": + if args.new_value not in [None, 1]: + raise esptool.FatalError("Efuse %s is type 'flag'. New value is not accepted for this efuse (will always burn 0->1)" % efuse.register_name) + args.new_value = 1 + if old_value: + print("Efuse %s is already burned." % efuse.register_name) + return + elif efuse.efuse_type == "int": + if args.new_value is None: + raise esptool.FatalError("New value required for efuse %s" % efuse.register_name) + elif efuse.efuse_type == "spipin": + if args.new_value is None or args.new_value == 0: + raise esptool.FatalError("New value required for efuse %s" % efuse.register_name) + elif efuse.efuse_type == "bitcount": + if args.new_value is None: # find the first unset bit and set it + args.new_value = old_value + bit = 1 + while args.new_value == old_value: + args.new_value = bit | old_value + bit <<= 1 + + if args.new_value & (efuse.mask >> efuse.shift) != args.new_value: + raise esptool.FatalError("Value mask for efuse %s is 0x%x. Value 0x%x is too large." % (efuse.register_name, efuse.mask >> efuse.shift, args.new_value)) + if args.new_value | old_value != args.new_value: + print("WARNING: New value contains some bits that cannot be cleared (value will be 0x%x)" % (old_value | args.new_value)) + + confirm("Burning efuse %s (%s) 0x%x -> 0x%x" % (efuse.register_name, efuse.description, old_value, args.new_value | old_value), args) + burned_value = efuse.burn(args.new_value) + if burned_value == old_value: + raise esptool.FatalError("Efuse %s failed to burn. Protected?" % efuse.register_name) + + +def read_protect_efuse(esp, efuses, args): + efuse = efuses[args.efuse_name] + if not efuse.is_readable(): + print("Efuse %s is already read protected" % efuse.register_name) + else: + # make full list of which efuses will be disabled (ie share a read disable bit) + all_disabling = [e for e in efuses if e.read_disable_bit == efuse.read_disable_bit] + names = ", ".join(e.register_name for e in all_disabling) + confirm("Permanently read-disabling efuse%s %s" % ("s" if len(all_disabling) > 1 else "",names), args) + efuse.disable_read() + + +def write_protect_efuse(esp, efuses, args): + efuse = efuses[args.efuse_name] + if not efuse.is_writeable(): + print("]fuse %s is already write protected" % efuse.register_name) + else: + # make full list of which efuses will be disabled (ie share a write disable bit) + all_disabling = [e for e in efuses if e.write_disable_bit == efuse.write_disable_bit] + names = ", ".join(e.register_name for e in all_disabling) + confirm("Permanently write-disabling efuse%s %s" % ("s" if len(all_disabling) > 1 else "",names), args) + efuse.disable_write() + + +def burn_key(esp, efuses, args): + # check block choice + if args.block in ["flash_encryption", "BLK1"]: + block_num = 1 + elif args.block in ["secure_boot", "BLK2"]: + block_num = 2 + elif args.block == "BLK3": + block_num = 3 + else: + raise RuntimeError("args.block argument not in list!") + + num_bytes = efuses.get_block_len() + + # check keyfile + keyfile = args.keyfile + keyfile.seek(0,2) # seek t oend + size = keyfile.tell() + keyfile.seek(0) + if size != num_bytes: + raise esptool.FatalError("Incorrect key file size %d. Key file must be %d bytes (%d bits) of raw binary key data." % + (size, num_bytes, num_bytes * 8)) + + # check existing data + efuse = [e for e in efuses if e.register_name == "BLK%d" % block_num][0] + original = efuse.get_raw() + EMPTY_KEY = b'\x00' * num_bytes + if original != EMPTY_KEY: + if not args.force_write_always: + raise esptool.FatalError("Key block already has value %s." % efuse.get()) + else: + print("WARNING: Key appears to have a value already. Trying anyhow, due to --force-write-always (result will be bitwise OR of new and old values.)") + if not efuse.is_writeable(): + if not args.force_write_always: + raise esptool.FatalError("The efuse block has already been write protected.") + else: + print("WARNING: Key appears to be write protected. Trying anyhow, due to --force-write-always") + msg = "Write key in efuse block %d. " % block_num + if args.no_protect_key: + msg += "The key block will left readable and writeable (due to --no-protect-key)" + else: + msg += "The key block will be read and write protected (no further changes or readback)" + confirm(msg, args) + + new_value = keyfile.read(num_bytes) + new = efuse.burn_key(new_value) + print("Burned key data. New value: %s" % (new,)) + if not args.no_protect_key: + print("Disabling read/write to key efuse block...") + efuse.disable_write() + efuse.disable_read() + if efuse.is_readable(): + print("WARNING: Key does not appear to have been read protected. Perhaps read disable efuse is write protected?") + if efuse.is_writeable(): + print("WARNING: Key does not appear to have been write protected. Perhaps write disable efuse is write protected?") + else: + print("Key is left unprotected as per --no-protect-key argument.") + + +def burn_block_data(esp, efuses, args): + num_bytes = efuses.get_block_len() + offset = args.offset + data = args.datafile.read() + + if offset >= num_bytes: + raise RuntimeError("Invalid offset: Key block only holds %d bytes." % num_bytes) + if len(data) > num_bytes - offset: + raise RuntimeError("Data will not fit: Key block size %d bytes, data file is %d bytes" % (num_bytes, len(data))) + if efuses.coding_scheme == CODING_SCHEME_34: + if offset % 6 != 0: + raise RuntimeError("Device has 3/4 Coding Scheme. Can only write at offsets which are a multiple of 6.") + if len(data) % 6 != 0: + raise RuntimeError("Device has 3/4 Coding Scheme. Can only write data lengths which are a multiple of 6 (data is %d bytes)" % len(data)) + + efuse = [e for e in efuses if e.register_name == args.block.upper()][0] + + if not args.force_write_always and \ + efuse.get_raw() != b'\x00' * num_bytes: + raise esptool.FatalError("Efuse block already has values written.") + + if efuses.coding_scheme == CODING_SCHEME_NONE: + pad = offset % 4 + if pad != 0: # left-pad to a word boundary + data = (b'\x00' * pad) + data + offset -= pad + pad = len(data) % 4 + if pad != 0: # right-pad to a word boundary + data += (b'\x00' * (4 - pad)) + words = struct.unpack("<" + "I" * (len(data) // 4), data) + word_offset = offset // 4 + else: # CODING_SCHEME_34 + words = efuse.apply_34_encoding(data) + word_offset = (offset // 6) * 2 + + confirm("Burning efuse %s (%s) with %d bytes of data at offset %d in the block" % (efuse.register_name, efuse.description, len(data), offset), args) + efuse.burn_words(words, word_offset) + + +def set_flash_voltage(esp, efuses, args): + sdio_force = efuses["XPD_SDIO_FORCE"] + sdio_tieh = efuses["XPD_SDIO_TIEH"] + sdio_reg = efuses["XPD_SDIO_REG"] + + # check efuses aren't burned in a way which makes this impossible + if args.voltage == 'OFF' and sdio_reg.get() != 0: + raise esptool.FatalError("Can't set flash regulator to OFF as XPD_SDIO_REG efuse is already burned") + + if args.voltage == '1.8V' and sdio_tieh.get() != 0: + raise esptool.FatalError("Can't set regulator to 1.8V is XPD_SDIO_TIEH efuse is already burned") + + if args.voltage == 'OFF': + msg = """ +Disable internal flash voltage regulator (VDD_SDIO). SPI flash will need to be powered from an external source. +The following efuse is burned: XPD_SDIO_FORCE. +It is possible to later re-enable the internal regulator (%s) by burning an additional efuse +""" % ("to 3.3V" if sdio_tieh.get() != 0 else "to 1.8V or 3.3V") + elif args.voltage == '1.8V': + msg = """ +Set internal flash voltage regulator (VDD_SDIO) to 1.8V. +The following efuses are burned: XPD_SDIO_FORCE, XPD_SDIO_REG. +It is possible to later increase the voltage to 3.3V (permanently) by burning additional efuse XPD_SDIO_TIEH +""" + elif args.voltage == '3.3V': + msg = """ +Enable internal flash voltage regulator (VDD_SDIO) to 3.3V. +The following efuses are burned: XPD_SDIO_FORCE, XPD_SDIO_REG, XPD_SDIO_TIEH. +""" + + confirm(msg, args) + + sdio_force.burn(1) # Disable GPIO12 + if args.voltage != 'OFF': + sdio_reg.burn(1) # Enable internal regulator + if args.voltage == '3.3V': + sdio_tieh.burn(1) + print("VDD_SDIO setting complete.") + + +def adc_info(esp, efuses, args): + adc_vref = efuses["ADC_VREF"] + blk3_reserve = efuses["BLK3_PART_RESERVE"] + + vref_raw = adc_vref.get_raw() + if vref_raw == 0: + print("ADC VRef calibration: None (1100mV nominal)") + else: + print("ADC VRef calibration: %dmV" % adc_vref.get()) + + if blk3_reserve.get(): + print("ADC readings stored in efuse BLK3:") + print(" ADC1 Low reading (150mV): %d" % efuses["ADC1_TP_LOW"].get()) + print(" ADC1 High reading (850mV): %d" % efuses["ADC1_TP_HIGH"].get()) + print(" ADC2 Low reading (150mV): %d" % efuses["ADC2_TP_LOW"].get()) + print(" ADC2 High reading (850mV): %d" % efuses["ADC2_TP_HIGH"].get()) + + +class CustomMacAddressField(object): + """ + The custom MAC field uses the formatting according to the specification for version 1 + """ + def __init__(self, efuses): + self.efuse = [e for e in efuses if e.register_name == 'BLK3'][0] + self.parent = self.efuse.parent + + def get_raw(self): + words = [self.parent.read_efuse(self.efuse.data_reg_offs + word) for word in [0, 1]] + bitstring = struct.pack("> 24) & 0xFF + + def get_block(self, new_mac, new_version): + """ + Returns a byte array which can be written directly to BLK3 + """ + num_words = self.parent.get_block_len() // 4 + words = [self.parent.read_efuse(self.efuse.data_reg_offs + word) for word in range(num_words)] + B = sum([x << (i * 32) for i, x in enumerate(words)]) # integer representation of the whole BLK content + + new_mac_b = struct.pack(">Q", new_mac)[2:] # Q has 8-bytes. Removing two MSB bytes to get a 6-byte MAC + new_mac_rev = struct.unpack("> i * 8) & 0xFF for i in range(self.parent.get_block_len())]) + + +def burn_custom_mac(esp, efuses, args): + write_always = args.force_write_always + c = CustomMacAddressField(efuses) + old_version = c.get_version() + new_version = old_version | 1 # Only version 1 MAC Addresses are supported yet + if (not write_always and old_version != 0) or (write_always and old_version not in [0, new_version]): + raise esptool.FatalError("The version of the custom MAC Address is already burned ({})!".format(old_version)) + old_mac_b = c.get_raw() + old_mac = struct.unpack(">Q", b'\x00\x00' + old_mac_b)[0] + new_mac_b = struct.pack(">Q", args.mac)[2:] # Q has 8-bytes. Removing two MSB bytes to get a 6-byte MAC + new_mac = args.mac + if (not write_always and old_mac != 0) or (write_always and new_mac | old_mac != new_mac): + raise esptool.FatalError("Custom MAC Address was previously burned ({})!".format(hexify(old_mac_b, ":"))) + old_crc = c.get_stored_crc() + new_crc = c.calc_crc(new_mac_b) + if (not write_always and old_crc != 0) or (write_always and new_crc | old_crc != new_crc): + raise esptool.FatalError("The CRC of the custom MAC Address was previously burned ({})!".format(old_crc)) + confirm("Burning efuse for custom MAC address {} (version {}, CRC 0x{:x}) -> {} (version {}, CRC 0x{:x})" + "".format(hexify(old_mac_b, ":"), old_version, old_crc, hexify(new_mac_b, ":"), new_version, new_crc), args) + with io.BytesIO(c.get_block(new_mac, new_version)) as buf: + args.do_not_confirm = True # Custom MAC burning was already confirmed. No need to ask twice. + # behavour of burn_block_data() for args.force_write_always is compatible + args.offset = 0 + args.datafile = buf + args.block = 'BLK3' + burn_block_data(esp, efuses, args) + + +def get_custom_mac(esp, efuses, args): + c = CustomMacAddressField(efuses) + version = c.get_version() + + if version > 0: + print("Custom MAC Address version {}: {}".format(version, c.get())) + else: + print("Custom MAC Address is not set in the device.") + + +def hexify(bitstring, separator=""): + try: + as_bytes = tuple(ord(b) for b in bitstring) + except TypeError: # python 3, items in bitstring already ints + as_bytes = tuple(b for b in bitstring) + return separator.join(("%02x" % b) for b in as_bytes) + + +def arg_auto_int(x): + return int(x, 0) + + +def mac_int(string): + if string.count(":") != 5: + raise argparse.ArgumentTypeError("MAC Address needs to be a 6-byte hexadecimal format separated by colons (:)!") + hexad = string.replace(":", "") + if len(hexad) != 12: + raise argparse.ArgumentTypeError("MAC Address needs to be a 6-byte hexadecimal number (12 hexadecimal characters)!") + return int(hexad, 16) + + +def main(): + parser = argparse.ArgumentParser(description='espefuse.py v%s - ESP32 efuse get/set tool' % esptool.__version__, prog='espefuse') + + parser.add_argument( + '--baud', '-b', + help='Serial port baud rate used when flashing/reading', + type=arg_auto_int, + default=os.environ.get('ESPTOOL_BAUD', esptool.ESPLoader.ESP_ROM_BAUD)) + + parser.add_argument( + '--port', '-p', + help='Serial port device', + default=os.environ.get('ESPTOOL_PORT', esptool.ESPLoader.DEFAULT_PORT)) + + parser.add_argument( + '--before', + help='What to do before connecting to the chip', + choices=['default_reset', 'no_reset', 'esp32r1', 'no_reset_no_sync'], + default='default_reset') + + parser.add_argument('--do-not-confirm', + help='Do not pause for confirmation before permanently writing efuses. Use with caution.', action='store_true') + + def add_force_write_always(p): + p.add_argument('--force-write-always', help="Write the efuse even if it looks like it's already been written, or is write protected. " + + "Note that this option can't disable write protection, or clear any bit which has already been set.", action='store_true') + + subparsers = parser.add_subparsers( + dest='operation', + help='Run espefuse.py {command} -h for additional help') + + subparsers.add_parser('dump', help='Dump raw hex values of all efuses') + p = subparsers.add_parser('summary', + help='Print human-readable summary of efuse values') + p.add_argument('--format', help='Select the summary format',choices=['summary','json'],default='summary') + p.add_argument('--file', help='File to save the efuse summary',type=argparse.FileType('w'),default=sys.stdout) + + p = subparsers.add_parser('burn_efuse', + help='Burn the efuse with the specified name') + p.add_argument('efuse_name', help='Name of efuse register to burn', + choices=[efuse[0] for efuse in EFUSES]) + p.add_argument('new_value', help='New value to burn (not needed for flag-type efuses', nargs='?', type=esptool.arg_auto_int) + + p = subparsers.add_parser('read_protect_efuse', + help='Disable readback for the efuse with the specified name') + p.add_argument('efuse_name', help='Name of efuse register to burn', + choices=[efuse[0] for efuse in EFUSES if efuse[6] is not None]) # only allow if read_disable_bit is not None + + p = subparsers.add_parser('write_protect_efuse', + help='Disable writing to the efuse with the specified name') + p.add_argument('efuse_name', help='Name of efuse register to burn', + choices=[efuse[0] for efuse in EFUSES]) + + p = subparsers.add_parser('burn_key', + help='Burn a 256-bit AES key to EFUSE BLK1,BLK2 or BLK3 (flash_encryption, secure_boot).') + p.add_argument('--no-protect-key', help='Disable default read- and write-protecting of the key. ' + + 'If this option is not set, once the key is flashed it cannot be read back or changed.', action='store_true') + add_force_write_always(p) + p.add_argument('block', help='Key block to burn. "flash_encryption" is an alias for BLK1, ' + + '"secure_boot" is an alias for BLK2.', choices=["secure_boot", "flash_encryption","BLK1","BLK2","BLK3"]) + p.add_argument('keyfile', help='File containing 256 bits of binary key data', type=argparse.FileType('rb')) + + p = subparsers.add_parser('burn_block_data', + help="Burn non-key data to EFUSE BLK1, BLK2 or BLK3. " + + " Don't use this command to burn key data for Flash Encryption or Secure Boot, " + + "as the byte order of keys is swapped (use burn_key).") + p.add_argument('--offset', '-o', help='Byte offset in the efuse block', type=int, default=0) + add_force_write_always(p) + p.add_argument('block', help='Efuse block to burn.', choices=["BLK1","BLK2","BLK3"]) + p.add_argument('datafile', help='File containing data to burn into the efuse block', type=argparse.FileType('rb')) + + p = subparsers.add_parser('set_flash_voltage', + help='Permanently set the internal flash voltage regulator to either 1.8V, 3.3V or OFF. ' + + 'This means GPIO12 can be high or low at reset without changing the flash voltage.') + p.add_argument('voltage', help='Voltage selection', + choices=['1.8V', '3.3V', 'OFF']) + + p = subparsers.add_parser('adc_info', + help='Display information about ADC calibration data stored in efuse.') + + p = subparsers.add_parser('burn_custom_mac', + help='Burn a 48-bit Custom MAC Address to EFUSE BLK3.') + p.add_argument('mac', help='Custom MAC Address to burn given in hexadecimal format with bytes separated by colons' + + ' (e.g. AB:CD:EF:01:02:03).', type=mac_int) + add_force_write_always(p) + + p = subparsers.add_parser('get_custom_mac', + help='Prints the Custom MAC Address.') + + args = parser.parse_args() + print('espefuse.py v%s' % esptool.__version__) + if args.operation is None: + parser.print_help() + parser.exit(1) + + # each 'operation' is a module-level function of the same name + operation_func = globals()[args.operation] + + esp = esptool.ESP32ROM(args.port, baud=args.baud) + esp.connect(args.before) + + # dict mapping register name to its efuse object + efuses = EspEfuses(esp) + operation_func(esp, efuses, args) + + +def _main(): + try: + main() + except esptool.FatalError as e: + print('\nA fatal error occurred: %s' % e) + sys.exit(2) + + +if __name__ == '__main__': + _main() diff --git a/dist/bin/esptool/espsecure.py b/dist/bin/esptool/espsecure.py new file mode 100644 index 000000000..553d2901b --- /dev/null +++ b/dist/bin/esptool/espsecure.py @@ -0,0 +1,484 @@ +#!/usr/bin/env python +# ESP32 secure boot utility +# https://github.com/themadinventor/esptool +# +# Copyright (C) 2016 Espressif Systems (Shanghai) PTE LTD +# +# 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 hashlib +import os +import struct +import sys + +import ecdsa +import esptool + +try: # use pycrypto API if available + from Crypto.Cipher import AES + + def ECB(key): + return AES.new(key, AES.MODE_ECB) + +except ImportError: + import pyaes + + def ECB(key): + return pyaes.AESModeOfOperationECB(key) + + +def get_chunks(source, chunk_len): + """ Returns an iterator over 'chunk_len' chunks of 'source' """ + return (source[i: i + chunk_len] for i in range(0, len(source), chunk_len)) + + +def endian_swap_words(source): + """ Endian-swap each word in 'source' bitstring """ + assert len(source) % 4 == 0 + words = "I" * (len(source) // 4) + return struct.pack("<" + words, *struct.unpack(">" + words, source)) + + +def swap_word_order(source): + """ Swap the order of the words in 'source' bitstring """ + assert len(source) % 4 == 0 + words = "I" * (len(source) // 4) + return struct.pack(words, *reversed(struct.unpack(words, source))) + + +def _load_hardware_key(keyfile): + """ Load a 256-bit key, similar to stored in efuse, from a file + + 192-bit keys will be extended to 256-bit using the same algorithm used + by hardware if 3/4 Coding Scheme is set. + """ + key = keyfile.read() + if len(key) not in [24, 32]: + raise esptool.FatalError("Key file contains wrong length (%d bytes), 24 or 32 expected." % len(key)) + if len(key) == 24: + key = key + key[8:16] + print("Using 192-bit key (extended)") + else: + print("Using 256-bit key") + + assert len(key) == 32 + return key + + +def digest_secure_bootloader(args): + """ Calculate the digest of a bootloader image, in the same way the hardware + secure boot engine would do so. Can be used with a pre-loaded key to update a + secure bootloader. """ + if args.iv is not None: + print("WARNING: --iv argument is for TESTING PURPOSES ONLY") + iv = args.iv.read(128) + else: + iv = os.urandom(128) + plaintext_image = args.image.read() + args.image.seek(0) + + # secure boot engine reads in 128 byte blocks (ie SHA512 block + # size), but also doesn't look for any appended SHA-256 digest + fw_image = esptool.ESP32FirmwareImage(args.image) + if fw_image.append_digest: + if len(plaintext_image) % 128 <= 32: + # ROM bootloader will read to the end of the 128 byte block, but not + # to the end of the SHA-256 digest at the end + new_len = len(plaintext_image) - (len(plaintext_image) % 128) + plaintext_image = plaintext_image[:new_len] + + # if image isn't 128 byte multiple then pad with 0xFF (ie unwritten flash) + # as this is what the secure boot engine will see + if len(plaintext_image) % 128 != 0: + plaintext_image += b"\xFF" * (128 - (len(plaintext_image) % 128)) + + plaintext = iv + plaintext_image + + # Secure Boot digest algorithm in hardware uses AES256 ECB to + # produce a ciphertext, then feeds output through SHA-512 to + # produce the digest. Each block in/out of ECB is reordered + # (due to hardware quirks not for security.) + + key = _load_hardware_key(args.keyfile) + aes = ECB(key) + digest = hashlib.sha512() + + for block in get_chunks(plaintext, 16): + block = block[::-1] # reverse each input block + + cipher_block = aes.encrypt(block) + # reverse and then byte swap each word in the output block + cipher_block = cipher_block[::-1] + for block in get_chunks(cipher_block, 4): + # Python hashlib can build each SHA block internally + digest.update(block[::-1]) + + if args.output is None: + args.output = os.path.splitext(args.image.name)[0] + "-digest-0x0000.bin" + with open(args.output, "wb") as f: + f.write(iv) + digest = digest.digest() + for word in get_chunks(digest, 4): + f.write(word[::-1]) # swap word order in the result + f.write(b'\xFF' * (0x1000 - f.tell())) # pad to 0x1000 + f.write(plaintext_image) + print("digest+image written to %s" % args.output) + + +def generate_signing_key(args): + """ Generate an ECDSA signing key for signing secure boot images (post-bootloader) """ + if os.path.exists(args.keyfile): + raise esptool.FatalError("ERROR: Key file %s already exists" % args.keyfile) + sk = ecdsa.SigningKey.generate(curve=ecdsa.NIST256p) + with open(args.keyfile, "wb") as f: + f.write(sk.to_pem()) + print("ECDSA NIST256p private key in PEM format written to %s" % args.keyfile) + + +def _load_ecdsa_signing_key(args): + sk = ecdsa.SigningKey.from_pem(args.keyfile.read()) + if sk.curve != ecdsa.NIST256p: + raise esptool.FatalError("Signing key uses incorrect curve. ESP32 Secure Boot only supports NIST256p (openssl calls this curve 'prime256v1") + return sk + + +def sign_data(args): + """ Sign a data file with a ECDSA private key, append binary signature to file contents """ + sk = _load_ecdsa_signing_key(args) + + # calculate signature of binary data + binary_content = args.datafile.read() + signature = sk.sign_deterministic(binary_content, hashlib.sha256) + + # back-verify signature + vk = sk.get_verifying_key() + vk.verify(signature, binary_content, hashlib.sha256) # throws exception on failure + + if args.output is None or os.path.abspath(args.output) == os.path.abspath(args.datafile.name): # append signature to input file + args.datafile.close() + outfile = open(args.datafile.name, "ab") + else: # write file & signature to new file + outfile = open(args.output, "wb") + outfile.write(binary_content) + outfile.write(struct.pack("I", 0)) # Version indicator, allow for different curves/formats later + outfile.write(signature) + outfile.close() + print("Signed %d bytes of data from %s with key %s" % (len(binary_content), args.datafile.name, args.keyfile.name)) + + +def verify_signature(args): + """ Verify a previously signed binary image, using the ECDSA public key """ + key_data = args.keyfile.read() + if b"-BEGIN EC PRIVATE KEY" in key_data: + sk = ecdsa.SigningKey.from_pem(key_data) + vk = sk.get_verifying_key() + elif b"-BEGIN PUBLIC KEY" in key_data: + vk = ecdsa.VerifyingKey.from_pem(key_data) + elif len(key_data) == 64: + vk = ecdsa.VerifyingKey.from_string(key_data, + curve=ecdsa.NIST256p) + else: + raise esptool.FatalError("Verification key does not appear to be an EC key in PEM format or binary EC public key data. Unsupported") + + if vk.curve != ecdsa.NIST256p: + raise esptool.FatalError("Public key uses incorrect curve. ESP32 Secure Boot only supports NIST256p (openssl calls this curve 'prime256v1") + + binary_content = args.datafile.read() + data = binary_content[0:-68] + sig_version, signature = struct.unpack("I64s", binary_content[-68:]) + if sig_version != 0: + raise esptool.FatalError("Signature block has version %d. This version of espsecure only supports version 0." % sig_version) + print("Verifying %d bytes of data" % len(data)) + try: + if vk.verify(signature, data, hashlib.sha256): + print("Signature is valid") + else: + raise esptool.FatalError("Signature is not valid") + except ecdsa.keys.BadSignatureError: + raise esptool.FatalError("Signature is not valid") + + +def extract_public_key(args): + """ Load an ECDSA private key and extract the embedded public key as raw binary data. """ + sk = _load_ecdsa_signing_key(args) + vk = sk.get_verifying_key() + args.public_keyfile.write(vk.to_string()) + print("%s public key extracted to %s" % (args.keyfile.name, args.public_keyfile.name)) + + +def digest_private_key(args): + sk = _load_ecdsa_signing_key(args) + repr(sk.to_string()) + digest = hashlib.sha256() + digest.update(sk.to_string()) + result = digest.digest() + if args.keylen == 192: + result = result[0:24] + args.digest_file.write(result) + print("SHA-256 digest of private key %s%s written to %s" % (args.keyfile.name, + "" if args.keylen == 256 + else " (truncated to 192 bits)", + args.digest_file.name)) + + +# flash encryption key tweaking pattern: the nth bit of the key is +# flipped if the kth bit in the flash offset is set, where mapping +# from n to k is provided by this list of 'n' bit offsets (range k) +_FLASH_ENCRYPTION_TWEAK_PATTERN = [ + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, + 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, + 12, 11, 10, 9, 8, 7, 6, 5, + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, + 10, 9, 8, 7, 6, 5, + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, + 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, + 8, 7, 6, 5 +] +assert len(_FLASH_ENCRYPTION_TWEAK_PATTERN) == 256 + + +def _flash_encryption_tweak_range(flash_crypt_config=0xF): + """ Return a list of the bit indexes that the "key tweak" applies to, + as determined by the FLASH_CRYPT_CONFIG 4 bit efuse value. + """ + tweak_range = [] + if (flash_crypt_config & 1) != 0: + tweak_range += range(67) + if (flash_crypt_config & 2) != 0: + tweak_range += range(67, 132) + if (flash_crypt_config & 4) != 0: + tweak_range += range(132, 195) + if (flash_crypt_config & 8) != 0: + tweak_range += range(195, 256) + return tweak_range + + +def _flash_encryption_tweak_range_bits(flash_crypt_config=0xF): + """ Return bits (in reverse order) that the "key tweak" applies to, + as determined by the FLASH_CRYPT_CONFIG 4 bit efuse value. + """ + tweak_range = 0 + if (flash_crypt_config & 1) != 0: + tweak_range |= 0xFFFFFFFFFFFFFFFFE00000000000000000000000000000000000000000000000 + if (flash_crypt_config & 2) != 0: + tweak_range |= 0x00000000000000001FFFFFFFFFFFFFFFF0000000000000000000000000000000 + if (flash_crypt_config & 4) != 0: + tweak_range |= 0x000000000000000000000000000000000FFFFFFFFFFFFFFFE000000000000000 + if (flash_crypt_config & 8) != 0: + tweak_range |= 0x0000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFF + return tweak_range + + +# Forward bit order masks +mul1 = 0x0000200004000080000004000080001000000200004000080000040000800010 +mul2 = 0x0000000000000000200000000000000010000000000000002000000000000001 + +mul1_mask = 0xffffffffffffff801ffffffffffffff00ffffffffffffff81ffffffffffffff0 +mul2_mask = 0x000000000000007fe00000000000000ff000000000000007e00000000000000f + + +def _flash_encryption_tweak_key(key, offset, tweak_range): + """Apply XOR "tweak" values to the key, derived from flash offset + 'offset'. This matches the ESP32 hardware flash encryption. + + tweak_range is a list of bit indexes to apply the tweak to, as + generated by _flash_encryption_tweak_range() from the + FLASH_CRYPT_CONFIG efuse value. + + Return tweaked key + """ + if esptool.PYTHON2: + key = [ord(k) for k in key] + assert len(key) == 32 + + offset_bits = [(offset & (1 << x)) != 0 for x in range(24)] + + for bit in tweak_range: + if offset_bits[_FLASH_ENCRYPTION_TWEAK_PATTERN[bit]]: + # note that each byte has a backwards bit order, compared + # to how it is looked up in the tweak pattern table + key[bit // 8] ^= 1 << (7 - (bit % 8)) + + key = b"".join(chr(k) for k in key) + return key + + else: + addr = offset >> 5 + key ^= ((mul1 * addr) | ((mul2 * addr) & mul2_mask)) & tweak_range + return int.to_bytes(key, length=32, byteorder='big', signed=False) + + +def generate_flash_encryption_key(args): + print("Writing %d random bits to key file %s" % (args.keylen, args.key_file.name)) + args.key_file.write(os.urandom(args.keylen // 8)) + + +def _flash_encryption_operation(output_file, input_file, flash_address, keyfile, flash_crypt_conf, do_decrypt): + key = _load_hardware_key(keyfile) + + if flash_address % 16 != 0: + raise esptool.FatalError("Starting flash address 0x%x must be a multiple of 16" % flash_address) + + if flash_crypt_conf == 0: + print("WARNING: Setting FLASH_CRYPT_CONF to zero is not recommended") + + if esptool.PYTHON2: + tweak_range = _flash_encryption_tweak_range(flash_crypt_conf) + else: + tweak_range = _flash_encryption_tweak_range_bits(flash_crypt_conf) + key = int.from_bytes(key, byteorder='big', signed=False) + + aes = None + block_offs = flash_address + while True: + block = input_file.read(16) + if len(block) == 0: + break + elif len(block) < 16: + if do_decrypt: + raise esptool.FatalError("Data length is not a multiple of 16 bytes") + pad = 16 - len(block) + block = block + os.urandom(pad) + print("Note: Padding with %d bytes of random data (encrypted data must be multiple of 16 bytes long)" % pad) + + if (block_offs % 32 == 0) or aes is None: + # each bit of the flash encryption key is XORed with tweak bits derived from the offset of 32 byte block of flash + block_key = _flash_encryption_tweak_key(key, block_offs, tweak_range) + aes = ECB(block_key) + + block = block[::-1] # reverse input block byte order + + # note AES is used inverted for flash encryption, so + # "decrypting" flash uses AES encrypt algorithm and vice + # versa. (This does not weaken AES.) + if do_decrypt: + block = aes.encrypt(block) + else: + block = aes.decrypt(block) + + block = block[::-1] # reverse output block byte order + output_file.write(block) + block_offs += len(block) + + +def decrypt_flash_data(args): + return _flash_encryption_operation(args.output, args.encrypted_file, args.address, args.keyfile, args.flash_crypt_conf, True) + + +def encrypt_flash_data(args): + return _flash_encryption_operation(args.output, args.plaintext_file, args.address, args.keyfile, args.flash_crypt_conf, False) + + +def main(): + parser = argparse.ArgumentParser(description='espsecure.py v%s - ESP32 Secure Boot & Flash Encryption tool' % esptool.__version__, prog='espsecure') + + subparsers = parser.add_subparsers( + dest='operation', + help='Run espsecure.py {command} -h for additional help') + + p = subparsers.add_parser('digest_secure_bootloader', + help='Take a bootloader binary image and a secure boot key, and output a combined digest+binary ' + + 'suitable for flashing along with the precalculated secure boot key.') + p.add_argument('--keyfile', '-k', help="256 bit key for secure boot digest.", type=argparse.FileType('rb'), required=True) + p.add_argument('--output', '-o', help="Output file for signed digest image.") + p.add_argument('--iv', help="128 byte IV file. Supply a file for testing purposes only, if not supplied an IV will be randomly generated.", + type=argparse.FileType('rb')) + p.add_argument('image', help="Bootloader image file to calculate digest from", type=argparse.FileType('rb')) + + p = subparsers.add_parser('generate_signing_key', + help='Generate a private key for signing secure boot images. Key file is generated in PEM format, ' + + 'and contains a ECDSA NIST256p private key and matching public key.') + p.add_argument('keyfile', help="Filename for private key file (embedded public key)") + + p = subparsers.add_parser('sign_data', + help='Sign a data file for use with secure boot. Signing algorithm is determinsitic ECDSA w/ SHA-512.') + p.add_argument('--keyfile', '-k', help="Private key file for signing. Key is in PEM format, ECDSA NIST256p curve. " + + "generate_signing_key command can be used to generate a suitable signing key.", type=argparse.FileType('rb'), required=True) + p.add_argument('--output', '-o', help="Output file for signed digest image. Default is to append signature to existing file.") + p.add_argument('datafile', help="Data file to sign.", type=argparse.FileType('rb')) + + p = subparsers.add_parser('verify_signature', + help='Verify a data file previously signed by "sign_data", using the public key.') + p.add_argument('--keyfile', '-k', help="Public key file for verification. Can be private or public key in PEM format, " + + "or a binary public key produced by extract_public_key command.", + type=argparse.FileType('rb'), required=True) + p.add_argument('datafile', help="Signed data file to verify signature.", type=argparse.FileType('rb')) + + p = subparsers.add_parser('extract_public_key', + help='Extract the public verification key for signatures, save it as a raw binary file.') + p.add_argument('--keyfile', '-k', help="Private key file (PEM format) to extract the public verification key from.", type=argparse.FileType('rb'), + required=True) + p.add_argument('public_keyfile', help="File to save new public key into", type=argparse.FileType('wb')) + + p = subparsers.add_parser('digest_private_key', help='Generate an SHA-256 digest of the private signing key. ' + + 'This can be used as a reproducible secure bootloader or flash encryption key.') + p.add_argument('--keyfile', '-k', help="Private key file (PEM format) to generate a digest from.", type=argparse.FileType('rb'), + required=True) + p.add_argument('--keylen', '-l', help="Length of private key digest file to generate (in bits). 3/4 Coding Scheme requires 192 bit key.", + choices=[192, 256], default=256, type=int) + p.add_argument('digest_file', help="File to write 32 byte digest into", type=argparse.FileType('wb')) + + p = subparsers.add_parser('generate_flash_encryption_key', help='Generate a development-use 32 byte flash encryption key with random data.') + p.add_argument('--keylen', '-l', help="Length of private key digest file to generate (in bits). 3/4 Coding Scheme requires 192 bit key.", + choices=[192, 256], default=256, type=int) + p.add_argument('key_file', help="File to write 24 or 32 byte digest into", type=argparse.FileType('wb')) + + p = subparsers.add_parser('decrypt_flash_data', help='Decrypt some data read from encrypted flash (using known key)') + p.add_argument('encrypted_file', help="File with encrypted flash contents", type=argparse.FileType('rb')) + p.add_argument('--keyfile', '-k', help="File with flash encryption key", type=argparse.FileType('rb'), + required=True) + p.add_argument('--output', '-o', help="Output file for plaintext data.", type=argparse.FileType('wb'), + required=True) + p.add_argument('--address', '-a', help="Address offset in flash that file was read from.", required=True, type=esptool.arg_auto_int) + p.add_argument('--flash_crypt_conf', help="Override FLASH_CRYPT_CONF efuse value (default is 0XF).", required=False, default=0xF, type=esptool.arg_auto_int) + + p = subparsers.add_parser('encrypt_flash_data', help='Encrypt some data suitable for encrypted flash (using known key)') + p.add_argument('--keyfile', '-k', help="File with flash encryption key", type=argparse.FileType('rb'), + required=True) + p.add_argument('--output', '-o', help="Output file for encrypted data.", type=argparse.FileType('wb'), + required=True) + p.add_argument('--address', '-a', help="Address offset in flash where file will be flashed.", required=True, type=esptool.arg_auto_int) + p.add_argument('--flash_crypt_conf', help="Override FLASH_CRYPT_CONF efuse value (default is 0XF).", required=False, default=0xF, type=esptool.arg_auto_int) + p.add_argument('plaintext_file', help="File with plaintext content for encrypting", type=argparse.FileType('rb')) + + args = parser.parse_args() + print('espsecure.py v%s' % esptool.__version__) + if args.operation is None: + parser.print_help() + parser.exit(1) + + # each 'operation' is a module-level function of the same name + operation_func = globals()[args.operation] + operation_func(args) + + +def _main(): + try: + main() + except esptool.FatalError as e: + print('\nA fatal error occurred: %s' % e) + sys.exit(2) + + +if __name__ == '__main__': + _main() diff --git a/dist/bin/esptool/esptool.py b/dist/bin/esptool/esptool.py new file mode 100644 index 000000000..c68e6fc1f --- /dev/null +++ b/dist/bin/esptool/esptool.py @@ -0,0 +1,3201 @@ +#!/usr/bin/env python +# +# ESP8266 & ESP32 ROM Bootloader Utility +# Copyright (C) 2014-2016 Fredrik Ahlberg, Angus Gratton, Espressif Systems (Shanghai) PTE 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 os +import shlex +import struct +import sys +import time +import zlib +import string + +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 + +__version__ = "2.8" + +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 +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 + + +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 + + +DETECTED_FLASH_SIZES = {0x12: '256KB', 0x13: '512KB', 0x14: '1MB', + 0x15: '2MB', 0x16: '4MB', 0x17: '8MB', 0x18: '16MB'} + + +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 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 ROM """ + return check_supported_function(func, lambda o: o.IS_STUB or o.CHIP_NAME == "ESP32") + + +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 _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_CHANGE_BAUDRATE = 0x0F + ESP_FLASH_DEFL_BEGIN = 0x10 + ESP_FLASH_DEFL_DATA = 0x11 + ESP_FLASH_DEFL_END = 0x12 + ESP_SPI_FLASH_MD5 = 0x13 + + # 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 debug more command + ESP_FLASH_ENCRYPT_DATA = 0xD4 + + # 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 + + # This register happens to exist on both ESP8266 & ESP32 + UART_DATA_REG_ADDR = 0x60000078 + + 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 + + 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(). + + """ + 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 + + 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): + """ 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) + try: + print('Detecting chip type...', end='') + sys.stdout.flush() + date_reg = detect_port.read_reg(ESPLoader.UART_DATA_REG_ADDR) + + for cls in [ESP8266ROM, ESP32ROM]: + if date_reg == cls.DATE_REG_VALUE: + # don't connect a second time + inst = cls(detect_port._port, baud, trace_enabled=trace_enabled) + print(' %s' % inst.CHIP_NAME, end='') + return inst + finally: + print('') # end line + raise FatalError("Unexpected UART datecode value 0x%08x. Failed to autodetect chip type." % date_reg) + + """ Read a SLIP packet from the serial port """ + def read(self): + return next(self._slip_reader) + + """ Write bytes to the serial port while performing SLIP escaping """ + def write(self, packet): + buf = b'\xc0' \ + + (packet.replace(b'\xdb',b'\xdb\xdd').replace(b'\xc0',b'\xdb\xdc')) \ + + b'\xc0' + self.trace("Write %d bytes: %s", len(buf), HexFormatter(buf)) + self._port.write(buf) + + def trace(self, message, *format_args): + if self._trace_enabled: + now = time.time() + try: + + delta = now - self._last_trace + except AttributeError: + delta = 0.0 + self._last_trace = now + prefix = "TRACE +%.3f " % delta + print(prefix + (message % format_args)) + + """ Calculate checksum of a blob, as it is defined by the ROM """ + @staticmethod + def checksum(data, state=ESP_CHECKSUM_MAGIC): + for b in data: + if type(b) is int: # python 2/3 compat + state ^= b + else: + state ^= ord(b) + + return state + + """ Send a request and read the response """ + def command(self, op=None, data=b"", chk=0, wait_response=True, timeout=DEFAULT_TIMEOUT): + saved_timeout = self._port.timeout + new_timeout = min(timeout, MAX_TIMEOUT) + if new_timeout != saved_timeout: + self._port.timeout = new_timeout + + try: + if op is not None: + self.trace("command op=0x%02x data len=%s wait_response=%d timeout=%.3f data=%s", + op, len(data), 1 if wait_response else 0, timeout, HexFormatter(data)) + pkt = struct.pack(b' self.STATUS_BYTES_LENGTH: + return data[:-self.STATUS_BYTES_LENGTH] + else: # otherwise, just return the 'val' field which comes from the reply header (this is used by read_reg) + return val + + def flush_input(self): + self._port.flushInput() + self._slip_reader = slip_reader(self._port, self.trace) + + def sync(self): + self.command(self.ESP_SYNC, b'\x07\x07\x12\x20' + 32 * b'\x55', + timeout=SYNC_TIMEOUT) + for i in range(7): + self.command() + + 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 _connect_attempt(self, mode='default_reset', esp32r0_delay=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 + + # issue reset-to-bootloader: + # 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 mode != 'no_reset': + 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 + + 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 connect(self, mode='default_reset'): + """ Try connecting repeatedly until successful, or giving up """ + print('Connecting...', end='') + sys.stdout.flush() + last_error = None + + try: + for _ in range(7): + last_error = self._connect_attempt(mode=mode, esp32r0_delay=False) + if last_error is None: + return + last_error = self._connect_attempt(mode=mode, esp32r0_delay=True) + if last_error is None: + return + finally: + print('') # end 'Connecting...' line + raise FatalError('Failed to connect to %s: %s' % (self.CHIP_NAME, last_error)) + + def read_reg(self, addr): + """ 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(' start: + raise FatalError(("Software loader is resident at 0x%08x-0x%08x. " + + "Can't load binary at overlapping address range 0x%08x-0x%08x. " + + "Either change binary loading address, or use the --no-stub " + + "option to disable the software loader.") % (start, end, load_start, load_end)) + + return self.check_command("enter RAM download mode", self.ESP_MEM_BEGIN, + struct.pack(' length: + raise FatalError('Read more than expected') + + digest_frame = self.read() + if len(digest_frame) != 16: + raise FatalError('Expected digest, got: %s' % hexify(digest_frame)) + expected_digest = hexify(digest_frame).upper() + digest = hashlib.md5(data).hexdigest().upper() + if digest != expected_digest: + raise FatalError('Digest mismatch: expected %s, got %s' % (expected_digest, digest)) + return data + + def flash_spi_attach(self, hspi_arg): + """Send SPI attach command to enable the SPI flash pins + + ESP8266 ROM does this when you send flash_begin, ESP32 ROM + has it as a SPI command. + """ + # last 3 bytes in ESP_SPI_ATTACH argument are reserved values + arg = struct.pack(' 0: + self.write_reg(SPI_MOSI_DLEN_REG, mosi_bits - 1) + if miso_bits > 0: + self.write_reg(SPI_MISO_DLEN_REG, miso_bits - 1) + else: + + def set_data_lengths(mosi_bits, miso_bits): + SPI_DATA_LEN_REG = SPI_USR1_REG + SPI_MOSI_BITLEN_S = 17 + SPI_MISO_BITLEN_S = 8 + mosi_mask = 0 if (mosi_bits == 0) else (mosi_bits - 1) + miso_mask = 0 if (miso_bits == 0) else (miso_bits - 1) + self.write_reg(SPI_DATA_LEN_REG, + (miso_mask << SPI_MISO_BITLEN_S) | ( + mosi_mask << SPI_MOSI_BITLEN_S)) + + # SPI peripheral "command" bitmasks for SPI_CMD_REG + SPI_CMD_USR = (1 << 18) + + # shift values + SPI_USR2_DLEN_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_DLEN_SHIFT) | spiflash_command) + if data_bits == 0: + self.write_reg(SPI_W0_REG, 0) # clear data register before we read it + else: + data = pad_to(data, 4, b'\00') # pad to 32-bit multiple + words = struct.unpack("I" * (len(data) // 4), data) + next_reg = SPI_W0_REG + for word in words: + self.write_reg(next_reg, word) + next_reg += 4 + self.write_reg(SPI_CMD_REG, SPI_CMD_USR) + + def wait_done(): + for _ in range(10): + if (self.read_reg(SPI_CMD_REG) & SPI_CMD_USR) == 0: + return + raise FatalError("SPI command did not complete in time") + wait_done() + + status = self.read_reg(SPI_W0_REG) + # restore some SPI controller registers + self.write_reg(SPI_USR_REG, old_spi_usr) + self.write_reg(SPI_USR2_REG, old_spi_usr2) + return status + + def read_status(self, num_bytes=2): + """Read up to 24 bits (num_bytes) of SPI flash status register contents + via RDSR, RDSR2, RDSR3 commands + + Not all SPI flash supports all three commands. The upper 1 or 2 + bytes may be 0xFF. + """ + SPIFLASH_RDSR = 0x05 + SPIFLASH_RDSR2 = 0x35 + SPIFLASH_RDSR3 = 0x15 + + status = 0 + shift = 0 + for cmd in [SPIFLASH_RDSR, SPIFLASH_RDSR2, SPIFLASH_RDSR3][0:num_bytes]: + status += self.run_spiflash_command(cmd, read_bits=8) << shift + shift += 8 + return status + + def write_status(self, new_status, num_bytes=2, set_non_volatile=False): + """Write up to 24 bits (num_bytes) of new status register + + num_bytes can be 1, 2 or 3. + + Not all flash supports the additional commands to write the + second and third byte of the status register. When writing 2 + bytes, esptool also sends a 16-byte WRSR command (as some + flash types use this instead of WRSR2.) + + If the set_non_volatile flag is set, non-volatile bits will + be set as well as volatile ones (WREN used instead of WEVSR). + + """ + SPIFLASH_WRSR = 0x01 + SPIFLASH_WRSR2 = 0x31 + SPIFLASH_WRSR3 = 0x11 + SPIFLASH_WEVSR = 0x50 + SPIFLASH_WREN = 0x06 + SPIFLASH_WRDI = 0x04 + + enable_cmd = SPIFLASH_WREN if set_non_volatile else SPIFLASH_WEVSR + + # try using a 16-bit WRSR (not supported by all chips) + # this may be redundant, but shouldn't hurt + if num_bytes == 2: + self.run_spiflash_command(enable_cmd) + self.run_spiflash_command(SPIFLASH_WRSR, struct.pack(">= 8 + + self.run_spiflash_command(SPIFLASH_WRDI) + + def get_crystal_freq(self): + # Figure out the crystal frequency from the UART clock divider + # Returns a normalized value in integer MHz (40 or 26 are the only supported values) + # + # The logic here is: + # - We know that our baud rate and the ESP UART baud rate are roughly the same, or we couldn't communicate + # - We can read the UART clock divider register to know how the ESP derives this from the APB bus frequency + # - Multiplying these two together gives us the bus frequency which is either the crystal frequency (ESP32) + # or double the crystal frequency (ESP8266). See the self.XTAL_CLK_DIVIDER parameter for this factor. + uart_div = self.read_reg(self.UART_CLKDIV_REG) & self.UART_CLKDIV_MASK + est_xtal = (self._port.baudrate * uart_div) / 1e6 / self.XTAL_CLK_DIVIDER + norm_xtal = 40 if est_xtal > 33 else 26 + if abs(norm_xtal - est_xtal) > 1: + print("WARNING: Detected crystal freq %.2fMHz is quite different to normalized freq %dMHz. Unsupported crystal in use?" % (est_xtal, norm_xtal)) + return norm_xtal + + def hard_reset(self): + 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 + + DATE_REG_VALUE = 0x00062000 + + # OTP ROM addresses + ESP_OTP_MAC0 = 0x3ff00050 + ESP_OTP_MAC1 = 0x3ff00054 + ESP_OTP_MAC3 = 0x3ff0005c + + SPI_REG_BASE = 0x60000200 + SPI_W0_OFFS = 0x40 + SPI_HAS_MOSI_DLEN_REG = False + + 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 + return (self.read_reg(0x3ff0005c) << 96 | + self.read_reg(0x3ff00058) << 64 | + self.read_reg(0x3ff00054) << 32 | + self.read_reg(0x3ff00050)) + + 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 + return "ESP8285" if is_8285 else "ESP8266EX" + + def get_chip_features(self): + features = ["WiFi"] + if self.get_chip_description() == "ESP8285": + 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._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 + + DATE_REG_VALUE = 0x15122500 + + 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 = 0x60002000 + EFUSE_REG_BASE = 0x6001a000 + + DR_REG_SYSCON_BASE = 0x3ff66000 + + SPI_W0_OFFS = 0x80 + SPI_HAS_MOSI_DLEN_REG = True + + 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 = [[0x3F400000, 0x3F800000, "DROM"], + [0x3F800000, 0x3FC00000, "EXTRAM_DATA"], + [0x3FF80000, 0x3FF82000, "RTC_DRAM"], + [0x3FF90000, 0x40000000, "BYTE_ACCESSIBLE"], + [0x3FFAE000, 0x40000000, "DRAM"], + [0x3FFAE000, 0x40000000, "DMA"], + [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"]] + + """ 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 + + """ For flash encryption related commands we need to make sure + user has programmed all the relevant efuse correctly so at + the end of 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 + """ + + def get_flash_crypt_config(self): + """ 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_chip_description(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 + pkg_version = (word3 >> 9) & 0x07 + + chip_name = { + 0: "ESP32D0WDQ6", + 1: "ESP32D0WDQ5", + 2: "ESP32D2WDQ5", + 5: "ESP32-PICO-D4", + }.get(pkg_version, "unknown ESP32") + + chip_revision = 0 + if rev_bit0: + if rev_bit1: + if rev_bit2: + chip_revision = 3 + else: + chip_revision = 2 + else: + chip_revision = 1 + 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 = (word3 >> 9) & 0x07 + if pkg_version in [2, 4, 5]: + features += ["Embedded Flash"] + + 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_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) + + +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._port = rom_loader._port + self._trace_enabled = rom_loader._trace_enabled + self.flush_input() # resets _slip_reader + + +ESP32ROM.STUB_CLASS = ESP32StubLoader + + +class ESPBOOTLOADER(object): + """ These are constants related to software ESP 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 ESP8266 or ESP32. 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). + """ + with open(filename, 'rb') as f: + if chip.lower() == 'esp32': + return ESP32FirmwareImage(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 pad_to_alignment(self, alignment): + self.data = pad_to(self.data, alignment, b'\x00') + + +class ELFSection(ImageSegment): + """ Wrapper class for a section in an ELF image, has a section + name as well as the common properties of an ImageSegment. """ + def __init__(self, name, addr, data): + super(ELFSection, self).__init__(addr, data) + self.name = name.decode("utf-8") + + def __repr__(self): + return "%s %s" % (self.name, super(ELFSection, self).__repr__()) + + +class BaseFirmwareImage(object): + SEG_HEADER_LEN = 8 + SHA256_DIGEST_LEN = 32 + + """ Base class with common firmware image functions """ + def __init__(self): + self.segments = [] + self.entrypoint = 0 + self.elf_sha256 = None + self.elf_sha256_offset = 0 + + def load_common_header(self, load_file, expected_magic): + (magic, segments, self.flash_mode, self.flash_size_freq, self.entrypoint) = struct.unpack(' 16: + raise FatalError('Invalid segment count %d (max 16). Usually this indicates a linker script problem.' % len(self.segments)) + + def load_segment(self, f, is_irom_segment=False): + """ Load the next segment from the image file """ + file_offs = f.tell() + (offset, size) = struct.unpack(' 0x40200000 or offset < 0x3ffe0000 or size > 65536: + print('WARNING: Suspicious segment 0x%x, length %d' % (offset, size)) + + def maybe_patch_segment_data(self, f, segment_data): + """If SHA256 digest of the ELF file needs to be inserted into this segment, do so. Returns segment data.""" + segment_len = len(segment_data) + file_pos = f.tell() # file_pos is position in the .bin file + if self.elf_sha256_offset >= file_pos and self.elf_sha256_offset < file_pos + segment_len: + # SHA256 digest needs to be patched into this binary segment, + # calculate offset of the digest inside the binary segment. + patch_offset = self.elf_sha256_offset - file_pos + # Sanity checks + if patch_offset < self.SEG_HEADER_LEN or patch_offset + self.SHA256_DIGEST_LEN > segment_len: + raise FatalError('Cannot place SHA256 digest on segment boundary' + + '(elf_sha256_offset=%d, file_pos=%d, segment_size=%d)' % + (self.elf_sha256_offset, file_pos, segment_len)) + 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) + # offset relative to the data part + patch_offset -= self.SEG_HEADER_LEN + segment_data = segment_data[0:patch_offset] + self.elf_sha256 + \ + segment_data[patch_offset + self.SHA256_DIGEST_LEN:] + return segment_data + + def save_segment(self, f, segment, checksum=None): + """ Save the next segment to the image file, return next checksum value if provided """ + segment_data = self.maybe_patch_segment_data(f, segment.data) + f.write(struct.pack(' 0: + if len(irom_segments) != 1: + raise FatalError('Found %d segments that could be irom0. Bad ELF file?' % len(irom_segments)) + return irom_segments[0] + return None + + def get_non_irom_segments(self): + irom_segment = self.get_irom_segment() + return [s for s in self.segments if s != irom_segment] + + +class ESP8266ROMFirmwareImage(BaseFirmwareImage): + """ 'Version 1' firmware image, segments loaded directly by the ROM bootloader. """ + + ROM_LOADER = ESP8266ROM + + def __init__(self, load_file=None): + super(ESP8266ROMFirmwareImage, self).__init__() + self.flash_mode = 0 + self.flash_size_freq = 0 + self.version = 1 + + if load_file is not None: + segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC) + + for _ in range(segments): + self.load_segment(load_file) + self.checksum = self.read_checksum(load_file) + + self.verify() + + def default_output_name(self, input_file): + """ Derive a default output name from the ELF name. """ + return input_file + '-' + + def save(self, basename): + """ Save a set of V1 images for flashing. Parameter is a base filename. """ + # IROM data goes in its own plain binary file + irom_segment = self.get_irom_segment() + if irom_segment is not None: + with open("%s0x%05x.bin" % (basename, irom_segment.addr - ESP8266ROM.IROM_MAP_START), "wb") as f: + f.write(irom_segment.data) + + # everything but IROM goes at 0x00000 in an image file + normal_segments = self.get_non_irom_segments() + with open("%s0x00000.bin" % basename, 'wb') as f: + self.write_common_header(f, normal_segments) + checksum = ESPLoader.ESP_CHECKSUM_MAGIC + for segment in normal_segments: + checksum = self.save_segment(f, segment, checksum) + self.append_checksum(f, checksum) + + +ESP8266ROM.BOOTLOADER_IMAGE = ESP8266ROMFirmwareImage + + +class ESP8266V2FirmwareImage(BaseFirmwareImage): + """ 'Version 2' firmware image, segments loaded by software bootloader stub + (ie Espressif bootloader or rboot) + """ + + ROM_LOADER = ESP8266ROM + + def __init__(self, load_file=None): + super(ESP8266V2FirmwareImage, self).__init__() + self.version = 2 + if load_file is not None: + segments = self.load_common_header(load_file, ESPBOOTLOADER.IMAGE_V2_MAGIC) + if segments != ESPBOOTLOADER.IMAGE_V2_SEGMENT: + # segment count is not really segment count here, but we expect to see '4' + print('Warning: V2 header has unexpected "segment" count %d (usually 4)' % segments) + + # irom segment comes before the second header + # + # the file is saved in the image with a zero load address + # in the header, so we need to calculate a load address + irom_segment = self.load_segment(load_file, True) + irom_segment.addr = 0 # for actual mapped addr, add ESP8266ROM.IROM_MAP_START + flashing_addr + 8 + irom_segment.include_in_checksum = False + + first_flash_mode = self.flash_mode + first_flash_size_freq = self.flash_size_freq + first_entrypoint = self.entrypoint + # load the second header + + segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC) + + if first_flash_mode != self.flash_mode: + print('WARNING: Flash mode value in first header (0x%02x) disagrees with second (0x%02x). Using second value.' + % (first_flash_mode, self.flash_mode)) + if first_flash_size_freq != self.flash_size_freq: + print('WARNING: Flash size/freq value in first header (0x%02x) disagrees with second (0x%02x). Using second value.' + % (first_flash_size_freq, self.flash_size_freq)) + if first_entrypoint != self.entrypoint: + print('WARNING: Entrypoint address in first header (0x%08x) disagrees with second header (0x%08x). Using second value.' + % (first_entrypoint, self.entrypoint)) + + # load all the usual segments + for _ in range(segments): + self.load_segment(load_file) + self.checksum = self.read_checksum(load_file) + + self.verify() + + def default_output_name(self, input_file): + """ Derive a default output name from the ELF name. """ + irom_segment = self.get_irom_segment() + if irom_segment is not None: + irom_offs = irom_segment.addr - ESP8266ROM.IROM_MAP_START + else: + irom_offs = 0 + return "%s-0x%05x.bin" % (os.path.splitext(input_file)[0], + irom_offs & ~(ESPLoader.FLASH_SECTOR_SIZE - 1)) + + def save(self, filename): + with open(filename, 'wb') as f: + # Save first header for irom0 segment + f.write(struct.pack(b' 0: + last_addr = flash_segments[0].addr + for segment in flash_segments[1:]: + if segment.addr // self.IROM_ALIGN == last_addr // self.IROM_ALIGN: + raise FatalError(("Segment loaded at 0x%08x lands in same 64KB flash mapping as segment loaded at 0x%08x. " + + "Can't generate binary. Suggest changing linker script or ELF to merge sections.") % + (segment.addr, last_addr)) + last_addr = segment.addr + + def get_alignment_data_needed(segment): + # Actual alignment (in data bytes) required for a segment header: positioned so that + # after we write the next 8 byte header, file_offs % IROM_ALIGN == segment.addr % IROM_ALIGN + # + # (this is because the segment's vaddr may not be IROM_ALIGNed, more likely is aligned + # IROM_ALIGN+0x18 to account for the binary file header + align_past = (segment.addr % self.IROM_ALIGN) - self.SEG_HEADER_LEN + pad_len = (self.IROM_ALIGN - (f.tell() % self.IROM_ALIGN)) + align_past + if pad_len == 0 or pad_len == self.IROM_ALIGN: + return 0 # already aligned + + # subtract SEG_HEADER_LEN a second time, as the padding block has a header as well + pad_len -= self.SEG_HEADER_LEN + if pad_len < 0: + pad_len += self.IROM_ALIGN + return pad_len + + # try to fit each flash segment on a 64kB aligned boundary + # by padding with parts of the non-flash segments... + while len(flash_segments) > 0: + segment = flash_segments[0] + pad_len = get_alignment_data_needed(segment) + if pad_len > 0: # need to pad + if len(ram_segments) > 0 and pad_len > self.SEG_HEADER_LEN: + pad_segment = ram_segments[0].split_image(pad_len) + if len(ram_segments[0].data) == 0: + ram_segments.pop(0) + else: + pad_segment = ImageSegment(0, b'\x00' * pad_len, f.tell()) + checksum = self.save_segment(f, pad_segment, checksum) + total_segments += 1 + else: + # write the flash segment + assert (f.tell() + 8) % self.IROM_ALIGN == segment.addr % self.IROM_ALIGN + checksum = self.save_flash_segment(f, segment, checksum) + flash_segments.pop(0) + total_segments += 1 + + # flash segments all written, so write any remaining RAM segments + for segment in ram_segments: + checksum = self.save_segment(f, segment, checksum) + total_segments += 1 + + if self.secure_pad: + # pad the image so that after signing it will end on a a 64KB boundary. + # This ensures all mapped flash content will be verified. + if not self.append_digest: + raise FatalError("secure_pad only applies if a SHA-256 digest is also appended to the image") + align_past = (f.tell() + self.SEG_HEADER_LEN) % self.IROM_ALIGN + # 16 byte aligned checksum (force the alignment to simplify calculations) + checksum_space = 16 + # 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 + 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 ELFFile(object): + SEC_TYPE_PROGBITS = 0x01 + SEC_TYPE_STRTAB = 0x03 + + LEN_SEC_HEADER = 0x28 + + 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 != 0x5e: + raise FatalError("%s does not appear to be an Xtensa 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 0x28)" % (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) + + def _read_sections(self, f, section_header_offs, section_header_count, shstrndx): + f.seek(section_header_offs) + len_bytes = section_header_count * self.LEN_SEC_HEADER + section_header = f.read(len_bytes) + if len(section_header) == 0: + raise FatalError("No section header found at offset %04x in ELF file." % section_header_offs) + if len(section_header) != (len_bytes): + raise FatalError("Only read 0x%x bytes from section header (expected 0x%x.) Truncated ELF file?" % (len(section_header), len_bytes)) + + # walk through the section header and extract all sections + section_header_offsets = range(0, len(section_header), self.LEN_SEC_HEADER) + + def read_section_header(offs): + name_offs,sec_type,_flags,lma,sec_offs,size = struct.unpack_from(" 0] + self.sections = prog_sections + + def sha256(self): + # return SHA256 hash of the input ELF file + sha256 = hashlib.sha256() + with open(self.name, 'rb') as f: + sha256.update(f.read()) + return sha256.digest() + + +def slip_reader(port, trace_function): + """Generator to read SLIP packets from a serial port. + Yields one full SLIP packet at a time, raises exception on timeout or invalid data. + + Designed to avoid too many calls to serial.read(1), which can bog + down on slow systems. + """ + partial_packet = None + in_escape = False + while True: + waiting = port.inWaiting() + read_bytes = port.read(1 if waiting == 0 else waiting) + if read_bytes == b'': + waiting_for = "header" if partial_packet is None else "content" + trace_function("Timed out waiting for packet %s", waiting_for) + raise FatalError("Timed out waiting for packet %s" % waiting_for) + trace_function("Read %d bytes: %s", len(read_bytes), HexFormatter(read_bytes)) + for b in read_bytes: + if type(b) is int: + b = bytes([b]) # python 2/3 compat + + if partial_packet is None: # waiting for packet header + if b == b'\xc0': + partial_packet = b"" + else: + trace_function("Read invalid data: %s", HexFormatter(read_bytes)) + trace_function("Remaining data in serial buffer: %s", HexFormatter(port.read(port.inWaiting()))) + raise FatalError('Invalid head of packet (0x%s)' % hexify(b)) + elif in_escape: # part-way through escape sequence + in_escape = False + if b == b'\xdc': + partial_packet += b'\xc0' + elif b == b'\xdd': + partial_packet += b'\xdb' + else: + trace_function("Read invalid data: %s", HexFormatter(read_bytes)) + trace_function("Remaining data in serial buffer: %s", HexFormatter(port.read(port.inWaiting()))) + raise FatalError('Invalid SLIP escape (0xdb, 0x%s)' % (hexify(b))) + elif b == b'\xdb': # start of escape sequence + in_escape = True + elif b == b'\xc0': # end of packet + trace_function("Received full packet: %s", HexFormatter(partial_packet)) + yield partial_packet + partial_packet = None + else: # normal byte in packet + partial_packet += b + + +def arg_auto_int(x): + return int(x, 0) + + +def div_roundup(a, b): + """ Return a/b rounded up to nearest integer, + equivalent result to int(math.ceil(float(int(a)) / float(int(b))), only + without possible floating point accuracy errors. + """ + return (int(a) + int(b) - 1) // int(b) + + +def align_file_position(f, size): + """ Align the position in the file to the next block of specified size """ + align = (size - 1) - (f.tell() % size) + f.seek(align, 1) + + +def flash_size_bytes(size): + """ Given a flash size of the type passed in args.flash_size + (ie 512KB or 1MB) then return the size in bytes. + """ + if "MB" in size: + return int(size[:size.index("MB")]) * 1024 * 1024 + elif "KB" in size: + return int(size[:size.index("KB")]) * 1024 + else: + raise FatalError("Unknown size %s" % size) + + +def hexify(s, uppercase=True): + format_str = '%02X' if uppercase else '%02x' + if not PYTHON2: + return ''.join(format_str % c for c in s) + else: + return ''.join(format_str % ord(c) for c in s) + + +class HexFormatter(object): + """ + Wrapper class which takes binary data in its constructor + and returns a hex string as it's __str__ method. + + This is intended for "lazy formatting" of trace() output + in hex format. Avoids overhead (significant on slow computers) + of generating long hex strings even if tracing is disabled. + + Note that this doesn't save any overhead if passed as an + argument to "%", only when passed to trace() + + If auto_split is set (default), any long line (> 16 bytes) will be + printed as separately indented lines, with ASCII decoding at the end + of each line. + """ + def __init__(self, binary_string, auto_split=True): + self._s = binary_string + self._auto_split = auto_split + + def __str__(self): + if self._auto_split and len(self._s) > 16: + result = "" + s = self._s + while len(s) > 0: + line = s[:16] + ascii_line = "".join(c if (c == ' ' or (c in string.printable and c not in string.whitespace)) + else '.' for c in line.decode('ascii', 'replace')) + s = s[16:] + result += "\n %-16s %-16s | %s" % (hexify(line[:8], False), hexify(line[8:], False), ascii_line) + return result + else: + return hexify(self._s, False) + + +def pad_to(data, alignment, pad_character=b'\xFF'): + """ Pad to the next alignment boundary """ + pad_mod = len(data) % alignment + if pad_mod != 0: + data += pad_character * (alignment - pad_mod) + return data + + +class FatalError(RuntimeError): + """ + Wrapper class for runtime errors that aren't caused by internal bugs, but by + ESP8266 responses or input content. + """ + def __init__(self, message): + RuntimeError.__init__(self, message) + + @staticmethod + def WithResult(message, result): + """ + Return a fatal error object that appends the hex values of + 'result' as a string formatted argument. + """ + message += " (result was %s)" % hexify(result) + return FatalError(message) + + +class NotImplementedInROMError(FatalError): + """ + Wrapper class for the error thrown when a particular ESP bootloader function + is not implemented in the ROM bootloader. + """ + def __init__(self, bootloader, func): + FatalError.__init__(self, "%s ROM does not support function %s." % (bootloader.CHIP_NAME, func.__name__)) + + +class NotSupportedError(FatalError): + def __init__(self, esp, function_name): + FatalError.__init__(self, "Function %s is not supported for %s." % (function_name, esp.CHIP_NAME)) + +# "Operation" commands, executable at command line. One function each +# +# Each function takes either two args (, ) or a single +# argument. + + +def load_ram(esp, args): + image = LoadFirmwareImage(esp.CHIP_NAME, args.filename) + + print('RAM boot...') + for seg in image.segments: + size = len(seg.data) + print('Downloading %d bytes at %08x...' % (size, seg.addr), end=' ') + sys.stdout.flush() + esp.mem_begin(size, div_roundup(size, esp.ESP_RAM_BLOCK), esp.ESP_RAM_BLOCK, seg.addr) + + seq = 0 + while len(seg.data) > 0: + esp.mem_block(seg.data[0:esp.ESP_RAM_BLOCK], seq) + seg.data = seg.data[esp.ESP_RAM_BLOCK:] + seq += 1 + print('done!') + + print('All segments done, executing at %08x' % image.entrypoint) + esp.mem_finish(image.entrypoint) + + +def read_mem(esp, args): + print('0x%08x = 0x%08x' % (args.address, esp.read_reg(args.address))) + + +def write_mem(esp, args): + esp.write_reg(args.address, args.value, args.mask, 0) + print('Wrote %08x, mask %08x to %08x' % (args.value, args.mask, args.address)) + + +def dump_mem(esp, args): + with open(args.filename, 'wb') as f: + for i in range(args.size // 4): + d = esp.read_reg(args.address + (i * 4)) + f.write(struct.pack(b'> 16 + args.flash_size = DETECTED_FLASH_SIZES.get(size_id) + if args.flash_size is None: + print('Warning: Could not auto-detect Flash size (FlashID=0x%x, SizeID=0x%x), defaulting to 4MB' % (flash_id, size_id)) + args.flash_size = '4MB' + else: + print('Auto-detected Flash size:', args.flash_size) + + +def _update_image_flash_params(esp, address, args, image): + """ Modify the flash mode & size bytes if this looks like an executable bootloader image """ + if len(image) < 8: + return image # not long enough to be a bootloader image + + # unpack the (potential) image header + magic, _, flash_mode, flash_size_freq = struct.unpack("BBBB", image[:4]) + if address != esp.BOOTLOADER_FLASH_OFFSET: + return image # not flashing bootloader offset, so don't modify this + + if (args.flash_mode, args.flash_freq, args.flash_size) == ('keep',) * 3: + return image # all settings are 'keep', not modifying anything + + # easy check if this is an image: does it start with a magic byte? + if magic != esp.ESP_IMAGE_MAGIC: + print("Warning: Image file at 0x%x doesn't look like an image file, so not changing any flash settings." % address) + return image + + # make sure this really is an image, and not just data that + # starts with esp.ESP_IMAGE_MAGIC (mostly a problem for encrypted + # images that happen to start with a magic byte + try: + test_image = esp.BOOTLOADER_IMAGE(io.BytesIO(image)) + test_image.verify() + except Exception: + print("Warning: Image file at 0x%x is not a valid %s image, so not changing any flash settings." % (address,esp.CHIP_NAME)) + return image + + if args.flash_mode != 'keep': + flash_mode = {'qio':0, 'qout':1, 'dio':2, 'dout': 3}[args.flash_mode] + + flash_freq = flash_size_freq & 0x0F + if args.flash_freq != 'keep': + flash_freq = {'40m':0, '26m':1, '20m':2, '80m': 0xf}[args.flash_freq] + + flash_size = flash_size_freq & 0xF0 + if args.flash_size != 'keep': + flash_size = esp.parse_flash_size_arg(args.flash_size) + + flash_params = struct.pack(b'BB', flash_mode, flash_size + flash_freq) + if flash_params != image[2:4]: + print('Flash params set to 0x%04x' % struct.unpack(">H", flash_params)) + image = image[0:2] + flash_params + image[4:] + return image + + +def write_flash(esp, args): + # set args.compress based on default behaviour: + # -> if either --compress or --no-compress is set, honour that + # -> otherwise, set --compress unless --no-stub is set + if args.compress is None and not args.no_compress: + args.compress = not args.no_stub + + # For encrypt option we do few sanity checks before actual flash write + if args.encrypt: + do_write = True + crypt_cfg_efuse = esp.get_flash_crypt_config() + + if crypt_cfg_efuse != 0xF: + print('\nWARNING: Unexpected FLASH_CRYPT_CONFIG value', hex(crypt_cfg_efuse)) + print('\nMake sure flash encryption is enabled correctly, refer to Flash Encryption documentation') + do_write = False + + enc_key_valid = esp.is_flash_encryption_key_valid() + + if not enc_key_valid: + print('\nFlash encryption key is not programmed') + print('\nMake sure flash encryption is enabled correctly, refer to Flash Encryption documentation') + do_write = False + + if (esp.FLASH_WRITE_SIZE % 32) != 0: + print('\nWARNING - Flash write address is not aligned to the recommeded 32 bytes') + do_write = False + + if not do_write and not args.ignore_flash_encryption_efuse_setting: + raise FatalError("Incorrect efuse setting: aborting flash write") + + # 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,2) # seek to 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) + + if args.encrypt and args.compress: + print('\nWARNING: - compress and encrypt options are mutually exclusive ') + print('Will flash uncompressed') + args.compress = False + + for address, argfile in args.addr_filename: + if args.no_stub: + print('Erasing flash...') + image = pad_to(argfile.read(), 32 if args.encrypt 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 args.compress: + uncimage = image + image = zlib.compress(uncimage, 9) + ratio = uncsize / len(image) + blocks = esp.flash_defl_begin(uncsize, len(image), address) + else: + ratio = 1.0 + blocks = esp.flash_begin(uncsize, address) + argfile.seek(0) # in case we need it again + seq = 0 + written = 0 + t = time.time() + while len(image) > 0: + print('\rWriting at 0x%08x... (%d %%)' % (address + seq * esp.FLASH_WRITE_SIZE, 100 * (seq + 1) // blocks), end='') + sys.stdout.flush() + block = image[0:esp.FLASH_WRITE_SIZE] + if args.compress: + esp.flash_defl_block(block, seq, timeout=DEFAULT_TIMEOUT * ratio * 2) + else: + # Pad the last block + block = block + b'\xff' * (esp.FLASH_WRITE_SIZE - len(block)) + if args.encrypt: + esp.flash_encrypt_block(block, seq) + else: + esp.flash_block(block, seq) + image = image[esp.FLASH_WRITE_SIZE:] + seq += 1 + written += len(block) + t = time.time() - t + speed_msg = "" + if args.compress: + if t > 0.0: + speed_msg = " (effective %.1f kbit/s)" % (uncsize / t * 8 / 1000) + print('\rWrote %d bytes (%d compressed) at 0x%08x in %.1f seconds%s...' % (uncsize, written, address, t, speed_msg)) + else: + if t > 0.0: + speed_msg = " (%.1f kbit/s)" % (written / t * 8 / 1000) + print('\rWrote %d bytes at 0x%08x in %.1f seconds%s...' % (written, address, t, speed_msg)) + + if not args.encrypt: + 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) + if args.compress: + 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.)') + 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 + seg_name = ", ".join([seg_range[2] for seg_range in image.ROM_LOADER.MEMORY_MAP if seg_range[0] <= seg.addr < seg_range[1]]) + 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() + image.secure_pad = args.secure_pad + image.min_rev = int(args.min_rev) + elif args.version == '1': # ESP8266 + image = ESP8266ROMFirmwareImage() + else: + image = ESP8266V2FirmwareImage() + image.entrypoint = e.entrypoint + image.segments = e.sections # ELFSection is a subclass of ImageSegment + image.flash_mode = {'qio':0, 'qout':1, 'dio':2, 'dout': 3}[args.flash_mode] + 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 + + 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('\rRead %d bytes at 0x%x in %.1f seconds (%.1f kbit/s)...' + % (len(data), args.address, t, len(data) / t * 8 / 1000)) + 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 version(args): + print(__version__) + +# +# End of operations functions +# + + +def main(custom_commandline=None): + """ + Main function for esptool + + custom_commandline - 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']. + """ + parser = argparse.ArgumentParser(description='esptool.py v%s - ESP8266 ROM Bootloader Utility' % __version__, prog='esptool') + + parser.add_argument('--chip', '-c', + help='Target chip type', + choices=['auto', 'esp8266', 'esp32'], + 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', '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'], + 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='?') + + 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) + + def add_spi_flash_subparsers(parent, is_elf2image): + """ Add common parser arguments for SPI flash properties """ + extra_keep_args = [] if is_elf2image else ['keep'] + auto_detect = not is_elf2image + + if auto_detect: + extra_fs_message = ", detect, 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', '40m' if is_elf2image else 'keep')) + parent.add_argument('--flash_mode', '-fm', help='SPI Flash mode', + choices=extra_keep_args + ['qio', 'qout', 'dio', 'dout'], + default=os.environ.get('ESPTOOL_FM', 'qio' if is_elf2image else 'keep')) + 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', 'detect' if auto_detect else '1MB')) + add_spi_connection_arg(parent) + + parser_write_flash = subparsers.add_parser( + 'write_flash', + help='Write a binary blob to flash') + + parser_write_flash.add_argument('addr_filename', metavar='
', help='Address followed by binary filename, separated by space', + action=AddrFilenamePairAction) + parser_write_flash.add_argument('--erase-all', '-e', + help='Erase all regions of flash (not just write areas) before programming', + action="store_true") + + add_spi_flash_subparsers(parser_write_flash, is_elf2image=False) + 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='Encrypt before write ', + action='store_true') + 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 ESP32 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) + + add_spi_flash_subparsers(parser_elf2image, is_elf2image=True) + + 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, is_elf2image=False) + + 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) + + subparsers.add_parser( + 'version', help='Print esptool version') + + # 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 + + expand_file_arguments() + + args = parser.parse_args(custom_commandline) + + 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) + + 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 = sorted(ports.device for ports in list_ports.comports()) + print("Found %d serial ports" % len(ser_list)) + else: + ser_list = [args.port] + esp = None + for each_port in reversed(ser_list): + print("Serial port %s" % each_port) + try: + if args.chip == 'auto': + esp = ESPLoader.detect_chip(each_port, initial_baud, args.before, args.trace) + else: + chip_class = { + 'esp8266': ESP8266ROM, + 'esp32': ESP32ROM, + }[args.chip] + esp = chip_class(each_port, initial_baud, args.trace) + esp.connect(args.before) + break + except (FatalError, OSError) as err: + if args.port is not None: + raise + print("%s failed to connect: %s" % (each_port, err)) + esp = None + if esp is None: + raise FatalError("Could not connect to an Espressif device on any of the %d available serial ports." % len(ser_list)) + + 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: + 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': + print('Hard resetting via RTS pin...') + esp.hard_reset() + elif args.after == 'soft_reset': + print('Soft resetting...') + # flash_finish will trigger a soft reset + esp.soft_reset(False) + else: + print('Staying in bootloader.') + if esp.IS_STUB: + esp.soft_reset(True) # exit stub back to ROM loader + + esp._port.close() + + else: + operation_func(args) + + +def expand_file_arguments(): + """ 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 sys.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:]))) + sys.argv = new_args + + +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): + 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""" +eNrFPHl/2zaWX4WkHV+RE4CUKNC1G0s+ck/qpHHTXXdbEiQmnaPrKN5fMp10P/vyXSBIyXHSzmT/kE2QOB4e3o0H/HPzqnl/tbkXVZsX70tz8V6ri/dKHbZ/9MV75+B3evHeTOBt8DPy/FPbxAaFrlZbKNoHg/3c\ +4zZSMIp+Ut2Mw/LhoTw9mHED3et37PuN278TglSnF+8tvG2r1UVbbp+tiqhdDaOnF1fwroihFc/P17BUo30Bv6u2fd6+sBe/nK6H02qbNU2vabIm7y28j6K2rW1hanDCbY8loLFtU+SL9k121n4qoWY7t6aCB2hb\ +cFs1AUw8eum7fw1t5i04uAaHLRCunb6ujgBL8Mj15i/h717ewXWEf6UlDKJoEGg3beFJ91tE1AICPbRA1bjse2kfhDOpcbEJU2lfAQpyoRJfebv900H1PTQ9JcwilrvX416TDCDANdlLEO/tK18hfc7obwGwdoO/\ +pzR+XTCV1mMCRKl2WpYXSZkeGRTczsREE0AkQjgtjS2IfKBqiSVZ+MKEpCx03dx79mD2sK3bjlzi6t8zhGaNPBRyhqAp5COtBnyliEdWvTO2zzI0wqBiN8jgI4NLaPeFdvzNoMfxKnAZ5GCKxMu+k6r3pcfYZjvA\ +IHxxYQdZwNu+AELI9wZYL6kN9FCL9Bj711RHV9fLpyt5/Kb90wQFnUphPwATxvbjVyooAHmVyLj3vgoauKGQ85DBipVNIPFUCGbvIxTqYFAdCFyRVCEp9jFUSksr69WuvjFBoewKr3CVDo9P8d/oCf57/8DTzCN+\ +qsYP+cnaO/wEor5JuVCbAp9e+3cPSHbgV1juBkn58MmGgMRdtrxXMXsWIB+J97AJCe2kXEuQA2nSadmKMpuWrRSs0xLEZlqC8EvLe8QbzVREkKApJrpEwVaxgkB2BBrPQy4DkNLHybQFxAGWJrSCSjMEsDbKHu3E\ +pAYBWGw8ofVTOv6FB9d/4wFpdXZGoXZhweNhsM+pH5JeIeialsu5pBuN+4FVyqL1qC85EdCIu8uG3fH79FPfv2kXAin4jOaPKoEetDxUDJnqJDFCWa9YhgJXWp8jStY85Kxtdf52oIdZUw9VQIA/nSYzIKYEBTTA\ +YdfxzWR+dpSWyQaSFWhwm02hetuByUOpQIoRtXGK/1JgUFCVWiWRA22rt3b92FtQIymTPulomyRnR0wVaUgxoJ9L1mImO2nBmdDnBU3JDQclKeLSKBZZ0v5a0CvAUMbdIlYnxCZ+LLBFAADjeAkmtHxKg0K0oeRI\ +O3ThuoMazxirOXerpumfXKBbS27t8reRy+R9LS/BwjBuTd6j6DhlvlDuSDr/FYsveMptt4blMDyD9BJCKpagIssTmLMd6G0HmKmCHrCOk4anpE2kzyro8/S9QOjXsDeizQ0Nt9X2VlGTudSEUgvIL9StG4C6yyMC\ +Jouuie5VM4QipJ0AqoqhKsZsscHKtsbQJqz+qB3zSt4petf+DOPBdfRMVQYDroffsmBQppMJoCtYAgDawn8Hrd8QyeFK5DLSBg8rS5HJUkht/T/LwsaV1G/RzhybTXu2UDSaQcPFsOE56WmApsU4maaodEdsqXtQ\ +gcgmEZrH52tkcDdahGtOjMdjReQ0KP2XFcOBREQ9p7qPfjg3/Zqsd+N7ZMjtUlfPyS5ZAfa5+ASnf2WMWbJNbCqs7nGMVRC7UlcxtVdO1vhnWaX7S59e0ieb8xLLCEpvdNVUjp5bCjQ8jm/RTJq008AerlTojxb/\ +v1CS/wCi0kzesgTKnwaAwvKMEaI75mkr3XQd6OLm+p7Pu56tOZiX2QkQVyspr6D9PM4iXnv0naCXOrsNanYt+hORJCgjNHaJ52IWvX7sNHBKBIicTImOe9BeMQjEc1pK65cSdZOXzdxNAdIahtbmADqpWEwLEaLc\ +mXfUqKvs0KnoSMhrHew9ktST5/iP1RwwGjTSWrw6/c1RdJhG9HnaoRR1igJFFm1AtzI5DSjLAMTTcaCf0RdtPgMrNr+LWEmDKoaq6KE4bNK5eNdg7+S3O8lo0g5kFjybxMvUw1Yn4NG8JyH/DHkiFoKPBwSv8kQI\ +fuhJUVPoML6O6Xi0ptdMOj9l4FgcatQUZ8zbHn8Pur51FlLMLg3m1Bpg8vBr+IvLuArT6fOSLUOQYkDI6Xsa1leFj9PpCfQortuK+YB5TdQYLFCDXaFzu7eNts3jDL32x8diYAEqpkKTGcGA7AZoQDtq8pBmik6A\ +qId8IKKMl0OoNiedivHA5sse7yrJwyYiOHOTn9joVxRp+VEM8kt4OG8dGjtNQlvHkRXY6lPgnwmYdz52ADQzBntWTJMaOGm8kXZ1tLgP8FwE7rJdmxKGpaZF4z4VRYyjnrAdQDAwISkJrFF/MVl6SEVgkzf03Pdm\ +0N1+cNA3oGNyF10TrWFEIAG+QjnOJjlY0/2+UCzt8gpAjXxYA2RaJvErdhGavjb3cZ/m1Q8PnpqDBIc8+SsjHb5anhDbnVnS2bwoxqIsSkkgOZV0VGomj0SPcGiv+TSrPSFicRgjws6ScocJjqUvGmbjmOxx17wh\ +Gw8GqFKQG2p2REUjFJihmQYDFWm8Ax/KW4TyGsNXF5uF+HPtMNNFeasq0W4v85N9mkeVxjC0JpvVNVUMVKMeE0Lr8hh6hb5VdbUob5M1VubvuF8ky7dUKKDrSiTdgvWbochjkY6ABBbwd7L1N4QVPaFyfVFu778A\ +WvgAzDUicQHOrkOZW3mpDYQ11v8EMEmzbdEXxAWqhdiG0q4TdBC/BFaA/yYtgGoKcovKaX4PPfZ1tqmaCIsbVKyh2CLgFvSVRFswB/0fHDC1zF9oRTy0P3AEEARifsq6EKIygBeQnWP2Glo4v6UuQHXYvGB1k7Om\ +BL/Z2wi5dLDP/N6LqfkeB33RhFxNaKD51Fwqp/u7ocHMPMODFTLZFvfgpE7AVXMNTm4b0JoH8tBPbSrje4BO+8EklcUQE6GF8BFXUbIqPggQYrFSvM+vUEYSRZj0FeHcqLtoY4BEUa399bycIGmXKUQCgcCBvN8u\ +iwaiiNZCex5PkNbLNB4LG1j0nEYAXfQSUH3ebS60ZtBYmNhEE44JokE/Y+Zxop/efN0JBu0wbvUcvLrvpsH6OYyQK2I+xAKEidQCelbvIgg+fNdpCe1Oh2sVs+PRjI6KiolPiY7IwtXljy4/eMBEMeW142EVacCw\ +Mo2xEMpH64fjjbRsWcyc2hSVMEGgaLIBhWihEIlT+IG8jk26mDTRgZ/yb12kV9eHogXAZGyQQSuibrRQ2NWBQIVi9dLq4vWWpyoTxbNttgdBk2cz0h8YGarPWdDWbW0UPDDSErbEkDBLuFoSFPD44pzkjCv6BojH\ +i/kCeIF+ZVsIZ8eBy0PGAxLhB4A/ogZauBY2rvLfCGwHM6z3UYDcYvjUCix4CRJtS08W3E3H5KHGfcnjUZFfhwp1PSo+bfJOBXsvGHeqg5UHKilT2XWrIQzoPQPEj5mxfZUPId+fXYsIZp01hUwf8/5RTXoDo1X2\ +LYnhioVHhwjZIaoD+e7KOfhDGXVCiNpZJzhDPPXCiLJfBtMsk0cJVT+VRYBxxjKODcdB817GsTIOxwygx/wZW4u17hgO8Vx/ALM8IYMJPeqUn230Tl1eAiW84/gOcxM5NT30jd6edrtdFM8D8putEmCn4bQ/To37\ +GTNkk/8/MqQJGLLBPakrsYg8wfXIs/ZymjXn/hFTNhravTnHJTbY5K0F9LPiDaoxxU2NE4H9JdG0hq7NOe/ycOzOO5qWnGuE2S2NFqglgjL+Lhy+EcR5Jmn5YoTm6J/FrvtnEAZbXsIRKMRT+TxYXaEBUC9GPyUY\ +rPoV/nyzBX/fkfeiNCxN+o79ZSw5CAVNweOwuxjRQCNiAd5F345gV7MOTYkFmhBiOYAV0ZoTlySSjLp8iX8J4RhSs1M2YTwaBWHozeXeJhrMEHfiYP5lPbCo3Ib4bEKtTGzJa7CNRuxNgKfs6vlrrmxfBNNsSU4v\ +T7YshpNF1abDybbTb+fbYgDQZzjOVDBoZjw/egXTSDCEhMIf94sAI0RP6QN24agOOApUjQw+zKHAzbdXIJa/Z1zZfwSb9QgWbGQ4+FOQhNol00NX6X0iW6dj2PiqcfurFvhCnTL3oi1hj9khq73H7bOfvfRM/NBo\ +HPwuRQjuDMwTqCTUhTdLH7MUividulDkTiv9kVNn3/kZBuE1MmS5x6zr8fAZ+1uwbg2FiBRbXwJZ1rMrHqDXhtS+s65gD10lHBoVZVKBD2Z0/BgplQEnr2G1Yf+XZQIlllhl1RuRcKhEDr7CdVu/SW93Ji/03GTn\ +czL+zcCCWXaB/oXWy2nPdEEczjh0N6TJFttqfec4vjswbupOd3ubl/Aec/ypBqNXZ9N75MJrm0FcWGcoIk9lQ1k96UIJuKlr9fG1AnO+UoDg9o4mk9xZnC6nYWH+Sjpjc6He2Wam7gZjE6JIaS9zb3mAltvAhtNG\ +iAiTtq7vSqHz0utki8Kh0FNS7nLopyVX3gUtMJMhKe887UitlYN9alM9+oCB3auO0jX1mCbCAIRrncZ3Du8CKFMxTdDPArmht3FDtRwF4Qx1RO0xyq2PH4FqmZdbbA64eTxChMOL8njn7hwaO9nMNSWSAARVafMj\ +A3NPkxJ1FhKcjJ0n0IfLk1HMMTaOdtjiFS/mDoSsYDo5ehXiobMuqCSMmu3cTcs9XLoRWIZZgrvQ+ZMlzlsyJUbgY8KesbMFisD/9dEunKBkz7RqDJk15xRBDakIFB5CPQKYgRUo2mXF2EIdbDu2CiAuPHowuW+M\ +Q2wlpYnWI/wKQBbF4WyG89ial8U5R7lZpUC/Zlo8xpZ7lw+YYtQxRkOPqWs9RzjbGqrzmFECmEOuVrIVjbv0eoeigjQDU8N8C0nAsJy0ZOh/uIlYEK+1q3T3EarGV/79FpGxwaUq77KWzSRzrjKdZYz7CbCukBxU\ +QJQDkyUsxQCNpMcoijhSr4cC0JyYWuF+bY0A2KcUG6WoahNtzzoLJ/SqCknS4qxLg7HM3YurD+RG4zscAUWOCuDRLGDsow4xnwqPF1GobQ1TVQQ9zhL6YAlBhs0EzGbAcPc4OgYYEcDDh0hZCNqjZ61EJhw7C3UK\ +lcR7ybfQ1zzewyEh4FkdixE0L/EtMGy7Wnfa2kjZRRfGp8ShNN7dWJOd8iBfIcwtgJ2KmvM0KFIr8SO1Q2KvzBP5tCEJMRmtcaNo1cFdsztrELsvkEW8q5hiaDvSlG+JLqRS22Al2ucXmwdi3d8Sj7MKUssyhizb\ +4kUNDIdLQDm4iS1at1tIN99QFA6gQiRlvyYeHAnA1rz7hIFnKzukykRg8de00/BRtb8ieoM7QeAvwvoV6gvr/nANZWemLrvNnmG+kN8yMdVBz25DM9oPf/uasdciGV5MB63ipkcLb2ZEC5eI/DSkhaxHC4ZoodDQ\ +p5GwD8Nb6B+oVySE8QfSG7oSvI6RCCBHj4VxtKV21kHAJJf9VX9KEq8ArPt9mL7x6U3TpNyQ7baLTdiNGse7otTAOYYA0LOOxigZrBDLSeU5+XwboC+nJLhr3rtBMiF5sXE6NNIHNKZZg6v8PzHv7JQw6neHvD2X\ +lByD/Frotp8XeJOroe0aBzpMF+75FD/jIxS7iNeXCaejl5bZkILeHfBudRqv6zREMm1D9VTeO+qjAImgqnd+zw0XEA3wxSCabXoLvPMONqccZx/TWu/cFdMDVqu4WDDWcNOvPMLMJWdJvaTSO8YCrjVoC2o9dP9/\ +xXTDvsMx3PiQ0Bj8qjmIFr2Av+mvV+/87v27Lquu4DiPza+JCGj7AkTR2nLCNoyBm7c25rw9e4Cm6uppkfePyXr9yZG7H3PsqUoRaAQ93eOcVjTUB41WRAdkj7faYqlcJdu8xPAJpDprWzC82Y6rOa5VpV3ky2D2\ +EEZi9nhUlo4l5vk1M3bdq2/GotvWRAX9SfTjOr3plOLpXeaLrvIGx03Ruqtw8wCEX08BVisUoFutAO2yAlSrFOHGJyjChdgVKxQgaUZCHoWNb7/F2IwowBIFCdoxH5MkK7TgW0oz1pKKSfHF67Xh5EtoQ86Q+LIK\ +EYisuoks0I8Ad0iJOiRDelsC5GpZKRItoC5cdHatrtZ6BEEqjU1j7Mpw7qLLUBQdctq6InLEELtlQiwgMcqryf7eT+TVZJfP4qacOIyoPukSSENpjKlSHByu7fDERz8sfYKIk7gw7gt0gehT2h/qm67AYELfSgc0\ +I0kaa9F1dMOr2i1d4Krw0s0gD7TaS1aYtrSESuOa1XnWZXeXeLpp/QMHCVcaLGqq9sazvxCdtJrtMGN7CXPU0GxJ2WiWSE4doPWapSG/Q2VhKhkoR/RhG0AX2CVwCmEFutABxdT869gM7A/Lm7sd1uZLWHsDAdnq\ +Edti8xBr5OVG+vAhJ+4Q+RCMvwDe7vMUp0P5tmgFP+I0uthsdSsEFzbfPBEMDg2/39hHB1vf1UJFwWII8g4HyEuJ43DjA3GeDuLJLVKgN30bMlNzPjrhAFIzZp7wfthEcIgB1dYQcSIZ3gEifk04MX89rlnNtQbR\ +r4tEDjVpTCLMCbSqPhaQws3m02t2msXeW4OTB3g6hZWJ5t0aNBRYUH9CtsbSFld67RYXRNjd0BTMyIwjWa28GViVNOsudBWYftxGVRjRyvik1TCY3Df35PRd4/WmLIh3bfcG6t+SRn/DocHKuzOXwpbAhQqPVWR9\ +UhYBjru1Csm6st/e/zTNPmYIx6jZ3YA+DSd++KOmY3VP7eHBNTaKMGAFNmqZXfYFCCv5jFNWzfgpBIaIilglLhESr+30U2nJMC0BTVl2GMDe8zRU/GEaWiYgtUrl40pnQQokZ80bP5/59aoeI2rp9aqehh1HaknV\ +k9WO6celJxnWPJ9GNUaoRq1Q+K35u6B1RmKZsvX3ERGpCrUHW3m4b6f6FOEF4lqg4q8RhTNONa+E6/TeMe7RzMv1GR+Crlr+3hsbjI+B46xZSqkcrcn5Z1KY8RRmlimsYvOhyv4NFFakNwsqcLlAVoF/688fcKNp\ +rxFvo3fyisOL/kjptIfyy0D7ULajZreiXEBPuPMx8c7ybc4lceVlwS9plEcZRp238eTClDWPxujHm/+G+jvH96f3T0XAxnzmshy9KKpTPFmx/yImxAItK7teIBZ/vN5Wc1hVbDWMd8bbCDQAegk+HGWPUpeU56ow\ +kCHqFOVXkZxI6LncRsUrrI9nT/j8jPcm7GAHAPNeWWv4ZPWa+pRPmjS2FNOvMnyRfvUSQtn3SIKUHM/WvhkpeuXAAZwM+oJJveTwtoSY0+5Ylmh/18j2OoTn0aGc8mlTVA8/E0F3+WTz1flkJr9By3NUZ0RQii9m\ +8tccSFuVxpJ/qTSW+nfnlZG+Ql/mk1LLBjuzI85LYXTYHPIH7Kpg15dILJOM/zBth3N5AgvhhoyBgfM9Iv3bLfjhv3KG+hkxL8U7H/BY3lXY5n0hTPSCFKtaclwHsuIhypo1yQJVYx8E7dUDDTeOj2lMiAeiDdnO\ +eBt0+oL0n8Pc5YoCrLD11aSLitUxvobjFbXtEnIU86dFOeokLf0Uj5Hrn9k7NrAueKpgXmY+B17OApaDs4BMum78A6fp8EvI9zdslij7jzkGubp9Xz4SVPosCj4EAD+wI+DnP7JtUfIPkzL4qJM/5ZTx96kcoudy\ +WGf8kW+Tj3zLP/JtMB7A1nDZVMkezOI7tLK/WZsB9hNaAryOAG9ZycP8bq8OoGXX184es3/6HehTB3kirsRzSEE+N4UoaddfluxsuGRg27gS8/Yx7HI3Yv3Yr4bpWl0e0zccd+TUgTPMAm0XdB5nnXlj7OtdIuJK\ +Fd0hrCbr2B639bUc4ON1hgyvpuCdcybT2n5LpFgyTTUNbxo2nAaIG+jZkksaBJNtZxg7ubsFUkr1bR6bzMpyq1XWOyCUYWcBdUuzDXkEFSQZYNARnFSrgjzpMrobQfyxWZQ70V0inxoGMHSWkFPbYEukztjmAcE0\ +ObzFMSCJ20i4vWyXcPdicYKRCmQVvOCkkeMqcKsJ+hUFNy629vm2CcTQXS2nb8AppIAH2y1gvdb+mK4cc5xuX1xAhT0+LIuBtwY65dNhDzkZRE4Fdocg5GXBXZwgFnfDyCeKrrgDGcPg2bL8wPdm+b0BYwRfwuFL\ +K6eZ8wMBqjvVtFhuLSFvisLtsSmnOc7fqxxxNJ3DAHg2QQ7YwmlRB+M7NYogwgn7bv7snGF3x7Ix5iqUmlkiZ7YmIwmghucpLJ/dJNpFL/v9b6//ToevtnY72kXTKif0hZNr6vD+BGIGm3aRDLQRKm6vltv7/vm0\ +GE13a9ccrLqhAU/21f6oIedSxXw/U/HgB7P7UI6vAYMVRSc0EXq4fAJkF1IBqjy+mqM7caoos9pABksFiq046MDU9qGcUUx9xSA7rxxEuZWcccvxgDFSNJhPwG2OD6MpmXYaH/UvLrFpjFeTxHg1CR7DSeN7FMfX\ +OrxUSClJKJbLs1RQUEsFH6OJeXPN9c//KbV+fsSpZqjd4bIOV/YqIm41hdaC1w9712vgzSLnV70aWwHJZBFmYahEDadjlu4wOuxuD+qiMuGcg4u6bFryKmjOQUBwS+SJETXUeNQNz7ryzDClmhUp7qDg2eaJ4AAq\ +SAIeKRJNwe8hVvrIPMerSMwT5kw5rQpZc7i/CPuGeNdEZUbfcDTNWkj3E+VNl0SYUclbbO7wRe8eMhgm92umyax0Dghbw1niZrIEI27wqU9bOfMkwvfFEzPaWdseye1XQD2rLrl6Inf9yEeUCkC1Vv/IyMRsH7MP\ +umN2cfUGNMxZJ06cRAh1F2LXfDcYamRDSIGQWe1oJrWdDXfIWHRO+Bya23rCDiUaYFsjcEdKPucARCIZtjrnPUm3xcfY1DOwUtTXfB1Od1VbQLBWMujsuNVF2PLBPqsIpzDPaBtFCJzgNG8AgQC+OeNzA874S8Mi\ +QWZ4VZZSZyccbOoo/UqOmY3n6BarJ7OHtzyzQt3J9gT3eMePkp21aDTbPpEkxaSRizXerpDMRotMnERrJNFX32hGP/XVYNEROH9mnDOvtFwwU4tSqIaYlAqhu9Q7N0nN5Yy0CZTTyn5qrjBecVOR0R+f1ECoFLG/\ +AvCK7yVS6R6I6J7pFzhsfnOG79ARSQMBZn+cKGUb1OJDetMNckq9Ck+TXvaPlmKyrfEXaz3Gp3HwDoM6hVynFSzBErcCayKnev7srtISXk3IbNRyuFGYdrLiqhYxuwpRiJKAjsl9Uznhwe/c04sFqF7NVxx4E2LY\ +rU2fAnq00lEADbJpvVy9ZseoZrvYKjja7+pYxGTa9UG3LYmxO7yBqkkD6Zxh7ROZ+W5n1izl/pd4kEIO5Duvzz9ZKPTpsrvG6YpTvqvqd5D665CMrsLC+7DwoU9tZnDbYDEsdxfE1V+FNFcPaa7kCKtj4kN6E+JD\ +SgyJT5dum4/SOl6Gur9w3jzBU24jvhYMd0UCK5hcvOeeW/9Om2+raU2OIJruFi3a27hPgWu+G6IqV90al8vVnGgu7ZzBQMfMRfZ7uVPmq+GdoHKdaM2xtzIzRG2NWpOLNOSglEikM9naPGBHo3wJNtfkjDjL4fEp\ +i+/lih9/u9zgxjjZs2nHfd0FMf0dK2nyd9KWtu4sbTpsOeMsIeiRWa6d8M8s0yeybv4QFwvIhsU02myaWAkSQ9WTiFicqhZuCOwZZ3ggsC9fsVlf3jojIuT3QDSTmHxIepVUCc/VZn6uiOMpXSdYwqohq7mf7gj1\ +0cbLfZ+NFATZ8u7eQOxVr4C0snKqAI+bpbf420T2YWhZG07VdJhzD7qjGktDR8xny5wuZO3Cn/6GCwcRiowVZZ1uPLnYjKkV4H0RyDezpBxP5O7Y+MOSlKOVkgmbicjHUXBln+HziD03WqLdMRm8zlW7xG9tp+QV\ +bBKn2PLPXwe5QjVfaxIGbTGdfvx7BCgGB+ciNWHc7D7fpQeumg3c2e5mh+Bgh8rwvgvqTI7yTTcSin2apcT4uZgseFYRh7qNseJtucTWLNk+eNpggjoSQcGqeI9WX3JL33h0N5VpVL1mncW1EH1B3NPraXMU4aXN\ +P769KhdwdbNW07EZT6fjcful+eVq8Y/wpWlf1uVVyXc8925uxUWeBNab5GoKJjP+yWV7Kac24P2gJiiI7sWCrYKCCOG2sMVbjvB66oIC3KXiC0ED3hwrsbBHR12pTh0Wwga9AggfuK1gRb8VxQSxIPdM+Gn5wnVd\ +l2wkDV7/J5/EVnzExaGTePg9X9ksN6i6iTTVYeHaaVxXiK6r0/QKfIWUFpXOS/OabHFaAOfRfMc/kQ/LhXdBj6LMfifYv6fwKoAKHBxfMCqYxtLVycMgbzYoD72W4TXn0175qldaDPyPwdjhTVF0+1yornv3B3SF\ +cvnS5p6DrFdcFa0H9fXgezooZ4PyeFDOB2UzKNtBHHZFXLYrR2GhVzO8n1r/tCIY/+/66RvK6WfS0E00dRONDcv5DeXpDWXz0fLVR0q/fKS0nIG1IiPrI+XFx3jnxt/n8m3+WTi6+ox5DyF3N0iBAeR6AMnw4nLd\ +628tLNwOC71ue77IUVh4ERZ6C/J2IGkGcJaDsh2Um2wFl+gvyMX/binwR6XEH5Uif1TK/FEpdFP5M39adYEyz4FT5LzuasgqPP8uF/xJRMlz2iodd+1MN9n6DY3lbJqq1jb+7f8APK6wwQ==\ +"""))) +ESP32ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" +eNqNWntX3DYW/yrGCQxQ6JFsj0dmT8uj6YQk3d2QbghJZ0/Hlm0I27ApnRPIabqffXVfkjwzbPcPQNbz6j5+9yF+Hy26+8XoIGlGs/veJO5XcQgtha3yeHavXLPS7rN1P/3s3qqEOo2ZLdxvaKnH56c0ijPr/2em\ +hv0UTZAfrYQCFbWiHyMUdWM3bGknA2c21FauT2X+7B1YE1O1sUJe+p525M/t2eL8dpV+3AY215ncxH0XybZafxOljojULtCpS0dJHWju2ohndunMqqIzQwcScf75Ye75Hx3aRoXVFoTaH9IG8qPoHpG4N4SaLIUh\ +oPs71xjDTUy4SVfTaD0W7l+cEIt6YVV+DNvC0Bs3D3qbixRoeg2idgTZMczIeFMQXQ7cTo8u3KfedP15JGLFbbjWGHY4C51BVsC3Ma1os8HgydVA0qfIzwVvak5OUxa1VQcFbHRSpUuMNvQX9RLJVUtKih+Vinit\ +zBG3TCQOpLSIv4+OpHVK3bhGD/Yt/L5BUshe4M4EGsfS8Le8AHt4/h2onR4MbLtfZZIsWJoZmKgtH8GQm9x1NLnRoe15XGdb3MCfhRNUI3rm9rKsGaDXXazvTbRnEwmzor+wR8Ntf5Zl26ucLHUzOJY4TVBFg6Dl\ +0FdxXzCe7CdmuqPH2MFdTnkkOgAp1xGFvF81gTO+jZcbuiveTwV7btG2J2TwupoSKin1xU1zS4wb0cBmN7IYWi9vvWL+0FH4fR5GiioQ1GoPigu+ej5gygUf26RbvDFApxNhW7HIoV0IUjh9s0NdeEtLhmj0ch9U\ +i/HP/apxg4tCjS0rWw6WDjZevPvx5Wzm5phSVnfEITLKJ261G9HCZfOYeIegk5EbELbH8Api0QVwKU8coU2WMIYwInSRNRt7kJJO2WLnH0jVweu38AdIhquC4Q1xYOic0HQ/ohE573J4+hjvD/NT4kQdnJZwtm4J\ +qE0E/oGqb2Y3wd10luwIPYAmTa2zgO9gP1qgTxNz2i7yLVkEovmyLQffXCcxjmbsF5tsg2eCivereBzzslaPSMmW/RMqkLgbJV681vcRmewO8XpKrvocZmbX8ImyOk6hnSNlYJE1OJRqd5eONTQnxAGmer7zjvUF\ +tWp/dsP72zHTWw3o3SOs8Q48EATYi2LLSAlxWkeygPG2GcYiA8bIHMu6ng/3xrWyp+F9Jv9jn5bnFKtzVmMDuskB7F0GBJH95Fs3KQdWjYBXxti6NlaT9kX84QCuBXUfO/1vzddsCZWNusFzwn3dR7mxQTQAVKGA\ +o/hxYKRlwElw+yW7ErSil98Borccj4iEdJgW7wTWBCfI/IYNcYWKfHnt8/QI4fPckrKiQbDpNtFqAPa6Zp/QrZEh9FdR0NPIms2gpaghxHdEJ7ssBW9DkXbCibb9M424iuX1Mf5YxB/38QeA1CVjHeA5mwgcccXG\ +sgFiqyJ8kCvWPd3PmGegDreBU2il5d7sBvyIaS553gOSw9shAD11DIfObMpuA4U+iafES/8Kp5wJeoHsGyHt9TUtkrBRlftRhOYFdWSJwXSNkBGQ8syvRP/KBATCzgYAZtXZMJZHEQZyYfwKbl9+IDKq+mw6uxVK\ +NoUfc3ciHsE6i+6PNRAhjk8kWojHjU6cjOsJE9WsGNd1ev1kQmhqLQcOeNrLdv2lzcQN1zkd0cV5BPOsLdYLT+IRzYE2Bn3ZNnr9P1jLJsgfyMDK6ewGLjymiXX2QmCqp6MJH79QMNlwrNVOtqazERkZsqT/CIPJ\ +0CzrFWjtwlVwqVpnn8zxceA42fceuGsUef6Q8b0CojScCxdq9RviuOkoyPJJ0BqkxZi/O/z76fEzMj4Kng8LjOkXR6xBlCZgulEcLiV4a7JD0EQzwPOjQfq6lgaCTP/hBDmKdiiiZMUfzyREdEu4yps0IV36csVH\ +dww2FxIZHv2yi67EZOxRrN3D1g/0p6CcEBeDFld0iXtyQIqE6xzOhYeuH8j54gpd0C6QvGiOdNveW+dNCq7LnHBcoh6C9g1ycpiPtVKQKIXlJ5hcvEgnIOrJJoeLeMILgnGrEw4S0Ql0AhHHTE0nZHWURLV5BCB2\ +F8JO+87HcM+CHxSkCaR6nExJ37EjZIy50+hkMxnUUXJGyvwB467XsMOaQbhpOMOwhENgXDZLHoEipdCMawcJYQwcp9gITXTn+Hjgt+UrtvqMj4w6gVmYhXTE9VoqJNYbbreGQ3B+372CQ7865t0AvBuU7fVjf68r\ +ToPKT4Pr3nNsZUkNuPd96NVZesyXB++FemK3sQ+D37PZ4v3ZVogKtS0vKSfQ/TTcX/kN8oYbWKWC8oWep0l/Ao1tsNUo+c92zqbDYpG2aeqO3IxyPgCJRrSTkBsU1NasO3Qqm9LwYI7Gs2SDGI5xwYRCJa3jwGWT\ +MbAPnWBceFCEteQ+ppJcNzHTsNgzovqD4ji3whj7Mvsb3oQz24bv25efkr6U/kvpRK72W9IPkYOaSnmjl/S7RG/Vn/N9S8EbatfWaxdIbokq9KKOKjzoUyDMtNEOE9w/LMQJ47BtE207/Z2JrHMR4+BQW/6FTtwG\ +S/Cd38tk6HDkcMLZLxEsPgDu2IQlejDNEKNQgyLCOiYMPxUn2bGcpnueF50Mo0uBXF630qWp6xExx/TxbvmQjiegEDJWDMcOmRxTB0aiLZUX30vesI+A4Lm9xfJiKRvmMIhDj2PjcSpcI57Op8k8JdXX5S5nseV/\ +oowLfiBFNfrfy5hzTjCquxM2QQy3M/KP1j4mKnQXTq7VHBzuObHModMbNrQox6z5p8ey6OWaQ7FIZedP4CB/4lMu8q5sRcTP1+wDUZJuVok/F/idckCgBRWl8hI4Pb0OduDnKraHJgjjVng7XR0jN4KCmkfKovRW\ +mKbKn6UMhkEM1syOGVhqFS2znO/rSmznn1waMVTXHhE+1VHRbG1tHvStQEr3YN1TONFELrxcPrBGhryWV4hJdGRC/NKcu1iollNUu0GRGPg6rGbr95DXt9lXMBNPABvI9/nO5bNQ81QcXIjHqjPKuoUuwMmqFjaf\ +kusbbb8l/EdnjzWpnjS28gg/3wE4doy1E6rmVZyhNIL3zDev15nk+1jXrWhW04C6A58x253sku/3zmfMBQ1oF+IfSnZEzK0aX0MSVhrowSpymcj8SUS45tsoaiRzcod4utphF4PMmubDHAgTtjawUlz4EjO9Sn0t\ +7zRGxs1lUDlIkt14gyzfhggNsYXB2BCkkvkQdUqFownnplvejk5lYiHgnizZEIYrJeMlyYRWxUMrNsxJZzfYbHAOl3KZtVj3jNXNSCzL72yRUonC1KxXVO0D1IBIsao4jIo4+4qcPQqi+wDq1OITlMyCl4bJN9+T\ +dnSSvwxvBBt1B15v96ngZ30RbZf8i2P94kWKZNy8oEQR71Al9MpH2prRhy7ffkVKDHOgvoCxLdytiQGjH/qeuh96LR9VlsMgTqll/Or7hN9LIKKqxlxJwJ7a93QSpZ1DKD5Ziq56ebQojy7BkMYhFvoNifUh1j36\ +zZJFr8chDSLFWBMowPAHHwnYdRGGKqvgelUXp7aDuEKXTwfeX5fkz2/CCrilZc3DjMJwYuA9Wc0BLk/AalE3CPYBuYAdhh2VlgIcgsy8BU8LAmsUF7Qb8hJcm8SXrpKBXlKxbk2lN+fnoojkeA5WQZg65XmAjxbe\ +qVGoCA48eCr/Cl2KfUitXyL6NA7nKePRkJ02TULTkClMpoB6Pf6dI3R8G+iWlHLNzojASQ8pAlRCesiaxX0a4FvV+qn4eLRFmSPyOeM3LQT4H46Hxe0qq2e38spYZRsQL9TZDteIS3au0DY75DmlQlGP3z3woJTN\ +gTphAF8ay0ndqmyMPbiVZ6XPr6CYml3ffYY/99n8EtTlPRgIltq65qFMqpdMqk9SOTPiEaHMNLatYzbo7tfwrNJlj1EGNW3dqTs4/xY+y/tN2Ka6K4Oxg9cBRG25/ml19xMz0kDvr5St9PxUB1AORmVYQxuWWcXv\ +Ub0kj4ZSNyDBSh9XHKtilYNO9jdgORDZmF8lAiBDayrolUCj4ffBGl6DasO1OpiLYMjRQw1w3PD7KHZgTehj2BUrRzVRhy8ywuI2vP7g61bJIB+Vnolhm7/EjPrMMRZkbOU9CNfeUbwx6THwuONAqbyjciZoA5ai\ +vS+b7PwIUvpWxHsO4Vq/jUS8khvLO/mKK/C+bufHKddDO//uNzqnl5AatfjTGgjSraDgxzUKrn9b7QTFrHDV1YTtFUvPXBS/+TOcu1qd0DHQSVJQRYBFoS8svF5DoOgmv7NBsIbSMo4XHe6yPRtdTeZYpQgv/U1B\ +EgaWgrrY8so/8giUUivbvJMaWhzjY5gCZZt8yo8xvbmDG16Ct3kDOz9lB0KY7AMeLF4NrvGBE/ObOT9xqnLuIWkU18ZerPEPVrznW/F/eON1tSh6+QA+lxeYRlSSQ7y5goNQLbMNSDG69yEUlIeaSo+jYM5czRZe\ +MefbQPkHXK550wpfnau4FEbTYNdnnlhU8xSi/Om/aOX6RzDvJnzGsc1oypaJYZvNAqsxjojDWPQN+RcqENIpWxxpjbdSbpWh1j54uVU7j+Qs2MamXDDFBXHU31qMaqr4H8pw7j5X+KLr+RPaLwSi/jLNYGkaCAnL\ +ecmQXaO9BP/h7+ffFvUt/NufVpMir3JljBvpbha3n31nUUwy19nWi5r/PzCq2o94JN4on+RlqbM//gt1kVkF\ +"""))) + + +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/dist/bin/esptool/flasher_stub/Makefile b/dist/bin/esptool/flasher_stub/Makefile new file mode 100644 index 000000000..6f1b9baae --- /dev/null +++ b/dist/bin/esptool/flasher_stub/Makefile @@ -0,0 +1,82 @@ +# Makefile to compile the flasher stub program +# +# Note that YOU DO NOT NEED TO COMPILE THIS IN ORDER TO JUST USE +# esptool.py - a precompiled version is embedded in esptool.py, +# so if you don't want to modify the stub code then you are good to go. +# +# See the comments in the top of the Makefile for parameters that +# you probably want to override. +# +# Copyright (c) 2016 Cesanta Software Limited & Angus Gratton +# All rights reserved +# +# +# 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. + +# Adapted from Cesanta's original Makefile at +# https://github.com/cesanta/fnc/tree/master/common/platforms/esp8266/stubs + +# Override these variables on the command line +# or set them in a local.mk +-include local.mk + +# Prefix for ESP8266 & ESP32 cross compilers (can include a directory path) +CROSS_8266 ?= xtensa-lx106-elf- +CROSS_32 ?= xtensa-esp32-elf- + +# Python command to invoke wrap_stub.py +WRAP_STUB ?= ./wrap_stub.py + +# Pass V=1 to see the commands being executed by make +ifneq ("$(V)","1") +Q = @ +endif + +STUB = stub_flasher +SRCS = stub_flasher.c slip.c stub_commands.c stub_write_flash.c +SRCS_8266 = miniz.c + +BUILD_DIR = build + +STUB_ELF_8266 = $(BUILD_DIR)/$(STUB)_8266.elf +STUB_ELF_32 = $(BUILD_DIR)/$(STUB)_32.elf +STUB_PY = $(BUILD_DIR)/$(STUB)_snippet.py + +.PHONY: all clean + +all: $(STUB_PY) + +$(BUILD_DIR): + $(Q) mkdir $@ + +CFLAGS = -std=c99 -Wall -Werror -Os \ + -mtext-section-literals -mlongcalls -nostdlib -fno-builtin -flto \ + -Wl,-static -g -ffunction-sections -Wl,--gc-sections -Iinclude + +CFLAGS_8266 = $(CFLAGS) -Iinclude/esp8266 -Lld +CFLAGS_32 = $(CFLAGS) -Iinclude/esp32 -Lld + +$(STUB_ELF_8266): $(SRCS) $(SRCS_8266) $(BUILD_DIR) ld/stub_8266.ld | Makefile + @echo " CC(8266) $^ -> $@" + $(Q) $(CROSS_8266)gcc $(CFLAGS_8266) -DESP8266 -Tstub_8266.ld -Wl,-Map=$(@:.elf=.map) -o $@ $(filter %.c, $^) + +$(STUB_ELF_32): $(SRCS) $(BUILD_DIR) ld/stub_32.ld | Makefile + @echo " CC(32) $^ -> $@" + $(Q) $(CROSS_32)gcc $(CFLAGS_32) -DESP32 -Tstub_32.ld -Wl,-Map=$(@:.elf=.map) -o $@ $(filter %.c, $^) + +$(STUB_PY): $(STUB_ELF_8266) $(STUB_ELF_32) wrap_stub.py + @echo " WRAP $^ -> $@" + $(Q) $(WRAP_STUB) $(filter %.elf,$^) $@ + +clean: + $(Q) rm -rf $(BUILD_DIR) diff --git a/dist/bin/esptool/flasher_stub/README.md b/dist/bin/esptool/flasher_stub/README.md new file mode 100644 index 000000000..0e4ae6837 --- /dev/null +++ b/dist/bin/esptool/flasher_stub/README.md @@ -0,0 +1,39 @@ +This is the source of the software flasher stub. + +esptool.py loads the flasher stub into memory and executes it to: + +* Add features that the ESP8266 & ESP32 bootloader ROMs do not have. + +* Add features to the ESP8266 bootloader ROM which are only in the ESP32 ROM. + +* Improve flashing performance over the ROM bootloaders. + +* Work around bugs in the ESP8266 ROM bootloader. + +Thanks to [Cesanta](http://cesanta.com/) who provided the original ESP8266 stub loader upon which this loader is based. + +# To Use + +The stub loader is already automatically integrated into esptool.py. You don't need to do anything special to use it. + +# To Build + +If you want to build the stub to test modifications or updates, here's how: + +* You will need both an ESP8266 gcc toolchain (xtensa-lx106-elf-) and an ESP32 toolchain (xtensa-esp32-elf-) on your PATH. + +* Set the environment variables SDK_PATH to the path to an ESP8266 IoT NON-OS SDK directory (last stub was built with SDK v1.5.1). + +* Set the environment variable IDF_PATH to the path to an ESP-IDF directory. + +* Set any other environment variables you'd like to override in the Makefile. + +* To build type `make` + +# To Test + +To test the build stub, you can either copy-paste the code from `build/stub_flasher_snippet.py` into esptool.py manually. Or there are some convenience wrappers to make testing quicker to iterate on: + +* Running `esptool_test_stub.py` is the same as running `esptool.py`, only it uses the just-compiled stubs from the build directory. + +* Running `run_tests_with_stub.py` is the same as running `test/test_esptool.py`, only it uses the just-compiled stubs from the build directory. diff --git a/dist/bin/esptool/flasher_stub/compare_stubs.py b/dist/bin/esptool/flasher_stub/compare_stubs.py new file mode 100644 index 000000000..a92b090dd --- /dev/null +++ b/dist/bin/esptool/flasher_stub/compare_stubs.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +import sys + +# Compare the esptool stub loaders to freshly built ones +# in the build directory +# +# (Used by Travis to verify the stubs are up to date.) + +def verbose_diff(new, old): + for k in [ "data_start", "text_start"]: + if new[k] != old[k]: + print("New %s 0x%x old %s 0%x" % (k, new[k], old[k])) + + for k in [ "data", "text" ]: + if len(new[k]) != len(old[k]): + print("New %s %d bytes, old stub code %d bytes" % (k, len(new[k]), len(old[k]))) + if new[k] != old[k]: + print("%s is different" % k) + if len(new[k]) == len(old[k]): + for b in range(len(new[k])): + if new[k][b] != old[k][b]: + print(" Byte 0x%x: new 0x%02x old 0x%02x" % (b, ord(new[k][b]), ord(old[k][b]))) + +if __name__ == "__main__": + same = True + sys.path.append("..") + import esptool + + old_8266_stub = esptool.ESP8266ROM.STUB_CODE + old_32_stub = esptool.ESP32ROM.STUB_CODE + + # hackiness: importing this module edits the esptool module + import esptool_test_stub + + if esptool.ESP8266ROM.STUB_CODE != old_8266_stub: + print("ESP8266 stub code in esptool.py is different to just-built stub") + verbose_diff(esptool.ESP8266ROM.STUB_CODE, old_8266_stub) + same = False + if esptool.ESP32ROM.STUB_CODE != old_32_stub: + print("ESP32 stub code in esptool.py is different to just-built stub.") + verbose_diff(esptool.ESP32ROM.STUB_CODE, old_32_stub) + same = False + if same: + print("Stub code is the same") + + sys.exit(0 if same else 1) diff --git a/dist/bin/esptool/flasher_stub/esptool_test_stub.py b/dist/bin/esptool/flasher_stub/esptool_test_stub.py new file mode 100644 index 000000000..3f242ec89 --- /dev/null +++ b/dist/bin/esptool/flasher_stub/esptool_test_stub.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# +# Trivial wrapper program to run esptool.py using the just-compiled +# flasher stub in the build/ subdirectory +# +# For use when developing new flasher_stubs, not important otherwise. +# +# Copyright (C) 2014-2016 Fredrik Ahlberg, Angus Gratton, other contributors as noted. +# +# 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. +# +import os.path +import sys + +THIS_DIR=os.path.dirname(sys.argv[0]) + +sys.path.append("..") +import esptool +# Python hackiness: evaluate the snippet in the context of the esptool module, so it +# edits the esptool's global variables +exec(open("%s/build/stub_flasher_snippet.py" % THIS_DIR).read(), esptool.__dict__, esptool.__dict__) + +if __name__ == "__main__": + try: + esptool.main() + except esptool.FatalError as e: + print('\nA fatal error occurred: %s' % e) + sys.exit(2) + diff --git a/dist/bin/esptool/flasher_stub/include/miniz.h b/dist/bin/esptool/flasher_stub/include/miniz.h new file mode 100644 index 000000000..69696dc4b --- /dev/null +++ b/dist/bin/esptool/flasher_stub/include/miniz.h @@ -0,0 +1,803 @@ +/* + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to +*/ +#ifndef MINIZ_HEADER_INCLUDED +#define MINIZ_HEADER_INCLUDED + +#include + +// Defines to completely disable specific portions of miniz.c: +// If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. + +// Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. +#define MINIZ_NO_STDIO + +// If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or +// get/set file times, and the C run-time funcs that get/set times won't be called. +// The current downside is the times written to your archives will be from 1979. +#define MINIZ_NO_TIME + +// Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. +#define MINIZ_NO_ARCHIVE_APIS + +// Define MINIZ_NO_ARCHIVE_APIS to disable all writing related ZIP archive API's. +#define MINIZ_NO_ARCHIVE_WRITING_APIS + +// Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. +#define MINIZ_NO_ZLIB_APIS + +// Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. +#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES + +// Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. +// Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc +// callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user +// functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. +#define MINIZ_NO_MALLOC + +#if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) + // TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux + #define MINIZ_NO_TIME +#endif + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) + #include +#endif + +//Hardcoded options for Xtensa - JD +#define MINIZ_X86_OR_X64_CPU 0 +#define MINIZ_LITTLE_ENDIAN 1 +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#define MINIZ_HAS_64BIT_REGISTERS 0 +#define TINFL_USE_64BIT_BITBUF 0 + + +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) +// MINIZ_X86_OR_X64_CPU is only used to help set the below macros. +#define MINIZ_X86_OR_X64_CPU 1 +#endif + +#if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU +// Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. +#define MINIZ_LITTLE_ENDIAN 1 +#endif + +#if MINIZ_X86_OR_X64_CPU +// Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 +#endif + +#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) +// Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). +#define MINIZ_HAS_64BIT_REGISTERS 1 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// ------------------- zlib-style API Definitions. + +// For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! +typedef unsigned long mz_ulong; + +// mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. +void mz_free(void *p); + +#define MZ_ADLER32_INIT (1) +// mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); + +#define MZ_CRC32_INIT (0) +// mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. +mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); + +// Compression strategies. +enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 }; + +// Method +#define MZ_DEFLATED 8 + +#ifndef MINIZ_NO_ZLIB_APIS + +// Heap allocation callbacks. +// Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long. +typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); +typedef void (*mz_free_func)(void *opaque, void *address); +typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); + +#define MZ_VERSION "9.1.15" +#define MZ_VERNUM 0x91F0 +#define MZ_VER_MAJOR 9 +#define MZ_VER_MINOR 1 +#define MZ_VER_REVISION 15 +#define MZ_VER_SUBREVISION 0 + +// Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). +enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 }; + +// Return status codes. MZ_PARAM_ERROR is non-standard. +enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 }; + +// Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. +enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_UBER_COMPRESSION = 10, MZ_DEFAULT_LEVEL = 6, MZ_DEFAULT_COMPRESSION = -1 }; + +// Window bits +#define MZ_DEFAULT_WINDOW_BITS 15 + +struct mz_internal_state; + +// Compression/decompression stream struct. +typedef struct mz_stream_s +{ + const unsigned char *next_in; // pointer to next byte to read + unsigned int avail_in; // number of bytes available at next_in + mz_ulong total_in; // total number of bytes consumed so far + + unsigned char *next_out; // pointer to next byte to write + unsigned int avail_out; // number of bytes that can be written to next_out + mz_ulong total_out; // total number of bytes produced so far + + char *msg; // error msg (unused) + struct mz_internal_state *state; // internal state, allocated by zalloc/zfree + + mz_alloc_func zalloc; // optional heap allocation function (defaults to malloc) + mz_free_func zfree; // optional heap free function (defaults to free) + void *opaque; // heap alloc function user pointer + + int data_type; // data_type (unused) + mz_ulong adler; // adler32 of the source or uncompressed data + mz_ulong reserved; // not used +} mz_stream; + +typedef mz_stream *mz_streamp; + +// Returns the version string of miniz.c. +const char *mz_version(void); + +// mz_deflateInit() initializes a compressor with default options: +// Parameters: +// pStream must point to an initialized mz_stream struct. +// level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. +// level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. +// (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) +// Return values: +// MZ_OK on success. +// MZ_STREAM_ERROR if the stream is bogus. +// MZ_PARAM_ERROR if the input parameters are bogus. +// MZ_MEM_ERROR on out of memory. +int mz_deflateInit(mz_streamp pStream, int level); + +// mz_deflateInit2() is like mz_deflate(), except with more control: +// Additional parameters: +// method must be MZ_DEFLATED +// window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) +// mem_level must be between [1, 9] (it's checked but ignored by miniz.c) +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); + +// Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). +int mz_deflateReset(mz_streamp pStream); + +// mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. +// Parameters: +// pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. +// flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. +// Return values: +// MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). +// MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. +// MZ_STREAM_ERROR if the stream is bogus. +// MZ_PARAM_ERROR if one of the parameters is invalid. +// MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) +int mz_deflate(mz_streamp pStream, int flush); + +// mz_deflateEnd() deinitializes a compressor: +// Return values: +// MZ_OK on success. +// MZ_STREAM_ERROR if the stream is bogus. +int mz_deflateEnd(mz_streamp pStream); + +// mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); + +// Single-call compression functions mz_compress() and mz_compress2(): +// Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); + +// mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). +mz_ulong mz_compressBound(mz_ulong source_len); + +// Initializes a decompressor. +int mz_inflateInit(mz_streamp pStream); + +// mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: +// window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). +int mz_inflateInit2(mz_streamp pStream, int window_bits); + +// Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. +// Parameters: +// pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. +// flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. +// On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). +// MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. +// Return values: +// MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. +// MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. +// MZ_STREAM_ERROR if the stream is bogus. +// MZ_DATA_ERROR if the deflate stream is invalid. +// MZ_PARAM_ERROR if one of the parameters is invalid. +// MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again +// with more input data, or with more room in the output buffer (except when using single call decompression, described above). +int mz_inflate(mz_streamp pStream, int flush); + +// Deinitializes a decompressor. +int mz_inflateEnd(mz_streamp pStream); + +// Single-call decompression. +// Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); + +// Returns a string description of the specified error code, or NULL if the error code is invalid. +const char *mz_error(int err); + +// Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. +// Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. +#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES + typedef unsigned char Byte; + typedef unsigned int uInt; + typedef mz_ulong uLong; + typedef Byte Bytef; + typedef uInt uIntf; + typedef char charf; + typedef int intf; + typedef void *voidpf; + typedef uLong uLongf; + typedef void *voidp; + typedef void *const voidpc; + #define Z_NULL 0 + #define Z_NO_FLUSH MZ_NO_FLUSH + #define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH + #define Z_SYNC_FLUSH MZ_SYNC_FLUSH + #define Z_FULL_FLUSH MZ_FULL_FLUSH + #define Z_FINISH MZ_FINISH + #define Z_BLOCK MZ_BLOCK + #define Z_OK MZ_OK + #define Z_STREAM_END MZ_STREAM_END + #define Z_NEED_DICT MZ_NEED_DICT + #define Z_ERRNO MZ_ERRNO + #define Z_STREAM_ERROR MZ_STREAM_ERROR + #define Z_DATA_ERROR MZ_DATA_ERROR + #define Z_MEM_ERROR MZ_MEM_ERROR + #define Z_BUF_ERROR MZ_BUF_ERROR + #define Z_VERSION_ERROR MZ_VERSION_ERROR + #define Z_PARAM_ERROR MZ_PARAM_ERROR + #define Z_NO_COMPRESSION MZ_NO_COMPRESSION + #define Z_BEST_SPEED MZ_BEST_SPEED + #define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION + #define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION + #define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY + #define Z_FILTERED MZ_FILTERED + #define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY + #define Z_RLE MZ_RLE + #define Z_FIXED MZ_FIXED + #define Z_DEFLATED MZ_DEFLATED + #define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS + #define alloc_func mz_alloc_func + #define free_func mz_free_func + #define internal_state mz_internal_state + #define z_stream mz_stream + #define deflateInit mz_deflateInit + #define deflateInit2 mz_deflateInit2 + #define deflateReset mz_deflateReset + #define deflate mz_deflate + #define deflateEnd mz_deflateEnd + #define deflateBound mz_deflateBound + #define compress mz_compress + #define compress2 mz_compress2 + #define compressBound mz_compressBound + #define inflateInit mz_inflateInit + #define inflateInit2 mz_inflateInit2 + #define inflate mz_inflate + #define inflateEnd mz_inflateEnd + #define uncompress mz_uncompress + #define crc32 mz_crc32 + #define adler32 mz_adler32 + #define MAX_WBITS 15 + #define MAX_MEM_LEVEL 9 + #define zError mz_error + #define ZLIB_VERSION MZ_VERSION + #define ZLIB_VERNUM MZ_VERNUM + #define ZLIB_VER_MAJOR MZ_VER_MAJOR + #define ZLIB_VER_MINOR MZ_VER_MINOR + #define ZLIB_VER_REVISION MZ_VER_REVISION + #define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION + #define zlibVersion mz_version + #define zlib_version mz_version() +#endif // #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES + +#endif // MINIZ_NO_ZLIB_APIS + +// ------------------- Types and macros + +typedef unsigned char mz_uint8; +typedef signed short mz_int16; +typedef unsigned short mz_uint16; +typedef unsigned int mz_uint32; +typedef unsigned int mz_uint; +typedef long long mz_int64; +typedef unsigned long long mz_uint64; +typedef int mz_bool; + +#define MZ_FALSE (0) +#define MZ_TRUE (1) + +// An attempt to work around MSVC's spammy "warning C4127: conditional expression is constant" message. +#ifdef _MSC_VER + #define MZ_MACRO_END while (0, 0) +#else + #define MZ_MACRO_END while (0) +#endif + +// ------------------- ZIP archive reading/writing + +#ifndef MINIZ_NO_ARCHIVE_APIS + +enum +{ + MZ_ZIP_MAX_IO_BUF_SIZE = 64*1024, + MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 260, + MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 256 +}; + +typedef struct +{ + mz_uint32 m_file_index; + mz_uint32 m_central_dir_ofs; + mz_uint16 m_version_made_by; + mz_uint16 m_version_needed; + mz_uint16 m_bit_flag; + mz_uint16 m_method; +#ifndef MINIZ_NO_TIME + time_t m_time; +#endif + mz_uint32 m_crc32; + mz_uint64 m_comp_size; + mz_uint64 m_uncomp_size; + mz_uint16 m_internal_attr; + mz_uint32 m_external_attr; + mz_uint64 m_local_header_ofs; + mz_uint32 m_comment_size; + char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; + char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; +} mz_zip_archive_file_stat; + +typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); +typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); + +struct mz_zip_internal_state_tag; +typedef struct mz_zip_internal_state_tag mz_zip_internal_state; + +typedef enum +{ + MZ_ZIP_MODE_INVALID = 0, + MZ_ZIP_MODE_READING = 1, + MZ_ZIP_MODE_WRITING = 2, + MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 +} mz_zip_mode; + +typedef struct mz_zip_archive_tag +{ + mz_uint64 m_archive_size; + mz_uint64 m_central_directory_file_ofs; + mz_uint m_total_files; + mz_zip_mode m_zip_mode; + + mz_uint m_file_offset_alignment; + + mz_alloc_func m_pAlloc; + mz_free_func m_pFree; + mz_realloc_func m_pRealloc; + void *m_pAlloc_opaque; + + mz_file_read_func m_pRead; + mz_file_write_func m_pWrite; + void *m_pIO_opaque; + + mz_zip_internal_state *m_pState; + +} mz_zip_archive; + +typedef enum +{ + MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, + MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, + MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800 +} mz_zip_flags; + +// ZIP archive reading + +// Inits a ZIP archive reader. +// These functions read and validate the archive's central directory. +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags); +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags); + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); +#endif + +// Returns the total number of files in the archive. +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); + +// Returns detailed information about an archive file entry. +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); + +// Determines if an archive file entry is a directory entry. +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); + +// Retrieves the filename of an archive file entry. +// Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); + +// Attempts to locates a file in the archive's central directory. +// Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH +// Returns -1 if the file cannot be found. +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); + +// Extracts a archive file to a memory buffer using no memory allocation. +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); + +// Extracts a archive file to a memory buffer. +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); + +// Extracts a archive file to a dynamically allocated heap buffer. +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); + +// Extracts a archive file using a callback function to output the file's data. +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +// Extracts a archive file to a disk file and sets its last accessed and modified times. +// This function only extracts files, not archive directory records. +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); +#endif + +// Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. +mz_bool mz_zip_reader_end(mz_zip_archive *pZip); + +// ZIP archive writing + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +// Inits a ZIP archive writer. +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); +#endif + +// Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. +// For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. +// For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). +// Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. +// Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before +// the archive is finalized the file's central directory will be hosed. +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); + +// Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. +// To add a directory entry, call this method with an archive name ending in a forwardslash with empty buffer. +// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); + +#ifndef MINIZ_NO_STDIO +// Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. +// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); +#endif + +// Adds a file to an archive by fully cloning the data from another archive. +// This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data, and comment fields. +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index); + +// Finalizes the archive by writing the central directory records followed by the end of central directory record. +// After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). +// An archive must be manually finalized by calling this function for it to be valid. +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize); + +// Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. +// Note for the archive to be valid, it must have been finalized before ending. +mz_bool mz_zip_writer_end(mz_zip_archive *pZip); + +// Misc. high-level helper functions: + +// mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. +// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. +mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + +// Reads a single file from an archive into a heap block. +// Returns NULL on failure. +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags); + +#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +#endif // #ifndef MINIZ_NO_ARCHIVE_APIS + +// ------------------- Low-level Decompression API Definitions + +// Decompression flags used by tinfl_decompress(). +// TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. +// TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. +// TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). +// TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. +enum +{ + TINFL_FLAG_PARSE_ZLIB_HEADER = 1, + TINFL_FLAG_HAS_MORE_INPUT = 2, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, + TINFL_FLAG_COMPUTE_ADLER32 = 8 +}; + +// High level decompression functions: +// tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). +// On entry: +// pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. +// On return: +// Function returns a pointer to the decompressed data, or NULL on failure. +// *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. +// The caller must call mz_free() on the returned block when it's no longer needed. +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +// tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. +// Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. +#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +// tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. +// Returns 1 on success or 0 on failure. +typedef int (*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); +int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; + +// Max size of LZ dictionary. +#define TINFL_LZ_DICT_SIZE 32768 + +// Return status. +typedef enum +{ + TINFL_STATUS_BAD_PARAM = -3, + TINFL_STATUS_ADLER32_MISMATCH = -2, + TINFL_STATUS_FAILED = -1, + TINFL_STATUS_DONE = 0, + TINFL_STATUS_NEEDS_MORE_INPUT = 1, + TINFL_STATUS_HAS_MORE_OUTPUT = 2 +} tinfl_status; + +// Initializes the decompressor to its initial state. +#define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END +#define tinfl_get_adler32(r) (r)->m_check_adler32 + +// Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. +// This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. +tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); + +// Internal/private bits follow. +enum +{ + TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19, + TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS +}; + +typedef struct +{ + mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; + mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; +} tinfl_huff_table; + +#if MINIZ_HAS_64BIT_REGISTERS + #define TINFL_USE_64BIT_BITBUF 1 +#endif + +#if TINFL_USE_64BIT_BITBUF + typedef mz_uint64 tinfl_bit_buf_t; + #define TINFL_BITBUF_SIZE (64) +#else + typedef mz_uint32 tinfl_bit_buf_t; + #define TINFL_BITBUF_SIZE (32) +#endif + +struct tinfl_decompressor_tag +{ + mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; + tinfl_bit_buf_t m_bit_buf; + size_t m_dist_from_out_buf_start; + tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; + mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; +}; + +// ------------------- Low-level Compression API Definitions + +// Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). +#define TDEFL_LESS_MEMORY 1 + +// tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): +// TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). +enum +{ + TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF +}; + +// TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. +// TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). +// TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. +// TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). +// TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) +// TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. +// TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. +// TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. +// The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). +enum +{ + TDEFL_WRITE_ZLIB_HEADER = 0x01000, + TDEFL_COMPUTE_ADLER32 = 0x02000, + TDEFL_GREEDY_PARSING_FLAG = 0x04000, + TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, + TDEFL_RLE_MATCHES = 0x10000, + TDEFL_FILTER_MATCHES = 0x20000, + TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, + TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 +}; + +// High level compression functions: +// tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). +// On entry: +// pSrc_buf, src_buf_len: Pointer and size of source block to compress. +// flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. +// On return: +// Function returns a pointer to the compressed data, or NULL on failure. +// *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. +// The caller must free() the returned block when it's no longer needed. +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +// tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. +// Returns 0 on failure. +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +// Compresses an image to a compressed PNG file in memory. +// On entry: +// pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. +// The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. +// level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL +// If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). +// On return: +// Function returns a pointer to the compressed data, or NULL on failure. +// *pLen_out will be set to the size of the PNG image file. +// The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. +void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); + +// Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. +typedef mz_bool (*tdefl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); + +// tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = 32768, TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 }; + +// TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). +#if TDEFL_LESS_MEMORY +enum { TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; +#else +enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; +#endif + +// The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. +typedef enum +{ + TDEFL_STATUS_BAD_PARAM = -2, + TDEFL_STATUS_PUT_BUF_FAILED = -1, + TDEFL_STATUS_OKAY = 0, + TDEFL_STATUS_DONE = 1, +} tdefl_status; + +// Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums +typedef enum +{ + TDEFL_NO_FLUSH = 0, + TDEFL_SYNC_FLUSH = 2, + TDEFL_FULL_FLUSH = 3, + TDEFL_FINISH = 4 +} tdefl_flush; + +// tdefl's compression state structure. +typedef struct +{ + tdefl_put_buf_func_ptr m_pPut_buf_func; + void *m_pPut_buf_user; + mz_uint m_flags, m_max_probes[2]; + int m_greedy_parsing; + mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; + mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; + mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; + mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; + tdefl_status m_prev_return_status; + const void *m_pIn_buf; + void *m_pOut_buf; + size_t *m_pIn_buf_size, *m_pOut_buf_size; + tdefl_flush m_flush; + const mz_uint8 *m_pSrc; + size_t m_src_buf_left, m_out_buf_ofs; + mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; + mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; + mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; + mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; + mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; +} tdefl_compressor; + +// Initializes the compressor. +// There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. +// pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. +// If pBut_buf_func is NULL the user should always call the tdefl_compress() API. +// flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +// Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); + +// tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. +// tdefl_compress_buffer() always consumes the entire input buffer. +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); +mz_uint32 tdefl_get_adler32(tdefl_compressor *d); + +// Can't use tdefl_create_comp_flags_from_zip_params if MINIZ_NO_ZLIB_APIS isn't defined, because it uses some of its macros. +#ifndef MINIZ_NO_ZLIB_APIS +// Create tdefl_compress() flags given zlib-style compression parameters. +// level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) +// window_bits may be -15 (raw deflate) or 15 (zlib) +// strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); +#endif // #ifndef MINIZ_NO_ZLIB_APIS + +#ifdef __cplusplus +} +#endif + +#endif // MINIZ_HEADER_INCLUDED + diff --git a/dist/bin/esptool/flasher_stub/include/rom_functions.h b/dist/bin/esptool/flasher_stub/include/rom_functions.h new file mode 100644 index 000000000..da1df5533 --- /dev/null +++ b/dist/bin/esptool/flasher_stub/include/rom_functions.h @@ -0,0 +1,105 @@ +/* Declarations for functions in ESP8266 ROM code + * + * Copyright (c) 2016-2019 Espressif Systems (Shanghai) PTE LTD & Cesanta Software Limited + * All rights reserved + * + * 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. + */ + + +/* ROM function prototypes for functions in ROM which are + called by the flasher stubs. +*/ +#pragma once + +#include + +int uart_rx_one_char(uint8_t *ch); +uint8_t uart_rx_one_char_block(); +int uart_tx_one_char(char ch); +void uart_div_modify(uint32_t uart_no, uint32_t baud_div); + +int SendMsg(uint8_t *msg, uint8_t size); +int send_packet(const void *packet, uint32_t size); + +void _putc1(char *ch); + +void ets_delay_us(uint32_t us); + +typedef enum { SPI_FLASH_RESULT_OK = 0, + SPI_FLASH_RESULT_ERR = 1, + SPI_FLASH_RESULT_TIMEOUT = 2 } SpiFlashOpResult; + +SpiFlashOpResult SPILock(); +SpiFlashOpResult SPIUnlock(); +SpiFlashOpResult SPIRead(uint32_t addr, void *dst, uint32_t size); +SpiFlashOpResult SPIWrite(uint32_t addr, const uint8_t *src, uint32_t size); +SpiFlashOpResult SPIEraseChip(); +SpiFlashOpResult SPIEraseBlock(uint32_t block_num); +SpiFlashOpResult SPIEraseSector(uint32_t sector_num); +uint32_t SPI_read_status(); +uint32_t Wait_SPI_Idle(); +void spi_flash_attach(); + +void SelectSpiFunction(); +void SPIFlashModeConfig(uint32_t a, uint32_t b); +void SPIReadModeCnfig(uint32_t a); +uint32_t SPIParamCfg(uint32_t deviceId, uint32_t chip_size, uint32_t block_size, uint32_t sector_size, uint32_t page_size, uint32_t status_mask); + +void memset(void *addr, uint8_t c, uint32_t len); + +void ets_delay_us(uint32_t delay_micros); + +void ets_isr_mask(uint32_t ints); +void ets_isr_unmask(uint32_t ints); +void ets_set_user_start(void (*user_start_fn)()); + +void software_reset(); + +struct MD5Context { + uint32_t buf[4]; + uint32_t bits[2]; + uint8_t in[64]; +}; + +void MD5Init(struct MD5Context *ctx); +void MD5Update(struct MD5Context *ctx, void *buf, uint32_t len); +void MD5Final(uint8_t digest[16], struct MD5Context *ctx); + +typedef struct { + uint32_t device_id; + uint32_t chip_size; // chip size in bytes + uint32_t block_size; + uint32_t sector_size; + uint32_t page_size; + uint32_t status_mask; +} esp_rom_spiflash_chip_t; + + +typedef void (*int_handler_t)(void *arg); +int_handler_t ets_isr_attach(uint32_t int_num, int_handler_t handler, + void *arg); +/* Some ESP32-only ROM functions */ +#ifdef ESP32 +uint32_t ets_get_detected_xtal_freq(void); +void uart_tx_flush(int uart); +uint32_t ets_efuse_get_spiconfig(void); +SpiFlashOpResult esp_rom_spiflash_write_encrypted(uint32_t addr, const uint8_t *src, uint32_t size); + +/* Note: this is a static function whose first argument was elided by the + compiler. */ +SpiFlashOpResult SPI_read_status_high(uint32_t *status); + +SpiFlashOpResult SPI_write_status(esp_rom_spiflash_chip_t *spi, uint32_t status_value); + +#endif diff --git a/dist/bin/esptool/flasher_stub/include/slip.h b/dist/bin/esptool/flasher_stub/include/slip.h new file mode 100644 index 000000000..1fca816f8 --- /dev/null +++ b/dist/bin/esptool/flasher_stub/include/slip.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2016 Cesanta Software Limited & (c) 2016-2019 Espressif Systems (Shanghai) PTE LTD + * All rights reserved + * + * 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. + */ + +#ifndef SLIP_H_ +#define SLIP_H_ + +#include + +/* Send the SLIP frame begin/end delimiter. */ +void SLIP_send_frame_delimiter(void); + +/* Send a single character of SLIP frame data, escaped as per SLIP escaping. */ +void SLIP_send_frame_data(char ch); + +/* Send some SLIP frame data, escaped as per SLIP escaping. */ +void SLIP_send_frame_data_buf(const void *buf, uint32_t size); + +/* Send a full SLIP frame, with specified contents. */ +void SLIP_send(const void *pkt, uint32_t size); + +typedef enum { + SLIP_NO_FRAME, + SLIP_FRAME, + SLIP_FRAME_ESCAPING +} slip_state_t; + +int16_t SLIP_recv_byte(char byte, slip_state_t *state); + +#define SLIP_FINISHED_FRAME -2 +#define SLIP_NO_BYTE -1 + +/* Receive a SLIP frame, with specified contents. */ +uint32_t SLIP_recv(void *pkt, uint32_t max_len); + +#endif /* SLIP_H_ */ diff --git a/dist/bin/esptool/flasher_stub/include/soc_support.h b/dist/bin/esptool/flasher_stub/include/soc_support.h new file mode 100644 index 000000000..39dcd1105 --- /dev/null +++ b/dist/bin/esptool/flasher_stub/include/soc_support.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2016-2019 Espressif Systems (Shanghai) PTE LTD + * All rights reserved + * + * This file is part of the esptool.py binary flasher stub. + * + * 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. + */ + +/* SoC-level support for ESP8266/ESP32. + * + * Provide a unified register-level interface. + * + * This is the same information provided in the register headers + * of ESP8266 Non-OS SDK and ESP-IDF soc component, however + * only values that are needed for the flasher stub are included here. + * + */ +#pragma once + +#include +#include +#include + +#define READ_REG(REG) (*((volatile uint32_t *)(REG))) +#define WRITE_REG(REG, VAL) *((volatile uint32_t *)(REG)) = (VAL) +#define REG_SET_MASK(reg, mask) WRITE_REG((reg), (READ_REG(reg)|(mask))) + + +/********************************************************** + * Per-SOC based peripheral register base addresses + */ +#ifdef ESP8266 +#define UART_BASE_REG 0x60000000 /* UART0 */ +#define SPI_BASE_REG 0x60000200 /* SPI peripheral 0 */ +#endif + +#ifdef ESP32 +#define UART_BASE_REG 0x3ff40000 /* UART0 */ +#define SPI_BASE_REG 0x3ff42000 /* SPI peripheral 1, used for SPI flash */ +#define SPI0_BASE_REG 0x3ff43000 /* SPI peripheral 0, inner state machine */ +#define GPIO_BASE_REG 0x3ff44000 +#endif + + +/********************************************************** + * UART peripheral + * + * The features we use are the same on both ESP8266 and ESP32 + * + * Only UART0 is used + */ +#define UART_CLKDIV_REG(X) (UART_BASE_REG + 0x14) +#define UART_CLKDIV_M (0x000FFFFF) + +#ifdef ESP32 +#define UART_CLKDIV_FRAG_S 20 +#define UART_CLKDIV_FRAG_V 0xF +#endif + +#define UART_FIFO(X) (UART_BASE_REG + 0x00) +#define UART_INT_ST(X) (UART_BASE_REG + 0x08) // TODO RENAME +#define UART_INT_ENA(X) (UART_BASE_REG + 0x0C) +#define UART_INT_CLR(X) (UART_BASE_REG + 0x10) +#define UART_STATUS(X) (UART_BASE_REG + 0x1C) + +#define UART_RXFIFO_FULL_INT_ENA (1<<0) +#define UART_RXFIFO_TOUT_INT_ENA (1<<8) + +#define ETS_UART0_INUM 5 + + +/********************************************************** + * SPI peripheral + * + * The features we use are mostly the same on both ESP8266 and ESP32, + * except for W0 base address & option for 2-byte status command + * + * Only one SPI peripheral is used (0 on ESP8266, 1 on ESP32) + */ +#define SPI_CMD_REG (SPI_BASE_REG + 0x00) +#define SPI_FLASH_RDSR (1<<27) +#define SPI_FLASH_SE (1<<24) +#define SPI_FLASH_BE (1<<23) +#define SPI_FLASH_WREN (1<<30) + +#define SPI_ADDR_REG (SPI_BASE_REG + 0x04) + +#define SPI_CTRL_REG (SPI_BASE_REG + 0x08) +#ifdef ESP32 +#define SPI_WRSR_2B (1<<22) +#endif + +#define SPI_RD_STATUS_REG (SPI_BASE_REG + 0x10) + +#ifdef ESP8266 +#define SPI_W0_REG (SPI_BASE_REG + 0x40) +#endif +#ifdef ESP32 +#define SPI_W0_REG (SPI_BASE_REG + 0x80) +#endif + +#define SPI_EXT2_REG (SPI_BASE_REG + 0xF8) +#define SPI_ST 0x7 + +#ifdef ESP32 +/* On ESP32 the SPI peripherals are layered + * flash, this lets us check the state of the internal + * state machine under the SPI flash controller + */ +#define SPI0_EXT2_REG (SPI0_BASE_REG + 0xF8) +#endif + + +/********************************************************** + * GPIO peripheral + * + * We only need to read the strapping register on ESP32 + */ +#define GPIO_STRAP_REG (GPIO_BASE_REG + 0x38) diff --git a/dist/bin/esptool/flasher_stub/include/stub_commands.h b/dist/bin/esptool/flasher_stub/include/stub_commands.h new file mode 100644 index 000000000..a314d5624 --- /dev/null +++ b/dist/bin/esptool/flasher_stub/include/stub_commands.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016-2019 Espressif Systems (Shanghai) PTE LTD + * All rights reserved + * + * This file is part of the esptool.py binary flasher stub. + * + * 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. + */ + +/* Flasher command handlers, called from stub_flasher.c + + Commands related to writing flash are in stub_write_flash_xxx. +*/ +#pragma once +#include "stub_flasher.h" +#include + +int handle_flash_erase(uint32_t addr, uint32_t len); + +void handle_flash_read(uint32_t addr, uint32_t len, uint32_t block_size, uint32_t max_in_flight); + +int handle_flash_get_md5sum(uint32_t addr, uint32_t len); + +int handle_flash_read_chip_id(); + +esp_command_error handle_spi_set_params(uint32_t *args, int *status); + +esp_command_error handle_spi_attach(uint32_t hspi_config_arg); + +esp_command_error handle_mem_begin(uint32_t size, uint32_t offset); + +esp_command_error handle_mem_data(void *data, uint32_t length); + +esp_command_error handle_mem_finish(void); diff --git a/dist/bin/esptool/flasher_stub/include/stub_flasher.h b/dist/bin/esptool/flasher_stub/include/stub_flasher.h new file mode 100644 index 000000000..7a8c732f5 --- /dev/null +++ b/dist/bin/esptool/flasher_stub/include/stub_flasher.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2016 Cesanta Software Limited and 2016-2019 Espressif Systems (Shanghai) PTE LTD + * + * All rights reserved + * + * 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. + */ + +#ifndef STUB_FLASHER_H_ +#define STUB_FLASHER_H_ + +#include + +/* Maximum write block size, used for various buffers. */ +#define MAX_WRITE_BLOCK 0x4000 + +/* Flash geometry constants */ +#define FLASH_SECTOR_SIZE 4096 +#define FLASH_BLOCK_SIZE 65536 +#define FLASH_PAGE_SIZE 256 +#define FLASH_STATUS_MASK 0xFFFF +#define SECTORS_PER_BLOCK (FLASH_BLOCK_SIZE / FLASH_SECTOR_SIZE) + +/* Full set of protocol commands */ +typedef enum { + /* Commands supported by the ESP8266 & ESP32 bootloaders */ + 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, + + /* Commands supported by the ESP32 bootloader */ + ESP_SPI_SET_PARAMS = 0x0b, + ESP_PIN_READ = 0x0c, /* ??? */ + ESP_SPI_ATTACH = 0x0d, + ESP_SPI_READ = 0x0e, + ESP_SET_BAUD = 0x0f, + ESP_FLASH_DEFLATED_BEGIN = 0x10, + ESP_FLASH_DEFLATED_DATA = 0x11, + ESP_FLASH_DEFLATED_END = 0x12, + ESP_FLASH_VERIFY_MD5 = 0x13, + + /* Stub-only commands */ + ESP_ERASE_FLASH = 0xD0, + ESP_ERASE_REGION = 0xD1, + ESP_READ_FLASH = 0xD2, + ESP_RUN_USER_CODE = 0xD3, + + /* Flash encryption debug mode supported command */ + ESP_FLASH_ENCRYPT_DATA = 0xD4, +} esp_command; + +/* Command request header */ +typedef struct __attribute__((packed)) { + uint8_t zero; + uint8_t op; /* maps to esp_command enum */ + uint16_t data_len; + int32_t checksum; + uint8_t data_buf[32]; /* actually variable length, determined by data_len */ +} esp_command_req_t; + +/* Command response header */ +typedef struct __attribute__((packed)) { + uint8_t resp; /* should be '1' */ + uint8_t op_ret; /* Should match 'op' */ + uint16_t len_ret; /* Length of result data (can be ignored as SLIP framing helps) */ + int32_t value; /* 32-bit response used by some commands */ +} esp_command_response_t; + + +/* command response has some (optional) data after it, then 2 (or 4 on ESP32 ROM) + bytes of status. + */ +typedef struct __attribute__((packed)) { + uint8_t error; /* non-zero = failed */ + uint8_t status; /* status of a failure */ +} esp_command_data_status_t; + +/* Error codes */ +typedef enum { + ESP_OK = 0, + + ESP_BAD_DATA_LEN = 0xC0, + ESP_BAD_DATA_CHECKSUM = 0xC1, + ESP_BAD_BLOCKSIZE = 0xC2, + ESP_INVALID_COMMAND = 0xC3, + ESP_FAILED_SPI_OP = 0xC4, + ESP_FAILED_SPI_UNLOCK = 0xC5, + ESP_NOT_IN_FLASH_MODE = 0xC6, + ESP_INFLATE_ERROR = 0xC7, + ESP_NOT_ENOUGH_DATA = 0xC8, + ESP_TOO_MUCH_DATA = 0xC9, + + ESP_CMD_NOT_IMPLEMENTED = 0xFF, +} esp_command_error; + +#endif /* STUB_FLASHER_H_ */ diff --git a/dist/bin/esptool/flasher_stub/include/stub_write_flash.h b/dist/bin/esptool/flasher_stub/include/stub_write_flash.h new file mode 100644 index 000000000..ac61cd156 --- /dev/null +++ b/dist/bin/esptool/flasher_stub/include/stub_write_flash.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016-2019 Espressif Systems (Shanghai) PTE LTD + * All rights reserved + * + * This file is part of the esptool.py binary flasher stub. + * + * 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. + */ + +/* Flasher commands related to writing flash */ +#pragma once +#include "stub_flasher.h" +#include + +bool is_in_flash_mode(void); + +esp_command_error get_flash_error(void); + +esp_command_error handle_flash_begin(uint32_t total_size, uint32_t offset); + +esp_command_error handle_flash_deflated_begin(uint32_t uncompressed_size, uint32_t compressed_size, uint32_t offset); + +void handle_flash_data(void *data_buf, uint32_t length); + +#ifdef ESP32 +void handle_flash_encrypt_data(void *data_buf, uint32_t length); +#endif + +void handle_flash_deflated_data(void *data_buf, uint32_t length); + +/* same command used for deflated or non-deflated mode */ +esp_command_error handle_flash_end(void); + diff --git a/dist/bin/esptool/flasher_stub/ld/rom_32.ld b/dist/bin/esptool/flasher_stub/ld/rom_32.ld new file mode 100644 index 000000000..073eacfb1 --- /dev/null +++ b/dist/bin/esptool/flasher_stub/ld/rom_32.ld @@ -0,0 +1,1782 @@ +/* Taken from esp-idf */ +/* +ESP32 ROM address table +Generated for ROM with MD5sum: +ab8282ae908fe9e7a63fb2a4ac2df013 ../../rom_image/prorom.elf +*/ +PROVIDE ( abort = 0x4000bba4 ); +PROVIDE ( abs = 0x40056340 ); +PROVIDE ( __absvdi2 = 0x4006387c ); +PROVIDE ( __absvsi2 = 0x40063868 ); +PROVIDE ( Add2SelfBigHex256 = 0x40015b7c ); +PROVIDE ( AddBigHex256 = 0x40015b28 ); +PROVIDE ( AddBigHexModP256 = 0x40015c98 ); +PROVIDE ( __adddf3 = 0x40002590 ); +PROVIDE ( AddP256 = 0x40015c74 ); +PROVIDE ( AddPdiv2_256 = 0x40015ce0 ); +PROVIDE ( __addsf3 = 0x400020e8 ); +PROVIDE ( __addvdi3 = 0x40002cbc ); +PROVIDE ( __addvsi3 = 0x40002c98 ); +PROVIDE ( aes_128_cbc_decrypt = 0x4005cc7c ); +PROVIDE ( aes_128_cbc_encrypt = 0x4005cc18 ); +PROVIDE ( aes_unwrap = 0x4005ccf0 ); +PROVIDE ( app_gpio_arg = 0x3ffe003c ); +PROVIDE ( app_gpio_handler = 0x3ffe0040 ); +PROVIDE ( __ascii_wctomb = 0x40058ef0 ); +PROVIDE ( asctime = 0x40059588 ); +PROVIDE ( asctime_r = 0x40000ec8 ); +PROVIDE ( __ashldi3 = 0x4000c818 ); +PROVIDE ( __ashrdi3 = 0x4000c830 ); +PROVIDE ( atoi = 0x400566c4 ); +PROVIDE ( _atoi_r = 0x400566d4 ); +PROVIDE ( atol = 0x400566ec ); +PROVIDE ( _atol_r = 0x400566fc ); +PROVIDE ( base64_decode = 0x4005ced8 ); +PROVIDE ( base64_encode = 0x4005cdbc ); +PROVIDE ( BasePoint_x_256 = 0x3ff97488 ); +PROVIDE ( BasePoint_y_256 = 0x3ff97468 ); +PROVIDE ( bigHexInversion256 = 0x400168f0 ); +PROVIDE ( bigHexP256 = 0x3ff973bc ); +PROVIDE ( __bswapdi2 = 0x400649c4 ); +PROVIDE ( __bswapsi2 = 0x4006499c ); +PROVIDE ( btdm_r_ble_bt_handler_tab_p_get = 0x40019b0c ); +PROVIDE ( btdm_r_btdm_option_data_p_get = 0x40010004 ); +PROVIDE ( btdm_r_btdm_rom_version_get = 0x40010078 ); +PROVIDE ( btdm_r_data_init = 0x4001002c ); +PROVIDE ( btdm_r_import_rf_phy_func_p_get = 0x40054298 ); +PROVIDE ( btdm_r_ip_func_p_get = 0x40019af0 ); +PROVIDE ( btdm_r_ip_func_p_set = 0x40019afc ); +PROVIDE ( btdm_r_modules_func_p_get = 0x4005427c ); +PROVIDE ( btdm_r_modules_func_p_set = 0x40054270 ); +PROVIDE ( btdm_r_plf_func_p_set = 0x40054288 ); +PROVIDE ( bt_util_buf_env = 0x3ffb8bd4 ); +PROVIDE ( bzero = 0x4000c1f4 ); +PROVIDE ( cache_flash_mmu_set = 0x400095e0 ); +PROVIDE ( Cache_Flush = 0x40009a14 ); +PROVIDE ( Cache_Read_Disable = 0x40009ab8 ); +PROVIDE ( Cache_Read_Enable = 0x40009a84 ); +PROVIDE ( Cache_Read_Init = 0x40009950 ); +PROVIDE ( cache_sram_mmu_set = 0x400097f4 ); +/* This is static function, but can be used, not generated by script*/ +PROVIDE ( calc_rtc_memory_crc = 0x40008170 ); +PROVIDE ( calloc = 0x4000bee4 ); +PROVIDE ( _calloc_r = 0x4000bbf8 ); +PROVIDE ( check_pos = 0x400068b8 ); +PROVIDE ( _cleanup = 0x40001df8 ); +PROVIDE ( _cleanup_r = 0x40001d48 ); +PROVIDE ( __clear_cache = 0x40063860 ); +PROVIDE ( close = 0x40001778 ); +PROVIDE ( _close_r = 0x4000bd3c ); +PROVIDE ( __clrsbdi2 = 0x40064a38 ); +PROVIDE ( __clrsbsi2 = 0x40064a20 ); +PROVIDE ( __clzdi2 = 0x4000ca50 ); +PROVIDE ( __clzsi2 = 0x4000c7e8 ); +PROVIDE ( __cmpdi2 = 0x40063820 ); +PROVIDE ( co_default_bdaddr = 0x3ffae704 ); +PROVIDE ( co_null_bdaddr = 0x3ffb80e0 ); +PROVIDE ( co_sca2ppm = 0x3ff971e8 ); +PROVIDE ( crc16_be = 0x4005d09c ); +PROVIDE ( crc16_le = 0x4005d05c ); +PROVIDE ( crc32_be = 0x4005d024 ); +PROVIDE ( crc32_le = 0x4005cfec ); +PROVIDE ( crc8_be = 0x4005d114 ); +PROVIDE ( crc8_le = 0x4005d0e0 ); +PROVIDE ( creat = 0x40000e8c ); +PROVIDE ( ctime = 0x400595b0 ); +PROVIDE ( ctime_r = 0x400595c4 ); +PROVIDE ( _ctype_ = 0x3ff96354 ); +PROVIDE ( __ctype_ptr__ = 0x3ff96350 ); +PROVIDE ( __ctzdi2 = 0x4000ca64 ); +PROVIDE ( __ctzsi2 = 0x4000c7f0 ); +PROVIDE ( _data_end = 0x4000d5c8 ); +PROVIDE ( _data_end_btdm_rom = 0x4000d4f8 ); +PROVIDE ( _data_start = 0x4000d4f8 ); +PROVIDE ( _data_start_btdm_rom = 0x4000d4f4 ); +PROVIDE ( _data_start_btdm = 0x3ffae6e0); +PROVIDE ( _data_end_btdm = 0x3ffaff10); +PROVIDE ( _bss_start_btdm = 0x3ffb8000); +PROVIDE ( _bss_end_btdm = 0x3ffbff70); +PROVIDE ( _daylight = 0x3ffae0a4 ); +PROVIDE ( dbg_default_handler = 0x3ff97218 ); +PROVIDE ( dbg_state = 0x3ffb8d5d ); +PROVIDE ( DebugE256PublicKey_x = 0x3ff97428 ); +PROVIDE ( DebugE256PublicKey_y = 0x3ff97408 ); +PROVIDE ( DebugE256SecretKey = 0x3ff973e8 ); +PROVIDE ( _DebugExceptionVector = 0x40000280 ); +PROVIDE ( debug_timer = 0x3ffe042c ); +PROVIDE ( debug_timerfn = 0x3ffe0430 ); +PROVIDE ( dh_group14_generator = 0x3ff9ac60 ); +PROVIDE ( dh_group14_prime = 0x3ff9ab60 ); +PROVIDE ( dh_group15_generator = 0x3ff9ab5f ); +PROVIDE ( dh_group15_prime = 0x3ff9a9df ); +PROVIDE ( dh_group16_generator = 0x3ff9a9de ); +PROVIDE ( dh_group16_prime = 0x3ff9a7de ); +PROVIDE ( dh_group17_generator = 0x3ff9a7dd ); +PROVIDE ( dh_group17_prime = 0x3ff9a4dd ); +PROVIDE ( dh_group18_generator = 0x3ff9a4dc ); +PROVIDE ( dh_group18_prime = 0x3ff9a0dc ); +PROVIDE ( dh_group1_generator = 0x3ff9ae03 ); +PROVIDE ( dh_group1_prime = 0x3ff9ada3 ); +PROVIDE ( dh_group2_generator = 0x3ff9ada2 ); +PROVIDE ( dh_group2_prime = 0x3ff9ad22 ); +PROVIDE ( dh_group5_generator = 0x3ff9ad21 ); +PROVIDE ( dh_group5_prime = 0x3ff9ac61 ); +PROVIDE ( div = 0x40056348 ); +PROVIDE ( __divdc3 = 0x40064460 ); +PROVIDE ( __divdf3 = 0x40002954 ); +PROVIDE ( __divdi3 = 0x4000ca84 ); +PROVIDE ( __divsc3 = 0x40064200 ); +PROVIDE ( __divsf3 = 0x4000234c ); +PROVIDE ( __divsi3 = 0x4000c7b8 ); +PROVIDE ( _DoubleExceptionVector = 0x400003c0 ); +PROVIDE ( dummy_len_plus = 0x3ffae290 ); +PROVIDE ( __dummy_lock = 0x4000c728 ); +PROVIDE ( __dummy_lock_try = 0x4000c730 ); +PROVIDE ( ecc_env = 0x3ffb8d60 ); +PROVIDE ( ecc_Jacobian_InfinityPoint256 = 0x3ff972e8 ); +PROVIDE ( em_buf_env = 0x3ffb8d74 ); +PROVIDE ( environ = 0x3ffae0b4 ); +PROVIDE ( __env_lock = 0x40001fd4 ); +PROVIDE ( __env_unlock = 0x40001fe0 ); +PROVIDE ( __eqdf2 = 0x400636a8 ); +PROVIDE ( __eqsf2 = 0x40063374 ); +PROVIDE ( esp_crc8 = 0x4005d144 ); +PROVIDE ( _etext = 0x4000d66c ); +PROVIDE ( ets_aes_crypt = 0x4005c9b8 ); +PROVIDE ( ets_aes_disable = 0x4005c8f8 ); +PROVIDE ( ets_aes_enable = 0x4005c8cc ); +PROVIDE ( ets_aes_set_endian = 0x4005c928 ); +PROVIDE ( ets_aes_setkey_dec = 0x4005c994 ); +PROVIDE ( ets_aes_setkey_enc = 0x4005c97c ); +PROVIDE ( ets_bigint_disable = 0x4005c4e0 ); +PROVIDE ( ets_bigint_enable = 0x4005c498 ); +PROVIDE ( ets_bigint_mod_mult_getz = 0x4005c818 ); +PROVIDE ( ets_bigint_mod_mult_prepare = 0x4005c7b4 ); +PROVIDE ( ets_bigint_mod_power_getz = 0x4005c614 ); +PROVIDE ( ets_bigint_mod_power_prepare = 0x4005c54c ); +PROVIDE ( ets_bigint_montgomery_mult_getz = 0x4005c7a4 ); +PROVIDE ( ets_bigint_montgomery_mult_prepare = 0x4005c6fc ); +PROVIDE ( ets_bigint_mult_getz = 0x4005c6e8 ); +PROVIDE ( ets_bigint_mult_prepare = 0x4005c630 ); +PROVIDE ( ets_bigint_wait_finish = 0x4005c520 ); +PROVIDE ( ets_delay_us = 0x40008534 ); +PROVIDE ( ets_efuse_get_8M_clock = 0x40008710 ); +PROVIDE ( ets_efuse_get_spiconfig = 0x40008658 ); +PROVIDE ( ets_efuse_program_op = 0x40008628 ); +PROVIDE ( ets_efuse_read_op = 0x40008600 ); +PROVIDE ( ets_get_cpu_frequency = 0x4000855c ); +PROVIDE ( ets_get_detected_xtal_freq = 0x40008588 ); +PROVIDE ( ets_get_xtal_scale = 0x4000856c ); +PROVIDE ( ets_install_putc1 = 0x40007d18 ); +PROVIDE ( ets_install_putc2 = 0x40007d38 ); +PROVIDE ( ets_install_uart_printf = 0x40007d28 ); +PROVIDE ( ets_post = 0x4000673c ); +PROVIDE ( ets_printf = 0x40007d54 ); +PROVIDE ( ets_readySet_ = 0x3ffe01f0 ); +PROVIDE ( ets_run = 0x400066bc ); +PROVIDE ( ets_secure_boot_check = 0x4005cb40 ); +PROVIDE ( ets_secure_boot_check_finish = 0x4005cc04 ); +PROVIDE ( ets_secure_boot_check_start = 0x4005cbcc ); +PROVIDE ( ets_secure_boot_finish = 0x4005ca84 ); +PROVIDE ( ets_secure_boot_hash = 0x4005cad4 ); +PROVIDE ( ets_secure_boot_obtain = 0x4005cb14 ); +PROVIDE ( ets_secure_boot_rd_abstract = 0x4005cba8 ); +PROVIDE ( ets_secure_boot_rd_iv = 0x4005cb84 ); +PROVIDE ( ets_secure_boot_start = 0x4005ca34 ); +PROVIDE ( ets_set_appcpu_boot_addr = 0x4000689c ); +PROVIDE ( ets_set_idle_cb = 0x40006674 ); +PROVIDE ( ets_set_startup_callback = 0x4000688c ); +PROVIDE ( ets_set_user_start = 0x4000687c ); +PROVIDE ( ets_sha_disable = 0x4005c0a8 ); +PROVIDE ( ets_sha_enable = 0x4005c07c ); +PROVIDE ( ets_sha_finish = 0x4005c104 ); +PROVIDE ( ets_sha_init = 0x4005c0d4 ); +PROVIDE ( ets_sha_update = 0x4005c2a0 ); +PROVIDE ( ets_startup_callback = 0x3ffe0404 ); +PROVIDE ( ets_task = 0x40006688 ); +PROVIDE ( ets_timer_arm = 0x40008368 ); +PROVIDE ( ets_timer_arm_us = 0x400083ac ); +PROVIDE ( ets_timer_disarm = 0x400083ec ); +PROVIDE ( ets_timer_done = 0x40008428 ); +PROVIDE ( ets_timer_handler_isr = 0x40008454 ); +PROVIDE ( ets_timer_init = 0x400084e8 ); +PROVIDE ( ets_timer_setfn = 0x40008350 ); +PROVIDE ( ets_unpack_flash_code = 0x40007018 ); +PROVIDE ( ets_unpack_flash_code_legacy = 0x4000694c ); +PROVIDE ( ets_update_cpu_frequency = 0x40008550 ); +PROVIDE ( ets_waiti0 = 0x400067d8 ); +PROVIDE ( exc_cause_table = 0x3ff991d0 ); +PROVIDE ( _exit_r = 0x4000bd28 ); +PROVIDE ( __extendsfdf2 = 0x40002c34 ); +PROVIDE ( fclose = 0x400020ac ); +PROVIDE ( _fclose_r = 0x40001fec ); +PROVIDE ( fflush = 0x40059394 ); +PROVIDE ( _fflush_r = 0x40059320 ); +PROVIDE ( __ffsdi2 = 0x4000ca2c ); +PROVIDE ( __ffssi2 = 0x4000c804 ); +PROVIDE ( FilePacketSendDeflatedReqMsgProc = 0x40008b24 ); +PROVIDE ( FilePacketSendReqMsgProc = 0x40008860 ); +PROVIDE ( _findenv_r = 0x40001f44 ); +PROVIDE ( __fixdfdi = 0x40002ac4 ); +PROVIDE ( __fixdfsi = 0x40002a78 ); +PROVIDE ( __fixsfdi = 0x4000244c ); +PROVIDE ( __fixsfsi = 0x4000240c ); +PROVIDE ( __fixunsdfsi = 0x40002b30 ); +PROVIDE ( __fixunssfdi = 0x40002504 ); +PROVIDE ( __fixunssfsi = 0x400024ac ); +PROVIDE ( FlashDwnLdDeflatedStartMsgProc = 0x40008ad8 ); +PROVIDE ( FlashDwnLdParamCfgMsgProc = 0x4000891c ); +PROVIDE ( FlashDwnLdStartMsgProc = 0x40008820 ); +PROVIDE ( FlashDwnLdStopDeflatedReqMsgProc = 0x40008c18 ); +PROVIDE ( FlashDwnLdStopReqMsgProc = 0x400088ec ); +PROVIDE ( __floatdidf = 0x4000c988 ); +PROVIDE ( __floatdisf = 0x4000c8c0 ); +PROVIDE ( __floatsidf = 0x4000c944 ); +PROVIDE ( __floatsisf = 0x4000c870 ); +PROVIDE ( __floatundidf = 0x4000c978 ); +PROVIDE ( __floatundisf = 0x4000c8b0 ); +PROVIDE ( __floatunsidf = 0x4000c938 ); +PROVIDE ( __floatunsisf = 0x4000c864 ); +PROVIDE ( __fp_lock_all = 0x40001f1c ); +PROVIDE ( __fp_unlock_all = 0x40001f30 ); +PROVIDE ( fputwc = 0x40058ea8 ); +PROVIDE ( __fputwc = 0x40058da0 ); +PROVIDE ( _fputwc_r = 0x40058e4c ); +PROVIDE ( free = 0x4000beb8 ); +PROVIDE ( _free_r = 0x4000bbcc ); +PROVIDE ( _fstat_r = 0x4000bccc ); +PROVIDE ( _fwalk = 0x4000c738 ); +PROVIDE ( _fwalk_reent = 0x4000c770 ); +PROVIDE ( __gcc_bcmp = 0x40064a70 ); +PROVIDE ( __gedf2 = 0x40063768 ); +PROVIDE ( _GeneralException = 0x40000e14 ); +PROVIDE ( __gesf2 = 0x4006340c ); +PROVIDE ( __get_current_time_locale = 0x40001834 ); +PROVIDE ( _getenv_r = 0x40001fbc ); +PROVIDE ( _getpid_r = 0x4000bcfc ); +PROVIDE ( __getreent = 0x4000be8c ); +PROVIDE ( _gettimeofday_r = 0x4000bc58 ); +PROVIDE ( __gettzinfo = 0x40001fcc ); +PROVIDE ( GetUartDevice = 0x40009598 ); +PROVIDE ( GF_Jacobian_Point_Addition256 = 0x400163a4 ); +PROVIDE ( GF_Jacobian_Point_Double256 = 0x40016260 ); +PROVIDE ( GF_Point_Jacobian_To_Affine256 = 0x40016b0c ); +PROVIDE ( _global_impure_ptr = 0x3ffae0b0 ); +PROVIDE ( gmtime = 0x40059848 ); +PROVIDE ( gmtime_r = 0x40059868 ); +PROVIDE ( g_phyFuns_instance = 0x3ffae0c4 ); +PROVIDE ( g_rom_flashchip = 0x3ffae270 ); +PROVIDE ( gpio_init = 0x40009c20 ); +PROVIDE ( gpio_input_get = 0x40009b88 ); +PROVIDE ( gpio_input_get_high = 0x40009b9c ); +PROVIDE ( gpio_intr_ack = 0x40009dd4 ); +PROVIDE ( gpio_intr_ack_high = 0x40009e1c ); +PROVIDE ( gpio_intr_handler_register = 0x40009e6c ); +PROVIDE ( gpio_intr_pending = 0x40009cec ); +PROVIDE ( gpio_intr_pending_high = 0x40009cf8 ); +PROVIDE ( gpio_matrix_in = 0x40009edc ); +PROVIDE ( gpio_matrix_out = 0x40009f0c ); +PROVIDE ( gpio_output_set = 0x40009b24 ); +PROVIDE ( gpio_output_set_high = 0x40009b5c ); +PROVIDE ( gpio_pad_hold = 0x4000a734 ); +PROVIDE ( gpio_pad_pulldown = 0x4000a348 ); +PROVIDE ( gpio_pad_pullup = 0x4000a22c ); +PROVIDE ( gpio_pad_select_gpio = 0x40009fdc ); +PROVIDE ( gpio_pad_set_drv = 0x4000a11c ); +PROVIDE ( gpio_pad_unhold = 0x4000a484 ); +PROVIDE ( gpio_pending_mask = 0x3ffe0038 ); +PROVIDE ( gpio_pending_mask_high = 0x3ffe0044 ); +PROVIDE ( gpio_pin_intr_state_set = 0x40009d04 ); +PROVIDE ( gpio_pin_wakeup_disable = 0x40009eb0 ); +PROVIDE ( gpio_pin_wakeup_enable = 0x40009e7c ); +PROVIDE ( gpio_register_get = 0x40009cbc ); +PROVIDE ( gpio_register_set = 0x40009bbc ); +PROVIDE ( __gtdf2 = 0x400636dc ); +PROVIDE ( __gtsf2 = 0x400633a0 ); +PROVIDE ( gTxMsg = 0x3ffe0050 ); +PROVIDE ( hci_cmd_desc_root_tab = 0x3ff976d4 ); +PROVIDE ( hci_cmd_desc_tab_ctrl_bb = 0x3ff97b70 ); +PROVIDE ( hci_cmd_desc_tab_info_par = 0x3ff97b1c ); +PROVIDE ( hci_cmd_desc_tab_le = 0x3ff97870 ); +PROVIDE ( hci_cmd_desc_tab_lk_ctrl = 0x3ff97fc0 ); +PROVIDE ( hci_cmd_desc_tab_lk_pol = 0x3ff97f3c ); +PROVIDE ( hci_cmd_desc_tab_stat_par = 0x3ff97ac8 ); +PROVIDE ( hci_cmd_desc_tab_testing = 0x3ff97a98 ); +PROVIDE ( hci_cmd_desc_tab_vs = 0x3ff97714 ); +PROVIDE ( hci_command_handler = 0x4004c928 ); +PROVIDE ( hci_env = 0x3ffb9350 ); +PROVIDE ( hci_evt_dbg_desc_tab = 0x3ff9750c ); +PROVIDE ( hci_evt_desc_tab = 0x3ff9751c ); +PROVIDE ( hci_evt_le_desc_tab = 0x3ff974b4 ); +PROVIDE ( hci_fc_env = 0x3ffb9340 ); +PROVIDE ( hmac_md5 = 0x4005d264 ); +PROVIDE ( hmac_md5_vector = 0x4005d17c ); +PROVIDE ( hmac_sha1 = 0x40060acc ); +PROVIDE ( hmac_sha1_vector = 0x400609e4 ); +PROVIDE ( hmac_sha256 = 0x40060d58 ); +PROVIDE ( hmac_sha256_vector = 0x40060c84 ); +PROVIDE ( intr_matrix_set = 0x4000681c ); +PROVIDE ( isalnum = 0x40000f04 ); +PROVIDE ( isalpha = 0x40000f18 ); +PROVIDE ( isascii = 0x4000c20c ); +PROVIDE ( _isatty_r = 0x40000ea0 ); +PROVIDE ( isblank = 0x40000f2c ); +PROVIDE ( iscntrl = 0x40000f50 ); +PROVIDE ( isdigit = 0x40000f64 ); +PROVIDE ( isgraph = 0x40000f94 ); +PROVIDE ( islower = 0x40000f78 ); +PROVIDE ( isprint = 0x40000fa8 ); +PROVIDE ( ispunct = 0x40000fc0 ); +PROVIDE ( isspace = 0x40000fd4 ); +PROVIDE ( isupper = 0x40000fe8 ); +PROVIDE ( itoa = 0x400566b4 ); +PROVIDE ( __itoa = 0x40056678 ); +PROVIDE ( jd_decomp = 0x400613e8 ); +PROVIDE ( jd_prepare = 0x40060fa8 ); +PROVIDE ( ke_env = 0x3ffb93cc ); +PROVIDE ( _KernelExceptionVector = 0x40000300 ); +PROVIDE ( _kill_r = 0x4000bd10 ); +PROVIDE ( labs = 0x40056370 ); +PROVIDE ( lb_default_handler = 0x3ff982b8 ); +PROVIDE ( lb_default_state_tab_p_get = 0x4001c198 ); +PROVIDE ( lb_env = 0x3ffb9424 ); +PROVIDE ( lb_hci_cmd_handler_tab_p_get = 0x4001c18c ); +PROVIDE ( lb_state = 0x3ffb94e8 ); +PROVIDE ( lc_default_handler = 0x3ff98648 ); +PROVIDE ( lc_default_state_tab_p_get = 0x4002f494 ); +PROVIDE ( lc_env = 0x3ffb94ec ); +PROVIDE ( lc_hci_cmd_handler_tab_p_get = 0x4002f488 ); +PROVIDE ( lc_state = 0x3ffb9508 ); +PROVIDE ( ld_acl_br_sizes = 0x3ff98a2a ); +PROVIDE ( ld_acl_br_types = 0x3ff98a36 ); +PROVIDE ( ld_acl_edr_sizes = 0x3ff98a14 ); +PROVIDE ( ld_acl_edr_types = 0x3ff98a22 ); +PROVIDE ( ld_env = 0x3ffb9510 ); +PROVIDE ( ldiv = 0x40056378 ); +PROVIDE ( ld_pcm_settings_dft = 0x3ff98a0c ); +PROVIDE ( ld_sched_params = 0x3ffb96c0 ); +PROVIDE ( ld_sync_train_channels = 0x3ff98a3c ); +PROVIDE ( __ledf2 = 0x40063704 ); +PROVIDE ( __lesf2 = 0x400633c0 ); +PROVIDE ( _Level2FromVector = 0x40000954 ); +PROVIDE ( _Level2Vector = 0x40000180 ); +PROVIDE ( _Level3FromVector = 0x40000a28 ); +PROVIDE ( _Level3Vector = 0x400001c0 ); +PROVIDE ( _Level4FromVector = 0x40000af8 ); +PROVIDE ( _Level4Vector = 0x40000200 ); +PROVIDE ( _Level5FromVector = 0x40000c68 ); +PROVIDE ( _Level5Vector = 0x40000240 ); +PROVIDE ( _LevelOneInterrupt = 0x40000835 ); +PROVIDE ( _link_r = 0x4000bc9c ); +PROVIDE ( llc_default_handler = 0x3ff98b3c ); +PROVIDE ( llc_default_state_tab_p_get = 0x40046058 ); +PROVIDE ( llc_env = 0x3ffb96d0 ); +PROVIDE ( llc_hci_acl_data_tx_handler = 0x40042398 ); +PROVIDE ( llc_hci_cmd_handler_tab_p_get = 0x40042358 ); +PROVIDE ( llc_hci_command_handler = 0x40042360 ); +PROVIDE ( llcp_pdu_handler_tab_p_get = 0x40043f64 ); +PROVIDE ( llc_state = 0x3ffb96f8 ); +PROVIDE ( lldesc_build_chain = 0x4000a850 ); +PROVIDE ( lldesc_num2link = 0x4000a948 ); +PROVIDE ( lldesc_set_owner = 0x4000a974 ); +PROVIDE ( lld_evt_env = 0x3ffb9704 ); +PROVIDE ( lld_pdu_adv_pk_desc_tab = 0x3ff98c70 ); +PROVIDE ( lld_pdu_llcp_pk_desc_tab = 0x3ff98b68 ); +PROVIDE ( LLM_AA_CT1 = 0x3ff98d8a ); +PROVIDE ( LLM_AA_CT2 = 0x3ff98d88 ); +PROVIDE ( llm_default_handler = 0x3ff98d80 ); +PROVIDE ( llm_default_state_tab_p_get = 0x4004e718 ); +PROVIDE ( llm_hci_cmd_handler_tab_p_get = 0x4004c920 ); +PROVIDE ( llm_le_env = 0x3ffb976c ); +PROVIDE ( llm_local_cmds = 0x3ff98d38 ); +PROVIDE ( llm_local_data_len_values = 0x3ff98d1c ); +PROVIDE ( llm_local_le_feats = 0x3ff98d30 ); +PROVIDE ( llm_local_le_states = 0x3ff98d28 ); +PROVIDE ( llm_state = 0x3ffb985c ); +PROVIDE ( lm_default_handler = 0x3ff990e0 ); +PROVIDE ( lm_default_state_tab_p_get = 0x40054268 ); +PROVIDE ( lm_env = 0x3ffb9860 ); +PROVIDE ( lm_hci_cmd_handler_tab_p_get = 0x4005425c ); +PROVIDE ( lm_local_supp_feats = 0x3ff990ee ); +PROVIDE ( lm_n_page_tab = 0x3ff990e8 ); +PROVIDE ( lmp_desc_tab = 0x3ff96e6c ); +PROVIDE ( lmp_ext_desc_tab = 0x3ff96d9c ); +PROVIDE ( lm_state = 0x3ffb9a1c ); +PROVIDE ( __locale_charset = 0x40059540 ); +PROVIDE ( __locale_cjk_lang = 0x40059558 ); +PROVIDE ( localeconv = 0x4005957c ); +PROVIDE ( _localeconv_r = 0x40059560 ); +PROVIDE ( __locale_mb_cur_max = 0x40059548 ); +PROVIDE ( __locale_msgcharset = 0x40059550 ); +PROVIDE ( localtime = 0x400595dc ); +PROVIDE ( localtime_r = 0x400595fc ); +PROVIDE ( _lock_acquire = 0x4000be14 ); +PROVIDE ( _lock_acquire_recursive = 0x4000be28 ); +PROVIDE ( _lock_close = 0x4000bdec ); +PROVIDE ( _lock_close_recursive = 0x4000be00 ); +PROVIDE ( _lock_init = 0x4000bdc4 ); +PROVIDE ( _lock_init_recursive = 0x4000bdd8 ); +PROVIDE ( _lock_release = 0x4000be64 ); +PROVIDE ( _lock_release_recursive = 0x4000be78 ); +PROVIDE ( _lock_try_acquire = 0x4000be3c ); +PROVIDE ( _lock_try_acquire_recursive = 0x4000be50 ); +PROVIDE ( longjmp = 0x400562cc ); +PROVIDE ( _lseek_r = 0x4000bd8c ); +PROVIDE ( __lshrdi3 = 0x4000c84c ); +PROVIDE ( __ltdf2 = 0x40063790 ); +PROVIDE ( __ltsf2 = 0x4006342c ); +PROVIDE ( malloc = 0x4000bea0 ); +PROVIDE ( _malloc_r = 0x4000bbb4 ); +PROVIDE ( maxSecretKey_256 = 0x3ff97448 ); +PROVIDE ( __mb_cur_max = 0x3ff96530 ); +PROVIDE ( MD5Final = 0x4005db1c ); +PROVIDE ( MD5Init = 0x4005da7c ); +PROVIDE ( MD5Update = 0x4005da9c ); +PROVIDE ( md5_vector = 0x4005db80 ); +PROVIDE ( memccpy = 0x4000c220 ); +PROVIDE ( memchr = 0x4000c244 ); +PROVIDE ( memcmp = 0x4000c260 ); +PROVIDE ( memcpy = 0x4000c2c8 ); +PROVIDE ( MemDwnLdStartMsgProc = 0x40008948 ); +PROVIDE ( MemDwnLdStopReqMsgProc = 0x400089dc ); +PROVIDE ( memmove = 0x4000c3c0 ); +PROVIDE ( MemPacketSendReqMsgProc = 0x40008978 ); +PROVIDE ( memrchr = 0x4000c400 ); +PROVIDE ( memset = 0x4000c44c ); +PROVIDE ( mktime = 0x4005a5e8 ); +PROVIDE ( mmu_init = 0x400095a4 ); +PROVIDE ( __moddi3 = 0x4000cd4c ); +PROVIDE ( __modsi3 = 0x4000c7c0 ); +PROVIDE ( __month_lengths = 0x3ff9609c ); +PROVIDE ( __muldc3 = 0x40063bf4 ); +PROVIDE ( __muldf3 = 0x4006358c ); +PROVIDE ( __muldi3 = 0x4000c9fc ); +PROVIDE ( __mulsc3 = 0x40063934 ); +PROVIDE ( __mulsf3 = 0x400632c8 ); +PROVIDE ( __mulsi3 = 0x4000c7b0 ); +PROVIDE ( MultiplyBigHexByUint32_256 = 0x40016214 ); +PROVIDE ( MultiplyBigHexModP256 = 0x400160b8 ); +PROVIDE ( MultiplyByU32ModP256 = 0x40015fdc ); +PROVIDE ( multofup = 0x4000ab8c ); +PROVIDE ( __mulvdi3 = 0x40002d78 ); +PROVIDE ( __mulvsi3 = 0x40002d60 ); +PROVIDE ( mz_adler32 = 0x4005edbc ); +PROVIDE ( mz_crc32 = 0x4005ee88 ); +PROVIDE ( mz_free = 0x4005eed4 ); +PROVIDE ( __nedf2 = 0x400636a8 ); +PROVIDE ( __negdf2 = 0x400634a0 ); +PROVIDE ( __negdi2 = 0x4000ca14 ); +PROVIDE ( __negsf2 = 0x400020c0 ); +PROVIDE ( __negvdi2 = 0x40002e98 ); +PROVIDE ( __negvsi2 = 0x40002e78 ); +PROVIDE ( __nesf2 = 0x40063374 ); +PROVIDE ( _NMIExceptionVector = 0x400002c0 ); +PROVIDE ( notEqual256 = 0x40015b04 ); +PROVIDE ( __nsau_data = 0x3ff96544 ); +PROVIDE ( one_bits = 0x3ff971f8 ); +PROVIDE ( open = 0x4000178c ); +PROVIDE ( _open_r = 0x4000bd54 ); +PROVIDE ( __paritysi2 = 0x40002f3c ); +PROVIDE ( pbkdf2_sha1 = 0x40060ba4 ); +PROVIDE ( phy_get_romfuncs = 0x40004100 ); +PROVIDE ( __popcountdi2 = 0x40002ef8 ); +PROVIDE ( __popcountsi2 = 0x40002ed0 ); +PROVIDE ( __popcount_tab = 0x3ff96544 ); +PROVIDE ( __powidf2 = 0x400638d4 ); +PROVIDE ( __powisf2 = 0x4006389c ); +PROVIDE ( _Pri_4_HandlerAddress = 0x3ffe0648 ); +PROVIDE ( _Pri_5_HandlerAddress = 0x3ffe064c ); +PROVIDE ( qsort = 0x40056424 ); +PROVIDE ( _raise_r = 0x4000bc70 ); +PROVIDE ( rand = 0x40001058 ); +PROVIDE ( rand_r = 0x400010d4 ); +PROVIDE ( r_btdm_option_data = 0x3ffae6e0 ); +PROVIDE ( r_bt_util_buf_acl_rx_alloc = 0x40010218 ); +PROVIDE ( r_bt_util_buf_acl_rx_free = 0x40010234 ); +PROVIDE ( r_bt_util_buf_acl_tx_alloc = 0x40010268 ); +PROVIDE ( r_bt_util_buf_acl_tx_free = 0x40010280 ); +PROVIDE ( r_bt_util_buf_init = 0x400100e4 ); +PROVIDE ( r_bt_util_buf_lmp_tx_alloc = 0x400101d0 ); +PROVIDE ( r_bt_util_buf_lmp_tx_free = 0x400101ec ); +PROVIDE ( r_bt_util_buf_sync_clear = 0x400103c8 ); +PROVIDE ( r_bt_util_buf_sync_init = 0x400102c4 ); +PROVIDE ( r_bt_util_buf_sync_rx_alloc = 0x40010468 ); +PROVIDE ( r_bt_util_buf_sync_rx_free = 0x4001049c ); +PROVIDE ( r_bt_util_buf_sync_tx_alloc = 0x400103ec ); +PROVIDE ( r_bt_util_buf_sync_tx_free = 0x40010428 ); +PROVIDE ( rc4_skip = 0x40060928 ); +PROVIDE ( r_co_bdaddr_compare = 0x40014324 ); +PROVIDE ( r_co_bytes_to_string = 0x400142e4 ); +PROVIDE ( r_co_list_check_size_available = 0x400142c4 ); +PROVIDE ( r_co_list_extract = 0x4001404c ); +PROVIDE ( r_co_list_extract_after = 0x40014118 ); +PROVIDE ( r_co_list_find = 0x4001419c ); +PROVIDE ( r_co_list_init = 0x40013f14 ); +PROVIDE ( r_co_list_insert_after = 0x40014254 ); +PROVIDE ( r_co_list_insert_before = 0x40014200 ); +PROVIDE ( r_co_list_merge = 0x400141bc ); +PROVIDE ( r_co_list_pool_init = 0x40013f30 ); +PROVIDE ( r_co_list_pop_front = 0x40014028 ); +PROVIDE ( r_co_list_push_back = 0x40013fb8 ); +PROVIDE ( r_co_list_push_front = 0x40013ff4 ); +PROVIDE ( r_co_list_size = 0x400142ac ); +PROVIDE ( r_co_nb_good_channels = 0x40014360 ); +PROVIDE ( r_co_slot_to_duration = 0x40014348 ); +PROVIDE ( RcvMsg = 0x4000954c ); +PROVIDE ( r_dbg_init = 0x40014394 ); +PROVIDE ( r_dbg_platform_reset_complete = 0x400143d0 ); +PROVIDE ( r_dbg_swdiag_init = 0x40014470 ); +PROVIDE ( r_dbg_swdiag_read = 0x400144a4 ); +PROVIDE ( r_dbg_swdiag_write = 0x400144d0 ); +PROVIDE ( r_E1 = 0x400108e8 ); +PROVIDE ( r_E21 = 0x40010968 ); +PROVIDE ( r_E22 = 0x400109b4 ); +PROVIDE ( r_E3 = 0x40010a58 ); +PROVIDE ( r_ea_alarm_clear = 0x40015ab4 ); +PROVIDE ( r_ea_alarm_set = 0x40015a10 ); +PROVIDE ( read = 0x400017dc ); +PROVIDE ( _read_r = 0x4000bda8 ); +PROVIDE ( r_ea_elt_cancel = 0x400150d0 ); +PROVIDE ( r_ea_elt_create = 0x40015264 ); +PROVIDE ( r_ea_elt_insert = 0x400152a8 ); +PROVIDE ( r_ea_elt_remove = 0x400154f0 ); +PROVIDE ( r_ea_finetimer_isr = 0x400155d4 ); +PROVIDE ( r_ea_init = 0x40015228 ); +PROVIDE ( r_ea_interval_create = 0x4001555c ); +PROVIDE ( r_ea_interval_delete = 0x400155a8 ); +PROVIDE ( r_ea_interval_duration_req = 0x4001597c ); +PROVIDE ( r_ea_interval_insert = 0x4001557c ); +PROVIDE ( r_ea_interval_remove = 0x40015590 ); +PROVIDE ( realloc = 0x4000becc ); +PROVIDE ( _realloc_r = 0x4000bbe0 ); +PROVIDE ( r_ea_offset_req = 0x40015748 ); +PROVIDE ( r_ea_sleep_check = 0x40015928 ); +PROVIDE ( r_ea_sw_isr = 0x40015724 ); +PROVIDE ( r_ea_time_get_halfslot_rounded = 0x40015894 ); +PROVIDE ( r_ea_time_get_slot_rounded = 0x400158d4 ); +PROVIDE ( r_ecc_abort_key256_generation = 0x40017070 ); +PROVIDE ( r_ecc_generate_key256 = 0x40016e00 ); +PROVIDE ( r_ecc_gen_new_public_key = 0x400170c0 ); +PROVIDE ( r_ecc_gen_new_secret_key = 0x400170e4 ); +PROVIDE ( r_ecc_get_debug_Keys = 0x40017224 ); +PROVIDE ( r_ecc_init = 0x40016dbc ); +PROVIDE ( RecvBuff = 0x3ffe009c ); +PROVIDE ( recv_packet = 0x40009424 ); +PROVIDE ( r_em_buf_init = 0x4001729c ); +PROVIDE ( r_em_buf_rx_buff_addr_get = 0x400173e8 ); +PROVIDE ( r_em_buf_rx_free = 0x400173c4 ); +PROVIDE ( r_em_buf_tx_buff_addr_get = 0x40017404 ); +PROVIDE ( r_em_buf_tx_free = 0x4001741c ); +PROVIDE ( _rename_r = 0x4000bc28 ); +PROVIDE ( _ResetHandler = 0x40000450 ); +PROVIDE ( _ResetVector = 0x40000400 ); +PROVIDE ( r_F1_256 = 0x400133e4 ); +PROVIDE ( r_F2_256 = 0x40013568 ); +PROVIDE ( r_F3_256 = 0x40013664 ); +PROVIDE ( RFPLL_ICP_TABLE = 0x3ffb8b7c ); +PROVIDE ( r_G_256 = 0x40013470 ); +PROVIDE ( r_H3 = 0x40013760 ); +PROVIDE ( r_H4 = 0x40013830 ); +PROVIDE ( r_h4tl_init = 0x40017878 ); +PROVIDE ( r_h4tl_start = 0x40017924 ); +PROVIDE ( r_h4tl_stop = 0x40017934 ); +PROVIDE ( r_h4tl_write = 0x400178d0 ); +PROVIDE ( r_H5 = 0x400138dc ); +PROVIDE ( r_hashConcat = 0x40013a38 ); +PROVIDE ( r_hci_acl_tx_data_alloc = 0x4001951c ); +PROVIDE ( r_hci_acl_tx_data_received = 0x40019654 ); +PROVIDE ( r_hci_bt_acl_bdaddr_register = 0x40018900 ); +PROVIDE ( r_hci_bt_acl_bdaddr_unregister = 0x400189ac ); +PROVIDE ( r_hci_bt_acl_conhdl_register = 0x4001895c ); +PROVIDE ( r_hci_cmd_get_max_param_size = 0x400192d0 ); +PROVIDE ( r_hci_cmd_received = 0x400192f8 ); +PROVIDE ( r_hci_evt_filter_add = 0x40018a64 ); +PROVIDE ( r_hci_evt_mask_set = 0x400189e4 ); +PROVIDE ( r_hci_fc_acl_buf_size_set = 0x40017988 ); +PROVIDE ( r_hci_fc_acl_en = 0x400179d8 ); +PROVIDE ( r_hci_fc_acl_packet_sent = 0x40017a3c ); +PROVIDE ( r_hci_fc_check_host_available_nb_acl_packets = 0x40017aa4 ); +PROVIDE ( r_hci_fc_check_host_available_nb_sync_packets = 0x40017ac8 ); +PROVIDE ( r_hci_fc_host_nb_acl_pkts_complete = 0x40017a6c ); +PROVIDE ( r_hci_fc_host_nb_sync_pkts_complete = 0x40017a88 ); +PROVIDE ( r_hci_fc_init = 0x40017974 ); +PROVIDE ( r_hci_fc_sync_buf_size_set = 0x400179b0 ); +PROVIDE ( r_hci_fc_sync_en = 0x40017a30 ); +PROVIDE ( r_hci_fc_sync_packet_sent = 0x40017a54 ); +PROVIDE ( r_hci_init = 0x40018538 ); +PROVIDE ( r_hci_look_for_cmd_desc = 0x40018454 ); +PROVIDE ( r_hci_look_for_dbg_evt_desc = 0x400184c4 ); +PROVIDE ( r_hci_look_for_evt_desc = 0x400184a0 ); +PROVIDE ( r_hci_look_for_le_evt_desc = 0x400184e0 ); +PROVIDE ( r_hci_reset = 0x4001856c ); +PROVIDE ( r_hci_send_2_host = 0x400185bc ); +PROVIDE ( r_hci_sync_tx_data_alloc = 0x40019754 ); +PROVIDE ( r_hci_sync_tx_data_received = 0x400197c0 ); +PROVIDE ( r_hci_tl_init = 0x40019290 ); +PROVIDE ( r_hci_tl_send = 0x40019228 ); +PROVIDE ( r_hci_util_pack = 0x40019874 ); +PROVIDE ( r_hci_util_unpack = 0x40019998 ); +PROVIDE ( r_hci_voice_settings_get = 0x40018bdc ); +PROVIDE ( r_hci_voice_settings_set = 0x40018be8 ); +PROVIDE ( r_HMAC = 0x40013968 ); +PROVIDE ( r_import_rf_phy_func = 0x3ffb8354 ); +PROVIDE ( r_import_rf_phy_func_p = 0x3ffafd64 ); +PROVIDE ( r_ip_funcs = 0x3ffae710 ); +PROVIDE ( r_ip_funcs_p = 0x3ffae70c ); +PROVIDE ( r_ke_check_malloc = 0x40019de0 ); +PROVIDE ( r_ke_event_callback_set = 0x40019ba8 ); +PROVIDE ( r_ke_event_clear = 0x40019c2c ); +PROVIDE ( r_ke_event_flush = 0x40019ccc ); +PROVIDE ( r_ke_event_get = 0x40019c78 ); +PROVIDE ( r_ke_event_get_all = 0x40019cc0 ); +PROVIDE ( r_ke_event_init = 0x40019b90 ); +PROVIDE ( r_ke_event_schedule = 0x40019cdc ); +PROVIDE ( r_ke_event_set = 0x40019be0 ); +PROVIDE ( r_ke_flush = 0x4001a374 ); +PROVIDE ( r_ke_free = 0x4001a014 ); +PROVIDE ( r_ke_get_max_mem_usage = 0x4001a1c8 ); +PROVIDE ( r_ke_get_mem_usage = 0x4001a1a0 ); +PROVIDE ( r_ke_init = 0x4001a318 ); +PROVIDE ( r_ke_is_free = 0x4001a184 ); +PROVIDE ( r_ke_malloc = 0x40019eb4 ); +PROVIDE ( r_ke_mem_init = 0x40019d3c ); +PROVIDE ( r_ke_mem_is_empty = 0x40019d8c ); +PROVIDE ( r_ke_msg_alloc = 0x4001a1e0 ); +PROVIDE ( r_ke_msg_dest_id_get = 0x4001a2e0 ); +PROVIDE ( r_ke_msg_discard = 0x4001a850 ); +PROVIDE ( r_ke_msg_forward = 0x4001a290 ); +PROVIDE ( r_ke_msg_forward_new_id = 0x4001a2ac ); +PROVIDE ( r_ke_msg_free = 0x4001a2cc ); +PROVIDE ( r_ke_msg_in_queue = 0x4001a2f8 ); +PROVIDE ( r_ke_msg_save = 0x4001a858 ); +PROVIDE ( r_ke_msg_send = 0x4001a234 ); +PROVIDE ( r_ke_msg_send_basic = 0x4001a26c ); +PROVIDE ( r_ke_msg_src_id_get = 0x4001a2ec ); +PROVIDE ( r_ke_queue_extract = 0x40055fd0 ); +PROVIDE ( r_ke_queue_insert = 0x40056020 ); +PROVIDE ( r_ke_sleep_check = 0x4001a3d8 ); +PROVIDE ( r_ke_state_get = 0x4001a7d8 ); +PROVIDE ( r_ke_state_set = 0x4001a6fc ); +PROVIDE ( r_ke_stats_get = 0x4001a3f0 ); +PROVIDE ( r_ke_task_check = 0x4001a8a4 ); +PROVIDE ( r_ke_task_create = 0x4001a674 ); +PROVIDE ( r_ke_task_delete = 0x4001a6c0 ); +PROVIDE ( r_ke_task_init = 0x4001a650 ); +PROVIDE ( r_ke_task_msg_flush = 0x4001a860 ); +PROVIDE ( r_ke_timer_active = 0x4001ac08 ); +PROVIDE ( r_ke_timer_adjust_all = 0x4001ac30 ); +PROVIDE ( r_ke_timer_clear = 0x4001ab90 ); +PROVIDE ( r_ke_timer_init = 0x4001aa9c ); +PROVIDE ( r_ke_timer_set = 0x4001aac0 ); +PROVIDE ( r_ke_timer_sleep_check = 0x4001ac50 ); +PROVIDE ( r_KPrimC = 0x40010ad4 ); +PROVIDE ( r_lb_clk_adj_activate = 0x4001ae70 ); +PROVIDE ( r_lb_clk_adj_id_get = 0x4001af14 ); +PROVIDE ( r_lb_clk_adj_period_update = 0x4001af20 ); +PROVIDE ( r_lb_init = 0x4001acd4 ); +PROVIDE ( r_lb_mst_key = 0x4001afc0 ); +PROVIDE ( r_lb_mst_key_cmp = 0x4001af74 ); +PROVIDE ( r_lb_mst_key_restart_enc = 0x4001b0d4 ); +PROVIDE ( r_lb_mst_start_act_bcst_enc = 0x4001b198 ); +PROVIDE ( r_lb_mst_stop_act_bcst_enc = 0x4001b24c ); +PROVIDE ( r_lb_reset = 0x4001ad38 ); +PROVIDE ( r_lb_send_lmp = 0x4001adbc ); +PROVIDE ( r_lb_send_pdu_clk_adj = 0x4001af3c ); +PROVIDE ( r_lb_util_get_csb_mode = 0x4001ada4 ); +PROVIDE ( r_lb_util_get_nb_broadcast = 0x4001ad80 ); +PROVIDE ( r_lb_util_get_res_lt_addr = 0x4001ad98 ); +PROVIDE ( r_lb_util_set_nb_broadcast = 0x4001ad8c ); +PROVIDE ( r_lc_afh_set = 0x4001cc74 ); +PROVIDE ( r_lc_afh_start = 0x4001d240 ); +PROVIDE ( r_lc_auth_cmp = 0x4001cd54 ); +PROVIDE ( r_lc_calc_link_key = 0x4001ce7c ); +PROVIDE ( r_lc_chg_pkt_type_cmp = 0x4001d038 ); +PROVIDE ( r_lc_chg_pkt_type_cont = 0x4001cfbc ); +PROVIDE ( r_lc_chg_pkt_type_retry = 0x4001d0ac ); +PROVIDE ( r_lc_chk_to = 0x4001d2a8 ); +PROVIDE ( r_lc_cmd_stat_send = 0x4001c914 ); +PROVIDE ( r_lc_comb_key_svr = 0x4001d30c ); +PROVIDE ( r_lc_con_cmp = 0x4001d44c ); +PROVIDE ( r_lc_con_cmp_evt_send = 0x4001d4fc ); +PROVIDE ( r_lc_conn_seq_done = 0x40021334 ); +PROVIDE ( r_lc_detach = 0x4002037c ); +PROVIDE ( r_lc_dhkey = 0x4001d564 ); +PROVIDE ( r_lc_enc_cmp = 0x4001d8bc ); +PROVIDE ( r_lc_enc_key_refresh = 0x4001d720 ); +PROVIDE ( r_lc_end_chk_colli = 0x4001d858 ); +PROVIDE ( r_lc_end_of_sniff_nego = 0x4001d9a4 ); +PROVIDE ( r_lc_enter_sniff_mode = 0x4001ddb8 ); +PROVIDE ( r_lc_epr_change_lk = 0x4001db38 ); +PROVIDE ( r_lc_epr_cmp = 0x4001da88 ); +PROVIDE ( r_lc_epr_resp = 0x4001e0b4 ); +PROVIDE ( r_lc_epr_rsw_cmp = 0x4001dd40 ); +PROVIDE ( r_lc_ext_feat = 0x40020d6c ); +PROVIDE ( r_lc_feat = 0x40020984 ); +PROVIDE ( r_lc_hl_connect = 0x400209e8 ); +PROVIDE ( r_lc_init = 0x4001c948 ); +PROVIDE ( r_lc_init_calc_f3 = 0x4001deb0 ); +PROVIDE ( r_lc_initiator_epr = 0x4001e064 ); +PROVIDE ( r_lc_init_passkey_loop = 0x4001dfc0 ); +PROVIDE ( r_lc_init_start_mutual_auth = 0x4001df60 ); +PROVIDE ( r_lc_key_exch_end = 0x4001e140 ); +PROVIDE ( r_lc_legacy_pair = 0x4001e1c0 ); +PROVIDE ( r_lc_local_switch = 0x4001e22c ); +PROVIDE ( r_lc_local_trans_mode = 0x4001e2e4 ); +PROVIDE ( r_lc_local_untrans_mode = 0x4001e3a0 ); +PROVIDE ( r_lc_loc_auth = 0x40020ecc ); +PROVIDE ( r_lc_locepr_lkref = 0x4001d648 ); +PROVIDE ( r_lc_locepr_rsw = 0x4001d5d0 ); +PROVIDE ( r_lc_loc_sniff = 0x40020a6c ); +PROVIDE ( r_lc_max_slot_mgt = 0x4001e410 ); +PROVIDE ( r_lc_mst_key = 0x4001e7c0 ); +PROVIDE ( r_lc_mst_qos_done = 0x4001ea80 ); +PROVIDE ( r_lc_mst_send_mst_key = 0x4001e8f4 ); +PROVIDE ( r_lc_mutual_auth_end = 0x4001e670 ); +PROVIDE ( r_lc_mutual_auth_end2 = 0x4001e4f4 ); +PROVIDE ( r_lc_packet_type = 0x40021038 ); +PROVIDE ( r_lc_pair = 0x40020ddc ); +PROVIDE ( r_lc_pairing_cont = 0x4001eafc ); +PROVIDE ( r_lc_passkey_comm = 0x4001ed20 ); +PROVIDE ( r_lc_prepare_all_links_for_clk_adj = 0x40021430 ); +PROVIDE ( r_lc_proc_rcv_dhkey = 0x4001edec ); +PROVIDE ( r_lc_ptt = 0x4001ee2c ); +PROVIDE ( r_lc_ptt_cmp = 0x4001eeec ); +PROVIDE ( r_lc_qos_setup = 0x4001ef50 ); +PROVIDE ( r_lc_rd_rem_name = 0x4001efd0 ); +PROVIDE ( r_lc_release = 0x4001f8a8 ); +PROVIDE ( r_lc_rem_enc = 0x4001f124 ); +PROVIDE ( r_lc_rem_name_cont = 0x4001f290 ); +PROVIDE ( r_lc_rem_nego_trans_mode = 0x4001f1b4 ); +PROVIDE ( r_lc_rem_sniff = 0x40020ca4 ); +PROVIDE ( r_lc_rem_sniff_sub_rate = 0x40020b10 ); +PROVIDE ( r_lc_rem_switch = 0x4001f070 ); +PROVIDE ( r_lc_rem_trans_mode = 0x4001f314 ); +PROVIDE ( r_lc_rem_unsniff = 0x400207a0 ); +PROVIDE ( r_lc_rem_untrans_mode = 0x4001f36c ); +PROVIDE ( r_lc_reset = 0x4001c99c ); +PROVIDE ( r_lc_resp_auth = 0x4001f518 ); +PROVIDE ( r_lc_resp_calc_f3 = 0x4001f710 ); +PROVIDE ( r_lc_resp_num_comp = 0x40020074 ); +PROVIDE ( r_lc_resp_oob_nonce = 0x4001f694 ); +PROVIDE ( r_lc_resp_oob_wait_nonce = 0x4001f66c ); +PROVIDE ( r_lc_resp_pair = 0x400208a4 ); +PROVIDE ( r_lc_resp_sec_auth = 0x4001f4a0 ); +PROVIDE ( r_lc_resp_wait_dhkey_cont = 0x4001f86c ); +PROVIDE ( r_lc_restart_enc = 0x4001f8ec ); +PROVIDE ( r_lc_restart_enc_cont = 0x4001f940 ); +PROVIDE ( r_lc_restore_afh_reporting = 0x4001f028 ); +PROVIDE ( r_lc_restore_to = 0x4001f9e0 ); +PROVIDE ( r_lc_ret_sniff_max_slot_chg = 0x4001fa30 ); +PROVIDE ( r_lc_rsw_clean_up = 0x4001dc70 ); +PROVIDE ( r_lc_rsw_done = 0x4001db94 ); +PROVIDE ( r_lc_sco_baseband_ack = 0x40022b00 ); +PROVIDE ( r_lc_sco_detach = 0x40021e40 ); +PROVIDE ( r_lc_sco_host_accept = 0x40022118 ); +PROVIDE ( r_lc_sco_host_reject = 0x400222b8 ); +PROVIDE ( r_lc_sco_host_request = 0x40021f4c ); +PROVIDE ( r_lc_sco_host_request_disc = 0x4002235c ); +PROVIDE ( r_lc_sco_init = 0x40021dc8 ); +PROVIDE ( r_lc_sco_peer_accept = 0x40022780 ); +PROVIDE ( r_lc_sco_peer_accept_disc = 0x40022a08 ); +PROVIDE ( r_lc_sco_peer_reject = 0x40022824 ); +PROVIDE ( r_lc_sco_peer_reject_disc = 0x40022a8c ); +PROVIDE ( r_lc_sco_peer_request = 0x4002240c ); +PROVIDE ( r_lc_sco_peer_request_disc = 0x400228ec ); +PROVIDE ( r_lc_sco_release = 0x40021eec ); +PROVIDE ( r_lc_sco_reset = 0x40021dfc ); +PROVIDE ( r_lc_sco_timeout = 0x40022bd4 ); +PROVIDE ( r_lc_sec_auth_compute_sres = 0x4001f3ec ); +PROVIDE ( r_lc_semi_key_cmp = 0x40020294 ); +PROVIDE ( r_lc_send_enc_chg_evt = 0x4002134c ); +PROVIDE ( r_lc_send_enc_mode = 0x40020220 ); +PROVIDE ( r_lc_send_lmp = 0x4001c1a8 ); +PROVIDE ( r_lc_send_pdu_acc = 0x4001c21c ); +PROVIDE ( r_lc_send_pdu_acc_ext4 = 0x4001c240 ); +PROVIDE ( r_lc_send_pdu_au_rand = 0x4001c308 ); +PROVIDE ( r_lc_send_pdu_auto_rate = 0x4001c5d0 ); +PROVIDE ( r_lc_send_pdu_clk_adj_ack = 0x4001c46c ); +PROVIDE ( r_lc_send_pdu_clk_adj_req = 0x4001c494 ); +PROVIDE ( r_lc_send_pdu_comb_key = 0x4001c368 ); +PROVIDE ( r_lc_send_pdu_dhkey_chk = 0x4001c8e8 ); +PROVIDE ( r_lc_send_pdu_encaps_head = 0x4001c440 ); +PROVIDE ( r_lc_send_pdu_encaps_payl = 0x4001c410 ); +PROVIDE ( r_lc_send_pdu_enc_key_sz_req = 0x4001c670 ); +PROVIDE ( r_lc_send_pdu_esco_lk_rem_req = 0x4001c5a8 ); +PROVIDE ( r_lc_send_pdu_feats_ext_req = 0x4001c6ec ); +PROVIDE ( r_lc_send_pdu_feats_res = 0x4001c694 ); +PROVIDE ( r_lc_send_pdu_in_rand = 0x4001c338 ); +PROVIDE ( r_lc_send_pdu_io_cap_res = 0x4001c72c ); +PROVIDE ( r_lc_send_pdu_lsto = 0x4001c64c ); +PROVIDE ( r_lc_send_pdu_max_slot = 0x4001c3c8 ); +PROVIDE ( r_lc_send_pdu_max_slot_req = 0x4001c3ec ); +PROVIDE ( r_lc_send_pdu_not_acc = 0x4001c26c ); +PROVIDE ( r_lc_send_pdu_not_acc_ext4 = 0x4001c294 ); +PROVIDE ( r_lc_send_pdu_num_comp_fail = 0x4001c770 ); +PROVIDE ( r_lc_send_pdu_pause_enc_aes_req = 0x4001c794 ); +PROVIDE ( r_lc_send_pdu_paus_enc_req = 0x4001c7c0 ); +PROVIDE ( r_lc_send_pdu_ptt_req = 0x4001c4c0 ); +PROVIDE ( r_lc_send_pdu_qos_req = 0x4001c82c ); +PROVIDE ( r_lc_send_pdu_resu_enc_req = 0x4001c7e4 ); +PROVIDE ( r_lc_send_pdu_sco_lk_rem_req = 0x4001c580 ); +PROVIDE ( r_lc_send_pdu_set_afh = 0x4001c2c8 ); +PROVIDE ( r_lc_send_pdu_setup_cmp = 0x4001c808 ); +PROVIDE ( r_lc_send_pdu_slot_off = 0x4001c854 ); +PROVIDE ( r_lc_send_pdu_sniff_req = 0x4001c5f0 ); +PROVIDE ( r_lc_send_pdu_sp_cfm = 0x4001c518 ); +PROVIDE ( r_lc_send_pdu_sp_nb = 0x4001c4e8 ); +PROVIDE ( r_lc_send_pdu_sres = 0x4001c548 ); +PROVIDE ( r_lc_send_pdu_tim_acc = 0x4001c6cc ); +PROVIDE ( r_lc_send_pdu_unit_key = 0x4001c398 ); +PROVIDE ( r_lc_send_pdu_unsniff_req = 0x4001c894 ); +PROVIDE ( r_lc_send_pdu_vers_req = 0x4001c8b4 ); +PROVIDE ( r_lc_skip_hl_oob_req = 0x400201bc ); +PROVIDE ( r_lc_sniff_init = 0x40022cac ); +PROVIDE ( r_lc_sniff_max_slot_chg = 0x40020590 ); +PROVIDE ( r_lc_sniff_reset = 0x40022cc8 ); +PROVIDE ( r_lc_sniff_slot_unchange = 0x40021100 ); +PROVIDE ( r_lc_sniff_sub_mode = 0x400204fc ); +PROVIDE ( r_lc_sp_end = 0x400213a8 ); +PROVIDE ( r_lc_sp_fail = 0x40020470 ); +PROVIDE ( r_lc_sp_oob_tid_fail = 0x400204cc ); +PROVIDE ( r_lc_ssr_nego = 0x4002125c ); +PROVIDE ( r_lc_start = 0x4001ca28 ); +PROVIDE ( r_lc_start_enc = 0x4001fb28 ); +PROVIDE ( r_lc_start_enc_key_size = 0x4001fd9c ); +PROVIDE ( r_lc_start_key_exch = 0x4001fe10 ); +PROVIDE ( r_lc_start_lmp_to = 0x4001fae8 ); +PROVIDE ( r_lc_start_oob = 0x4001fffc ); +PROVIDE ( r_lc_start_passkey = 0x4001feac ); +PROVIDE ( r_lc_start_passkey_loop = 0x4001ff88 ); +PROVIDE ( r_lc_stop_afh_report = 0x40020184 ); +PROVIDE ( r_lc_stop_enc = 0x40020110 ); +PROVIDE ( r_lc_switch_cmp = 0x40020448 ); +PROVIDE ( r_lc_unit_key_svr = 0x400206d8 ); +PROVIDE ( r_lc_unsniff = 0x40020c50 ); +PROVIDE ( r_lc_unsniff_cmp = 0x40020810 ); +PROVIDE ( r_lc_unsniff_cont = 0x40020750 ); +PROVIDE ( r_lc_upd_to = 0x4002065c ); +PROVIDE ( r_lc_util_convert_pref_rate_to_packet_type = 0x4002f9b0 ); +PROVIDE ( r_lc_util_get_max_packet_size = 0x4002f4ac ); +PROVIDE ( r_lc_util_get_offset_clke = 0x4002f538 ); +PROVIDE ( r_lc_util_get_offset_clkn = 0x4002f51c ); +PROVIDE ( r_lc_util_set_loc_trans_coll = 0x4002f500 ); +PROVIDE ( r_lc_version = 0x40020a30 ); +PROVIDE ( r_ld_acl_active_hop_types_get = 0x40036e10 ); +PROVIDE ( r_ld_acl_afh_confirm = 0x40036d40 ); +PROVIDE ( r_ld_acl_afh_prepare = 0x40036c84 ); +PROVIDE ( r_ld_acl_afh_set = 0x40036b60 ); +PROVIDE ( r_ld_acl_allowed_tx_packet_types_set = 0x40036810 ); +PROVIDE ( r_ld_acl_bcst_rx_dec = 0x40036394 ); +PROVIDE ( r_ld_acl_bit_off_get = 0x40036b18 ); +PROVIDE ( r_ld_acl_clk_adj_set = 0x40036a00 ); +PROVIDE ( r_ld_acl_clk_off_get = 0x40036b00 ); +PROVIDE ( r_ld_acl_clk_set = 0x40036950 ); +PROVIDE ( r_ld_acl_clock_offset_get = 0x400364c0 ); +PROVIDE ( r_ld_acl_current_tx_power_get = 0x400368f0 ); +PROVIDE ( r_ld_acl_data_flush = 0x400357bc ); +PROVIDE ( r_ld_acl_data_tx = 0x4003544c ); +PROVIDE ( r_ld_acl_edr_set = 0x4003678c ); +PROVIDE ( r_ld_acl_enc_key_load = 0x40036404 ); +PROVIDE ( r_ld_acl_flow_off = 0x40035400 ); +PROVIDE ( r_ld_acl_flow_on = 0x4003541c ); +PROVIDE ( r_ld_acl_flush_timeout_get = 0x40035f9c ); +PROVIDE ( r_ld_acl_flush_timeout_set = 0x40035fe0 ); +PROVIDE ( r_ld_acl_init = 0x40034d08 ); +PROVIDE ( r_ld_acl_lmp_flush = 0x40035d80 ); +PROVIDE ( r_ld_acl_lmp_tx = 0x40035b34 ); +PROVIDE ( r_ld_acl_lsto_get = 0x400366b4 ); +PROVIDE ( r_ld_acl_lsto_set = 0x400366f8 ); +PROVIDE ( r_ld_acl_reset = 0x40034d24 ); +PROVIDE ( r_ld_acl_role_get = 0x40036b30 ); +PROVIDE ( r_ld_acl_rssi_delta_get = 0x40037028 ); +PROVIDE ( r_ld_acl_rsw_req = 0x40035e74 ); +PROVIDE ( r_ld_acl_rx_enc = 0x40036344 ); +PROVIDE ( r_ld_acl_rx_max_slot_get = 0x40036e58 ); +PROVIDE ( r_ld_acl_rx_max_slot_set = 0x40036ea0 ); +PROVIDE ( r_ld_acl_slot_offset_get = 0x4003653c ); +PROVIDE ( r_ld_acl_slot_offset_set = 0x40036658 ); +PROVIDE ( r_ld_acl_sniff = 0x4003617c ); +PROVIDE ( r_ld_acl_sniff_trans = 0x400360a8 ); +PROVIDE ( r_ld_acl_ssr_set = 0x40036274 ); +PROVIDE ( r_ld_acl_start = 0x40034ddc ); +PROVIDE ( r_ld_acl_stop = 0x4003532c ); +PROVIDE ( r_ld_acl_test_mode_set = 0x40036f24 ); +PROVIDE ( r_ld_acl_timing_accuracy_set = 0x4003673c ); +PROVIDE ( r_ld_acl_t_poll_get = 0x40036024 ); +PROVIDE ( r_ld_acl_t_poll_set = 0x40036068 ); +PROVIDE ( r_ld_acl_tx_enc = 0x400362f8 ); +PROVIDE ( r_ld_acl_unsniff = 0x400361e0 ); +PROVIDE ( r_ld_active_check = 0x4003cac4 ); +PROVIDE ( r_ld_afh_ch_assess_data_get = 0x4003caec ); +PROVIDE ( r_ld_bcst_acl_data_tx = 0x40038d3c ); +PROVIDE ( r_ld_bcst_acl_init = 0x40038bd0 ); +PROVIDE ( r_ld_bcst_acl_reset = 0x40038bdc ); +PROVIDE ( r_ld_bcst_acl_start = 0x4003882c ); +PROVIDE ( r_ld_bcst_afh_update = 0x40038f3c ); +PROVIDE ( r_ld_bcst_enc_key_load = 0x4003906c ); +PROVIDE ( r_ld_bcst_lmp_tx = 0x40038bf8 ); +PROVIDE ( r_ld_bcst_tx_enc = 0x40038ff8 ); +PROVIDE ( r_ld_bd_addr_get = 0x4003ca20 ); +PROVIDE ( r_ld_channel_assess = 0x4003c184 ); +PROVIDE ( r_ld_class_of_dev_get = 0x4003ca34 ); +PROVIDE ( r_ld_class_of_dev_set = 0x4003ca50 ); +PROVIDE ( r_ld_csb_rx_afh_update = 0x40039af4 ); +PROVIDE ( r_ld_csb_rx_init = 0x40039690 ); +PROVIDE ( r_ld_csb_rx_reset = 0x4003969c ); +PROVIDE ( r_ld_csb_rx_start = 0x4003972c ); +PROVIDE ( r_ld_csb_rx_stop = 0x40039bb8 ); +PROVIDE ( r_ld_csb_tx_afh_update = 0x4003a5fc ); +PROVIDE ( r_ld_csb_tx_clr_data = 0x4003a71c ); +PROVIDE ( r_ld_csb_tx_dis = 0x4003a5e8 ); +PROVIDE ( r_ld_csb_tx_en = 0x4003a1c0 ); +PROVIDE ( r_ld_csb_tx_init = 0x4003a0e8 ); +PROVIDE ( r_ld_csb_tx_reset = 0x4003a0f8 ); +PROVIDE ( r_ld_csb_tx_set_data = 0x4003a6c0 ); +PROVIDE ( r_ld_fm_clk_isr = 0x4003a7a8 ); +PROVIDE ( r_ld_fm_frame_isr = 0x4003a82c ); +PROVIDE ( r_ld_fm_init = 0x4003a760 ); +PROVIDE ( r_ld_fm_prog_check = 0x4003ab28 ); +PROVIDE ( r_ld_fm_prog_disable = 0x4003a984 ); +PROVIDE ( r_ld_fm_prog_enable = 0x4003a944 ); +PROVIDE ( r_ld_fm_prog_push = 0x4003a9d4 ); +PROVIDE ( r_ld_fm_reset = 0x4003a794 ); +PROVIDE ( r_ld_fm_rx_isr = 0x4003a7f4 ); +PROVIDE ( r_ld_fm_sket_isr = 0x4003a8a4 ); +PROVIDE ( r_ld_init = 0x4003c294 ); +PROVIDE ( r_ld_inq_init = 0x4003b15c ); +PROVIDE ( r_ld_inq_reset = 0x4003b168 ); +PROVIDE ( r_ld_inq_start = 0x4003b1f0 ); +PROVIDE ( r_ld_inq_stop = 0x4003b4f0 ); +PROVIDE ( r_ld_iscan_eir_get = 0x4003c118 ); +PROVIDE ( r_ld_iscan_eir_set = 0x4003bfa0 ); +PROVIDE ( r_ld_iscan_init = 0x4003b9f0 ); +PROVIDE ( r_ld_iscan_reset = 0x4003ba14 ); +PROVIDE ( r_ld_iscan_restart = 0x4003ba44 ); +PROVIDE ( r_ld_iscan_start = 0x4003bb28 ); +PROVIDE ( r_ld_iscan_stop = 0x4003bf1c ); +PROVIDE ( r_ld_iscan_tx_pwr_get = 0x4003c138 ); +PROVIDE ( r_ld_page_init = 0x4003d808 ); +PROVIDE ( r_ld_page_reset = 0x4003d814 ); +PROVIDE ( r_ld_page_start = 0x4003d848 ); +PROVIDE ( r_ld_page_stop = 0x4003da54 ); +PROVIDE ( r_ld_pca_coarse_clock_adjust = 0x4003e324 ); +PROVIDE ( r_ld_pca_init = 0x4003deb4 ); +PROVIDE ( r_ld_pca_initiate_clock_dragging = 0x4003e4ac ); +PROVIDE ( r_ld_pca_local_config = 0x4003df6c ); +PROVIDE ( r_ld_pca_mws_frame_sync = 0x4003e104 ); +PROVIDE ( r_ld_pca_mws_moment_offset_gt = 0x4003e278 ); +PROVIDE ( r_ld_pca_mws_moment_offset_lt = 0x4003e280 ); +PROVIDE ( r_ld_pca_reporting_enable = 0x4003e018 ); +PROVIDE ( r_ld_pca_reset = 0x4003df0c ); +PROVIDE ( r_ld_pca_update_target_offset = 0x4003e050 ); +PROVIDE ( r_ld_pscan_evt_handler = 0x4003f238 ); +PROVIDE ( r_ld_pscan_init = 0x4003f474 ); +PROVIDE ( r_ld_pscan_reset = 0x4003f498 ); +PROVIDE ( r_ld_pscan_restart = 0x4003f4b8 ); +PROVIDE ( r_ld_pscan_start = 0x4003f514 ); +PROVIDE ( r_ld_pscan_stop = 0x4003f618 ); +PROVIDE ( r_ld_read_clock = 0x4003c9e4 ); +PROVIDE ( r_ld_reset = 0x4003c714 ); +PROVIDE ( r_ld_sched_acl_add = 0x4003f978 ); +PROVIDE ( r_ld_sched_acl_remove = 0x4003f99c ); +PROVIDE ( r_ld_sched_compute = 0x4003f6f8 ); +PROVIDE ( r_ld_sched_init = 0x4003f7ac ); +PROVIDE ( r_ld_sched_inq_add = 0x4003f8a8 ); +PROVIDE ( r_ld_sched_inq_remove = 0x4003f8d0 ); +PROVIDE ( r_ld_sched_iscan_add = 0x4003f7e8 ); +PROVIDE ( r_ld_sched_iscan_remove = 0x4003f808 ); +PROVIDE ( r_ld_sched_page_add = 0x4003f910 ); +PROVIDE ( r_ld_sched_page_remove = 0x4003f938 ); +PROVIDE ( r_ld_sched_pscan_add = 0x4003f828 ); +PROVIDE ( r_ld_sched_pscan_remove = 0x4003f848 ); +PROVIDE ( r_ld_sched_reset = 0x4003f7d4 ); +PROVIDE ( r_ld_sched_sco_add = 0x4003fa4c ); +PROVIDE ( r_ld_sched_sco_remove = 0x4003fa9c ); +PROVIDE ( r_ld_sched_sniff_add = 0x4003f9c4 ); +PROVIDE ( r_ld_sched_sniff_remove = 0x4003fa0c ); +PROVIDE ( r_ld_sched_sscan_add = 0x4003f868 ); +PROVIDE ( r_ld_sched_sscan_remove = 0x4003f888 ); +PROVIDE ( r_ld_sco_audio_isr = 0x40037cc8 ); +PROVIDE ( r_ld_sco_data_tx = 0x40037ee8 ); +PROVIDE ( r_ld_sco_start = 0x40037110 ); +PROVIDE ( r_ld_sco_stop = 0x40037c40 ); +PROVIDE ( r_ld_sco_update = 0x40037a74 ); +PROVIDE ( r_ld_sscan_activated = 0x4004031c ); +PROVIDE ( r_ld_sscan_init = 0x400402f0 ); +PROVIDE ( r_ld_sscan_reset = 0x400402fc ); +PROVIDE ( r_ld_sscan_start = 0x40040384 ); +PROVIDE ( r_ld_strain_init = 0x400409f4 ); +PROVIDE ( r_ld_strain_reset = 0x40040a00 ); +PROVIDE ( r_ld_strain_start = 0x40040a8c ); +PROVIDE ( r_ld_strain_stop = 0x40040df0 ); +PROVIDE ( r_ld_timing_accuracy_get = 0x4003caac ); +PROVIDE ( r_ld_util_active_master_afh_map_get = 0x4004131c ); +PROVIDE ( r_ld_util_active_master_afh_map_set = 0x40041308 ); +PROVIDE ( r_ld_util_bch_create = 0x40040fcc ); +PROVIDE ( r_ld_util_fhs_pk = 0x400411c8 ); +PROVIDE ( r_ld_util_fhs_unpk = 0x40040e54 ); +PROVIDE ( r_ld_util_stp_pk = 0x400413f4 ); +PROVIDE ( r_ld_util_stp_unpk = 0x40041324 ); +PROVIDE ( r_ld_version_get = 0x4003ca6c ); +PROVIDE ( r_ld_wlcoex_set = 0x4003caf8 ); +PROVIDE ( r_llc_ch_assess_get_current_ch_map = 0x40041574 ); +PROVIDE ( r_llc_ch_assess_get_local_ch_map = 0x4004150c ); +PROVIDE ( r_llc_ch_assess_local = 0x40041494 ); +PROVIDE ( r_llc_ch_assess_merge_ch = 0x40041588 ); +PROVIDE ( r_llc_ch_assess_reass_ch = 0x400415c0 ); +PROVIDE ( r_llc_common_cmd_complete_send = 0x40044eac ); +PROVIDE ( r_llc_common_cmd_status_send = 0x40044ee0 ); +PROVIDE ( r_llc_common_enc_change_evt_send = 0x40044f6c ); +PROVIDE ( r_llc_common_enc_key_ref_comp_evt_send = 0x40044f38 ); +PROVIDE ( r_llc_common_flush_occurred_send = 0x40044f0c ); +PROVIDE ( r_llc_common_nb_of_pkt_comp_evt_send = 0x40045000 ); +PROVIDE ( r_llc_con_update_complete_send = 0x40044d68 ); +PROVIDE ( r_llc_con_update_finished = 0x4004518c ); +PROVIDE ( r_llc_con_update_ind = 0x40045038 ); +PROVIDE ( r_llc_discon_event_complete_send = 0x40044a30 ); +PROVIDE ( r_llc_end_evt_defer = 0x40046330 ); +PROVIDE ( r_llc_feats_rd_event_send = 0x40044e0c ); +PROVIDE ( r_llc_init = 0x40044778 ); +PROVIDE ( r_llc_le_con_cmp_evt_send = 0x40044a78 ); +PROVIDE ( r_llc_llcp_ch_map_update_pdu_send = 0x40043f94 ); +PROVIDE ( r_llc_llcp_con_param_req_pdu_send = 0x400442fc ); +PROVIDE ( r_llc_llcp_con_param_rsp_pdu_send = 0x40044358 ); +PROVIDE ( r_llc_llcp_con_update_pdu_send = 0x400442c4 ); +PROVIDE ( r_llc_llcp_enc_req_pdu_send = 0x40044064 ); +PROVIDE ( r_llc_llcp_enc_rsp_pdu_send = 0x40044160 ); +PROVIDE ( r_llc_llcp_feats_req_pdu_send = 0x400443b4 ); +PROVIDE ( r_llc_llcp_feats_rsp_pdu_send = 0x400443f0 ); +PROVIDE ( r_llc_llcp_get_autorize = 0x4004475c ); +PROVIDE ( r_llc_llcp_length_req_pdu_send = 0x40044574 ); +PROVIDE ( r_llc_llcp_length_rsp_pdu_send = 0x400445ac ); +PROVIDE ( r_llc_llcp_pause_enc_req_pdu_send = 0x40043fd8 ); +PROVIDE ( r_llc_llcp_pause_enc_rsp_pdu_send = 0x40044010 ); +PROVIDE ( r_llc_llcp_ping_req_pdu_send = 0x4004454c ); +PROVIDE ( r_llc_llcp_ping_rsp_pdu_send = 0x40044560 ); +PROVIDE ( r_llc_llcp_recv_handler = 0x40044678 ); +PROVIDE ( r_llc_llcp_reject_ind_pdu_send = 0x4004425c ); +PROVIDE ( r_llc_llcp_start_enc_req_pdu_send = 0x4004441c ); +PROVIDE ( r_llc_llcp_start_enc_rsp_pdu_send = 0x400441f8 ); +PROVIDE ( r_llc_llcp_terminate_ind_pdu_send = 0x400444b0 ); +PROVIDE ( r_llc_llcp_tester_send = 0x400445e4 ); +PROVIDE ( r_llc_llcp_unknown_rsp_send_pdu = 0x40044534 ); +PROVIDE ( r_llc_llcp_version_ind_pdu_send = 0x40043f6c ); +PROVIDE ( r_llc_lsto_con_update = 0x40045098 ); +PROVIDE ( r_llc_ltk_req_send = 0x40044dc0 ); +PROVIDE ( r_llc_map_update_finished = 0x40045260 ); +PROVIDE ( r_llc_map_update_ind = 0x400450f0 ); +PROVIDE ( r_llc_pdu_acl_tx_ack_defer = 0x400464dc ); +PROVIDE ( r_llc_pdu_defer = 0x40046528 ); +PROVIDE ( r_llc_pdu_llcp_tx_ack_defer = 0x400463ac ); +PROVIDE ( r_llc_reset = 0x400447b8 ); +PROVIDE ( r_llc_start = 0x400447f4 ); +PROVIDE ( r_llc_stop = 0x400449ac ); +PROVIDE ( r_llc_util_bw_mgt = 0x4004629c ); +PROVIDE ( r_llc_util_clear_operation_ptr = 0x40046234 ); +PROVIDE ( r_llc_util_dicon_procedure = 0x40046130 ); +PROVIDE ( r_llc_util_get_free_conhdl = 0x400460c8 ); +PROVIDE ( r_llc_util_get_nb_active_link = 0x40046100 ); +PROVIDE ( r_llc_util_set_auth_payl_to_margin = 0x400461f4 ); +PROVIDE ( r_llc_util_set_llcp_discard_enable = 0x400461c8 ); +PROVIDE ( r_llc_util_update_channel_map = 0x400461ac ); +PROVIDE ( r_llc_version_rd_event_send = 0x40044e60 ); +PROVIDE ( r_lld_adv_start = 0x40048b38 ); +PROVIDE ( r_lld_adv_stop = 0x40048ea0 ); +PROVIDE ( r_lld_ch_map_ind = 0x4004a2f4 ); +PROVIDE ( r_lld_con_param_req = 0x40049f0c ); +PROVIDE ( r_lld_con_param_rsp = 0x40049e00 ); +PROVIDE ( r_lld_con_start = 0x400491f8 ); +PROVIDE ( r_lld_con_stop = 0x40049fdc ); +PROVIDE ( r_lld_con_update_after_param_req = 0x40049bcc ); +PROVIDE ( r_lld_con_update_ind = 0x4004a30c ); +PROVIDE ( r_lld_con_update_req = 0x40049b60 ); +PROVIDE ( r_lld_core_reset = 0x40048a9c ); +PROVIDE ( r_lld_crypt_isr = 0x4004a324 ); +PROVIDE ( r_lld_evt_adv_create = 0x400481f4 ); +PROVIDE ( r_lld_evt_canceled = 0x400485c8 ); +PROVIDE ( r_lld_evt_channel_next = 0x40046aac ); +PROVIDE ( r_lld_evt_deffered_elt_handler = 0x400482bc ); +PROVIDE ( r_lld_evt_delete_elt_handler = 0x40046974 ); +PROVIDE ( r_lld_evt_delete_elt_push = 0x40046a3c ); +PROVIDE ( r_lld_evt_drift_compute = 0x40047670 ); +PROVIDE ( r_lld_evt_elt_delete = 0x40047538 ); +PROVIDE ( r_lld_evt_elt_insert = 0x400474c8 ); +PROVIDE ( r_lld_evt_end = 0x400483e8 ); +PROVIDE ( r_lld_evt_end_isr = 0x4004862c ); +PROVIDE ( r_lld_evt_init = 0x40046b3c ); +PROVIDE ( r_lld_evt_init_evt = 0x40046cd0 ); +PROVIDE ( r_lld_evt_move_to_master = 0x40047ba0 ); +PROVIDE ( r_lld_evt_move_to_slave = 0x40047e18 ); +PROVIDE ( r_lld_evt_prevent_stop = 0x40047adc ); +PROVIDE ( r_lld_evt_restart = 0x40046d50 ); +PROVIDE ( r_lld_evt_rx = 0x40048578 ); +PROVIDE ( r_lld_evt_rx_isr = 0x40048678 ); +PROVIDE ( r_lld_evt_scan_create = 0x40047ae8 ); +PROVIDE ( r_lld_evt_schedule = 0x40047908 ); +PROVIDE ( r_lld_evt_schedule_next = 0x400477dc ); +PROVIDE ( r_lld_evt_schedule_next_instant = 0x400476a8 ); +PROVIDE ( r_lld_evt_slave_update = 0x40048138 ); +PROVIDE ( r_lld_evt_update_create = 0x40047cd8 ); +PROVIDE ( r_lld_get_mode = 0x40049ff8 ); +PROVIDE ( r_lld_init = 0x4004873c ); +PROVIDE ( r_lld_move_to_master = 0x400499e0 ); +PROVIDE ( r_lld_move_to_slave = 0x4004a024 ); +PROVIDE ( r_lld_pdu_adv_pack = 0x4004b488 ); +PROVIDE ( r_lld_pdu_check = 0x4004ac34 ); +PROVIDE ( r_lld_pdu_data_send = 0x4004b018 ); +PROVIDE ( r_lld_pdu_data_tx_push = 0x4004aecc ); +PROVIDE ( r_lld_pdu_rx_handler = 0x4004b4d4 ); +PROVIDE ( r_lld_pdu_send_packet = 0x4004b774 ); +PROVIDE ( r_lld_pdu_tx_flush = 0x4004b414 ); +PROVIDE ( r_lld_pdu_tx_loop = 0x4004ae40 ); +PROVIDE ( r_lld_pdu_tx_prog = 0x4004b120 ); +PROVIDE ( r_lld_pdu_tx_push = 0x4004b080 ); +PROVIDE ( r_lld_ral_renew_req = 0x4004a73c ); +PROVIDE ( r_lld_scan_start = 0x40048ee0 ); +PROVIDE ( r_lld_scan_stop = 0x40049190 ); +PROVIDE ( r_lld_test_mode_rx = 0x4004a540 ); +PROVIDE ( r_lld_test_mode_tx = 0x4004a350 ); +PROVIDE ( r_lld_test_stop = 0x4004a710 ); +PROVIDE ( r_lld_util_anchor_point_move = 0x4004bacc ); +PROVIDE ( r_lld_util_compute_ce_max = 0x4004bc0c ); +PROVIDE ( r_lld_util_connection_param_set = 0x4004ba40 ); +PROVIDE ( r_lld_util_dle_set_cs_fields = 0x4004ba90 ); +PROVIDE ( r_lld_util_eff_tx_time_set = 0x4004bd88 ); +PROVIDE ( r_lld_util_elt_programmed = 0x4004bce0 ); +PROVIDE ( r_lld_util_flush_list = 0x4004bbd8 ); +PROVIDE ( r_lld_util_freq2chnl = 0x4004b9e4 ); +PROVIDE ( r_lld_util_get_bd_address = 0x4004b8ac ); +PROVIDE ( r_lld_util_get_local_offset = 0x4004ba10 ); +PROVIDE ( r_lld_util_get_peer_offset = 0x4004ba24 ); +PROVIDE ( r_lld_util_get_tx_pkt_cnt = 0x4004bd80 ); +PROVIDE ( r_lld_util_instant_get = 0x4004b890 ); +PROVIDE ( r_lld_util_instant_ongoing = 0x4004bbfc ); +PROVIDE ( r_lld_util_priority_set = 0x4004bd10 ); +PROVIDE ( r_lld_util_priority_update = 0x4004bd78 ); +PROVIDE ( r_lld_util_ral_force_rpa_renew = 0x4004b980 ); +PROVIDE ( r_lld_util_set_bd_address = 0x4004b8f8 ); +PROVIDE ( r_lld_wlcoex_set = 0x4004bd98 ); +PROVIDE ( r_llm_ble_ready = 0x4004cc34 ); +PROVIDE ( r_llm_common_cmd_complete_send = 0x4004d288 ); +PROVIDE ( r_llm_common_cmd_status_send = 0x4004d2b4 ); +PROVIDE ( r_llm_con_req_ind = 0x4004cc54 ); +PROVIDE ( r_llm_con_req_tx_cfm = 0x4004d158 ); +PROVIDE ( r_llm_create_con = 0x4004de78 ); +PROVIDE ( r_llm_encryption_done = 0x4004dff8 ); +PROVIDE ( r_llm_encryption_start = 0x4004e128 ); +PROVIDE ( r_llm_end_evt_defer = 0x4004eb6c ); +PROVIDE ( r_llm_init = 0x4004c9f8 ); +PROVIDE ( r_llm_le_adv_report_ind = 0x4004cdf4 ); +PROVIDE ( r_llm_pdu_defer = 0x4004ec48 ); +PROVIDE ( r_llm_ral_clear = 0x4004e1fc ); +PROVIDE ( r_llm_ral_dev_add = 0x4004e23c ); +PROVIDE ( r_llm_ral_dev_rm = 0x4004e3bc ); +PROVIDE ( r_llm_ral_get_rpa = 0x4004e400 ); +PROVIDE ( r_llm_ral_set_timeout = 0x4004e4a0 ); +PROVIDE ( r_llm_ral_update = 0x4004e4f8 ); +PROVIDE ( r_llm_set_adv_data = 0x4004d960 ); +PROVIDE ( r_llm_set_adv_en = 0x4004d7ec ); +PROVIDE ( r_llm_set_adv_param = 0x4004d5f4 ); +PROVIDE ( r_llm_set_scan_en = 0x4004db64 ); +PROVIDE ( r_llm_set_scan_param = 0x4004dac8 ); +PROVIDE ( r_llm_set_scan_rsp_data = 0x4004da14 ); +PROVIDE ( r_llm_test_mode_start_rx = 0x4004d534 ); +PROVIDE ( r_llm_test_mode_start_tx = 0x4004d2fc ); +PROVIDE ( r_llm_util_adv_data_update = 0x4004e8fc ); +PROVIDE ( r_llm_util_apply_bd_addr = 0x4004e868 ); +PROVIDE ( r_llm_util_bd_addr_in_ral = 0x4004eb08 ); +PROVIDE ( r_llm_util_bd_addr_in_wl = 0x4004e788 ); +PROVIDE ( r_llm_util_bd_addr_wl_position = 0x4004e720 ); +PROVIDE ( r_llm_util_bl_add = 0x4004e9ac ); +PROVIDE ( r_llm_util_bl_check = 0x4004e930 ); +PROVIDE ( r_llm_util_bl_rem = 0x4004ea70 ); +PROVIDE ( r_llm_util_check_address_validity = 0x4004e7e4 ); +PROVIDE ( r_llm_util_check_evt_mask = 0x4004e8b0 ); +PROVIDE ( r_llm_util_check_map_validity = 0x4004e800 ); +PROVIDE ( r_llm_util_get_channel_map = 0x4004e8d4 ); +PROVIDE ( r_llm_util_get_supp_features = 0x4004e8e8 ); +PROVIDE ( r_llm_util_set_public_addr = 0x4004e89c ); +PROVIDE ( r_llm_wl_clr = 0x4004dc54 ); +PROVIDE ( r_llm_wl_dev_add = 0x4004dcc0 ); +PROVIDE ( r_llm_wl_dev_add_hdl = 0x4004dd38 ); +PROVIDE ( r_llm_wl_dev_rem = 0x4004dcfc ); +PROVIDE ( r_llm_wl_dev_rem_hdl = 0x4004dde0 ); +PROVIDE ( r_lm_acl_disc = 0x4004f148 ); +PROVIDE ( r_LM_AddSniff = 0x40022d20 ); +PROVIDE ( r_lm_add_sync = 0x40051358 ); +PROVIDE ( r_lm_afh_activate_timer = 0x4004f444 ); +PROVIDE ( r_lm_afh_ch_ass_en_get = 0x4004f3f8 ); +PROVIDE ( r_lm_afh_host_ch_class_get = 0x4004f410 ); +PROVIDE ( r_lm_afh_master_ch_map_get = 0x4004f43c ); +PROVIDE ( r_lm_afh_peer_ch_class_set = 0x4004f418 ); +PROVIDE ( r_lm_check_active_sync = 0x40051334 ); +PROVIDE ( r_LM_CheckEdrFeatureRequest = 0x4002f90c ); +PROVIDE ( r_LM_CheckSwitchInstant = 0x4002f8c0 ); +PROVIDE ( r_lm_check_sync_hl_rsp = 0x4005169c ); +PROVIDE ( r_lm_clk_adj_ack_pending_clear = 0x4004f514 ); +PROVIDE ( r_lm_clk_adj_instant_pending_set = 0x4004f4d8 ); +PROVIDE ( r_LM_ComputePacketType = 0x4002f554 ); +PROVIDE ( r_LM_ComputeSniffSubRate = 0x400233ac ); +PROVIDE ( r_lm_debug_key_compare_192 = 0x4004f3a8 ); +PROVIDE ( r_lm_debug_key_compare_256 = 0x4004f3d0 ); +PROVIDE ( r_lm_dhkey_calc_init = 0x40013234 ); +PROVIDE ( r_lm_dhkey_compare = 0x400132d8 ); +PROVIDE ( r_lm_dut_mode_en_get = 0x4004f3ec ); +PROVIDE ( r_LM_ExtractMaxEncKeySize = 0x4001aca4 ); +PROVIDE ( r_lm_f1 = 0x40012bb8 ); +PROVIDE ( r_lm_f2 = 0x40012cfc ); +PROVIDE ( r_lm_f3 = 0x40013050 ); +PROVIDE ( r_lm_g = 0x40012f90 ); +PROVIDE ( r_LM_GetAFHSwitchInstant = 0x4002f86c ); +PROVIDE ( r_lm_get_auth_en = 0x4004f1ac ); +PROVIDE ( r_lm_get_common_pkt_types = 0x4002fa1c ); +PROVIDE ( r_LM_GetConnectionAcceptTimeout = 0x4004f1f4 ); +PROVIDE ( r_LM_GetFeature = 0x4002f924 ); +PROVIDE ( r_LM_GetLinkTimeout = 0x400233ec ); +PROVIDE ( r_LM_GetLocalNameSeg = 0x4004f200 ); +PROVIDE ( r_lm_get_loopback_mode = 0x4004f248 ); +PROVIDE ( r_LM_GetMasterEncKeySize = 0x4001b29c ); +PROVIDE ( r_LM_GetMasterEncRand = 0x4001b288 ); +PROVIDE ( r_LM_GetMasterKey = 0x4001b260 ); +PROVIDE ( r_LM_GetMasterKeyRand = 0x4001b274 ); +PROVIDE ( r_lm_get_min_sync_intv = 0x400517a8 ); +PROVIDE ( r_lm_get_nb_acl = 0x4004ef9c ); +PROVIDE ( r_lm_get_nb_sync_link = 0x4005179c ); +PROVIDE ( r_lm_get_nonce = 0x400131c4 ); +PROVIDE ( r_lm_get_oob_local_commit = 0x4004f374 ); +PROVIDE ( r_lm_get_oob_local_data_192 = 0x4004f2d4 ); +PROVIDE ( r_lm_get_oob_local_data_256 = 0x4004f318 ); +PROVIDE ( r_LM_GetPINType = 0x4004f1e8 ); +PROVIDE ( r_lm_get_priv_key_192 = 0x4004f278 ); +PROVIDE ( r_lm_get_priv_key_256 = 0x4004f2b8 ); +PROVIDE ( r_lm_get_pub_key_192 = 0x4004f258 ); +PROVIDE ( r_lm_get_pub_key_256 = 0x4004f298 ); +PROVIDE ( r_LM_GetQoSParam = 0x4002f6e0 ); +PROVIDE ( r_lm_get_sec_con_host_supp = 0x4004f1d4 ); +PROVIDE ( r_LM_GetSniffSubratingParam = 0x4002325c ); +PROVIDE ( r_lm_get_sp_en = 0x4004f1c0 ); +PROVIDE ( r_LM_GetSwitchInstant = 0x4002f7f8 ); +PROVIDE ( r_lm_get_synchdl = 0x4005175c ); +PROVIDE ( r_lm_get_sync_param = 0x400503b4 ); +PROVIDE ( r_lm_init = 0x4004ed34 ); +PROVIDE ( r_lm_init_sync = 0x400512d8 ); +PROVIDE ( r_lm_is_acl_con = 0x4004f47c ); +PROVIDE ( r_lm_is_acl_con_role = 0x4004f49c ); +PROVIDE ( r_lm_is_clk_adj_ack_pending = 0x4004f4e8 ); +PROVIDE ( r_lm_is_clk_adj_instant_pending = 0x4004f4c8 ); +PROVIDE ( r_lm_local_ext_fr_configured = 0x4004f540 ); +PROVIDE ( r_lm_look_for_stored_link_key = 0x4002f948 ); +PROVIDE ( r_lm_look_for_sync = 0x40051774 ); +PROVIDE ( r_lm_lt_addr_alloc = 0x4004ef1c ); +PROVIDE ( r_lm_lt_addr_free = 0x4004ef74 ); +PROVIDE ( r_lm_lt_addr_reserve = 0x4004ef48 ); +PROVIDE ( r_LM_MakeCof = 0x4002f84c ); +PROVIDE ( r_LM_MakeRandVec = 0x400112d8 ); +PROVIDE ( r_lm_master_clk_adj_req_handler = 0x40054180 ); +PROVIDE ( r_LM_MaxSlot = 0x4002f694 ); +PROVIDE ( r_lm_modif_sync = 0x40051578 ); +PROVIDE ( r_lm_n_is_zero = 0x40012170 ); +PROVIDE ( r_lm_num_clk_adj_ack_pending_set = 0x4004f500 ); +PROVIDE ( r_lm_oob_f1 = 0x40012e54 ); +PROVIDE ( r_lm_pca_sscan_link_get = 0x4004f560 ); +PROVIDE ( r_lm_pca_sscan_link_set = 0x4004f550 ); +PROVIDE ( r_lmp_pack = 0x4001135c ); +PROVIDE ( r_lmp_unpack = 0x4001149c ); +PROVIDE ( r_lm_read_features = 0x4004f0d8 ); +PROVIDE ( r_LM_RemoveSniff = 0x40023124 ); +PROVIDE ( r_LM_RemoveSniffSubrating = 0x400233c4 ); +PROVIDE ( r_lm_remove_sync = 0x400517c8 ); +PROVIDE ( r_lm_reset_sync = 0x40051304 ); +PROVIDE ( r_lm_role_switch_finished = 0x4004f028 ); +PROVIDE ( r_lm_role_switch_start = 0x4004efe0 ); +PROVIDE ( r_lm_sco_nego_end = 0x40051828 ); +PROVIDE ( r_LM_SniffSubrateNegoRequired = 0x40023334 ); +PROVIDE ( r_LM_SniffSubratingHlReq = 0x40023154 ); +PROVIDE ( r_LM_SniffSubratingPeerReq = 0x400231dc ); +PROVIDE ( r_lm_sp_debug_mode_get = 0x4004f398 ); +PROVIDE ( r_lm_sp_n192_convert_wnaf = 0x400123c0 ); +PROVIDE ( r_lm_sp_n_one = 0x400123a4 ); +PROVIDE ( r_lm_sp_p192_add = 0x40012828 ); +PROVIDE ( r_lm_sp_p192_dbl = 0x4001268c ); +PROVIDE ( r_lm_sp_p192_invert = 0x40012b6c ); +PROVIDE ( r_lm_sp_p192_point_jacobian_to_affine = 0x40012468 ); +PROVIDE ( r_lm_sp_p192_points_jacobian_to_affine = 0x400124e4 ); +PROVIDE ( r_lm_sp_p192_point_to_inf = 0x40012458 ); +PROVIDE ( r_lm_sp_pre_compute_points = 0x40012640 ); +PROVIDE ( r_lm_sp_sha256_calculate = 0x400121a0 ); +PROVIDE ( r_LM_SuppressAclPacket = 0x4002f658 ); +PROVIDE ( r_lm_sync_flow_ctrl_en_get = 0x4004f404 ); +PROVIDE ( r_LM_UpdateAclEdrPacketType = 0x4002f5d8 ); +PROVIDE ( r_LM_UpdateAclPacketType = 0x4002f584 ); +PROVIDE ( r_modules_funcs = 0x3ffafd6c ); +PROVIDE ( r_modules_funcs_p = 0x3ffafd68 ); +PROVIDE ( r_nvds_del = 0x400544c4 ); +PROVIDE ( r_nvds_get = 0x40054488 ); +PROVIDE ( r_nvds_init = 0x40054410 ); +PROVIDE ( r_nvds_lock = 0x400544fc ); +PROVIDE ( r_nvds_put = 0x40054534 ); +PROVIDE ( rom_abs_temp = 0x400054f0 ); +PROVIDE ( rom_bb_bss_bw_40_en = 0x4000401c ); +PROVIDE ( rom_bb_bss_cbw40_dig = 0x40003bac ); +PROVIDE ( rom_bb_rx_ht20_cen_bcov_en = 0x40003734 ); +PROVIDE ( rom_bb_tx_ht20_cen = 0x40003760 ); +PROVIDE ( rom_bb_wdg_test_en = 0x40003b70 ); +PROVIDE ( rom_cbw2040_cfg = 0x400040b0 ); +PROVIDE ( rom_check_noise_floor = 0x40003c78 ); +PROVIDE ( rom_chip_i2c_readReg = 0x40004110 ); +PROVIDE ( rom_chip_i2c_writeReg = 0x40004168 ); +PROVIDE ( rom_chip_v7_bt_init = 0x40004d8c ); +PROVIDE ( rom_chip_v7_rx_init = 0x40004cec ); +PROVIDE ( rom_chip_v7_rx_rifs_en = 0x40003d90 ); +PROVIDE ( rom_chip_v7_tx_init = 0x40004d18 ); +PROVIDE ( rom_clk_force_on_vit = 0x40003710 ); +PROVIDE ( rom_correct_rf_ana_gain = 0x400062a8 ); +PROVIDE ( rom_dc_iq_est = 0x400055c8 ); +PROVIDE ( rom_disable_agc = 0x40002fa4 ); +PROVIDE ( rom_enable_agc = 0x40002fcc ); +PROVIDE ( rom_en_pwdet = 0x4000506c ); +PROVIDE ( rom_gen_rx_gain_table = 0x40003e3c ); +PROVIDE ( rom_get_data_sat = 0x4000312c ); +PROVIDE ( rom_get_fm_sar_dout = 0x40005204 ); +PROVIDE ( rom_get_power_db = 0x40005fc8 ); +PROVIDE ( rom_get_pwctrl_correct = 0x400065d4 ); +PROVIDE ( rom_get_rfcal_rxiq_data = 0x40005bbc ); +PROVIDE ( rom_get_rf_gain_qdb = 0x40006290 ); +PROVIDE ( rom_get_sar_dout = 0x40006564 ); +PROVIDE ( rom_i2c_readReg = 0x40004148 ); +PROVIDE ( rom_i2c_readReg_Mask = 0x400041c0 ); +PROVIDE ( rom_i2c_writeReg = 0x400041a4 ); +PROVIDE ( rom_i2c_writeReg_Mask = 0x400041fc ); +PROVIDE ( rom_index_to_txbbgain = 0x40004df8 ); +PROVIDE ( rom_iq_est_disable = 0x40005590 ); +PROVIDE ( rom_iq_est_enable = 0x40005514 ); +PROVIDE ( rom_linear_to_db = 0x40005f64 ); +PROVIDE ( rom_loopback_mode_en = 0x400030f8 ); +PROVIDE ( rom_main = 0x400076c4 ); +PROVIDE ( rom_meas_tone_pwr_db = 0x40006004 ); +PROVIDE ( rom_mhz2ieee = 0x4000404c ); +PROVIDE ( rom_noise_floor_auto_set = 0x40003bdc ); +PROVIDE ( rom_pbus_debugmode = 0x40004458 ); +PROVIDE ( rom_pbus_force_mode = 0x40004270 ); +PROVIDE ( rom_pbus_force_test = 0x400043c0 ); +PROVIDE ( rom_pbus_rd = 0x40004414 ); +PROVIDE ( rom_pbus_rd_addr = 0x40004334 ); +PROVIDE ( rom_pbus_rd_shift = 0x40004374 ); +PROVIDE ( rom_pbus_rx_dco_cal = 0x40005620 ); +PROVIDE ( rom_pbus_set_dco = 0x40004638 ); +PROVIDE ( rom_pbus_set_rxgain = 0x40004480 ); +PROVIDE ( rom_pbus_workmode = 0x4000446c ); +PROVIDE ( rom_pbus_xpd_rx_off = 0x40004508 ); +PROVIDE ( rom_pbus_xpd_rx_on = 0x4000453c ); +PROVIDE ( rom_pbus_xpd_tx_off = 0x40004590 ); +PROVIDE ( rom_pbus_xpd_tx_on = 0x400045e0 ); +PROVIDE ( rom_phy_disable_agc = 0x40002f6c ); +PROVIDE ( rom_phy_disable_cca = 0x40003000 ); +PROVIDE ( rom_phy_enable_agc = 0x40002f88 ); +PROVIDE ( rom_phy_enable_cca = 0x4000302c ); +PROVIDE ( rom_phy_freq_correct = 0x40004b44 ); +PROVIDE ( rom_phyFuns = 0x3ffae0c0 ); +PROVIDE ( rom_phy_get_noisefloor = 0x40003c2c ); +PROVIDE ( rom_phy_get_vdd33 = 0x4000642c ); +PROVIDE ( rom_pow_usr = 0x40003044 ); +PROVIDE ( rom_read_sar_dout = 0x400051c0 ); +PROVIDE ( rom_restart_cal = 0x400046e0 ); +PROVIDE ( rom_rfcal_pwrctrl = 0x40006058 ); +PROVIDE ( rom_rfcal_rxiq = 0x40005b4c ); +PROVIDE ( rom_rfcal_txcap = 0x40005dec ); +PROVIDE ( rom_rfpll_reset = 0x40004680 ); +PROVIDE ( rom_rfpll_set_freq = 0x400047f8 ); +PROVIDE ( rom_rtc_mem_backup = 0x40003db4 ); +PROVIDE ( rom_rtc_mem_recovery = 0x40003df4 ); +PROVIDE ( rom_rx_gain_force = 0x4000351c ); +PROVIDE ( rom_rxiq_cover_mg_mp = 0x40005a68 ); +PROVIDE ( rom_rxiq_get_mis = 0x400058e4 ); +PROVIDE ( rom_rxiq_set_reg = 0x40005a00 ); +PROVIDE ( rom_set_cal_rxdc = 0x400030b8 ); +PROVIDE ( rom_set_chan_cal_interp = 0x40005ce0 ); +PROVIDE ( rom_set_channel_freq = 0x40004880 ); +PROVIDE ( rom_set_loopback_gain = 0x40003060 ); +PROVIDE ( rom_set_noise_floor = 0x40003d48 ); +PROVIDE ( rom_set_pbus_mem = 0x400031a4 ); +PROVIDE ( rom_set_rf_freq_offset = 0x40004ca8 ); +PROVIDE ( rom_set_rxclk_en = 0x40003594 ); +PROVIDE ( rom_set_txcap_reg = 0x40005d50 ); +PROVIDE ( rom_set_txclk_en = 0x40003564 ); +PROVIDE ( rom_spur_coef_cfg = 0x40003ac8 ); +PROVIDE ( rom_spur_reg_write_one_tone = 0x400037f0 ); +PROVIDE ( rom_start_tx_tone = 0x400036b4 ); +PROVIDE ( rom_start_tx_tone_step = 0x400035d0 ); +PROVIDE ( rom_stop_tx_tone = 0x40003f98 ); +PROVIDE ( _rom_store = 0x4000d66c ); +PROVIDE ( _rom_store_table = 0x4000d4f8 ); +PROVIDE ( rom_target_power_add_backoff = 0x40006268 ); +PROVIDE ( rom_tx_atten_set_interp = 0x400061cc ); +PROVIDE ( rom_txbbgain_to_index = 0x40004dc0 ); +PROVIDE ( rom_txcal_work_mode = 0x4000510c ); +PROVIDE ( rom_txdc_cal_init = 0x40004e10 ); +PROVIDE ( rom_txdc_cal_v70 = 0x40004ea4 ); +PROVIDE ( rom_txiq_cover = 0x4000538c ); +PROVIDE ( rom_txiq_get_mis_pwr = 0x400052dc ); +PROVIDE ( rom_txiq_set_reg = 0x40005154 ); +PROVIDE ( rom_tx_pwctrl_bg_init = 0x4000662c ); +PROVIDE ( rom_txtone_linear_pwr = 0x40005290 ); +PROVIDE ( rom_wait_rfpll_cal_end = 0x400047a8 ); +PROVIDE ( rom_write_gain_mem = 0x4000348c ); +PROVIDE ( rom_write_rfpll_sdm = 0x40004740 ); +PROVIDE ( roundup2 = 0x4000ab7c ); +PROVIDE ( r_plf_funcs_p = 0x3ffb8360 ); +PROVIDE ( r_rf_rw_bt_init = 0x40054868 ); +PROVIDE ( r_rf_rw_init = 0x40054b0c ); +PROVIDE ( r_rf_rw_le_init = 0x400549d0 ); +PROVIDE ( r_rwble_activity_ongoing_check = 0x40054d8c ); +PROVIDE ( r_rwble_init = 0x40054bf4 ); +PROVIDE ( r_rwble_isr = 0x40054e08 ); +PROVIDE ( r_rwble_reset = 0x40054ce8 ); +PROVIDE ( r_rwble_sleep_check = 0x40054d78 ); +PROVIDE ( r_rwble_version = 0x40054dac ); +PROVIDE ( r_rwbt_init = 0x40055160 ); +PROVIDE ( r_rwbt_isr = 0x40055248 ); +PROVIDE ( r_rwbt_reset = 0x400551bc ); +PROVIDE ( r_rwbt_sleep_check = 0x4005577c ); +PROVIDE ( r_rwbt_sleep_enter = 0x400557a4 ); +PROVIDE ( r_rwbt_sleep_wakeup = 0x400557fc ); +PROVIDE ( r_rwbt_sleep_wakeup_end = 0x400558cc ); +PROVIDE ( r_rwbt_version = 0x4005520c ); +PROVIDE ( r_rwip_assert_err = 0x40055f88 ); +PROVIDE ( r_rwip_check_wakeup_boundary = 0x400558fc ); +PROVIDE ( r_rwip_ext_wakeup_enable = 0x40055f3c ); +PROVIDE ( r_rwip_init = 0x4005595c ); +PROVIDE ( r_rwip_pca_clock_dragging_only = 0x40055f48 ); +PROVIDE ( r_rwip_prevent_sleep_clear = 0x40055ec8 ); +PROVIDE ( r_rwip_prevent_sleep_set = 0x40055e64 ); +PROVIDE ( r_rwip_reset = 0x40055ab8 ); +PROVIDE ( r_rwip_schedule = 0x40055b38 ); +PROVIDE ( r_rwip_sleep = 0x40055b5c ); +PROVIDE ( r_rwip_sleep_enable = 0x40055f30 ); +PROVIDE ( r_rwip_version = 0x40055b20 ); +PROVIDE ( r_rwip_wakeup = 0x40055dc4 ); +PROVIDE ( r_rwip_wakeup_delay_set = 0x40055e4c ); +PROVIDE ( r_rwip_wakeup_end = 0x40055e18 ); +PROVIDE ( r_rwip_wlcoex_set = 0x40055f60 ); +PROVIDE ( r_SHA_256 = 0x40013a90 ); +PROVIDE ( rtc_boot_control = 0x4000821c ); +PROVIDE ( rtc_get_reset_reason = 0x400081d4 ); +PROVIDE ( rtc_get_wakeup_cause = 0x400081f4 ); +PROVIDE ( rtc_select_apb_bridge = 0x40008288 ); +PROVIDE ( rwip_coex_cfg = 0x3ff9914c ); +PROVIDE ( rwip_priority = 0x3ff99159 ); +PROVIDE ( rwip_rf = 0x3ffbdb28 ); +PROVIDE ( rwip_rf_p_get = 0x400558f4 ); +PROVIDE ( r_XorKey = 0x400112c0 ); +PROVIDE ( sbrk = 0x400017f4 ); +PROVIDE ( _sbrk_r = 0x4000bce4 ); +PROVIDE ( __sccl = 0x4000c498 ); +PROVIDE ( __sclose = 0x400011b8 ); +PROVIDE ( SelectSpiFunction = 0x40061f84 ); +PROVIDE ( SelectSpiQIO = 0x40061ddc ); +PROVIDE ( SendMsg = 0x40009384 ); +PROVIDE ( send_packet = 0x40009340 ); +PROVIDE ( __seofread = 0x40001148 ); +PROVIDE ( setjmp = 0x40056268 ); +PROVIDE ( setlocale = 0x40059568 ); +PROVIDE ( _setlocale_r = 0x4005950c ); +PROVIDE ( set_rtc_memory_crc = 0x40008208 ); +PROVIDE ( SetSpiDrvs = 0x40061e78 ); +PROVIDE ( __sf_fake_stderr = 0x3ff96458 ); +PROVIDE ( __sf_fake_stdin = 0x3ff96498 ); +PROVIDE ( __sf_fake_stdout = 0x3ff96478 ); +PROVIDE ( __sflush_r = 0x400591e0 ); +PROVIDE ( __sfmoreglue = 0x40001dc8 ); +PROVIDE ( __sfp = 0x40001e90 ); +PROVIDE ( __sfp_lock_acquire = 0x40001e08 ); +PROVIDE ( __sfp_lock_release = 0x40001e14 ); +PROVIDE ( __sfputs_r = 0x40057790 ); +PROVIDE ( __sfvwrite_r = 0x4005893c ); +PROVIDE ( sha1_prf = 0x40060ae8 ); +PROVIDE ( sha1_vector = 0x40060b64 ); +PROVIDE ( sha256_prf = 0x40060d70 ); +PROVIDE ( sha256_vector = 0x40060e08 ); +PROVIDE ( sha_blk_bits = 0x3ff99290 ); +PROVIDE ( sha_blk_bits_bytes = 0x3ff99288 ); +PROVIDE ( sha_blk_hash_bytes = 0x3ff9928c ); +PROVIDE ( sig_matrix = 0x3ffae293 ); +PROVIDE ( __sinit = 0x40001e38 ); +PROVIDE ( __sinit_lock_acquire = 0x40001e20 ); +PROVIDE ( __sinit_lock_release = 0x40001e2c ); +PROVIDE ( sip_after_tx_complete = 0x4000b358 ); +PROVIDE ( sip_alloc_to_host_evt = 0x4000ab9c ); +PROVIDE ( sip_get_ptr = 0x4000b34c ); +PROVIDE ( sip_get_state = 0x4000ae2c ); +PROVIDE ( sip_init_attach = 0x4000ae58 ); +PROVIDE ( sip_install_rx_ctrl_cb = 0x4000ae10 ); +PROVIDE ( sip_install_rx_data_cb = 0x4000ae20 ); +PROVIDE ( sip_is_active = 0x4000b3c0 ); +PROVIDE ( sip_post_init = 0x4000aed8 ); +PROVIDE ( sip_reclaim_from_host_cmd = 0x4000adbc ); +PROVIDE ( sip_reclaim_tx_data_pkt = 0x4000ad5c ); +PROVIDE ( sip_send = 0x4000af54 ); +PROVIDE ( sip_to_host_chain_append = 0x4000aef8 ); +PROVIDE ( sip_to_host_evt_send_done = 0x4000ac04 ); +PROVIDE ( slc_add_credits = 0x4000baf4 ); +PROVIDE ( slc_enable = 0x4000b64c ); +PROVIDE ( slc_from_host_chain_fetch = 0x4000b7e8 ); +PROVIDE ( slc_from_host_chain_recycle = 0x4000bb10 ); +PROVIDE ( slc_has_pkt_to_host = 0x4000b5fc ); +PROVIDE ( slc_init_attach = 0x4000b918 ); +PROVIDE ( slc_init_credit = 0x4000badc ); +PROVIDE ( slc_reattach = 0x4000b62c ); +PROVIDE ( slc_send_to_host_chain = 0x4000b6a0 ); +PROVIDE ( slc_set_host_io_max_window = 0x4000b89c ); +PROVIDE ( slc_to_host_chain_recycle = 0x4000b758 ); +PROVIDE ( __smakebuf_r = 0x40059108 ); +PROVIDE ( software_reset = 0x4000824c ); +PROVIDE ( software_reset_cpu = 0x40008264 ); +PROVIDE ( specialModP256 = 0x4001600c ); +PROVIDE ( spi_cache_sram_init = 0x400626e4 ); +PROVIDE ( SPIClkConfig = 0x40062bc8 ); +PROVIDE ( SPI_Common_Command = 0x4006246c ); +PROVIDE ( spi_dummy_len_fix = 0x40061d90 ); +PROVIDE ( SPI_Encrypt_Write = 0x40062e78 ); +PROVIDE ( SPIEraseArea = 0x400631ac ); +PROVIDE ( SPIEraseBlock = 0x40062c4c ); +PROVIDE ( SPIEraseChip = 0x40062c14 ); +PROVIDE ( SPIEraseSector = 0x40062ccc ); +PROVIDE ( spi_flash_attach = 0x40062a6c ); +/* NB: SPIUnlock @ 0x400628b0 has been replaced with an updated + version in the "spi_flash" component */ +PROVIDE ( SPILock = 0x400628f0 ); +PROVIDE ( SPIMasterReadModeCnfig = 0x40062b64 ); +PROVIDE ( spi_modes = 0x3ff99270 ); +PROVIDE ( SPIParamCfg = 0x40063238 ); +PROVIDE ( SPI_Prepare_Encrypt_Data = 0x40062e1c ); +PROVIDE ( SPIRead = 0x40062ed8 ); +PROVIDE ( SPIReadModeCnfig = 0x40062944 ); +/* This is static function, but can be used, not generated by script*/ +PROVIDE ( SPI_read_status = 0x4006226c ); +/* This is static function, but can be used, not generated by script*/ +PROVIDE ( SPI_read_status_high = 0x40062448 ); +PROVIDE ( SPI_user_command_read = 0x400621b0 ); +PROVIDE ( SPI_flashchip_data = 0x3ffae270 ); +PROVIDE ( SPIWrite = 0x40062d50 ); +/* This is static function, but can be used, not generated by script*/ +PROVIDE ( SPI_write_enable = 0x40062320 ); +PROVIDE ( SPI_Write_Encrypt_Disable = 0x40062e60 ); +PROVIDE ( SPI_Write_Encrypt_Enable = 0x40062df4 ); +/* This is static function, but can be used, not generated by script*/ +PROVIDE ( SPI_write_status = 0x400622f0 ); +PROVIDE ( srand = 0x40001004 ); +PROVIDE ( __sread = 0x40001118 ); +PROVIDE ( __srefill_r = 0x400593d4 ); +PROVIDE ( __sseek = 0x40001184 ); +PROVIDE ( __ssprint_r = 0x40056ff8 ); +PROVIDE ( __ssputs_r = 0x40056f2c ); +PROVIDE ( __ssrefill_r = 0x40057fec ); +PROVIDE ( __stack = 0x3ffe3f20 ); +PROVIDE ( __stack_app = 0x3ffe7e30 ); +PROVIDE ( _stack_sentry = 0x3ffe1320 ); +PROVIDE ( _stack_sentry_app = 0x3ffe5230 ); +PROVIDE ( _start = 0x40000704 ); +PROVIDE ( start_tb_console = 0x4005a980 ); +PROVIDE ( _stat_r = 0x4000bcb4 ); +PROVIDE ( _stext = 0x40000560 ); +PROVIDE ( strcasecmp = 0x400011cc ); +PROVIDE ( strcasestr = 0x40001210 ); +PROVIDE ( strcat = 0x4000c518 ); +PROVIDE ( strchr = 0x4000c53c ); +PROVIDE ( strcmp = 0x40001274 ); +PROVIDE ( strcoll = 0x40001398 ); +PROVIDE ( strcpy = 0x400013ac ); +PROVIDE ( strcspn = 0x4000c558 ); +PROVIDE ( strdup = 0x4000143c ); +PROVIDE ( _strdup_r = 0x40001450 ); +PROVIDE ( strftime = 0x40059ab4 ); +PROVIDE ( strlcat = 0x40001470 ); +PROVIDE ( strlcpy = 0x4000c584 ); +PROVIDE ( strlen = 0x400014c0 ); +PROVIDE ( strlwr = 0x40001524 ); +PROVIDE ( strncasecmp = 0x40001550 ); +PROVIDE ( strncat = 0x4000c5c4 ); +PROVIDE ( strncmp = 0x4000c5f4 ); +PROVIDE ( strncpy = 0x400015d4 ); +PROVIDE ( strndup = 0x400016b0 ); +PROVIDE ( _strndup_r = 0x400016c4 ); +PROVIDE ( strnlen = 0x4000c628 ); +PROVIDE ( strrchr = 0x40001708 ); +PROVIDE ( strsep = 0x40001734 ); +PROVIDE ( strspn = 0x4000c648 ); +PROVIDE ( strstr = 0x4000c674 ); +PROVIDE ( __strtok_r = 0x4000c6a8 ); +PROVIDE ( strtok_r = 0x4000c70c ); +PROVIDE ( strtol = 0x4005681c ); +PROVIDE ( _strtol_r = 0x40056714 ); +PROVIDE ( strtoul = 0x4005692c ); +PROVIDE ( _strtoul_r = 0x40056834 ); +PROVIDE ( strupr = 0x4000174c ); +PROVIDE ( __subdf3 = 0x400026e4 ); +PROVIDE ( __submore = 0x40058f3c ); +PROVIDE ( __subsf3 = 0x400021d0 ); +PROVIDE ( SubtractBigHex256 = 0x40015bcc ); +PROVIDE ( SubtractBigHexMod256 = 0x40015e8c ); +PROVIDE ( SubtractBigHexUint32_256 = 0x40015f8c ); +PROVIDE ( SubtractFromSelfBigHex256 = 0x40015c20 ); +PROVIDE ( SubtractFromSelfBigHexSign256 = 0x40015dc8 ); +PROVIDE ( __subvdi3 = 0x40002d20 ); +PROVIDE ( __subvsi3 = 0x40002cf8 ); +PROVIDE ( _sungetc_r = 0x40057f6c ); +PROVIDE ( __swbuf = 0x40058cb4 ); +PROVIDE ( __swbuf_r = 0x40058bec ); +PROVIDE ( __swrite = 0x40001150 ); +PROVIDE ( __swsetup_r = 0x40058cc8 ); +PROVIDE ( sw_to_hw = 0x3ffb8d40 ); +PROVIDE ( _SyscallException = 0x400007cf ); +PROVIDE ( syscall_table_ptr_app = 0x3ffae020 ); +PROVIDE ( syscall_table_ptr_pro = 0x3ffae024 ); +PROVIDE ( _system_r = 0x4000bc10 ); +PROVIDE ( tdefl_compress = 0x400600bc ); +PROVIDE ( tdefl_compress_buffer = 0x400607f4 ); +PROVIDE ( tdefl_compress_mem_to_mem = 0x40060900 ); +PROVIDE ( tdefl_compress_mem_to_output = 0x400608e0 ); +PROVIDE ( tdefl_get_adler32 = 0x400608d8 ); +PROVIDE ( tdefl_get_prev_return_status = 0x400608d0 ); +PROVIDE ( tdefl_init = 0x40060810 ); +PROVIDE ( tdefl_write_image_to_png_file_in_memory = 0x4006091c ); +PROVIDE ( tdefl_write_image_to_png_file_in_memory_ex = 0x40060910 ); +PROVIDE ( time = 0x40001844 ); +PROVIDE ( __time_load_locale = 0x4000183c ); +PROVIDE ( times = 0x40001808 ); +PROVIDE ( _times_r = 0x4000bc40 ); +PROVIDE ( _timezone = 0x3ffae0a0 ); +PROVIDE ( tinfl_decompress = 0x4005ef30 ); +PROVIDE ( tinfl_decompress_mem_to_callback = 0x40060090 ); +PROVIDE ( tinfl_decompress_mem_to_mem = 0x40060050 ); +PROVIDE ( toascii = 0x4000c720 ); +PROVIDE ( tolower = 0x40001868 ); +PROVIDE ( toupper = 0x40001884 ); +PROVIDE ( __truncdfsf2 = 0x40002b90 ); +PROVIDE ( __tzcalc_limits = 0x400018a0 ); +PROVIDE ( __tz_lock = 0x40001a04 ); +PROVIDE ( _tzname = 0x3ffae030 ); +PROVIDE ( tzset = 0x40001a1c ); +PROVIDE ( _tzset_r = 0x40001a28 ); +PROVIDE ( __tz_unlock = 0x40001a10 ); +PROVIDE ( uartAttach = 0x40008fd0 ); +PROVIDE ( uart_baudrate_detect = 0x40009034 ); +PROVIDE ( uart_buff_switch = 0x400093c0 ); +PROVIDE ( UartConnCheck = 0x40008738 ); +PROVIDE ( UartConnectProc = 0x40008a04 ); +PROVIDE ( UartDev = 0x3ffe019c ); +PROVIDE ( uart_div_modify = 0x400090cc ); +PROVIDE ( UartDwnLdProc = 0x40008ce8 ); +PROVIDE ( UartGetCmdLn = 0x40009564 ); +PROVIDE ( Uart_Init = 0x40009120 ); +PROVIDE ( UartRegReadProc = 0x40008a58 ); +PROVIDE ( UartRegWriteProc = 0x40008a14 ); +PROVIDE ( uart_rx_intr_handler = 0x40008f4c ); +PROVIDE ( uart_rx_one_char = 0x400092d0 ); +PROVIDE ( uart_rx_one_char_block = 0x400092a4 ); +PROVIDE ( uart_rx_readbuff = 0x40009394 ); +PROVIDE ( UartRxString = 0x400092fc ); +PROVIDE ( UartSetBaudProc = 0x40008aac ); +PROVIDE ( UartSpiAttachProc = 0x40008a6c ); +PROVIDE ( UartSpiReadProc = 0x40008a80 ); +PROVIDE ( uart_tx_flush = 0x40009258 ); +/*PROVIDE ( uart_tx_one_char = 0x40009200 );*/ +/* Note: remapping this to uart_tx_one_char2, to keep consistency */ +PROVIDE ( uart_tx_one_char = 0x4000922c ); +PROVIDE ( uart_tx_one_char2 = 0x4000922c ); +PROVIDE ( uart_tx_switch = 0x40009028 ); +PROVIDE ( uart_tx_wait_idle = 0x40009278 ); +PROVIDE ( __ucmpdi2 = 0x40063840 ); +PROVIDE ( __udivdi3 = 0x4000cff8 ); +PROVIDE ( __udivmoddi4 = 0x40064ab0 ); +PROVIDE ( __udivsi3 = 0x4000c7c8 ); +PROVIDE ( __udiv_w_sdiv = 0x40064aa8 ); +PROVIDE ( __umoddi3 = 0x4000d280 ); +PROVIDE ( __umodsi3 = 0x4000c7d0 ); +PROVIDE ( __umulsidi3 = 0x4000c7d8 ); +PROVIDE ( ungetc = 0x400590f4 ); +PROVIDE ( _ungetc_r = 0x40058fa0 ); +PROVIDE ( _unlink_r = 0x4000bc84 ); +PROVIDE ( __unorddf2 = 0x400637f4 ); +PROVIDE ( __unordsf2 = 0x40063478 ); +PROVIDE ( user_code_start = 0x3ffe0400 ); +PROVIDE ( _UserExceptionVector = 0x40000340 ); +PROVIDE ( utoa = 0x40056258 ); +PROVIDE ( __utoa = 0x400561f0 ); +PROVIDE ( VerifyFlashMd5Proc = 0x40008c44 ); +PROVIDE ( veryBigHexP256 = 0x3ff9736c ); +PROVIDE ( wcrtomb = 0x40058920 ); +PROVIDE ( _wcrtomb_r = 0x400588d8 ); +PROVIDE ( __wctomb = 0x3ff96540 ); +PROVIDE ( _wctomb_r = 0x40058f14 ); +PROVIDE ( _WindowOverflow12 = 0x40000100 ); +PROVIDE ( _WindowOverflow4 = 0x40000000 ); +PROVIDE ( _WindowOverflow8 = 0x40000080 ); +PROVIDE ( _WindowUnderflow12 = 0x40000140 ); +PROVIDE ( _WindowUnderflow4 = 0x40000040 ); +PROVIDE ( _WindowUnderflow8 = 0x400000c0 ); +PROVIDE ( write = 0x4000181c ); +PROVIDE ( _write_r = 0x4000bd70 ); +PROVIDE ( xthal_bcopy = 0x4000c098 ); +PROVIDE ( xthal_copy123 = 0x4000c124 ); +PROVIDE ( xthal_get_ccompare = 0x4000c078 ); +PROVIDE ( xthal_get_ccount = 0x4000c050 ); +PROVIDE ( xthal_get_interrupt = 0x4000c1e4 ); +PROVIDE ( xthal_get_intread = 0x4000c1e4 ); +PROVIDE ( Xthal_intlevel = 0x3ff9c2b4 ); +PROVIDE ( xthal_memcpy = 0x4000c0bc ); +PROVIDE ( xthal_set_ccompare = 0x4000c058 ); +PROVIDE ( xthal_set_intclear = 0x4000c1ec ); +PROVIDE ( _xtos_set_intlevel = 0x4000bfdc ); +PROVIDE ( _xtos_alloca_handler = 0x40000010 ); +PROVIDE ( _xtos_cause3_handler = 0x40000dd8 ); +PROVIDE ( _xtos_c_handler_table = 0x3ffe0548 ); +PROVIDE ( _xtos_c_wrapper_handler = 0x40000de8 ); +PROVIDE ( _xtos_enabled = 0x3ffe0650 ); +PROVIDE ( _xtos_exc_handler_table = 0x3ffe0448 ); +PROVIDE ( _xtos_interrupt_mask_table = 0x3ffe0758 ); +PROVIDE ( _xtos_interrupt_table = 0x3ffe0658 ); +PROVIDE ( _xtos_ints_off = 0x4000bfac ); +PROVIDE ( _xtos_ints_on = 0x4000bf88 ); +PROVIDE ( _xtos_intstruct = 0x3ffe0650 ); +PROVIDE ( _xtos_l1int_handler = 0x40000814 ); +PROVIDE ( _xtos_p_none = 0x4000bfd4 ); +PROVIDE ( _xtos_restore_intlevel = 0x40000928 ); +PROVIDE ( _xtos_return_from_exc = 0x4000c034 ); +PROVIDE ( _xtos_set_exception_handler = 0x4000074c ); +PROVIDE ( _xtos_set_interrupt_handler = 0x4000bf78 ); +PROVIDE ( _xtos_set_interrupt_handler_arg = 0x4000bf34 ); +PROVIDE ( _xtos_set_min_intlevel = 0x4000bff8 ); +PROVIDE ( _xtos_set_vpri = 0x40000934 ); +PROVIDE ( _xtos_syscall_handler = 0x40000790 ); +PROVIDE ( _xtos_unhandled_exception = 0x4000c024 ); +PROVIDE ( _xtos_unhandled_interrupt = 0x4000c01c ); +PROVIDE ( _xtos_vpri_enabled = 0x3ffe0654 ); +PROVIDE ( ets_intr_count = 0x3ffe03fc ); +PROVIDE ( ets_intr_lock = 0x400067b0 ); +PROVIDE ( ets_intr_unlock = 0x400067c4 ); +PROVIDE ( ets_isr_attach = 0x400067ec ); +PROVIDE ( ets_isr_mask = 0x400067fc ); +PROVIDE ( ets_isr_unmask = 0x40006808 ); +/* Following are static data, but can be used, not generated by script <<<<< btdm data */ +PROVIDE ( ld_acl_env = 0x3ffb8258 ); +PROVIDE ( ld_active_ch_map = 0x3ffb8334 ); +PROVIDE ( ld_bcst_acl_env = 0x3ffb8274 ); +PROVIDE ( ld_csb_rx_env = 0x3ffb8278 ); +PROVIDE ( ld_csb_tx_env = 0x3ffb827c ); +PROVIDE ( ld_env = 0x3ffb9510 ); +PROVIDE ( ld_fm_env = 0x3ffb8284 ); +PROVIDE ( ld_inq_env = 0x3ffb82e4 ); +PROVIDE ( ld_iscan_env = 0x3ffb82e8 ); +PROVIDE ( ld_page_env = 0x3ffb82f0 ); +PROVIDE ( ld_pca_env = 0x3ffb82f4 ); +PROVIDE ( ld_pscan_env = 0x3ffb8308 ); +PROVIDE ( ld_sched_env = 0x3ffb830c ); +PROVIDE ( ld_sched_params = 0x3ffb96c0 ); +PROVIDE ( ld_sco_env = 0x3ffb824c ); +PROVIDE ( ld_sscan_env = 0x3ffb832c ); +PROVIDE ( ld_strain_env = 0x3ffb8330 ); + + +PROVIDE ( esp_rom_spiflash_write_encrypted = 0x40062e78 ); +/* Above are static data, but can be used, not generated by script >>>>> btdm data */ diff --git a/dist/bin/esptool/flasher_stub/ld/rom_8266.ld b/dist/bin/esptool/flasher_stub/ld/rom_8266.ld new file mode 100644 index 000000000..9269fa2de --- /dev/null +++ b/dist/bin/esptool/flasher_stub/ld/rom_8266.ld @@ -0,0 +1,351 @@ +PROVIDE ( Cache_Read_Disable = 0x400047f0 ); +PROVIDE ( Cache_Read_Enable = 0x40004678 ); +PROVIDE ( FilePacketSendReqMsgProc = 0x400035a0 ); +PROVIDE ( FlashDwnLdParamCfgMsgProc = 0x4000368c ); +PROVIDE ( FlashDwnLdStartMsgProc = 0x40003538 ); +PROVIDE ( FlashDwnLdStopReqMsgProc = 0x40003658 ); +PROVIDE ( GetUartDevice = 0x40003f4c ); +PROVIDE ( MD5Final = 0x40009900 ); +PROVIDE ( MD5Init = 0x40009818 ); +PROVIDE ( MD5Update = 0x40009834 ); +PROVIDE ( MemDwnLdStartMsgProc = 0x400036c4 ); +PROVIDE ( MemDwnLdStopReqMsgProc = 0x4000377c ); +PROVIDE ( MemPacketSendReqMsgProc = 0x400036f0 ); +PROVIDE ( RcvMsg = 0x40003eac ); +PROVIDE ( SHA1Final = 0x4000b648 ); +PROVIDE ( SHA1Init = 0x4000b584 ); +PROVIDE ( SHA1Transform = 0x4000a364 ); +PROVIDE ( SHA1Update = 0x4000b5a8 ); +PROVIDE ( SPI_read_status = 0x400043c8 ); +PROVIDE ( SPI_write_status = 0x40004400 ); +PROVIDE ( SPI_write_enable = 0x4000443c ); +PROVIDE ( Wait_SPI_Idle = 0x4000448c ); +PROVIDE ( Enable_QMode = 0x400044c0 ); +PROVIDE ( SPIEraseArea = 0x40004b44 ); +PROVIDE ( SPIEraseBlock = 0x400049b4 ); +PROVIDE ( SPIEraseChip = 0x40004984 ); +PROVIDE ( SPIEraseSector = 0x40004a00 ); +PROVIDE ( SPILock = 0x400048a8 ); +PROVIDE ( SPIParamCfg = 0x40004c2c ); +PROVIDE ( SPIRead = 0x40004b1c ); +PROVIDE ( SPIReadModeCnfig = 0x400048ec ); +PROVIDE ( SPIUnlock = 0x40004878 ); +PROVIDE ( SPIWrite = 0x40004a4c ); +PROVIDE ( SelectSpiFunction = 0x40003f58 ); +PROVIDE ( SendMsg = 0x40003cf4 ); +PROVIDE ( UartConnCheck = 0x40003230 ); +PROVIDE ( UartConnectProc = 0x400037a0 ); +PROVIDE ( UartDwnLdProc = 0x40003368 ); +PROVIDE ( UartGetCmdLn = 0x40003ef4 ); +PROVIDE ( UartRegReadProc = 0x4000381c ); +PROVIDE ( UartRegWriteProc = 0x400037ac ); +PROVIDE ( UartRxString = 0x40003c30 ); +PROVIDE ( Uart_Init = 0x40003a14 ); +PROVIDE ( _DebugExceptionVector = 0x40000010 ); +PROVIDE ( _DoubleExceptionVector = 0x40000070 ); +PROVIDE ( _KernelExceptionVector = 0x40000030 ); +PROVIDE ( _NMIExceptionVector = 0x40000020 ); +PROVIDE ( _ResetHandler = 0x400000a4 ); +PROVIDE ( _ResetVector = 0x40000080 ); +PROVIDE ( _UserExceptionVector = 0x40000050 ); +PROVIDE ( __adddf3 = 0x4000c538 ); +PROVIDE ( __addsf3 = 0x4000c180 ); +PROVIDE ( __divdf3 = 0x4000cb94 ); +PROVIDE ( __divdi3 = 0x4000ce60 ); +PROVIDE ( __divsi3 = 0x4000dc88 ); +PROVIDE ( __extendsfdf2 = 0x4000cdfc ); +PROVIDE ( __fixdfsi = 0x4000ccb8 ); +PROVIDE ( __fixunsdfsi = 0x4000cd00 ); +PROVIDE ( __fixunssfsi = 0x4000c4c4 ); +PROVIDE ( __floatsidf = 0x4000e2f0 ); +PROVIDE ( __floatsisf = 0x4000e2ac ); +PROVIDE ( __floatunsidf = 0x4000e2e8 ); +PROVIDE ( __floatunsisf = 0x4000e2a4 ); +PROVIDE ( __muldf3 = 0x4000c8f0 ); +PROVIDE ( __muldi3 = 0x40000650 ); +PROVIDE ( __mulsf3 = 0x4000c3dc ); +PROVIDE ( __subdf3 = 0x4000c688 ); +PROVIDE ( __subsf3 = 0x4000c268 ); +PROVIDE ( __truncdfsf2 = 0x4000cd5c ); +PROVIDE ( __udivdi3 = 0x4000d310 ); +PROVIDE ( __udivsi3 = 0x4000e21c ); +PROVIDE ( __umoddi3 = 0x4000d770 ); +PROVIDE ( __umodsi3 = 0x4000e268 ); +PROVIDE ( __umulsidi3 = 0x4000dcf0 ); +PROVIDE ( _rom_store = 0x4000e388 ); +PROVIDE ( _rom_store_table = 0x4000e328 ); +PROVIDE ( _start = 0x4000042c ); +PROVIDE ( _xtos_alloca_handler = 0x4000dbe0 ); +PROVIDE ( _xtos_c_wrapper_handler = 0x40000598 ); +PROVIDE ( _xtos_cause3_handler = 0x40000590 ); +PROVIDE ( _xtos_ints_off = 0x4000bda4 ); +PROVIDE ( _xtos_ints_on = 0x4000bd84 ); +PROVIDE ( _xtos_l1int_handler = 0x4000048c ); +PROVIDE ( _xtos_p_none = 0x4000dbf8 ); +PROVIDE ( _xtos_restore_intlevel = 0x4000056c ); +PROVIDE ( _xtos_return_from_exc = 0x4000dc54 ); +PROVIDE ( _xtos_set_exception_handler = 0x40000454 ); +PROVIDE ( _xtos_set_interrupt_handler = 0x4000bd70 ); +PROVIDE ( _xtos_set_interrupt_handler_arg = 0x4000bd28 ); +PROVIDE ( _xtos_set_intlevel = 0x4000dbfc ); +PROVIDE ( _xtos_set_min_intlevel = 0x4000dc18 ); +PROVIDE ( _xtos_set_vpri = 0x40000574 ); +PROVIDE ( _xtos_syscall_handler = 0x4000dbe4 ); +PROVIDE ( _xtos_unhandled_exception = 0x4000dc44 ); +PROVIDE ( _xtos_unhandled_interrupt = 0x4000dc3c ); +PROVIDE ( aes_decrypt = 0x400092d4 ); +PROVIDE ( aes_decrypt_deinit = 0x400092e4 ); +PROVIDE ( aes_decrypt_init = 0x40008ea4 ); +PROVIDE ( aes_unwrap = 0x40009410 ); +PROVIDE ( base64_decode = 0x40009648 ); +PROVIDE ( base64_encode = 0x400094fc ); +PROVIDE ( bzero = 0x4000de84 ); +PROVIDE ( cmd_parse = 0x40000814 ); +PROVIDE ( conv_str_decimal = 0x40000b24 ); +PROVIDE ( conv_str_hex = 0x40000cb8 ); +PROVIDE ( convert_para_str = 0x40000a60 ); +PROVIDE ( dtm_get_intr_mask = 0x400026d0 ); +PROVIDE ( dtm_params_init = 0x4000269c ); +PROVIDE ( dtm_set_intr_mask = 0x400026c8 ); +PROVIDE ( dtm_set_params = 0x400026dc ); +PROVIDE ( eprintf = 0x40001d14 ); +PROVIDE ( eprintf_init_buf = 0x40001cb8 ); +PROVIDE ( eprintf_to_host = 0x40001d48 ); +PROVIDE ( est_get_printf_buf_remain_len = 0x40002494 ); +PROVIDE ( est_reset_printf_buf_len = 0x4000249c ); +PROVIDE ( ets_bzero = 0x40002ae8 ); +PROVIDE ( ets_char2xdigit = 0x40002b74 ); +PROVIDE ( ets_delay_us = 0x40002ecc ); +PROVIDE ( ets_enter_sleep = 0x400027b8 ); +PROVIDE ( ets_external_printf = 0x40002578 ); +PROVIDE ( ets_get_cpu_frequency = 0x40002f0c ); +PROVIDE ( ets_getc = 0x40002bcc ); +PROVIDE ( ets_install_external_printf = 0x40002450 ); +PROVIDE ( ets_install_putc1 = 0x4000242c ); +PROVIDE ( ets_install_putc2 = 0x4000248c ); +PROVIDE ( ets_install_uart_printf = 0x40002438 ); +PROVIDE ( ets_intr_lock = 0x40000f74 ); +PROVIDE ( ets_intr_unlock = 0x40000f80 ); +PROVIDE ( ets_isr_attach = 0x40000f88 ); +PROVIDE ( ets_isr_mask = 0x40000f98 ); +PROVIDE ( ets_isr_unmask = 0x40000fa8 ); +PROVIDE ( ets_memcmp = 0x400018d4 ); +PROVIDE ( ets_memcpy = 0x400018b4 ); +PROVIDE ( ets_memmove = 0x400018c4 ); +PROVIDE ( ets_memset = 0x400018a4 ); +PROVIDE ( ets_post = 0x40000e24 ); +PROVIDE ( ets_printf = 0x400024cc ); +PROVIDE ( ets_putc = 0x40002be8 ); +PROVIDE ( ets_rtc_int_register = 0x40002a40 ); +PROVIDE ( ets_run = 0x40000e04 ); +PROVIDE ( ets_set_idle_cb = 0x40000dc0 ); +PROVIDE ( ets_set_user_start = 0x40000fbc ); +PROVIDE ( ets_str2macaddr = 0x40002af8 ); +PROVIDE ( ets_strcmp = 0x40002aa8 ); +PROVIDE ( ets_strcpy = 0x40002a88 ); +PROVIDE ( ets_strlen = 0x40002ac8 ); +PROVIDE ( ets_strncmp = 0x40002ab8 ); +PROVIDE ( ets_strncpy = 0x40002a98 ); +PROVIDE ( ets_strstr = 0x40002ad8 ); +PROVIDE ( ets_task = 0x40000dd0 ); +PROVIDE ( ets_timer_arm = 0x40002cc4 ); +PROVIDE ( ets_timer_disarm = 0x40002d40 ); +PROVIDE ( ets_timer_done = 0x40002d80 ); +PROVIDE ( ets_timer_handler_isr = 0x40002da8 ); +PROVIDE ( ets_timer_init = 0x40002e68 ); +PROVIDE ( ets_timer_setfn = 0x40002c48 ); +PROVIDE ( ets_uart_printf = 0x40002544 ); +PROVIDE ( ets_update_cpu_frequency = 0x40002f04 ); +PROVIDE ( ets_vprintf = 0x40001f00 ); +PROVIDE ( ets_wdt_disable = 0x400030f0 ); +PROVIDE ( ets_wdt_enable = 0x40002fa0 ); +PROVIDE ( ets_wdt_get_mode = 0x40002f34 ); +PROVIDE ( ets_wdt_init = 0x40003170 ); +PROVIDE ( ets_wdt_restore = 0x40003158 ); +PROVIDE ( ets_write_char = 0x40001da0 ); +PROVIDE ( get_first_seg = 0x4000091c ); +PROVIDE ( gpio_init = 0x40004c50 ); +PROVIDE ( gpio_input_get = 0x40004cf0 ); +PROVIDE ( gpio_intr_ack = 0x40004dcc ); +PROVIDE ( gpio_intr_handler_register = 0x40004e28 ); +PROVIDE ( gpio_intr_pending = 0x40004d88 ); +PROVIDE ( gpio_intr_test = 0x40004efc ); +PROVIDE ( gpio_output_set = 0x40004cd0 ); +PROVIDE ( gpio_pin_intr_state_set = 0x40004d90 ); +PROVIDE ( gpio_pin_wakeup_disable = 0x40004ed4 ); +PROVIDE ( gpio_pin_wakeup_enable = 0x40004e90 ); +PROVIDE ( gpio_register_get = 0x40004d5c ); +PROVIDE ( gpio_register_set = 0x40004d04 ); +PROVIDE ( hmac_md5 = 0x4000a2cc ); +PROVIDE ( hmac_md5_vector = 0x4000a160 ); +PROVIDE ( hmac_sha1 = 0x4000ba28 ); +PROVIDE ( hmac_sha1_vector = 0x4000b8b4 ); +PROVIDE ( lldesc_build_chain = 0x40004f40 ); +PROVIDE ( lldesc_num2link = 0x40005050 ); +PROVIDE ( lldesc_set_owner = 0x4000507c ); +PROVIDE ( main = 0x40000fec ); +PROVIDE ( md5_vector = 0x400097ac ); +PROVIDE ( mem_calloc = 0x40001c2c ); +PROVIDE ( mem_free = 0x400019e0 ); +PROVIDE ( mem_init = 0x40001998 ); +PROVIDE ( mem_malloc = 0x40001b40 ); +PROVIDE ( mem_realloc = 0x40001c6c ); +PROVIDE ( mem_trim = 0x40001a14 ); +PROVIDE ( mem_zalloc = 0x40001c58 ); +PROVIDE ( memcmp = 0x4000dea8 ); +PROVIDE ( memcpy = 0x4000df48 ); +PROVIDE ( memmove = 0x4000e04c ); +PROVIDE ( memset = 0x4000e190 ); +PROVIDE ( multofup = 0x400031c0 ); +PROVIDE ( pbkdf2_sha1 = 0x4000b840 ); +PROVIDE ( phy_get_romfuncs = 0x40006b08 ); +PROVIDE ( rand = 0x40000600 ); +PROVIDE ( rc4_skip = 0x4000dd68 ); +PROVIDE ( recv_packet = 0x40003d08 ); +PROVIDE ( remove_head_space = 0x40000a04 ); +PROVIDE ( rijndaelKeySetupDec = 0x40008dd0 ); +PROVIDE ( rijndaelKeySetupEnc = 0x40009300 ); +PROVIDE ( rom_abs_temp = 0x400060c0 ); +PROVIDE ( rom_ana_inf_gating_en = 0x40006b10 ); +PROVIDE ( rom_cal_tos_v50 = 0x40007a28 ); +PROVIDE ( rom_chip_50_set_channel = 0x40006f84 ); +PROVIDE ( rom_chip_v5_disable_cca = 0x400060d0 ); +PROVIDE ( rom_chip_v5_enable_cca = 0x400060ec ); +PROVIDE ( rom_chip_v5_rx_init = 0x4000711c ); +PROVIDE ( rom_chip_v5_sense_backoff = 0x4000610c ); +PROVIDE ( rom_chip_v5_tx_init = 0x4000718c ); +PROVIDE ( rom_dc_iq_est = 0x4000615c ); +PROVIDE ( rom_en_pwdet = 0x400061b8 ); +PROVIDE ( rom_get_bb_atten = 0x40006238 ); +PROVIDE ( rom_get_corr_power = 0x40006260 ); +PROVIDE ( rom_get_fm_sar_dout = 0x400062dc ); +PROVIDE ( rom_get_noisefloor = 0x40006394 ); +PROVIDE ( rom_get_power_db = 0x400063b0 ); +PROVIDE ( rom_i2c_readReg = 0x40007268 ); +PROVIDE ( rom_i2c_readReg_Mask = 0x4000729c ); +PROVIDE ( rom_i2c_writeReg = 0x400072d8 ); +PROVIDE ( rom_i2c_writeReg_Mask = 0x4000730c ); +PROVIDE ( rom_iq_est_disable = 0x40006400 ); +PROVIDE ( rom_iq_est_enable = 0x40006430 ); +PROVIDE ( rom_linear_to_db = 0x40006484 ); +PROVIDE ( rom_mhz2ieee = 0x400065a4 ); +PROVIDE ( rom_pbus_dco___SA2 = 0x40007bf0 ); +PROVIDE ( rom_pbus_debugmode = 0x4000737c ); +PROVIDE ( rom_pbus_enter_debugmode = 0x40007410 ); +PROVIDE ( rom_pbus_exit_debugmode = 0x40007448 ); +PROVIDE ( rom_pbus_force_test = 0x4000747c ); +PROVIDE ( rom_pbus_rd = 0x400074d8 ); +PROVIDE ( rom_pbus_set_rxgain = 0x4000754c ); +PROVIDE ( rom_pbus_set_txgain = 0x40007610 ); +PROVIDE ( rom_pbus_workmode = 0x40007648 ); +PROVIDE ( rom_pbus_xpd_rx_off = 0x40007688 ); +PROVIDE ( rom_pbus_xpd_rx_on = 0x400076cc ); +PROVIDE ( rom_pbus_xpd_tx_off = 0x400076fc ); +PROVIDE ( rom_pbus_xpd_tx_on = 0x40007740 ); +PROVIDE ( rom_pbus_xpd_tx_on__low_gain = 0x400077a0 ); +PROVIDE ( rom_phy_reset_req = 0x40007804 ); +PROVIDE ( rom_restart_cal = 0x4000781c ); +PROVIDE ( rom_rfcal_pwrctrl = 0x40007eb4 ); +PROVIDE ( rom_rfcal_rxiq = 0x4000804c ); +PROVIDE ( rom_rfcal_rxiq_set_reg = 0x40008264 ); +PROVIDE ( rom_rfcal_txcap = 0x40008388 ); +PROVIDE ( rom_rfcal_txiq = 0x40008610 ); +PROVIDE ( rom_rfcal_txiq_cover = 0x400088b8 ); +PROVIDE ( rom_rfcal_txiq_set_reg = 0x40008a70 ); +PROVIDE ( rom_rfpll_reset = 0x40007868 ); +PROVIDE ( rom_rfpll_set_freq = 0x40007968 ); +PROVIDE ( rom_rxiq_cover_mg_mp = 0x40008b6c ); +PROVIDE ( rom_rxiq_get_mis = 0x40006628 ); +PROVIDE ( rom_sar_init = 0x40006738 ); +PROVIDE ( rom_set_ana_inf_tx_scale = 0x4000678c ); +PROVIDE ( rom_set_channel_freq = 0x40006c50 ); +PROVIDE ( rom_set_loopback_gain = 0x400067c8 ); +PROVIDE ( rom_set_noise_floor = 0x40006830 ); +PROVIDE ( rom_set_rxclk_en = 0x40006550 ); +PROVIDE ( rom_set_txbb_atten = 0x40008c6c ); +PROVIDE ( rom_set_txclk_en = 0x4000650c ); +PROVIDE ( rom_set_txiq_cal = 0x40008d34 ); +PROVIDE ( rom_start_noisefloor = 0x40006874 ); +PROVIDE ( rom_start_tx_tone = 0x400068b4 ); +PROVIDE ( rom_stop_tx_tone = 0x4000698c ); +PROVIDE ( rom_tx_mac_disable = 0x40006a98 ); +PROVIDE ( rom_tx_mac_enable = 0x40006ad4 ); +PROVIDE ( rom_txtone_linear_pwr = 0x40006a1c ); +PROVIDE ( rom_write_rfpll_sdm = 0x400078dc ); +PROVIDE ( roundup2 = 0x400031b4 ); +PROVIDE ( rtc_enter_sleep = 0x40002870 ); +PROVIDE ( rtc_get_reset_reason = 0x400025e0 ); +PROVIDE ( rtc_intr_handler = 0x400029ec ); +PROVIDE ( rtc_set_sleep_mode = 0x40002668 ); +PROVIDE ( save_rxbcn_mactime = 0x400027a4 ); +PROVIDE ( save_tsf_us = 0x400027ac ); +PROVIDE ( send_packet = 0x40003c80 ); +PROVIDE ( sha1_prf = 0x4000ba48 ); +PROVIDE ( sha1_vector = 0x4000a2ec ); +PROVIDE ( sip_alloc_to_host_evt = 0x40005180 ); +PROVIDE ( sip_get_ptr = 0x400058a8 ); +PROVIDE ( sip_get_state = 0x40005668 ); +PROVIDE ( sip_init_attach = 0x4000567c ); +PROVIDE ( sip_install_rx_ctrl_cb = 0x4000544c ); +PROVIDE ( sip_install_rx_data_cb = 0x4000545c ); +PROVIDE ( sip_post = 0x400050fc ); +PROVIDE ( sip_post_init = 0x400056c4 ); +PROVIDE ( sip_reclaim_from_host_cmd = 0x4000534c ); +PROVIDE ( sip_reclaim_tx_data_pkt = 0x400052c0 ); +PROVIDE ( sip_send = 0x40005808 ); +PROVIDE ( sip_to_host_chain_append = 0x40005864 ); +PROVIDE ( sip_to_host_evt_send_done = 0x40005234 ); +PROVIDE ( slc_add_credits = 0x400060ac ); +PROVIDE ( slc_enable = 0x40005d90 ); +PROVIDE ( slc_from_host_chain_fetch = 0x40005f24 ); +PROVIDE ( slc_from_host_chain_recycle = 0x40005e94 ); +PROVIDE ( slc_init_attach = 0x40005c50 ); +PROVIDE ( slc_init_credit = 0x4000608c ); +PROVIDE ( slc_pause_from_host = 0x40006014 ); +PROVIDE ( slc_reattach = 0x40005c1c ); +PROVIDE ( slc_resume_from_host = 0x4000603c ); +PROVIDE ( slc_select_tohost_gpio = 0x40005dc0 ); +PROVIDE ( slc_select_tohost_gpio_mode = 0x40005db8 ); +PROVIDE ( slc_send_to_host_chain = 0x40005de4 ); +PROVIDE ( slc_set_host_io_max_window = 0x40006068 ); +PROVIDE ( slc_to_host_chain_recycle = 0x40005f10 ); +PROVIDE ( software_reset = 0x4000264c ); +PROVIDE ( spi_flash_attach = 0x40004644 ); +PROVIDE ( srand = 0x400005f0 ); +PROVIDE ( strcmp = 0x4000bdc8 ); +PROVIDE ( strcpy = 0x4000bec8 ); +PROVIDE ( strlen = 0x4000bf4c ); +PROVIDE ( strncmp = 0x4000bfa8 ); +PROVIDE ( strncpy = 0x4000c0a0 ); +PROVIDE ( strstr = 0x4000e1e0 ); +PROVIDE ( timer_insert = 0x40002c64 ); +PROVIDE ( uartAttach = 0x4000383c ); +PROVIDE ( uart_baudrate_detect = 0x40003924 ); +PROVIDE ( uart_buff_switch = 0x400038a4 ); +PROVIDE ( uart_div_modify = 0x400039d8 ); +PROVIDE ( uart_rx_intr_handler = 0x40003bbc ); +PROVIDE ( uart_rx_one_char = 0x40003b8c ); +PROVIDE ( uart_rx_one_char_block = 0x40003b64 ); +PROVIDE ( uart_rx_readbuff = 0x40003ec8 ); +PROVIDE ( uart_tx_one_char = 0x40003b30 ); +PROVIDE ( wepkey_128 = 0x4000bc40 ); +PROVIDE ( wepkey_64 = 0x4000bb3c ); +PROVIDE ( xthal_bcopy = 0x40000688 ); +PROVIDE ( xthal_copy123 = 0x4000074c ); +PROVIDE ( xthal_get_ccompare = 0x4000dd4c ); +PROVIDE ( xthal_get_ccount = 0x4000dd38 ); +PROVIDE ( xthal_get_interrupt = 0x4000dd58 ); +PROVIDE ( xthal_get_intread = 0x4000dd58 ); +PROVIDE ( xthal_memcpy = 0x400006c4 ); +PROVIDE ( xthal_set_ccompare = 0x4000dd40 ); +PROVIDE ( xthal_set_intclear = 0x4000dd60 ); +PROVIDE ( xthal_spill_registers_into_stack_nw = 0x4000e320 ); +PROVIDE ( xthal_window_spill = 0x4000e324 ); +PROVIDE ( xthal_window_spill_nw = 0x4000e320 ); + +PROVIDE ( Te0 = 0x3fffccf0 ); +PROVIDE ( Td0 = 0x3fffd100 ); +PROVIDE ( Td4s = 0x3fffd500); +PROVIDE ( rcons = 0x3fffd0f0); +PROVIDE ( UartDev = 0x3fffde10 ); +PROVIDE ( flashchip = 0x3fffc714); diff --git a/dist/bin/esptool/flasher_stub/ld/stub_32.ld b/dist/bin/esptool/flasher_stub/ld/stub_32.ld new file mode 100644 index 000000000..80d451772 --- /dev/null +++ b/dist/bin/esptool/flasher_stub/ld/stub_32.ld @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016 Cesanta Software Limited + * All rights reserved + * + * 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. + */ + +/* Note: stub is deliberately loaded close to the very top + of available RAM, to reduce change of colliding with anything + else... */ +MEMORY { + iram : org = 0x4009F000, len = 0x1000 + dram : org = 0x3ffec000, len = 0x14000 +} + +ENTRY(stub_main) + +SECTIONS { + .text : ALIGN(4) { + *(.literal) + *(.text .text.*) + } > iram + + .bss : ALIGN(4) { + _bss_start = ABSOLUTE(.); + *(.bss) + _bss_end = ABSOLUTE(.); + } > dram + + .data : ALIGN(4) { + *(.data) + *(.rodata .rodata.*) + } > dram +} + +INCLUDE "rom_32.ld" diff --git a/dist/bin/esptool/flasher_stub/ld/stub_8266.ld b/dist/bin/esptool/flasher_stub/ld/stub_8266.ld new file mode 100644 index 000000000..2f693b3cb --- /dev/null +++ b/dist/bin/esptool/flasher_stub/ld/stub_8266.ld @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2016 Cesanta Software Limited + * All rights reserved + * + * 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. + */ + +/* Note: stub is deliberately loaded close to the very top + of available RAM, to reduce change of colliding with anything + else... */ +MEMORY { + iram : org = 0x4010E000, len = 0x2000 + dram : org = 0x3FFE8100, len = 0x13f00 +} + +ENTRY(stub_main_8266) + +SECTIONS { + .text : ALIGN(4) { + *(.literal) + *(.text .text.*) + } > iram + + .bss : ALIGN(4) { + _bss_start = ABSOLUTE(.); + *(.bss) + _bss_end = ABSOLUTE(.); + } > dram + + .data : ALIGN(4) { + *(.data) + *(.rodata .rodata.*) + } > dram +} + +INCLUDE "rom_8266.ld" + +PROVIDE(SPIFlashModeConfig = 0x40004568); +PROVIDE(SPIParamCfg = 0x40004c2c); diff --git a/dist/bin/esptool/flasher_stub/miniz.c b/dist/bin/esptool/flasher_stub/miniz.c new file mode 100644 index 000000000..9cecf432a --- /dev/null +++ b/dist/bin/esptool/flasher_stub/miniz.c @@ -0,0 +1,4932 @@ +/* + +Remark: If you change anything in the configuration of miniz, you MUST also update the header file miniz.h! - JD + +*/ + +/* miniz.c v1.15 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing + See "unlicense" statement at the end of this file. + Rich Geldreich , last updated Oct. 13, 2013 + Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt + + Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define + MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). + + * Change History + 10/13/13 v1.15 r4 - Interim bugfix release while I work on the next major release with Zip64 support (almost there!): + - Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com) which could cause locate files to not find files. This bug + would only have occured in earlier versions if you explicitly used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or mz_zip_add_mem_to_archive_file_in_place() + (which used this flag). If you can't switch to v1.15 but want to fix this bug, just remove the uses of this flag from both helper funcs (and of course don't use the flag). + - Bugfix in mz_zip_reader_extract_to_mem_no_alloc() from kymoon when pUser_read_buf is not NULL and compressed size is > uncompressed size + - Fixing mz_zip_reader_extract_*() funcs so they don't try to extract compressed data from directory entries, to account for weird zipfiles which contain zero-size compressed data on dir entries. + Hopefully this fix won't cause any issues on weird zip archives, because it assumes the low 16-bits of zip external attributes are DOS attributes (which I believe they always are in practice). + - Fixing mz_zip_reader_is_file_a_directory() so it doesn't check the internal attributes, just the filename and external attributes + - mz_zip_reader_init_file() - missing MZ_FCLOSE() call if the seek failed + - Added cmake support for Linux builds which builds all the examples, tested with clang v3.3 and gcc v4.6. + - Clang fix for tdefl_write_image_to_png_file_in_memory() from toffaletti + - Merged MZ_FORCEINLINE fix from hdeanclark + - Fix include before config #ifdef, thanks emil.brink + - Added tdefl_write_image_to_png_file_in_memory_ex(): supports Y flipping (super useful for OpenGL apps), and explicit control over the compression level (so you can + set it to 1 for real-time compression). + - Merged in some compiler fixes from paulharris's github repro. + - Retested this build under Windows (VS 2010, including static analysis), tcc 0.9.26, gcc v4.6 and clang v3.3. + - Added example6.c, which dumps an image of the mandelbrot set to a PNG file. + - Modified example2 to help test the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY flag more. + - In r3: Bugfix to mz_zip_writer_add_file() found during merge: Fix possible src file fclose() leak if alignment bytes+local header file write faiiled + - In r4: Minor bugfix to mz_zip_writer_add_from_zip_reader(): Was pushing the wrong central dir header offset, appears harmless in this release, but it became a problem in the zip64 branch + 5/20/12 v1.14 - MinGW32/64 GCC 4.6.1 compiler fixes: added MZ_FORCEINLINE, #include (thanks fermtect). + 5/19/12 v1.13 - From jason@cornsyrup.org and kelwert@mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit. + - Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files. + - Eliminated a bunch of warnings when compiling with GCC 32-bit/64. + - Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly + "Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning). + - Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64. + - Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test. + - Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives. + - Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.) + - Fix ftell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself). + 4/12/12 v1.12 - More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's. + level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson for the feedback/bug report. + 5/28/11 v1.11 - Added statement from unlicense.org + 5/27/11 v1.10 - Substantial compressor optimizations: + - Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a + - Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86). + - Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types. + - Refactored the compression code for better readability and maintainability. + - Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large + drop in throughput on some files). + 5/15/11 v1.09 - Initial stable release. + + * Low-level Deflate/Inflate implementation notes: + + Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or + greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses + approximately as well as zlib. + + Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function + coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory + block large enough to hold the entire file. + + The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. + + * zlib-style API notes: + + miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in + zlib replacement in many apps: + The z_stream struct, optional memory allocation callbacks + deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound + inflateInit/inflateInit2/inflate/inflateEnd + compress, compress2, compressBound, uncompress + CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. + Supports raw deflate streams or standard zlib streams with adler-32 checking. + + Limitations: + The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. + I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but + there are no guarantees that miniz.c pulls this off perfectly. + + * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by + Alex Evans. Supports 1-4 bytes/pixel images. + + * ZIP archive API notes: + + The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to + get the job done with minimal fuss. There are simple API's to retrieve file information, read files from + existing archives, create new archives, append new files to existing archives, or clone archive data from + one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), + or you can specify custom file read/write callbacks. + + - Archive reading: Just call this function to read a single file from a disk archive: + + void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, + size_t *pSize, mz_uint zip_flags); + + For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central + directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. + + - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: + + int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); + + The locate operation can optionally check file comments too, which (as one example) can be used to identify + multiple versions of the same file in an archive. This function uses a simple linear search through the central + directory, so it's not very fast. + + Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and + retrieve detailed info on each file by calling mz_zip_reader_file_stat(). + + - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data + to disk and builds an exact image of the central directory in memory. The central directory image is written + all at once at the end of the archive file when the archive is finalized. + + The archive writer can optionally align each file's local header and file data to any power of 2 alignment, + which can be useful when the archive will be read from optical media. Also, the writer supports placing + arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still + readable by any ZIP tool. + + - Archive appending: The simple way to add a single file to an archive is to call this function: + + mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, + const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + + The archive will be created if it doesn't already exist, otherwise it'll be appended to. + Note the appending is done in-place and is not an atomic operation, so if something goes wrong + during the operation it's possible the archive could be left without a central directory (although the local + file headers and file data will be fine, so the archive will be recoverable). + + For more complex archive modification scenarios: + 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to + preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the + compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and + you're done. This is safe but requires a bunch of temporary disk space or heap memory. + + 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), + append new files as needed, then finalize the archive which will write an updated central directory to the + original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a + possibility that the archive's central directory could be lost with this method if anything goes wrong, though. + + - ZIP archive support limitations: + No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files. + Requires streams capable of seeking. + + * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the + below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. + + * Important: For best perf. be sure to customize the below macros for your target platform: + #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 + #define MINIZ_LITTLE_ENDIAN 1 + #define MINIZ_HAS_64BIT_REGISTERS 1 + + * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz + uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files + (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). +*/ + +#ifndef MINIZ_HEADER_INCLUDED +#define MINIZ_HEADER_INCLUDED + +#include + +// Defines to completely disable specific portions of miniz.c: +// If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. + +// Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. +#define MINIZ_NO_STDIO + +// If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or +// get/set file times, and the C run-time funcs that get/set times won't be called. +// The current downside is the times written to your archives will be from 1979. +#define MINIZ_NO_TIME + +// Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. +#define MINIZ_NO_ARCHIVE_APIS + +// Define MINIZ_NO_ARCHIVE_APIS to disable all writing related ZIP archive API's. +#define MINIZ_NO_ARCHIVE_WRITING_APIS + +// Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. +#define MINIZ_NO_ZLIB_APIS + +// Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. +#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES + +// Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. +// Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc +// callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user +// functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. +#define MINIZ_NO_MALLOC + +#if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) + // TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux + #define MINIZ_NO_TIME +#endif + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) + #include +#endif + +//Hardcoded options for Xtensa - JD +#define MINIZ_X86_OR_X64_CPU 0 +#define MINIZ_LITTLE_ENDIAN 1 +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#define MINIZ_HAS_64BIT_REGISTERS 0 +#define TINFL_USE_64BIT_BITBUF 0 + + +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) +// MINIZ_X86_OR_X64_CPU is only used to help set the below macros. +#define MINIZ_X86_OR_X64_CPU 1 +#endif + +#if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU +// Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. +#define MINIZ_LITTLE_ENDIAN 1 +#endif + +#if MINIZ_X86_OR_X64_CPU +// Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 +#endif + +#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) +// Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). +#define MINIZ_HAS_64BIT_REGISTERS 1 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// ------------------- zlib-style API Definitions. + +// For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! +typedef unsigned long mz_ulong; + +// mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. +void mz_free(void *p); + +#define MZ_ADLER32_INIT (1) +// mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); + +#define MZ_CRC32_INIT (0) +// mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. +mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); + +// Compression strategies. +enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 }; + +// Method +#define MZ_DEFLATED 8 + +#ifndef MINIZ_NO_ZLIB_APIS + +// Heap allocation callbacks. +// Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long. +typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); +typedef void (*mz_free_func)(void *opaque, void *address); +typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); + +#define MZ_VERSION "9.1.15" +#define MZ_VERNUM 0x91F0 +#define MZ_VER_MAJOR 9 +#define MZ_VER_MINOR 1 +#define MZ_VER_REVISION 15 +#define MZ_VER_SUBREVISION 0 + +// Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). +enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 }; + +// Return status codes. MZ_PARAM_ERROR is non-standard. +enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 }; + +// Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. +enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_UBER_COMPRESSION = 10, MZ_DEFAULT_LEVEL = 6, MZ_DEFAULT_COMPRESSION = -1 }; + +// Window bits +#define MZ_DEFAULT_WINDOW_BITS 15 + +struct mz_internal_state; + +// Compression/decompression stream struct. +typedef struct mz_stream_s +{ + const unsigned char *next_in; // pointer to next byte to read + unsigned int avail_in; // number of bytes available at next_in + mz_ulong total_in; // total number of bytes consumed so far + + unsigned char *next_out; // pointer to next byte to write + unsigned int avail_out; // number of bytes that can be written to next_out + mz_ulong total_out; // total number of bytes produced so far + + char *msg; // error msg (unused) + struct mz_internal_state *state; // internal state, allocated by zalloc/zfree + + mz_alloc_func zalloc; // optional heap allocation function (defaults to malloc) + mz_free_func zfree; // optional heap free function (defaults to free) + void *opaque; // heap alloc function user pointer + + int data_type; // data_type (unused) + mz_ulong adler; // adler32 of the source or uncompressed data + mz_ulong reserved; // not used +} mz_stream; + +typedef mz_stream *mz_streamp; + +// Returns the version string of miniz.c. +const char *mz_version(void); + +// mz_deflateInit() initializes a compressor with default options: +// Parameters: +// pStream must point to an initialized mz_stream struct. +// level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. +// level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. +// (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) +// Return values: +// MZ_OK on success. +// MZ_STREAM_ERROR if the stream is bogus. +// MZ_PARAM_ERROR if the input parameters are bogus. +// MZ_MEM_ERROR on out of memory. +int mz_deflateInit(mz_streamp pStream, int level); + +// mz_deflateInit2() is like mz_deflate(), except with more control: +// Additional parameters: +// method must be MZ_DEFLATED +// window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) +// mem_level must be between [1, 9] (it's checked but ignored by miniz.c) +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); + +// Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). +int mz_deflateReset(mz_streamp pStream); + +// mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. +// Parameters: +// pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. +// flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. +// Return values: +// MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). +// MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. +// MZ_STREAM_ERROR if the stream is bogus. +// MZ_PARAM_ERROR if one of the parameters is invalid. +// MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) +int mz_deflate(mz_streamp pStream, int flush); + +// mz_deflateEnd() deinitializes a compressor: +// Return values: +// MZ_OK on success. +// MZ_STREAM_ERROR if the stream is bogus. +int mz_deflateEnd(mz_streamp pStream); + +// mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); + +// Single-call compression functions mz_compress() and mz_compress2(): +// Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); + +// mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). +mz_ulong mz_compressBound(mz_ulong source_len); + +// Initializes a decompressor. +int mz_inflateInit(mz_streamp pStream); + +// mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: +// window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). +int mz_inflateInit2(mz_streamp pStream, int window_bits); + +// Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. +// Parameters: +// pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. +// flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. +// On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). +// MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. +// Return values: +// MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. +// MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. +// MZ_STREAM_ERROR if the stream is bogus. +// MZ_DATA_ERROR if the deflate stream is invalid. +// MZ_PARAM_ERROR if one of the parameters is invalid. +// MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again +// with more input data, or with more room in the output buffer (except when using single call decompression, described above). +int mz_inflate(mz_streamp pStream, int flush); + +// Deinitializes a decompressor. +int mz_inflateEnd(mz_streamp pStream); + +// Single-call decompression. +// Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); + +// Returns a string description of the specified error code, or NULL if the error code is invalid. +const char *mz_error(int err); + +// Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. +// Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. +#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES + typedef unsigned char Byte; + typedef unsigned int uInt; + typedef mz_ulong uLong; + typedef Byte Bytef; + typedef uInt uIntf; + typedef char charf; + typedef int intf; + typedef void *voidpf; + typedef uLong uLongf; + typedef void *voidp; + typedef void *const voidpc; + #define Z_NULL 0 + #define Z_NO_FLUSH MZ_NO_FLUSH + #define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH + #define Z_SYNC_FLUSH MZ_SYNC_FLUSH + #define Z_FULL_FLUSH MZ_FULL_FLUSH + #define Z_FINISH MZ_FINISH + #define Z_BLOCK MZ_BLOCK + #define Z_OK MZ_OK + #define Z_STREAM_END MZ_STREAM_END + #define Z_NEED_DICT MZ_NEED_DICT + #define Z_ERRNO MZ_ERRNO + #define Z_STREAM_ERROR MZ_STREAM_ERROR + #define Z_DATA_ERROR MZ_DATA_ERROR + #define Z_MEM_ERROR MZ_MEM_ERROR + #define Z_BUF_ERROR MZ_BUF_ERROR + #define Z_VERSION_ERROR MZ_VERSION_ERROR + #define Z_PARAM_ERROR MZ_PARAM_ERROR + #define Z_NO_COMPRESSION MZ_NO_COMPRESSION + #define Z_BEST_SPEED MZ_BEST_SPEED + #define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION + #define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION + #define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY + #define Z_FILTERED MZ_FILTERED + #define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY + #define Z_RLE MZ_RLE + #define Z_FIXED MZ_FIXED + #define Z_DEFLATED MZ_DEFLATED + #define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS + #define alloc_func mz_alloc_func + #define free_func mz_free_func + #define internal_state mz_internal_state + #define z_stream mz_stream + #define deflateInit mz_deflateInit + #define deflateInit2 mz_deflateInit2 + #define deflateReset mz_deflateReset + #define deflate mz_deflate + #define deflateEnd mz_deflateEnd + #define deflateBound mz_deflateBound + #define compress mz_compress + #define compress2 mz_compress2 + #define compressBound mz_compressBound + #define inflateInit mz_inflateInit + #define inflateInit2 mz_inflateInit2 + #define inflate mz_inflate + #define inflateEnd mz_inflateEnd + #define uncompress mz_uncompress + #define crc32 mz_crc32 + #define adler32 mz_adler32 + #define MAX_WBITS 15 + #define MAX_MEM_LEVEL 9 + #define zError mz_error + #define ZLIB_VERSION MZ_VERSION + #define ZLIB_VERNUM MZ_VERNUM + #define ZLIB_VER_MAJOR MZ_VER_MAJOR + #define ZLIB_VER_MINOR MZ_VER_MINOR + #define ZLIB_VER_REVISION MZ_VER_REVISION + #define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION + #define zlibVersion mz_version + #define zlib_version mz_version() +#endif // #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES + +#endif // MINIZ_NO_ZLIB_APIS + +// ------------------- Types and macros + +typedef unsigned char mz_uint8; +typedef signed short mz_int16; +typedef unsigned short mz_uint16; +typedef unsigned int mz_uint32; +typedef unsigned int mz_uint; +typedef long long mz_int64; +typedef unsigned long long mz_uint64; +typedef int mz_bool; + +#define MZ_FALSE (0) +#define MZ_TRUE (1) + +// An attempt to work around MSVC's spammy "warning C4127: conditional expression is constant" message. +#ifdef _MSC_VER + #define MZ_MACRO_END while (0, 0) +#else + #define MZ_MACRO_END while (0) +#endif + +// ------------------- ZIP archive reading/writing + +#ifndef MINIZ_NO_ARCHIVE_APIS + +enum +{ + MZ_ZIP_MAX_IO_BUF_SIZE = 64*1024, + MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 260, + MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 256 +}; + +typedef struct +{ + mz_uint32 m_file_index; + mz_uint32 m_central_dir_ofs; + mz_uint16 m_version_made_by; + mz_uint16 m_version_needed; + mz_uint16 m_bit_flag; + mz_uint16 m_method; +#ifndef MINIZ_NO_TIME + time_t m_time; +#endif + mz_uint32 m_crc32; + mz_uint64 m_comp_size; + mz_uint64 m_uncomp_size; + mz_uint16 m_internal_attr; + mz_uint32 m_external_attr; + mz_uint64 m_local_header_ofs; + mz_uint32 m_comment_size; + char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; + char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; +} mz_zip_archive_file_stat; + +typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); +typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); + +struct mz_zip_internal_state_tag; +typedef struct mz_zip_internal_state_tag mz_zip_internal_state; + +typedef enum +{ + MZ_ZIP_MODE_INVALID = 0, + MZ_ZIP_MODE_READING = 1, + MZ_ZIP_MODE_WRITING = 2, + MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 +} mz_zip_mode; + +typedef struct mz_zip_archive_tag +{ + mz_uint64 m_archive_size; + mz_uint64 m_central_directory_file_ofs; + mz_uint m_total_files; + mz_zip_mode m_zip_mode; + + mz_uint m_file_offset_alignment; + + mz_alloc_func m_pAlloc; + mz_free_func m_pFree; + mz_realloc_func m_pRealloc; + void *m_pAlloc_opaque; + + mz_file_read_func m_pRead; + mz_file_write_func m_pWrite; + void *m_pIO_opaque; + + mz_zip_internal_state *m_pState; + +} mz_zip_archive; + +typedef enum +{ + MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, + MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, + MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800 +} mz_zip_flags; + +// ZIP archive reading + +// Inits a ZIP archive reader. +// These functions read and validate the archive's central directory. +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags); +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags); + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); +#endif + +// Returns the total number of files in the archive. +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); + +// Returns detailed information about an archive file entry. +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); + +// Determines if an archive file entry is a directory entry. +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); + +// Retrieves the filename of an archive file entry. +// Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); + +// Attempts to locates a file in the archive's central directory. +// Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH +// Returns -1 if the file cannot be found. +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); + +// Extracts a archive file to a memory buffer using no memory allocation. +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); + +// Extracts a archive file to a memory buffer. +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); + +// Extracts a archive file to a dynamically allocated heap buffer. +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); + +// Extracts a archive file using a callback function to output the file's data. +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +// Extracts a archive file to a disk file and sets its last accessed and modified times. +// This function only extracts files, not archive directory records. +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); +#endif + +// Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. +mz_bool mz_zip_reader_end(mz_zip_archive *pZip); + +// ZIP archive writing + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +// Inits a ZIP archive writer. +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); +#endif + +// Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. +// For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. +// For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). +// Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. +// Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before +// the archive is finalized the file's central directory will be hosed. +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); + +// Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. +// To add a directory entry, call this method with an archive name ending in a forwardslash with empty buffer. +// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); + +#ifndef MINIZ_NO_STDIO +// Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. +// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); +#endif + +// Adds a file to an archive by fully cloning the data from another archive. +// This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data, and comment fields. +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index); + +// Finalizes the archive by writing the central directory records followed by the end of central directory record. +// After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). +// An archive must be manually finalized by calling this function for it to be valid. +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize); + +// Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. +// Note for the archive to be valid, it must have been finalized before ending. +mz_bool mz_zip_writer_end(mz_zip_archive *pZip); + +// Misc. high-level helper functions: + +// mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. +// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. +mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + +// Reads a single file from an archive into a heap block. +// Returns NULL on failure. +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags); + +#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +#endif // #ifndef MINIZ_NO_ARCHIVE_APIS + +// ------------------- Low-level Decompression API Definitions + +// Decompression flags used by tinfl_decompress(). +// TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. +// TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. +// TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). +// TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. +enum +{ + TINFL_FLAG_PARSE_ZLIB_HEADER = 1, + TINFL_FLAG_HAS_MORE_INPUT = 2, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, + TINFL_FLAG_COMPUTE_ADLER32 = 8 +}; + +// High level decompression functions: +// tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). +// On entry: +// pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. +// On return: +// Function returns a pointer to the decompressed data, or NULL on failure. +// *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. +// The caller must call mz_free() on the returned block when it's no longer needed. +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +// tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. +// Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. +#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +// tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. +// Returns 1 on success or 0 on failure. +typedef int (*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); +int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; + +// Max size of LZ dictionary. +#define TINFL_LZ_DICT_SIZE 32768 + +// Return status. +typedef enum +{ + TINFL_STATUS_BAD_PARAM = -3, + TINFL_STATUS_ADLER32_MISMATCH = -2, + TINFL_STATUS_FAILED = -1, + TINFL_STATUS_DONE = 0, + TINFL_STATUS_NEEDS_MORE_INPUT = 1, + TINFL_STATUS_HAS_MORE_OUTPUT = 2 +} tinfl_status; + +// Initializes the decompressor to its initial state. +#define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END +#define tinfl_get_adler32(r) (r)->m_check_adler32 + +// Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. +// This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. +tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); + +// Internal/private bits follow. +enum +{ + TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19, + TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS +}; + +typedef struct +{ + mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; + mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; +} tinfl_huff_table; + +#if MINIZ_HAS_64BIT_REGISTERS + #define TINFL_USE_64BIT_BITBUF 1 +#endif + +#if TINFL_USE_64BIT_BITBUF + typedef mz_uint64 tinfl_bit_buf_t; + #define TINFL_BITBUF_SIZE (64) +#else + typedef mz_uint32 tinfl_bit_buf_t; + #define TINFL_BITBUF_SIZE (32) +#endif + +struct tinfl_decompressor_tag +{ + mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; + tinfl_bit_buf_t m_bit_buf; + size_t m_dist_from_out_buf_start; + tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; + mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; +}; + +// ------------------- Low-level Compression API Definitions + +// Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). +#define TDEFL_LESS_MEMORY 1 + +// tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): +// TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). +enum +{ + TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF +}; + +// TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. +// TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). +// TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. +// TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). +// TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) +// TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. +// TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. +// TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. +// The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). +enum +{ + TDEFL_WRITE_ZLIB_HEADER = 0x01000, + TDEFL_COMPUTE_ADLER32 = 0x02000, + TDEFL_GREEDY_PARSING_FLAG = 0x04000, + TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, + TDEFL_RLE_MATCHES = 0x10000, + TDEFL_FILTER_MATCHES = 0x20000, + TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, + TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 +}; + +// High level compression functions: +// tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). +// On entry: +// pSrc_buf, src_buf_len: Pointer and size of source block to compress. +// flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. +// On return: +// Function returns a pointer to the compressed data, or NULL on failure. +// *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. +// The caller must free() the returned block when it's no longer needed. +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +// tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. +// Returns 0 on failure. +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +// Compresses an image to a compressed PNG file in memory. +// On entry: +// pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. +// The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. +// level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL +// If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). +// On return: +// Function returns a pointer to the compressed data, or NULL on failure. +// *pLen_out will be set to the size of the PNG image file. +// The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. +void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); + +// Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. +typedef mz_bool (*tdefl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); + +// tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = (8*1024), TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 }; + +// TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). +#if TDEFL_LESS_MEMORY +enum { TDEFL_LZ_CODE_BUF_SIZE = 8 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; +#else +enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; +#endif + +// The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. +typedef enum +{ + TDEFL_STATUS_BAD_PARAM = -2, + TDEFL_STATUS_PUT_BUF_FAILED = -1, + TDEFL_STATUS_OKAY = 0, + TDEFL_STATUS_DONE = 1, +} tdefl_status; + +// Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums +typedef enum +{ + TDEFL_NO_FLUSH = 0, + TDEFL_SYNC_FLUSH = 2, + TDEFL_FULL_FLUSH = 3, + TDEFL_FINISH = 4 +} tdefl_flush; + +// tdefl's compression state structure. +typedef struct +{ + tdefl_put_buf_func_ptr m_pPut_buf_func; + void *m_pPut_buf_user; + mz_uint m_flags, m_max_probes[2]; + int m_greedy_parsing; + mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; + mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; + mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; + mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; + tdefl_status m_prev_return_status; + const void *m_pIn_buf; + void *m_pOut_buf; + size_t *m_pIn_buf_size, *m_pOut_buf_size; + tdefl_flush m_flush; + const mz_uint8 *m_pSrc; + size_t m_src_buf_left, m_out_buf_ofs; + mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; + mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; + mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; + mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; + mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; +} tdefl_compressor; + +// Initializes the compressor. +// There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. +// pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. +// If pBut_buf_func is NULL the user should always call the tdefl_compress() API. +// flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +// Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); + +// tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. +// tdefl_compress_buffer() always consumes the entire input buffer. +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); +mz_uint32 tdefl_get_adler32(tdefl_compressor *d); + +// Can't use tdefl_create_comp_flags_from_zip_params if MINIZ_NO_ZLIB_APIS isn't defined, because it uses some of its macros. +#ifndef MINIZ_NO_ZLIB_APIS +// Create tdefl_compress() flags given zlib-style compression parameters. +// level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) +// window_bits may be -15 (raw deflate) or 15 (zlib) +// strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); +#endif // #ifndef MINIZ_NO_ZLIB_APIS + +#ifdef __cplusplus +} +#endif + +#endif // MINIZ_HEADER_INCLUDED + +// ------------------- End of Header: Implementation follows. (If you only want the header, define MINIZ_HEADER_FILE_ONLY.) + +#ifndef MINIZ_HEADER_FILE_ONLY + +typedef unsigned char mz_validate_uint16[sizeof(mz_uint16)==2 ? 1 : -1]; +typedef unsigned char mz_validate_uint32[sizeof(mz_uint32)==4 ? 1 : -1]; +typedef unsigned char mz_validate_uint64[sizeof(mz_uint64)==8 ? 1 : -1]; + +#include +#include + +//Disable asserts +#define MZ_ASSERT(x) +//#define MZ_ASSERT(x) assert(x) + +#ifdef MINIZ_NO_MALLOC + #define MZ_MALLOC(x) NULL + #define MZ_FREE(x) (void)x, ((void)0) + #define MZ_REALLOC(p, x) NULL +#else + #define MZ_MALLOC(x) malloc(x) + #define MZ_FREE(x) free(x) + #define MZ_REALLOC(p, x) realloc(p, x) +#endif + +#define MZ_MAX(a,b) (((a)>(b))?(a):(b)) +#define MZ_MIN(a,b) (((a)<(b))?(a):(b)) +#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) + #define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) +#else + #define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) + #define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) +#endif + +#ifdef _MSC_VER + #define MZ_FORCEINLINE __forceinline +#elif defined(__GNUC__) + #define MZ_FORCEINLINE inline __attribute__((__always_inline__)) +#else + #define MZ_FORCEINLINE inline +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +// ------------------- zlib-style API's + +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) +{ + mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552; + if (!ptr) return MZ_ADLER32_INIT; + while (buf_len) { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { + s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; + } + for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; + } + return (s2 << 16) + s1; +} + +// Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) +{ + static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; + mz_uint32 crcu32 = (mz_uint32)crc; + if (!ptr) return MZ_CRC32_INIT; + crcu32 = ~crcu32; while (buf_len--) { mz_uint8 b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; } + return ~crcu32; +} + +void mz_free(void *p) +{ + MZ_FREE(p); +} + +#ifndef MINIZ_NO_ZLIB_APIS + +static void *def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); } +static void def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); } +static void *def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); } + +const char *mz_version(void) +{ + return MZ_VERSION; +} + +int mz_deflateInit(mz_streamp pStream, int level) +{ + return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); +} + +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) +{ + tdefl_compressor *pComp; + mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); + + if (!pStream) return MZ_STREAM_ERROR; + if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = MZ_ADLER32_INIT; + pStream->msg = NULL; + pStream->reserved = 0; + pStream->total_in = 0; + pStream->total_out = 0; + if (!pStream->zalloc) pStream->zalloc = def_alloc_func; + if (!pStream->zfree) pStream->zfree = def_free_func; + + pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pComp; + + if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) + { + mz_deflateEnd(pStream); + return MZ_PARAM_ERROR; + } + + return MZ_OK; +} + +int mz_deflateReset(mz_streamp pStream) +{ + if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) return MZ_STREAM_ERROR; + pStream->total_in = pStream->total_out = 0; + tdefl_init((tdefl_compressor*)pStream->state, NULL, NULL, ((tdefl_compressor*)pStream->state)->m_flags); + return MZ_OK; +} + +int mz_deflate(mz_streamp pStream, int flush) +{ + size_t in_bytes, out_bytes; + mz_ulong orig_total_in, orig_total_out; + int mz_status = MZ_OK; + + if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) return MZ_STREAM_ERROR; + if (!pStream->avail_out) return MZ_BUF_ERROR; + + if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; + + if (((tdefl_compressor*)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) + return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; + + orig_total_in = pStream->total_in; orig_total_out = pStream->total_out; + for ( ; ; ) + { + tdefl_status defl_status; + in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; + + defl_status = tdefl_compress((tdefl_compressor*)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); + pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; pStream->adler = tdefl_get_adler32((tdefl_compressor*)pStream->state); + + pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (defl_status < 0) + { + mz_status = MZ_STREAM_ERROR; + break; + } + else if (defl_status == TDEFL_STATUS_DONE) + { + mz_status = MZ_STREAM_END; + break; + } + else if (!pStream->avail_out) + break; + else if ((!pStream->avail_in) && (flush != MZ_FINISH)) + { + if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) + break; + return MZ_BUF_ERROR; // Can't make forward progress without some input. + } + } + return mz_status; +} + +int mz_deflateEnd(mz_streamp pStream) +{ + if (!pStream) return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) +{ + (void)pStream; + // This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) + return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); +} + +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) +{ + int status; + mz_stream stream; + memset(&stream, 0, sizeof(stream)); + + // In case mz_ulong is 64-bits (argh I hate longs). + if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_deflateInit(&stream, level); + if (status != MZ_OK) return status; + + status = mz_deflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) + { + mz_deflateEnd(&stream); + return (status == MZ_OK) ? MZ_BUF_ERROR : status; + } + + *pDest_len = stream.total_out; + return mz_deflateEnd(&stream); +} + +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); +} + +mz_ulong mz_compressBound(mz_ulong source_len) +{ + return mz_deflateBound(NULL, source_len); +} + +typedef struct +{ + tinfl_decompressor m_decomp; + mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits; + mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; + tinfl_status m_last_status; +} inflate_state; + +int mz_inflateInit2(mz_streamp pStream, int window_bits) +{ + inflate_state *pDecomp; + if (!pStream) return MZ_STREAM_ERROR; + if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + if (!pStream->zalloc) pStream->zalloc = def_alloc_func; + if (!pStream->zfree) pStream->zfree = def_free_func; + + pDecomp = (inflate_state*)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); + if (!pDecomp) return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pDecomp; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + pDecomp->m_window_bits = window_bits; + + return MZ_OK; +} + +int mz_inflateInit(mz_streamp pStream) +{ + return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); +} + +int mz_inflate(mz_streamp pStream, int flush) +{ + inflate_state* pState; + mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; + size_t in_bytes, out_bytes, orig_avail_in; + tinfl_status status; + + if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR; + if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; + if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; + + pState = (inflate_state*)pStream->state; + if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; + orig_avail_in = pStream->avail_in; + + first_call = pState->m_first_call; pState->m_first_call = 0; + if (pState->m_last_status < 0) return MZ_DATA_ERROR; + + if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; + pState->m_has_flushed |= (flush == MZ_FINISH); + + if ((flush == MZ_FINISH) && (first_call)) + { + // MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. + decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; + in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); + pState->m_last_status = status; + pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; + + if (status < 0) + return MZ_DATA_ERROR; + else if (status != TINFL_STATUS_DONE) + { + pState->m_last_status = TINFL_STATUS_FAILED; + return MZ_BUF_ERROR; + } + return MZ_STREAM_END; + } + // flush != MZ_FINISH then we must assume there's more input. + if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; + + if (pState->m_dict_avail) + { + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; + pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; + } + + for ( ; ; ) + { + in_bytes = pStream->avail_in; + out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; + + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); + pState->m_last_status = status; + + pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); + + pState->m_dict_avail = (mz_uint)out_bytes; + + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; + pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + + if (status < 0) + return MZ_DATA_ERROR; // Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). + else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) + return MZ_BUF_ERROR; // Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. + else if (flush == MZ_FINISH) + { + // The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. + if (status == TINFL_STATUS_DONE) + return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; + // status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. + else if (!pStream->avail_out) + return MZ_BUF_ERROR; + } + else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) + break; + } + + return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; +} + +int mz_inflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + mz_stream stream; + int status; + memset(&stream, 0, sizeof(stream)); + + // In case mz_ulong is 64-bits (argh I hate longs). + if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_inflateInit(&stream); + if (status != MZ_OK) + return status; + + status = mz_inflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) + { + mz_inflateEnd(&stream); + return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; + } + *pDest_len = stream.total_out; + + return mz_inflateEnd(&stream); +} + +const char *mz_error(int err) +{ + static struct { int m_err; const char *m_pDesc; } s_error_descs[] = + { + { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, + { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } + }; + mz_uint i; for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) if (s_error_descs[i].m_err == err) return s_error_descs[i].m_pDesc; + return NULL; +} + +#endif //MINIZ_NO_ZLIB_APIS + +// ------------------- Low-level Decompression (completely independent from all compression API's) + +#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) +#define TINFL_MEMSET(p, c, l) memset(p, c, l) + +#define TINFL_CR_BEGIN switch(r->m_state) { case 0: +#define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END +#define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END +#define TINFL_CR_FINISH } + +// TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never +// reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario. +#define TINFL_GET_BYTE(state_index, c) do { \ + if (pIn_buf_cur >= pIn_buf_end) { \ + for ( ; ; ) { \ + if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \ + TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \ + if (pIn_buf_cur < pIn_buf_end) { \ + c = *pIn_buf_cur++; \ + break; \ + } \ + } else { \ + c = 0; \ + break; \ + } \ + } \ + } else c = *pIn_buf_cur++; } MZ_MACRO_END + +#define TINFL_NEED_BITS(state_index, n) do { mz_uint c; TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (mz_uint)(n)) +#define TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END +#define TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END + +// TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. +// It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a +// Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the +// bit buffer contains >=15 bits (deflate's max. Huffman code size). +#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ + do { \ + temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ + if (temp >= 0) { \ + code_len = temp >> 9; \ + if ((code_len) && (num_bits >= code_len)) \ + break; \ + } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do { \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \ + } TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \ + } while (num_bits < 15); + +// TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read +// beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully +// decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. +// The slow path is only executed at the very end of the input buffer. +#define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \ + int temp; mz_uint code_len, c; \ + if (num_bits < 15) { \ + if ((pIn_buf_end - pIn_buf_cur) < 2) { \ + TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ + } else { \ + bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \ + } \ + } \ + if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ + code_len = temp >> 9, temp &= 511; \ + else { \ + code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \ + } sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END + +tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) +{ + static const int s_length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 }; + static const int s_length_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + static const int s_dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + static const int s_dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + static const mz_uint8 s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + static const int s_min_table_sizes[3] = { 257, 1, 4 }; + + tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; + const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; + mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; + size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; + + // Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). + if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; } + + num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start; + TINFL_CR_BEGIN + + bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1; + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1); + counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); + if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); + if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); } + } + + do + { + TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1; + if (r->m_type == 0) + { + TINFL_SKIP_BITS(5, num_bits & 7); + for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); } + if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); } + while ((counter) && (num_bits)) + { + TINFL_GET_BITS(51, dist, 8); + while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); } + *pOut_buf_cur++ = (mz_uint8)dist; + counter--; + } + while (counter) + { + size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); } + while (pIn_buf_cur >= pIn_buf_end) + { + if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) + { + TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT); + } + else + { + TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED); + } + } + n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); + TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n; + } + } + else if (r->m_type == 3) + { + TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); + } + else + { + if (r->m_type == 1) + { + mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i; + r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); + for ( i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8; + } + else + { + for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } + MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; } + r->m_table_sizes[2] = 19; + } + for ( ; (int)r->m_type >= 0; r->m_type--) + { + int tree_next, tree_cur; tinfl_huff_table *pTable; + mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree); + for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++; + used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; + for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); } + if ((65536 != total) && (used_syms > 1)) + { + TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); + } + for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) + { + mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue; + cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1); + if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; } + if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } + rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); + for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) + { + tree_cur -= ((rev_code >>= 1) & 1); + if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1]; + } + tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; + } + if (r->m_type == 2) + { + for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); ) + { + mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; } + if ((dist == 16) && (!counter)) + { + TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); + } + num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16]; + TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s; + } + if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) + { + TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); + } + TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); + } + } + for ( ; ; ) + { + mz_uint8 *pSrc; + for ( ; ; ) + { + if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) + { + TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); + if (counter >= 256) + break; + while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); } + *pOut_buf_cur++ = (mz_uint8)counter; + } + else + { + int sym2; mz_uint code_len; +#if TINFL_USE_64BIT_BITBUF + if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; } +#else + if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); + } + counter = sym2; bit_buf >>= code_len; num_bits -= code_len; + if (counter & 256) + break; + +#if !TINFL_USE_64BIT_BITBUF + if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); + } + bit_buf >>= code_len; num_bits -= code_len; + + pOut_buf_cur[0] = (mz_uint8)counter; + if (sym2 & 256) + { + pOut_buf_cur++; + counter = sym2; + break; + } + pOut_buf_cur[1] = (mz_uint8)sym2; + pOut_buf_cur += 2; + } + } + if ((counter &= 511) == 256) break; + + num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257]; + if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; } + + TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); + num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; + if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; } + + dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; + if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + { + TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); + } + + pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); + + if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) + { + while (counter--) + { + while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); } + *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; + } + continue; + } +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + else if ((counter >= 9) && (counter <= dist)) + { + const mz_uint8 *pSrc_end = pSrc + (counter & ~7); + do + { + ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; + ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; + pOut_buf_cur += 8; + } while ((pSrc += 8) < pSrc_end); + if ((counter &= 7) < 3) + { + if (counter) + { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + continue; + } + } +#endif + do + { + pOut_buf_cur[0] = pSrc[0]; + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur[2] = pSrc[2]; + pOut_buf_cur += 3; pSrc += 3; + } while ((int)(counter -= 3) > 2); + if ((int)counter > 0) + { + pOut_buf_cur[0] = pSrc[0]; + if ((int)counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + } + } + } while (!(r->m_final & 1)); + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; } + } + TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); + TINFL_CR_FINISH + +common_exit: + r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start; + *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next; + if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) + { + const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size; + mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552; + while (buf_len) + { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) + { + s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; + } + for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; + } + r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH; + } + return status; +} + +// Higher level helper functions. +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0; + *pOut_len = 0; + tinfl_init(&decomp); + for ( ; ; ) + { + size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8*)pBuf, pBuf ? (mz_uint8*)pBuf + *pOut_len : NULL, &dst_buf_size, + (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) + { + MZ_FREE(pBuf); *pOut_len = 0; return NULL; + } + src_buf_ofs += src_buf_size; + *pOut_len += dst_buf_size; + if (status == TINFL_STATUS_DONE) break; + new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128; + pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); + if (!pNew_buf) + { + MZ_FREE(pBuf); *pOut_len = 0; return NULL; + } + pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity; + } + return pBuf; +} + +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +{ + tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp); + status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf, &src_buf_len, (mz_uint8*)pOut_buf, (mz_uint8*)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; +} + +int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + int result = 0; + tinfl_decompressor decomp; + mz_uint8 *pDict = (mz_uint8*)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0; + if (!pDict) + return TINFL_STATUS_FAILED; + tinfl_init(&decomp); + for ( ; ; ) + { + size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, + (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); + in_buf_ofs += in_buf_size; + if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) + break; + if (status != TINFL_STATUS_HAS_MORE_OUTPUT) + { + result = (status == TINFL_STATUS_DONE); + break; + } + dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); + } + MZ_FREE(pDict); + *pIn_buf_size = in_buf_ofs; + return result; +} + +// ------------------- Low-level Compression (independent from all decompression API's) + +// Purposely making these tables static for faster init and thread safety. +static const mz_uint16 s_tdefl_len_sym[256] = { + 257,258,259,260,261,262,263,264,265,265,266,266,267,267,268,268,269,269,269,269,270,270,270,270,271,271,271,271,272,272,272,272, + 273,273,273,273,273,273,273,273,274,274,274,274,274,274,274,274,275,275,275,275,275,275,275,275,276,276,276,276,276,276,276,276, + 277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278, + 279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280, + 281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281, + 282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282, + 283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283, + 284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,285 }; + +static const mz_uint8 s_tdefl_len_extra[256] = { + 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0 }; + +static const mz_uint8 s_tdefl_small_dist_sym[512] = { + 0,1,2,3,4,4,5,5,6,6,6,6,7,7,7,7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 }; + +static const mz_uint8 s_tdefl_small_dist_extra[512] = { + 0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7 }; + +static const mz_uint8 s_tdefl_large_dist_sym[128] = { + 0,0,18,19,20,20,21,21,22,22,22,22,23,23,23,23,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28, + 28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 }; + +static const mz_uint8 s_tdefl_large_dist_extra[128] = { + 0,0,8,8,9,9,9,9,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 }; + +// Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. +typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq; +static tdefl_sym_freq* tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq* pSyms0, tdefl_sym_freq* pSyms1) +{ + mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq* pCur_syms = pSyms0, *pNew_syms = pSyms1; MZ_CLEAR_OBJ(hist); + for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; } + while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--; + for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) + { + const mz_uint32* pHist = &hist[pass << 8]; + mz_uint offsets[256], cur_ofs = 0; + for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; } + for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; + { tdefl_sym_freq* t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; } + } + return pCur_syms; +} + +// tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. +static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) +{ + int root, leaf, next, avbl, used, dpth; + if (n==0) return; else if (n==1) { A[0].m_key = 1; return; } + A[0].m_key += A[1].m_key; root = 0; leaf = 2; + for (next=1; next < n-1; next++) + { + if (leaf>=n || A[root].m_key=n || (root=0; next--) A[next].m_key = A[A[next].m_key].m_key+1; + avbl = 1; used = dpth = 0; root = n-2; next = n-1; + while (avbl>0) + { + while (root>=0 && (int)A[root].m_key==dpth) { used++; root--; } + while (avbl>used) { A[next--].m_key = (mz_uint16)(dpth); avbl--; } + avbl = 2*used; dpth++; used = 0; + } +} + +// Limits canonical Huffman code table's max code size. +enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 }; +static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) +{ + int i; mz_uint32 total = 0; if (code_list_len <= 1) return; + for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i]; + for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); + while (total != (1UL << max_code_size)) + { + pNum_codes[max_code_size]--; + for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; } + total--; + } +} + +static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) +{ + int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; MZ_CLEAR_OBJ(num_codes); + if (static_table) + { + for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++; + } + else + { + tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; + int num_used_syms = 0; + const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; + for (i = 0; i < table_len; i++) if (pSym_count[i]) { syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; syms0[num_used_syms++].m_sym_index = (mz_uint16)i; } + + pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); + + for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++; + + tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); + + MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); + for (i = 1, j = num_used_syms; i <= code_size_limit; i++) + for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); + } + + next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1); + + for (i = 0; i < table_len; i++) + { + mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue; + code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1); + d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; + } +} + +#define TDEFL_PUT_BITS(b, l) do { \ + mz_uint bits = b; mz_uint len = l; MZ_ASSERT(bits <= ((1U << len) - 1U)); \ + d->m_bit_buffer |= (bits << d->m_bits_in); d->m_bits_in += len; \ + while (d->m_bits_in >= 8) { \ + if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ + *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ + d->m_bit_buffer >>= 8; \ + d->m_bits_in -= 8; \ + } \ +} MZ_MACRO_END + +#define TDEFL_RLE_PREV_CODE_SIZE() { if (rle_repeat_count) { \ + if (rle_repeat_count < 3) { \ + d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ + while (rle_repeat_count--) packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ + } else { \ + d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); packed_code_sizes[num_packed_code_sizes++] = 16; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ +} rle_repeat_count = 0; } } + +#define TDEFL_RLE_ZERO_CODE_SIZE() { if (rle_z_count) { \ + if (rle_z_count < 3) { \ + d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); while (rle_z_count--) packed_code_sizes[num_packed_code_sizes++] = 0; \ + } else if (rle_z_count <= 10) { \ + d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); packed_code_sizes[num_packed_code_sizes++] = 17; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ + } else { \ + d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); packed_code_sizes[num_packed_code_sizes++] = 18; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ +} rle_z_count = 0; } } + +static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + +static void tdefl_start_dynamic_block(tdefl_compressor *d) +{ + int num_lit_codes, num_dist_codes, num_bit_lengths; mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; + mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; + + d->m_huff_count[0][256] = 1; + + tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); + tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); + + for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break; + for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break; + + memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); + memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); + total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0; + + memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); + for (i = 0; i < total_code_sizes_to_pack; i++) + { + mz_uint8 code_size = code_sizes_to_pack[i]; + if (!code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); } + } + else + { + TDEFL_RLE_ZERO_CODE_SIZE(); + if (code_size != prev_code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); packed_code_sizes[num_packed_code_sizes++] = code_size; + } + else if (++rle_repeat_count == 6) + { + TDEFL_RLE_PREV_CODE_SIZE(); + } + } + prev_code_size = code_size; + } + if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } else { TDEFL_RLE_ZERO_CODE_SIZE(); } + + tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); + + TDEFL_PUT_BITS(2, 2); + + TDEFL_PUT_BITS(num_lit_codes - 257, 5); + TDEFL_PUT_BITS(num_dist_codes - 1, 5); + + for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) break; + num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4); + for (i = 0; (int)i < num_bit_lengths; i++) TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); + + for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes; ) + { + mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); + TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); + if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); + } +} + +static void tdefl_start_static_block(tdefl_compressor *d) +{ + mz_uint i; + mz_uint8 *p = &d->m_huff_code_sizes[0][0]; + + for (i = 0; i <= 143; ++i) *p++ = 8; + for ( ; i <= 255; ++i) *p++ = 9; + for ( ; i <= 279; ++i) *p++ = 7; + for ( ; i <= 287; ++i) *p++ = 8; + + memset(d->m_huff_code_sizes[1], 5, 32); + + tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); + tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); + + TDEFL_PUT_BITS(1, 2); +} + +static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + mz_uint8 *pOutput_buf = d->m_pOutput_buf; + mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; + mz_uint64 bit_buffer = d->m_bit_buffer; + mz_uint bits_in = d->m_bits_in; + +#define TDEFL_PUT_BITS_FAST(b, l) { bit_buffer |= (((mz_uint64)(b)) << bits_in); bits_in += (l); } + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + + if (flags & 1) + { + mz_uint s0, s1, n0, n1, sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + // This sequence coaxes MSVC into using cmov's vs. jmp's. + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + n0 = s_tdefl_small_dist_extra[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[match_dist >> 8]; + n1 = s_tdefl_large_dist_extra[match_dist >> 8]; + sym = (match_dist < 512) ? s0 : s1; + num_extra_bits = (match_dist < 512) ? n0 : n1; + + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + } + + if (pOutput_buf >= d->m_pOutput_buf_end) + return MZ_FALSE; + + *(mz_uint64*)pOutput_buf = bit_buffer; + pOutput_buf += (bits_in >> 3); + bit_buffer >>= (bits_in & ~7); + bits_in &= 7; + } + +#undef TDEFL_PUT_BITS_FAST + + d->m_pOutput_buf = pOutput_buf; + d->m_bits_in = 0; + d->m_bit_buffer = 0; + + while (bits_in) + { + mz_uint32 n = MZ_MIN(bits_in, 16); + TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); + bit_buffer >>= n; + bits_in -= n; + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#else +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + if (flags & 1) + { + mz_uint sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + if (match_dist < 512) + { + sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist]; + } + else + { + sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; + } + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS + +static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) +{ + if (static_block) + tdefl_start_static_block(d); + else + tdefl_start_dynamic_block(d); + return tdefl_compress_lz_codes(d); +} + +static int tdefl_flush_block(tdefl_compressor *d, int flush) +{ + mz_uint saved_bit_buf, saved_bits_in; + mz_uint8 *pSaved_output_buf; + mz_bool comp_block_succeeded = MZ_FALSE; + int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; + mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; + + d->m_pOutput_buf = pOutput_buf_start; + d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; + + MZ_ASSERT(!d->m_output_flush_remaining); + d->m_output_flush_ofs = 0; + d->m_output_flush_remaining = 0; + + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); + d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); + + if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) + { + TDEFL_PUT_BITS(0x78, 8); TDEFL_PUT_BITS(0x01, 8); + } + + TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); + + pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in; + + if (!use_raw_block) + comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); + + // If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. + if ( ((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && + ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size) ) + { + mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + TDEFL_PUT_BITS(0, 2); + if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } + for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) + { + TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); + } + for (i = 0; i < d->m_total_lz_bytes; ++i) + { + TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); + } + } + // Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. + else if (!comp_block_succeeded) + { + d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + tdefl_compress_block(d, MZ_TRUE); + } + + if (flush) + { + if (flush == TDEFL_FINISH) + { + if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } + if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { mz_uint i, a = d->m_adler32; for (i = 0; i < 4; i++) { TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); a <<= 8; } } + } + else + { + mz_uint i, z = 0; TDEFL_PUT_BITS(0, 3); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, z ^= 0xFFFF) { TDEFL_PUT_BITS(z & 0xFFFF, 16); } + } + } + + MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); + + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; d->m_total_lz_bytes = 0; d->m_block_index++; + + if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) + { + if (d->m_pPut_buf_func) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) + return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); + } + else if (pOutput_buf_start == d->m_output_buf) + { + int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); + d->m_out_buf_ofs += bytes_to_copy; + if ((n -= bytes_to_copy) != 0) + { + d->m_output_flush_ofs = bytes_to_copy; + d->m_output_flush_remaining = n; + } + } + else + { + d->m_out_buf_ofs += n; + } + } + + return d->m_output_flush_remaining; +} + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES +#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16*)(p) +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint16 *s = (const mz_uint16*)(d->m_dict + pos), *p, *q; + mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD(s); + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; + for ( ; ; ) + { + for ( ; ; ) + { + if (--num_probes_left == 0) return; + #define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) break; + TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; + } + if (!dist) break; q = (const mz_uint16*)(d->m_dict + probe_pos); if (TDEFL_READ_UNALIGNED_WORD(q) != s01) continue; p = s; probe_len = 32; + do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && + (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); + if (!probe_len) + { + *pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); break; + } + else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8*)p == *(const mz_uint8*)q)) > match_len) + { + *pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break; + c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); + } + } +} +#else +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint8 *s = d->m_dict + pos, *p, *q; + mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; + for ( ; ; ) + { + for ( ; ; ) + { + if (--num_probes_left == 0) return; + #define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) break; + TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; + } + if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) if (*p++ != *q++) break; + if (probe_len > match_len) + { + *pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return; + c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1]; + } + } +} +#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +static mz_bool tdefl_compress_fast(tdefl_compressor *d) +{ + // Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. + mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; + mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; + mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + + while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) + { + const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; + mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); + d->m_src_buf_left -= num_bytes_to_process; + lookahead_size += num_bytes_to_process; + + while (num_bytes_to_process) + { + mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); + memcpy(d->m_dict + dst_pos, d->m_pSrc, n); + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); + d->m_pSrc += n; + dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; + num_bytes_to_process -= n; + } + + dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); + if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break; + + while (lookahead_size >= 4) + { + mz_uint cur_match_dist, cur_match_len = 1; + mz_uint8 *pCur_dict = d->m_dict + cur_pos; + mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF; + mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; + mz_uint probe_pos = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)lookahead_pos; + + if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((*(const mz_uint32 *)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) + { + const mz_uint16 *p = (const mz_uint16 *)pCur_dict; + const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); + mz_uint32 probe_len = 32; + do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && + (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); + cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); + if (!probe_len) + cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; + + if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U))) + { + cur_match_len = 1; + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + else + { + mz_uint32 s0, s1; + cur_match_len = MZ_MIN(cur_match_len, lookahead_size); + + MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); + + cur_match_dist--; + + pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); + *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; + pLZ_code_buf += 3; + *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); + + s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; + s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; + d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; + + d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; + } + } + else + { + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + + if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } + + total_lz_bytes += cur_match_len; + lookahead_pos += cur_match_len; + dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; + MZ_ASSERT(lookahead_size >= cur_match_len); + lookahead_size -= cur_match_len; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; + } + } + + while (lookahead_size) + { + mz_uint8 lit = d->m_dict[cur_pos]; + + total_lz_bytes++; + *pLZ_code_buf++ = lit; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } + + d->m_huff_count[0][lit]++; + + lookahead_pos++; + dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + lookahead_size--; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; + } + } + } + + d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; + return MZ_TRUE; +} +#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + +static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) +{ + d->m_total_lz_bytes++; + *d->m_pLZ_code_buf++ = lit; + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } + d->m_huff_count[0][lit]++; +} + +static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) +{ + mz_uint32 s0, s1; + + MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); + + d->m_total_lz_bytes += match_len; + + d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); + + match_dist -= 1; + d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); + d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); d->m_pLZ_code_buf += 3; + + *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } + + s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; + d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; + + if (match_len >= TDEFL_MIN_MATCH_LEN) d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; +} + +static mz_bool tdefl_compress_normal(tdefl_compressor *d) +{ + const mz_uint8 *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left; + tdefl_flush flush = d->m_flush; + + while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) + { + mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; + // Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. + if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) + { + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; + mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); + const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; + src_buf_left -= num_bytes_to_process; + d->m_lookahead_size += num_bytes_to_process; + while (pSrc != pSrc_end) + { + mz_uint8 c = *pSrc++; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); + dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++; + } + } + else + { + while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + { + mz_uint8 c = *pSrc++; + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + src_buf_left--; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) + { + mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; + mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); + } + } + } + d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); + if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + break; + + // Simple lazy/greedy parsing state machine. + len_to_move = 1; cur_match_dist = 0; cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) + { + if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) + { + mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; + cur_match_len = 0; while (cur_match_len < d->m_lookahead_size) { if (d->m_dict[cur_pos + cur_match_len] != c) break; cur_match_len++; } + if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1; + } + } + else + { + tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); + } + if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) + { + cur_match_dist = cur_match_len = 0; + } + if (d->m_saved_match_len) + { + if (cur_match_len > d->m_saved_match_len) + { + tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); + if (cur_match_len >= 128) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + d->m_saved_match_len = 0; len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[cur_pos]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; + } + } + else + { + tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); + len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0; + } + } + else if (!cur_match_dist) + tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); + else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; + } + // Move the lookahead forward by len_to_move bytes. + d->m_lookahead_pos += len_to_move; + MZ_ASSERT(d->m_lookahead_size >= len_to_move); + d->m_lookahead_size -= len_to_move; + d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE); + // Check if it's time to flush the current LZ codes to the internal output buffer. + if ( (d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || + ( (d->m_total_lz_bytes > 31*1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) ) + { + int n; + d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + } + } + + d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; + return MZ_TRUE; +} + +static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) +{ + if (d->m_pIn_buf_size) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + } + + if (d->m_pOut_buf_size) + { + size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); + d->m_output_flush_ofs += (mz_uint)n; + d->m_output_flush_remaining -= (mz_uint)n; + d->m_out_buf_ofs += n; + + *d->m_pOut_buf_size = d->m_out_buf_ofs; + } + + return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) +{ + if (!d) + { + if (pIn_buf_size) *pIn_buf_size = 0; + if (pOut_buf_size) *pOut_buf_size = 0; + return TDEFL_STATUS_BAD_PARAM; + } + + d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size; + d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size; + d->m_pSrc = (const mz_uint8 *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; + d->m_out_buf_ofs = 0; + d->m_flush = flush; + + if ( ((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || + (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf) ) + { + if (pIn_buf_size) *pIn_buf_size = 0; + if (pOut_buf_size) *pOut_buf_size = 0; + return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); + } + d->m_wants_to_finish |= (flush == TDEFL_FINISH); + + if ((d->m_output_flush_remaining) || (d->m_finished)) + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && + ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && + ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) + { + if (!tdefl_compress_fast(d)) + return d->m_prev_return_status; + } + else +#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + { + if (!tdefl_compress_normal(d)) + return d->m_prev_return_status; + } + + if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) + d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); + + if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) + { + if (tdefl_flush_block(d, flush) < 0) + return d->m_prev_return_status; + d->m_finished = (flush == TDEFL_FINISH); + if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_OBJ(d->m_hash); MZ_CLEAR_OBJ(d->m_next); d->m_dict_size = 0; } + } + + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); +} + +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) +{ + MZ_ASSERT(d->m_pPut_buf_func); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); +} + +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + d->m_pPut_buf_func = pPut_buf_func; d->m_pPut_buf_user = pPut_buf_user; + d->m_flags = (mz_uint)(flags); d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; + d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_hash); + d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; + d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; + d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY; + d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1; + d->m_pIn_buf = NULL; d->m_pOut_buf = NULL; + d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL; + d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0; + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + return TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) +{ + return d->m_prev_return_status; +} + +mz_uint32 tdefl_get_adler32(tdefl_compressor *d) +{ + return d->m_adler32; +} + +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + tdefl_compressor *pComp; mz_bool succeeded; if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE; + pComp = (tdefl_compressor*)MZ_MALLOC(sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE; + succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); + succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); + MZ_FREE(pComp); return succeeded; +} + +typedef struct +{ + size_t m_size, m_capacity; + mz_uint8 *m_pBuf; + mz_bool m_expandable; +} tdefl_output_buffer; + +static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) +{ + tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; + size_t new_size = p->m_size + len; + if (new_size > p->m_capacity) + { + size_t new_capacity = p->m_capacity; mz_uint8 *pNew_buf; if (!p->m_expandable) return MZ_FALSE; + do { new_capacity = MZ_MAX(128U, new_capacity << 1U); } while (new_size > new_capacity); + pNew_buf = (mz_uint8*)MZ_REALLOC(p->m_pBuf, new_capacity); if (!pNew_buf) return MZ_FALSE; + p->m_pBuf = pNew_buf; p->m_capacity = new_capacity; + } + memcpy((mz_uint8*)p->m_pBuf + p->m_size, pBuf, len); p->m_size = new_size; + return MZ_TRUE; +} + +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); + if (!pOut_len) return MZ_FALSE; else *pOut_len = 0; + out_buf.m_expandable = MZ_TRUE; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return NULL; + *pOut_len = out_buf.m_size; return out_buf.m_pBuf; +} + +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +{ + tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); + if (!pOut_buf) return 0; + out_buf.m_pBuf = (mz_uint8*)pOut_buf; out_buf.m_capacity = out_buf_len; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return 0; + return out_buf.m_size; +} + +#ifndef MINIZ_NO_ZLIB_APIS +static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + +// level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) +{ + mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); + if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER; + + if (!level) comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; + else if (strategy == MZ_FILTERED) comp_flags |= TDEFL_FILTER_MATCHES; + else if (strategy == MZ_HUFFMAN_ONLY) comp_flags &= ~TDEFL_MAX_PROBES_MASK; + else if (strategy == MZ_FIXED) comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; + else if (strategy == MZ_RLE) comp_flags |= TDEFL_RLE_MATCHES; + + return comp_flags; +} +#endif //MINIZ_NO_ZLIB_APIS + +#ifdef _MSC_VER +#pragma warning (push) +#pragma warning (disable:4204) // nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) +#endif + +// Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at +// http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. +// This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. +void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) +{ + // Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. + static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); tdefl_output_buffer out_buf; int i, bpl = w * num_chans, y, z; mz_uint32 c; *pLen_out = 0; + if (!pComp) return NULL; + MZ_CLEAR_OBJ(out_buf); out_buf.m_expandable = MZ_TRUE; out_buf.m_capacity = 57+MZ_MAX(64, (1+bpl)*h); if (NULL == (out_buf.m_pBuf = (mz_uint8*)MZ_MALLOC(out_buf.m_capacity))) { MZ_FREE(pComp); return NULL; } + // write dummy header + for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf); + // compress image data + tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); + for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8*)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); } + if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } + // write real header + *pLen_out = out_buf.m_size-41; + { + static const mz_uint8 chans[] = {0x00, 0x00, 0x04, 0x02, 0x06}; + mz_uint8 pnghdr[41]={0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52, + 0,0,(mz_uint8)(w>>8),(mz_uint8)w,0,0,(mz_uint8)(h>>8),(mz_uint8)h,8,chans[num_chans],0,0,0,0,0,0,0, + (mz_uint8)(*pLen_out>>24),(mz_uint8)(*pLen_out>>16),(mz_uint8)(*pLen_out>>8),(mz_uint8)*pLen_out,0x49,0x44,0x41,0x54}; + c=(mz_uint32)mz_crc32(MZ_CRC32_INIT,pnghdr+12,17); for (i=0; i<4; ++i, c<<=8) ((mz_uint8*)(pnghdr+29))[i]=(mz_uint8)(c>>24); + memcpy(out_buf.m_pBuf, pnghdr, 41); + } + // write footer (IDAT CRC-32, followed by IEND chunk) + if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { *pLen_out = 0; MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT,out_buf.m_pBuf+41-4, *pLen_out+4); for (i=0; i<4; ++i, c<<=8) (out_buf.m_pBuf+out_buf.m_size-16)[i] = (mz_uint8)(c >> 24); + // compute final size of file, grab compressed data buffer and return + *pLen_out += 57; MZ_FREE(pComp); return out_buf.m_pBuf; +} +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) +{ + // Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) + return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); +} + +#ifdef _MSC_VER +#pragma warning (pop) +#endif + +// ------------------- .ZIP archive reading + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef MINIZ_NO_STDIO + #define MZ_FILE void * +#else + #include + #include + + #if defined(_MSC_VER) || defined(__MINGW64__) + static FILE *mz_fopen(const char *pFilename, const char *pMode) + { + FILE* pFile = NULL; + fopen_s(&pFile, pFilename, pMode); + return pFile; + } + static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) + { + FILE* pFile = NULL; + if (freopen_s(&pFile, pPath, pMode, pStream)) + return NULL; + return pFile; + } + #ifndef MINIZ_NO_TIME + #include + #endif + #define MZ_FILE FILE + #define MZ_FOPEN mz_fopen + #define MZ_FCLOSE fclose + #define MZ_FREAD fread + #define MZ_FWRITE fwrite + #define MZ_FTELL64 _ftelli64 + #define MZ_FSEEK64 _fseeki64 + #define MZ_FILE_STAT_STRUCT _stat + #define MZ_FILE_STAT _stat + #define MZ_FFLUSH fflush + #define MZ_FREOPEN mz_freopen + #define MZ_DELETE_FILE remove + #elif defined(__MINGW32__) + #ifndef MINIZ_NO_TIME + #include + #endif + #define MZ_FILE FILE + #define MZ_FOPEN(f, m) fopen(f, m) + #define MZ_FCLOSE fclose + #define MZ_FREAD fread + #define MZ_FWRITE fwrite + #define MZ_FTELL64 ftello64 + #define MZ_FSEEK64 fseeko64 + #define MZ_FILE_STAT_STRUCT _stat + #define MZ_FILE_STAT _stat + #define MZ_FFLUSH fflush + #define MZ_FREOPEN(f, m, s) freopen(f, m, s) + #define MZ_DELETE_FILE remove + #elif defined(__TINYC__) + #ifndef MINIZ_NO_TIME + #include + #endif + #define MZ_FILE FILE + #define MZ_FOPEN(f, m) fopen(f, m) + #define MZ_FCLOSE fclose + #define MZ_FREAD fread + #define MZ_FWRITE fwrite + #define MZ_FTELL64 ftell + #define MZ_FSEEK64 fseek + #define MZ_FILE_STAT_STRUCT stat + #define MZ_FILE_STAT stat + #define MZ_FFLUSH fflush + #define MZ_FREOPEN(f, m, s) freopen(f, m, s) + #define MZ_DELETE_FILE remove + #elif defined(__GNUC__) && _LARGEFILE64_SOURCE + #ifndef MINIZ_NO_TIME + #include + #endif + #define MZ_FILE FILE + #define MZ_FOPEN(f, m) fopen64(f, m) + #define MZ_FCLOSE fclose + #define MZ_FREAD fread + #define MZ_FWRITE fwrite + #define MZ_FTELL64 ftello64 + #define MZ_FSEEK64 fseeko64 + #define MZ_FILE_STAT_STRUCT stat64 + #define MZ_FILE_STAT stat64 + #define MZ_FFLUSH fflush + #define MZ_FREOPEN(p, m, s) freopen64(p, m, s) + #define MZ_DELETE_FILE remove + #else + #ifndef MINIZ_NO_TIME + #include + #endif + #define MZ_FILE FILE + #define MZ_FOPEN(f, m) fopen(f, m) + #define MZ_FCLOSE fclose + #define MZ_FREAD fread + #define MZ_FWRITE fwrite + #define MZ_FTELL64 ftello + #define MZ_FSEEK64 fseeko + #define MZ_FILE_STAT_STRUCT stat + #define MZ_FILE_STAT stat + #define MZ_FFLUSH fflush + #define MZ_FREOPEN(f, m, s) freopen(f, m, s) + #define MZ_DELETE_FILE remove + #endif // #ifdef _MSC_VER +#endif // #ifdef MINIZ_NO_STDIO + +#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) + +// Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. +enum +{ + // ZIP archive identifiers and record sizes + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, + // Central directory header record offsets + MZ_ZIP_CDH_SIG_OFS = 0, MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, MZ_ZIP_CDH_BIT_FLAG_OFS = 8, + MZ_ZIP_CDH_METHOD_OFS = 10, MZ_ZIP_CDH_FILE_TIME_OFS = 12, MZ_ZIP_CDH_FILE_DATE_OFS = 14, MZ_ZIP_CDH_CRC32_OFS = 16, + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, + MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, MZ_ZIP_CDH_DISK_START_OFS = 34, MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, + // Local directory header offsets + MZ_ZIP_LDH_SIG_OFS = 0, MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, MZ_ZIP_LDH_BIT_FLAG_OFS = 6, MZ_ZIP_LDH_METHOD_OFS = 8, MZ_ZIP_LDH_FILE_TIME_OFS = 10, + MZ_ZIP_LDH_FILE_DATE_OFS = 12, MZ_ZIP_LDH_CRC32_OFS = 14, MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, + MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, + // End of central directory offsets + MZ_ZIP_ECDH_SIG_OFS = 0, MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, +}; + +typedef struct +{ + void *m_p; + size_t m_size, m_capacity; + mz_uint m_element_size; +} mz_zip_array; + +struct mz_zip_internal_state_tag +{ + mz_zip_array m_central_dir; + mz_zip_array m_central_dir_offsets; + mz_zip_array m_sorted_central_dir_offsets; + MZ_FILE *m_pFile; + void *m_pMem; + size_t m_mem_size; + size_t m_mem_capacity; +}; + +#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] + +static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) +{ + pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); + memset(pArray, 0, sizeof(mz_zip_array)); +} + +static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) +{ + void *pNew_p; size_t new_capacity = min_new_capacity; MZ_ASSERT(pArray->m_element_size); if (pArray->m_capacity >= min_new_capacity) return MZ_TRUE; + if (growing) { new_capacity = MZ_MAX(1, pArray->m_capacity); while (new_capacity < min_new_capacity) new_capacity *= 2; } + if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) return MZ_FALSE; + pArray->m_p = pNew_p; pArray->m_capacity = new_capacity; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) +{ + if (new_capacity > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return MZ_FALSE; } + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) +{ + if (new_size > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return MZ_FALSE; } + pArray->m_size = new_size; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) +{ + return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) +{ + size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE; + memcpy((mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); + return MZ_TRUE; +} + +#ifndef MINIZ_NO_TIME +static time_t mz_zip_dos_to_time_t(int dos_time, int dos_date) +{ + struct tm tm; + memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1; + tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; tm.tm_mon = ((dos_date >> 5) & 15) - 1; tm.tm_mday = dos_date & 31; + tm.tm_hour = (dos_time >> 11) & 31; tm.tm_min = (dos_time >> 5) & 63; tm.tm_sec = (dos_time << 1) & 62; + return mktime(&tm); +} + +static void mz_zip_time_to_dos_time(time_t time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) +{ +#ifdef _MSC_VER + struct tm tm_struct; + struct tm *tm = &tm_struct; + errno_t err = localtime_s(tm, &time); + if (err) + { + *pDOS_date = 0; *pDOS_time = 0; + return; + } +#else + struct tm *tm = localtime(&time); +#endif + *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); + *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); +} +#endif + +#ifndef MINIZ_NO_STDIO +static mz_bool mz_zip_get_file_modified_time(const char *pFilename, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) +{ +#ifdef MINIZ_NO_TIME + (void)pFilename; *pDOS_date = *pDOS_time = 0; +#else + struct MZ_FILE_STAT_STRUCT file_stat; + // On Linux with x86 glibc, this call will fail on large files (>= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. + if (MZ_FILE_STAT(pFilename, &file_stat) != 0) + return MZ_FALSE; + mz_zip_time_to_dos_time(file_stat.st_mtime, pDOS_time, pDOS_date); +#endif // #ifdef MINIZ_NO_TIME + return MZ_TRUE; +} + +#ifndef MINIZ_NO_TIME +static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time, time_t modified_time) +{ + struct utimbuf t; t.actime = access_time; t.modtime = modified_time; + return !utime(pFilename, &t); +} +#endif // #ifndef MINIZ_NO_TIME +#endif // #ifndef MINIZ_NO_STDIO + +static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint32 flags) +{ + (void)flags; + if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return MZ_FALSE; + + if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; + if (!pZip->m_pFree) pZip->m_pFree = def_free_func; + if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; + + pZip->m_zip_mode = MZ_ZIP_MODE_READING; + pZip->m_archive_size = 0; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return MZ_FALSE; + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; pR++; + } + return (pL == pE) ? (l_len < r_len) : (l < r); +} + +#define MZ_SWAP_UINT32(a, b) do { mz_uint32 t = a; a = b; b = t; } MZ_MACRO_END + +// Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) +static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const int size = pZip->m_total_files; + int start = (size - 2) >> 1, end; + while (start >= 0) + { + int child, root = start; + for ( ; ; ) + { + if ((child = (root << 1) + 1) >= size) + break; + child += (((child + 1) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1]))); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; + } + start--; + } + + end = size - 1; + while (end > 0) + { + int child, root = 0; + MZ_SWAP_UINT32(pIndices[end], pIndices[0]); + for ( ; ; ) + { + if ((child = (root << 1) + 1) >= end) + break; + child += (((child + 1) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1])); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; + } + end--; + } +} + +static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint32 flags) +{ + mz_uint cdir_size, num_this_disk, cdir_disk_index; + mz_uint64 cdir_ofs; + mz_int64 cur_file_ofs; + const mz_uint8 *p; + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); + // Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. + if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return MZ_FALSE; + // Find the end of central directory record by scanning the file from the end towards the beginning. + cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); + for ( ; ; ) + { + int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) + return MZ_FALSE; + for (i = n - 4; i >= 0; --i) + if (MZ_READ_LE32(pBuf + i) == MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) + break; + if (i >= 0) + { + cur_file_ofs += i; + break; + } + if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (0xFFFF + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) + return MZ_FALSE; + cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); + } + // Read and verify the end of central directory record. + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return MZ_FALSE; + if ((MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) || + ((pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS)) != MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS))) + return MZ_FALSE; + + num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); + cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); + if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) + return MZ_FALSE; + + if ((cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS)) < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) + return MZ_FALSE; + + cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); + if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) + return MZ_FALSE; + + pZip->m_central_directory_file_ofs = cdir_ofs; + + if (pZip->m_total_files) + { + mz_uint i, n; + + // Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and another to hold the sorted indices. + if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || + (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) + return MZ_FALSE; + + if (sort_central_dir) + { + if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) + return MZ_FALSE; + } + + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) + return MZ_FALSE; + + // Now create an index into the central directory file records, do some basic sanity checking on each record, and check for zip64 entries (which are not yet supported). + p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; + for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) + { + mz_uint total_header_size, comp_size, decomp_size, disk_index; + if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) + return MZ_FALSE; + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); + if (sort_central_dir) + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) || (comp_size == 0xFFFFFFFF)) + return MZ_FALSE; + disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); + if ((disk_index != num_this_disk) && (disk_index != 1)) + return MZ_FALSE; + if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) + return MZ_FALSE; + if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) + return MZ_FALSE; + n -= total_header_size; p += total_header_size; + } + } + + if (sort_central_dir) + mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags) +{ + if ((!pZip) || (!pZip->m_pRead)) + return MZ_FALSE; + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + pZip->m_archive_size = size; + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end(pZip); + return MZ_FALSE; + } + return MZ_TRUE; +} + +static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); + memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); + return s; +} + +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags) +{ + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + pZip->m_archive_size = size; + pZip->m_pRead = mz_zip_mem_read_func; + pZip->m_pIO_opaque = pZip; +#ifdef __cplusplus + pZip->m_pState->m_pMem = const_cast(pMem); +#else + pZip->m_pState->m_pMem = (void *)pMem; +#endif + pZip->m_pState->m_mem_size = size; + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end(pZip); + return MZ_FALSE; + } + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) +{ + mz_uint64 file_size; + MZ_FILE *pFile = MZ_FOPEN(pFilename, "rb"); + if (!pFile) + return MZ_FALSE; + if (MZ_FSEEK64(pFile, 0, SEEK_END)) + { + MZ_FCLOSE(pFile); + return MZ_FALSE; + } + file_size = MZ_FTELL64(pFile); + if (!mz_zip_reader_init_internal(pZip, flags)) + { + MZ_FCLOSE(pFile); + return MZ_FALSE; + } + pZip->m_pRead = mz_zip_file_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = file_size; + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end(pZip); + return MZ_FALSE; + } + return MZ_TRUE; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_total_files : 0; +} + +static MZ_FORCEINLINE const mz_uint8 *mz_zip_reader_get_cdh(mz_zip_archive *pZip, mz_uint file_index) +{ + if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return NULL; + return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); +} + +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint m_bit_flag; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if (!p) + return MZ_FALSE; + m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + return (m_bit_flag & 1); +} + +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint filename_len, external_attr; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if (!p) + return MZ_FALSE; + + // First see if the filename ends with a '/' character. + filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_len) + { + if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') + return MZ_TRUE; + } + + // Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. + // Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. + // FIXME: Remove this check? Is it necessary - we already check the filename. + external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + if ((external_attr & 0x10) != 0) + return MZ_TRUE; + + return MZ_FALSE; +} + +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) +{ + mz_uint n; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if ((!p) || (!pStat)) + return MZ_FALSE; + + // Unpack the central directory record. + pStat->m_file_index = file_index; + pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); + pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); + pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); + pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); +#ifndef MINIZ_NO_TIME + pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); +#endif + pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); + pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); + pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + + // Copy as much of the filename and comment as possible. + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); + memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pStat->m_filename[n] = '\0'; + + n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); + pStat->m_comment_size = n; + memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); pStat->m_comment[n] = '\0'; + + return MZ_TRUE; +} + +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) +{ + mz_uint n; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if (!p) { if (filename_buf_size) pFilename[0] = '\0'; return 0; } + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_buf_size) + { + n = MZ_MIN(n, filename_buf_size - 1); + memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pFilename[n] = '\0'; + } + return n + 1; +} + +static MZ_FORCEINLINE mz_bool mz_zip_reader_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) +{ + mz_uint i; + if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) + return 0 == memcmp(pA, pB, len); + for (i = 0; i < len; ++i) + if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) + return MZ_FALSE; + return MZ_TRUE; +} + +static MZ_FORCEINLINE int mz_zip_reader_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; pR++; + } + return (pL == pE) ? (int)(l_len - r_len) : (l - r); +} + +static int mz_zip_reader_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const int size = pZip->m_total_files; + const mz_uint filename_len = (mz_uint)strlen(pFilename); + int l = 0, h = size - 1; + while (l <= h) + { + int m = (l + h) >> 1, file_index = pIndices[m], comp = mz_zip_reader_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); + if (!comp) + return file_index; + else if (comp < 0) + l = m + 1; + else + h = m - 1; + } + return -1; +} + +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) +{ + mz_uint file_index; size_t name_len, comment_len; + if ((!pZip) || (!pZip->m_pState) || (!pName) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return -1; + if (((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) + return mz_zip_reader_locate_file_binary_search(pZip, pName); + name_len = strlen(pName); if (name_len > 0xFFFF) return -1; + comment_len = pComment ? strlen(pComment) : 0; if (comment_len > 0xFFFF) return -1; + for (file_index = 0; file_index < pZip->m_total_files; file_index++) + { + const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); + mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); + const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + if (filename_len < name_len) + continue; + if (comment_len) + { + mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); + const char *pFile_comment = pFilename + filename_len + file_extra_len; + if ((file_comment_len != comment_len) || (!mz_zip_reader_string_equal(pComment, pFile_comment, file_comment_len, flags))) + continue; + } + if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) + { + int ofs = filename_len - 1; + do + { + if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) + break; + } while (--ofs >= 0); + ofs++; + pFilename += ofs; filename_len -= ofs; + } + if ((filename_len == name_len) && (mz_zip_reader_string_equal(pName, pFilename, filename_len, flags))) + return file_index; + } + return -1; +} + +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + int status = TINFL_STATUS_DONE; + mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + tinfl_decompressor inflator; + + if ((buf_size) && (!pBuf)) + return MZ_FALSE; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes) + if (!file_stat.m_comp_size) + return MZ_TRUE; + + // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers). + // I'm torn how to handle this case - should it fail instead? + if (mz_zip_reader_is_file_a_directory(pZip, file_index)) + return MZ_TRUE; + + // Encryption and patch files are not supported. + if (file_stat.m_bit_flag & (1 | 32)) + return MZ_FALSE; + + // This function only supports stored and deflate. + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return MZ_FALSE; + + // Ensure supplied output buffer is large enough. + needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; + if (buf_size < needed_size) + return MZ_FALSE; + + // Read and parse the local directory entry. + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return MZ_FALSE; + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return MZ_FALSE; + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return MZ_FALSE; + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + // The file is stored or the caller has requested the compressed data. + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) + return MZ_FALSE; + return ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) != 0) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) == file_stat.m_crc32); + } + + // Decompress the file either directly from memory or from a file input buffer. + tinfl_init(&inflator); + + if (pZip->m_pState->m_pMem) + { + // Read directly from the archive in memory. + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else if (pUser_read_buf) + { + // Use a user provided read buffer. + if (!user_read_buf_size) + return MZ_FALSE; + pRead_buf = (mz_uint8 *)pUser_read_buf; + read_buf_size = user_read_buf_size; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + else + { + // Temporarily allocate a read buffer. + read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); +#ifdef _MSC_VER + if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) +#else + if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) +#endif + return MZ_FALSE; + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return MZ_FALSE; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + do + { + size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + out_buf_ofs += out_buf_size; + } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); + + if (status == TINFL_STATUS_DONE) + { + // Make sure the entire file was decompressed, and check its CRC. + if ((out_buf_ofs != file_stat.m_uncomp_size) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32)) + status = TINFL_STATUS_FAILED; + } + + if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); + if (file_index < 0) + return MZ_FALSE; + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); +} + +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); +} + +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); +} + +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) +{ + mz_uint64 comp_size, uncomp_size, alloc_size; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + void *pBuf; + + if (pSize) + *pSize = 0; + if (!p) + return NULL; + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + + alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; +#ifdef _MSC_VER + if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) +#else + if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) +#endif + return NULL; + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) + return NULL; + + if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return NULL; + } + + if (pSize) *pSize = (size_t)alloc_size; + return pBuf; +} + +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) +{ + int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); + if (file_index < 0) + { + if (pSize) *pSize = 0; + return MZ_FALSE; + } + return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); +} + +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + int status = TINFL_STATUS_DONE; mz_uint file_crc32 = MZ_CRC32_INIT; + mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf = NULL; void *pWrite_buf = NULL; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes) + if (!file_stat.m_comp_size) + return MZ_TRUE; + + // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers). + // I'm torn how to handle this case - should it fail instead? + if (mz_zip_reader_is_file_a_directory(pZip, file_index)) + return MZ_TRUE; + + // Encryption and patch files are not supported. + if (file_stat.m_bit_flag & (1 | 32)) + return MZ_FALSE; + + // This function only supports stored and deflate. + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return MZ_FALSE; + + // Read and parse the local directory entry. + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return MZ_FALSE; + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return MZ_FALSE; + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return MZ_FALSE; + + // Decompress the file either directly from memory or from a file input buffer. + if (pZip->m_pState->m_pMem) + { + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return MZ_FALSE; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + // The file is stored or the caller has requested the compressed data. + if (pZip->m_pState->m_pMem) + { +#ifdef _MSC_VER + if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) +#else + if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) +#endif + return MZ_FALSE; + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) + status = TINFL_STATUS_FAILED; + else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); + cur_file_ofs += file_stat.m_comp_size; + out_buf_ofs += file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + while (comp_remaining) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + break; + } + + if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + out_buf_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + } + } + } + else + { + tinfl_decompressor inflator; + tinfl_init(&inflator); + + if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) + status = TINFL_STATUS_FAILED; + else + { + do + { + mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + + if (out_buf_size) + { + if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) + { + status = TINFL_STATUS_FAILED; + break; + } + file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); + if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) + { + status = TINFL_STATUS_FAILED; + break; + } + } + } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); + } + } + + if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + { + // Make sure the entire file was decompressed, and check its CRC. + if ((out_buf_ofs != file_stat.m_uncomp_size) || (file_crc32 != file_stat.m_crc32)) + status = TINFL_STATUS_FAILED; + } + + if (!pZip->m_pState->m_pMem) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + if (pWrite_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); + if (file_index < 0) + return MZ_FALSE; + return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) +{ + (void)ofs; return MZ_FWRITE(pBuf, 1, n, (MZ_FILE*)pOpaque); +} + +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) +{ + mz_bool status; + mz_zip_archive_file_stat file_stat; + MZ_FILE *pFile; + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + pFile = MZ_FOPEN(pDst_filename, "wb"); + if (!pFile) + return MZ_FALSE; + status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); + if (MZ_FCLOSE(pFile) == EOF) + return MZ_FALSE; +#ifndef MINIZ_NO_TIME + if (status) + mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); +#endif + return status; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_bool mz_zip_reader_end(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return MZ_FALSE; + + if (pZip->m_pState) + { + mz_zip_internal_state *pState = pZip->m_pState; pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + MZ_FCLOSE(pState->m_pFile); + pState->m_pFile = NULL; + } +#endif // #ifndef MINIZ_NO_STDIO + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + } + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) +{ + int file_index = mz_zip_reader_locate_file(pZip, pArchive_filename, NULL, flags); + if (file_index < 0) + return MZ_FALSE; + return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); +} +#endif + +// ------------------- .ZIP archive writing + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +static void mz_write_le16(mz_uint8 *p, mz_uint16 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); } +static void mz_write_le32(mz_uint8 *p, mz_uint32 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); p[2] = (mz_uint8)(v >> 16); p[3] = (mz_uint8)(v >> 24); } +#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) +#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) + +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) +{ + if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return MZ_FALSE; + + if (pZip->m_file_offset_alignment) + { + // Ensure user specified file offset alignment is a power of 2. + if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) + return MZ_FALSE; + } + + if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; + if (!pZip->m_pFree) pZip->m_pFree = def_free_func; + if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + pZip->m_archive_size = existing_size; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return MZ_FALSE; + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + return MZ_TRUE; +} + +static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); +#ifdef _MSC_VER + if ((!n) || ((0, sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) +#else + if ((!n) || ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) +#endif + return 0; + if (new_size > pState->m_mem_capacity) + { + void *pNew_block; + size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); while (new_capacity < new_size) new_capacity *= 2; + if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) + return 0; + pState->m_pMem = pNew_block; pState->m_mem_capacity = new_capacity; + } + memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); + pState->m_mem_size = (size_t)new_size; + return n; +} + +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) +{ + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pIO_opaque = pZip; + if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) + return MZ_FALSE; + if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) + { + if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) + { + mz_zip_writer_end(pZip); + return MZ_FALSE; + } + pZip->m_pState->m_mem_capacity = initial_allocation_size; + } + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) +{ + MZ_FILE *pFile; + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pIO_opaque = pZip; + if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) + return MZ_FALSE; + if (NULL == (pFile = MZ_FOPEN(pFilename, "wb"))) + { + mz_zip_writer_end(pZip); + return MZ_FALSE; + } + pZip->m_pState->m_pFile = pFile; + if (size_to_reserve_at_beginning) + { + mz_uint64 cur_ofs = 0; char buf[4096]; MZ_CLEAR_OBJ(buf); + do + { + size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) + { + mz_zip_writer_end(pZip); + return MZ_FALSE; + } + cur_ofs += n; size_to_reserve_at_beginning -= n; + } while (size_to_reserve_at_beginning); + } + return MZ_TRUE; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) +{ + mz_zip_internal_state *pState; + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return MZ_FALSE; + // No sense in trying to write to an archive that's already at the support max size + if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) + return MZ_FALSE; + + pState = pZip->m_pState; + + if (pState->m_pFile) + { +#ifdef MINIZ_NO_STDIO + pFilename; return MZ_FALSE; +#else + // Archive is being read from stdio - try to reopen as writable. + if (pZip->m_pIO_opaque != pZip) + return MZ_FALSE; + if (!pFilename) + return MZ_FALSE; + pZip->m_pWrite = mz_zip_file_write_func; + if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) + { + // The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. + mz_zip_reader_end(pZip); + return MZ_FALSE; + } +#endif // #ifdef MINIZ_NO_STDIO + } + else if (pState->m_pMem) + { + // Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. + if (pZip->m_pIO_opaque != pZip) + return MZ_FALSE; + pState->m_mem_capacity = pState->m_mem_size; + pZip->m_pWrite = mz_zip_heap_write_func; + } + // Archive is being read via a user provided read function - make sure the user has specified a write function too. + else if (!pZip->m_pWrite) + return MZ_FALSE; + + // Start writing new files at the archive's current central directory location. + pZip->m_archive_size = pZip->m_central_directory_file_ofs; + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + pZip->m_central_directory_file_ofs = 0; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) +{ + return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); +} + +typedef struct +{ + mz_zip_archive *m_pZip; + mz_uint64 m_cur_archive_file_ofs; + mz_uint64 m_comp_size; +} mz_zip_writer_add_state; + +static mz_bool mz_zip_writer_add_put_buf_callback(const void* pBuf, int len, void *pUser) +{ + mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; + if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) + return MZ_FALSE; + pState->m_cur_archive_file_ofs += len; + pState->m_comp_size += len; + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, comp_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, uncomp_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, comp_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, uncomp_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_header_ofs); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) +{ + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; + size_t orig_central_dir_size = pState->m_central_dir.m_size; + mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + + // No zip64 support yet + if ((local_header_ofs > 0xFFFFFFFF) || (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + comment_size) > 0xFFFFFFFF)) + return MZ_FALSE; + + if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, extra_size, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) + return MZ_FALSE; + + if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) + { + // Try to push the central directory array back into its original state. + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) +{ + // Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. + if (*pArchive_name == '/') + return MZ_FALSE; + while (*pArchive_name) + { + if ((*pArchive_name == '\\') || (*pArchive_name == ':')) + return MZ_FALSE; + pArchive_name++; + } + return MZ_TRUE; +} + +static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) +{ + mz_uint32 n; + if (!pZip->m_file_offset_alignment) + return 0; + n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); + return (pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1); +} + +static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) +{ + char buf[4096]; + memset(buf, 0, MZ_MIN(sizeof(buf), n)); + while (n) + { + mz_uint32 s = MZ_MIN(sizeof(buf), n); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) + return MZ_FALSE; + cur_file_ofs += s; n -= s; + } + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) +{ + mz_uint16 method = 0, dos_time = 0, dos_date = 0; + mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; + mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + tdefl_compressor *pComp = NULL; + mz_bool store_data_uncompressed; + mz_zip_internal_state *pState; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + level = level_and_flags & 0xF; + store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (pZip->m_total_files == 0xFFFF) || (level > MZ_UBER_COMPRESSION)) + return MZ_FALSE; + + pState = pZip->m_pState; + + if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) + return MZ_FALSE; + // No zip64 support yet + if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) + return MZ_FALSE; + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return MZ_FALSE; + +#ifndef MINIZ_NO_TIME + { + time_t cur_time; time(&cur_time); + mz_zip_time_to_dos_time(cur_time, &dos_time, &dos_date); + } +#endif // #ifndef MINIZ_NO_TIME + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > 0xFFFF) + return MZ_FALSE; + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + // no zip64 support yet + if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF)) + return MZ_FALSE; + + if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) + { + // Set DOS Subdirectory attribute bit. + ext_attributes |= 0x10; + // Subdirectories cannot contain data. + if ((buf_size) || (uncomp_size)) + return MZ_FALSE; + } + + // Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) + if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size)) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) + return MZ_FALSE; + + if ((!store_data_uncompressed) && (buf_size)) + { + if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) + return MZ_FALSE; + } + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } + cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); + + MZ_CLEAR_OBJ(local_dir_header); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + cur_archive_file_ofs += archive_name_size; + + if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8*)pBuf, buf_size); + uncomp_size = buf_size; + if (uncomp_size <= 3) + { + level = 0; + store_data_uncompressed = MZ_TRUE; + } + } + + if (store_data_uncompressed) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + + cur_archive_file_ofs += buf_size; + comp_size = buf_size; + + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + method = MZ_DEFLATED; + } + else if (buf_size) + { + mz_zip_writer_add_state state; + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || + (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + + method = MZ_DEFLATED; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pComp = NULL; + + // no zip64 support yet + if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) + return MZ_FALSE; + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) + return MZ_FALSE; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return MZ_FALSE; + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +{ + mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; + mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; + mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + MZ_FILE *pSrc_file = NULL; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + level = level_and_flags & 0xF; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return MZ_FALSE; + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + return MZ_FALSE; + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return MZ_FALSE; + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > 0xFFFF) + return MZ_FALSE; + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + // no zip64 support yet + if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF)) + return MZ_FALSE; + + if (!mz_zip_get_file_modified_time(pSrc_filename, &dos_time, &dos_date)) + return MZ_FALSE; + + pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); + if (!pSrc_file) + return MZ_FALSE; + MZ_FSEEK64(pSrc_file, 0, SEEK_END); + uncomp_size = MZ_FTELL64(pSrc_file); + MZ_FSEEK64(pSrc_file, 0, SEEK_SET); + + if (uncomp_size > 0xFFFFFFFF) + { + // No zip64 support yet + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + if (uncomp_size <= 3) + level = 0; + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) + { + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } + cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); + + MZ_CLEAR_OBJ(local_dir_header); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + cur_archive_file_ofs += archive_name_size; + + if (uncomp_size) + { + mz_uint64 uncomp_remaining = uncomp_size; + void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); + if (!pRead_buf) + { + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + if (!level) + { + while (uncomp_remaining) + { + mz_uint n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining); + if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); + uncomp_remaining -= n; + cur_archive_file_ofs += n; + } + comp_size = uncomp_size; + } + else + { + mz_bool result = MZ_FALSE; + mz_zip_writer_add_state state; + tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + for ( ; ; ) + { + size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, MZ_ZIP_MAX_IO_BUF_SIZE); + tdefl_status status; + + if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size) + break; + + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size); + uncomp_remaining -= in_buf_size; + + status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH); + if (status == TDEFL_STATUS_DONE) + { + result = MZ_TRUE; + break; + } + else if (status != TDEFL_STATUS_OKAY) + break; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + + if (!result) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + + method = MZ_DEFLATED; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + } + + MZ_FCLOSE(pSrc_file); pSrc_file = NULL; + + // no zip64 support yet + if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) + return MZ_FALSE; + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) + return MZ_FALSE; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return MZ_FALSE; + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index) +{ + mz_uint n, bit_flags, num_alignment_padding_bytes; + mz_uint64 comp_bytes_remaining, local_dir_header_ofs; + mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + mz_uint8 central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + size_t orig_central_dir_size; + mz_zip_internal_state *pState; + void *pBuf; const mz_uint8 *pSrc_central_header; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return MZ_FALSE; + if (NULL == (pSrc_central_header = mz_zip_reader_get_cdh(pSource_zip, file_index))) + return MZ_FALSE; + pState = pZip->m_pState; + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + // no zip64 support yet + if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) + return MZ_FALSE; + + cur_src_file_ofs = MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + cur_dst_file_ofs = pZip->m_archive_size; + + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return MZ_FALSE; + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return MZ_FALSE; + cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) + return MZ_FALSE; + cur_dst_file_ofs += num_alignment_padding_bytes; + local_dir_header_ofs = cur_dst_file_ofs; + if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return MZ_FALSE; + cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + n = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + comp_bytes_remaining = n + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(sizeof(mz_uint32) * 4, MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining))))) + return MZ_FALSE; + + while (comp_bytes_remaining) + { + n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining); + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + cur_src_file_ofs += n; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + cur_dst_file_ofs += n; + + comp_bytes_remaining -= n; + } + + bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + if (bit_flags & 8) + { + // Copy data descriptor + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + + n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == 0x08074b50) ? 4 : 3); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + + cur_src_file_ofs += n; + cur_dst_file_ofs += n; + } + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + + // no zip64 support yet + if (cur_dst_file_ofs > 0xFFFFFFFF) + return MZ_FALSE; + + orig_central_dir_size = pState->m_central_dir.m_size; + + memcpy(central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + return MZ_FALSE; + + n = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return MZ_FALSE; + } + + if (pState->m_central_dir.m_size > 0xFFFFFFFF) + return MZ_FALSE; + n = (mz_uint32)orig_central_dir_size; + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return MZ_FALSE; + } + + pZip->m_total_files++; + pZip->m_archive_size = cur_dst_file_ofs; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState; + mz_uint64 central_dir_ofs, central_dir_size; + mz_uint8 hdr[MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE]; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return MZ_FALSE; + + pState = pZip->m_pState; + + // no zip64 support yet + if ((pZip->m_total_files > 0xFFFF) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) + return MZ_FALSE; + + central_dir_ofs = 0; + central_dir_size = 0; + if (pZip->m_total_files) + { + // Write central directory + central_dir_ofs = pZip->m_archive_size; + central_dir_size = pState->m_central_dir.m_size; + pZip->m_central_directory_file_ofs = central_dir_ofs; + if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) + return MZ_FALSE; + pZip->m_archive_size += central_dir_size; + } + + // Write end of central directory record + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, central_dir_size); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, central_dir_ofs); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, sizeof(hdr)) != sizeof(hdr)) + return MZ_FALSE; +#ifndef MINIZ_NO_STDIO + if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) + return MZ_FALSE; +#endif // #ifndef MINIZ_NO_STDIO + + pZip->m_archive_size += sizeof(hdr); + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize) +{ + if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pSize)) + return MZ_FALSE; + if (pZip->m_pWrite != mz_zip_heap_write_func) + return MZ_FALSE; + if (!mz_zip_writer_finalize_archive(pZip)) + return MZ_FALSE; + + *pBuf = pZip->m_pState->m_pMem; + *pSize = pZip->m_pState->m_mem_size; + pZip->m_pState->m_pMem = NULL; + pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; + return MZ_TRUE; +} + +mz_bool mz_zip_writer_end(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState; + mz_bool status = MZ_TRUE; + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) + return MZ_FALSE; + + pState = pZip->m_pState; + pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + MZ_FCLOSE(pState->m_pFile); + pState->m_pFile = NULL; + } +#endif // #ifndef MINIZ_NO_STDIO + + if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); + pState->m_pMem = NULL; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + return status; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +{ + mz_bool status, created_new_archive = MZ_FALSE; + mz_zip_archive zip_archive; + struct MZ_FILE_STAT_STRUCT file_stat; + MZ_CLEAR_OBJ(zip_archive); + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) + return MZ_FALSE; + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return MZ_FALSE; + if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) + { + // Create a new archive. + if (!mz_zip_writer_init_file(&zip_archive, pZip_filename, 0)) + return MZ_FALSE; + created_new_archive = MZ_TRUE; + } + else + { + // Append to an existing archive. + if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) + return MZ_FALSE; + if (!mz_zip_writer_init_from_reader(&zip_archive, pZip_filename)) + { + mz_zip_reader_end(&zip_archive); + return MZ_FALSE; + } + } + status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); + // Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) + if (!mz_zip_writer_finalize_archive(&zip_archive)) + status = MZ_FALSE; + if (!mz_zip_writer_end(&zip_archive)) + status = MZ_FALSE; + if ((!status) && (created_new_archive)) + { + // It's a new archive and something went wrong, so just delete it. + int ignoredStatus = MZ_DELETE_FILE(pZip_filename); + (void)ignoredStatus; + } + return status; +} + +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) +{ + int file_index; + mz_zip_archive zip_archive; + void *p = NULL; + + if (pSize) + *pSize = 0; + + if ((!pZip_filename) || (!pArchive_name)) + return NULL; + + MZ_CLEAR_OBJ(zip_archive); + if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) + return NULL; + + if ((file_index = mz_zip_reader_locate_file(&zip_archive, pArchive_name, NULL, flags)) >= 0) + p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); + + mz_zip_reader_end(&zip_archive); + return p; +} + +#endif // #ifndef MINIZ_NO_STDIO + +#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +#endif // #ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +} +#endif + +#endif // MINIZ_HEADER_FILE_ONLY + +/* + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to +*/ diff --git a/dist/bin/esptool/flasher_stub/run_tests_with_stub.sh b/dist/bin/esptool/flasher_stub/run_tests_with_stub.sh new file mode 100644 index 000000000..61b1fa183 --- /dev/null +++ b/dist/bin/esptool/flasher_stub/run_tests_with_stub.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# +# Run test/test_esptool.py using the newly compiled stub, for quick tests +# +# Usage same as test/test_esptool.py +[ -z $PYTHON ] && PYTHON=python + +THISDIR=`realpath -m $(dirname $0)` + +export ESPTOOL_PY="${THISDIR}/esptool_test_stub.py" +${PYTHON} ${THISDIR}/../test/test_esptool.py $@ diff --git a/dist/bin/esptool/flasher_stub/slip.c b/dist/bin/esptool/flasher_stub/slip.c new file mode 100644 index 000000000..74c4964a0 --- /dev/null +++ b/dist/bin/esptool/flasher_stub/slip.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2016 Cesanta Software Limited & Espressif Systems (Shanghai) PTE LTD + * All rights reserved + * + * 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. + */ + +#include "rom_functions.h" +#include "slip.h" + +void SLIP_send_frame_delimiter(void) { + uart_tx_one_char('\xc0'); +} + +void SLIP_send_frame_data(char ch) { + if(ch == '\xc0') { + uart_tx_one_char('\xdb'); + uart_tx_one_char('\xdc'); + } else if (ch == '\xdb') { + uart_tx_one_char('\xdb'); + uart_tx_one_char('\xdd'); + } else { + uart_tx_one_char(ch); + } +} + +void SLIP_send_frame_data_buf(const void *buf, uint32_t size) { + const uint8_t *buf_c = (const uint8_t *)buf; + for(int i = 0; i < size; i++) { + SLIP_send_frame_data(buf_c[i]); + } +} + +void SLIP_send(const void *pkt, uint32_t size) { + SLIP_send_frame_delimiter(); + SLIP_send_frame_data_buf(pkt, size); + SLIP_send_frame_delimiter(); +} + +int16_t SLIP_recv_byte(char byte, slip_state_t *state) +{ + if (byte == '\xc0') { + if (*state == SLIP_NO_FRAME) { + *state = SLIP_FRAME; + return SLIP_NO_BYTE; + } else { + *state = SLIP_NO_FRAME; + return SLIP_FINISHED_FRAME; + } + } + + switch(*state) { + case SLIP_NO_FRAME: + return SLIP_NO_BYTE; + case SLIP_FRAME: + if (byte == '\xdb') { + *state = SLIP_FRAME_ESCAPING; + return SLIP_NO_BYTE; + } + return byte; + case SLIP_FRAME_ESCAPING: + if (byte == '\xdc') { + *state = SLIP_FRAME; + return '\xc0'; + } + if (byte == '\xdd') { + *state = SLIP_FRAME; + return '\xdb'; + } + return SLIP_NO_BYTE; /* actually a framing error */ + } + return SLIP_NO_BYTE; /* actually a framing error */ +} + +uint32_t SLIP_recv(void *pkt, uint32_t max_len) { + uint32_t len = 0; + slip_state_t state = SLIP_NO_FRAME; + uint8_t *p = (uint8_t *) pkt; + + int16_t r; + do { + r = SLIP_recv_byte(uart_rx_one_char_block(), &state); + if(r >= 0 && len < max_len) { + p[len++] = (uint8_t)r; + } + } while(r != SLIP_FINISHED_FRAME); + + return len; +} diff --git a/dist/bin/esptool/flasher_stub/stub_commands.c b/dist/bin/esptool/flasher_stub/stub_commands.c new file mode 100644 index 000000000..2c8f3020d --- /dev/null +++ b/dist/bin/esptool/flasher_stub/stub_commands.c @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2016-2019 Espressif Systems (Shanghai) PTE LTD + * All rights reserved + * + * 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. + */ +#include +#include "stub_commands.h" +#include "stub_flasher.h" +#include "rom_functions.h" +#include "slip.h" +#include "soc_support.h" + +int handle_flash_erase(uint32_t addr, uint32_t len) { + if (addr % FLASH_SECTOR_SIZE != 0) return 0x32; + if (len % FLASH_SECTOR_SIZE != 0) return 0x33; + if (SPIUnlock() != 0) return 0x34; + + while (len > 0 && (addr % FLASH_BLOCK_SIZE != 0)) { + if (SPIEraseSector(addr / FLASH_SECTOR_SIZE) != 0) return 0x35; + len -= FLASH_SECTOR_SIZE; + addr += FLASH_SECTOR_SIZE; + } + + while (len > FLASH_BLOCK_SIZE) { + if (SPIEraseBlock(addr / FLASH_BLOCK_SIZE) != 0) return 0x36; + len -= FLASH_BLOCK_SIZE; + addr += FLASH_BLOCK_SIZE; + } + + while (len > 0) { + if (SPIEraseSector(addr / FLASH_SECTOR_SIZE) != 0) return 0x37; + len -= FLASH_SECTOR_SIZE; + addr += FLASH_SECTOR_SIZE; + } + + return 0; +} + +void handle_flash_read(uint32_t addr, uint32_t len, uint32_t block_size, + uint32_t max_in_flight) { + uint8_t buf[FLASH_SECTOR_SIZE]; + uint8_t digest[16]; + struct MD5Context ctx; + uint32_t num_sent = 0, num_acked = 0; + + /* This is one routine where we still do synchronous I/O */ + ets_isr_mask(1 << ETS_UART0_INUM); + + if (block_size > sizeof(buf)) { + return; + } + MD5Init(&ctx); + while (num_acked < len && num_acked <= num_sent) { + while (num_sent < len && num_sent - num_acked < max_in_flight) { + uint32_t n = len - num_sent; + if (n > block_size) n = block_size; + if (SPIRead(addr, (uint32_t *)buf, n) != 0) { + break; + } + SLIP_send(buf, n); + MD5Update(&ctx, buf, n); + addr += n; + num_sent += n; + } + int r = SLIP_recv(&num_acked, sizeof(num_acked)); + if (r != 4) { + break; + } + } + MD5Final(digest, &ctx); + SLIP_send(digest, sizeof(digest)); + + /* Go back to async UART */ + ets_isr_unmask(1 << ETS_UART0_INUM); +} + +int handle_flash_get_md5sum(uint32_t addr, uint32_t len) { + uint8_t buf[FLASH_SECTOR_SIZE]; + uint8_t digest[16]; + struct MD5Context ctx; + MD5Init(&ctx); + while (len > 0) { + uint32_t n = len; + if (n > FLASH_SECTOR_SIZE) { + n = FLASH_SECTOR_SIZE; + } + if (SPIRead(addr, (uint32_t *)buf, n) != 0) { + return 0x63; + } + MD5Update(&ctx, buf, n); + addr += n; + len -= n; + } + MD5Final(digest, &ctx); + /* ESP32 ROM sends as hex, but we just send raw bytes - esptool.py can handle either. */ + SLIP_send_frame_data_buf(digest, sizeof(digest)); + return 0; +} + +esp_command_error handle_spi_set_params(uint32_t *args, int *status) +{ + *status = SPIParamCfg(args[0], args[1], args[2], args[3], args[4], args[5]); + return *status ? ESP_FAILED_SPI_OP : ESP_OK; +} + +esp_command_error handle_spi_attach(uint32_t hspi_config_arg) +{ +#ifdef ESP8266 + /* ESP8266 doesn't yet support SPI flash on HSPI, but could: + see https://github.com/themadinventor/esptool/issues/98 */ + SelectSpiFunction(); +#else + /* spi_flash_attach calls SelectSpiFunction() and another + function to initialise SPI flash interface. + + Second argument 'legacy' mode is not currently supported. + */ + spi_flash_attach(hspi_config_arg, 0); +#endif + return ESP_OK; /* neither function/attach command takes an arg */ +} + +static uint32_t *mem_offset; +static uint32_t mem_remaining; + +esp_command_error handle_mem_begin(uint32_t size, uint32_t offset) +{ + mem_offset = (uint32_t *)offset; + mem_remaining = size; + return ESP_OK; +} + +esp_command_error handle_mem_data(void *data, uint32_t length) +{ + uint32_t *data_words = (uint32_t *)data; + if (mem_offset == NULL && length > 0) { + return ESP_NOT_IN_FLASH_MODE; + } + if (length > mem_remaining) { + return ESP_TOO_MUCH_DATA; + } + if (length % 4 != 0) { + return ESP_BAD_DATA_LEN; + } + + for(int i = 0; i < length; i+= 4) { + *mem_offset++ = *data_words++; + mem_remaining -= 4; + } + return ESP_OK; +} + +esp_command_error handle_mem_finish() +{ + esp_command_error res = mem_remaining > 0 ? ESP_NOT_ENOUGH_DATA : ESP_OK; + mem_remaining = 0; + mem_offset = NULL; + return res; +} diff --git a/dist/bin/esptool/flasher_stub/stub_flasher.c b/dist/bin/esptool/flasher_stub/stub_flasher.c new file mode 100644 index 000000000..01b8d27fd --- /dev/null +++ b/dist/bin/esptool/flasher_stub/stub_flasher.c @@ -0,0 +1,426 @@ +/* + * Copyright (c) 2016-2019 Espressif Systems (Shanghai) PTE LTD & Cesanta Software Limited + * All rights reserved + * + * This file is part of the esptool.py binary flasher stub. + * + * 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. + */ + +/* + * Main flasher stub logic + * + * This stub uses the same SLIP framing and basic command/response structure + * as the in-ROM flasher program, but with some enhanced + * functions and also standardizes the flasher features between different chips. + * + * Actual command handlers are implemented in stub_commands.c + */ +#include +#include "stub_flasher.h" +#include "rom_functions.h" +#include "slip.h" +#include "stub_commands.h" +#include "stub_write_flash.h" +#include "soc_support.h" + +#define UART_RX_INTS (UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_TOUT_INT_ENA) + +static uint32_t get_new_uart_divider(uint32_t current_baud, uint32_t new_baud) +{ + uint32_t master_freq; + /* ESP32 has ROM code to detect the crystal freq but ESP8266 does not have this... + So instead we use the previously auto-synced 115200 baud rate (which we know + is correct wrt the relative crystal accuracy of the ESP & the USB/serial adapter). + From this we can estimate crystal freq, and update for a new baud rate relative to that. + */ + uint32_t uart_reg = READ_REG(UART_CLKDIV_REG(0)); + uint32_t uart_div = uart_reg & UART_CLKDIV_M; +#ifdef ESP32 + // account for fractional part of divider (bottom 4 bits) + uint32_t fraction = (uart_reg >> UART_CLKDIV_FRAG_S) & UART_CLKDIV_FRAG_V; + uart_div = (uart_div << 4) + fraction; +#endif + master_freq = uart_div * current_baud; + return master_freq / new_baud; + +} + +/* Buffers for reading from UART. Data is read double-buffered, so + we can read into one buffer while handling data from the other one + (used for flashing throughput.) */ +typedef struct { + uint8_t buf_a[MAX_WRITE_BLOCK+64]; + uint8_t buf_b[MAX_WRITE_BLOCK+64]; + volatile uint8_t *reading_buf; /* Pointer to buf_a, or buf_b - which are we reading_buf? */ + uint16_t read; /* how many bytes have we read in the frame */ + slip_state_t state; + esp_command_req_t *command; /* Pointer to buf_a or buf_b as latest command received */ +} uart_buf_t; +static volatile uart_buf_t ub; + +/* esptool protcol "checksum" is XOR of 0xef and each byte of + data payload. */ +static uint8_t calculate_checksum(uint8_t *buf, int length) +{ + uint8_t res = 0xef; + for(int i = 0; i < length; i++) { + res ^= buf[i]; + } + return res; +} + +static void uart_isr_receive(char byte) +{ + int16_t r = SLIP_recv_byte(byte, (slip_state_t *)&ub.state); + if (r >= 0) { + ub.reading_buf[ub.read++] = (uint8_t) r; + if (ub.read == MAX_WRITE_BLOCK+64) { + /* shouldn't happen unless there are data errors */ + r = SLIP_FINISHED_FRAME; + } + } + if (r == SLIP_FINISHED_FRAME) { + /* end of frame, set 'command' + to be processed by main thread */ + if(ub.reading_buf == ub.buf_a) { + ub.command = (esp_command_req_t *)ub.buf_a; + ub.reading_buf = ub.buf_b; + } else { + ub.command = (esp_command_req_t *)ub.buf_b; + ub.reading_buf = ub.buf_a; + } + ub.read = 0; + } +} + +void uart_isr(void *arg) { + uint32_t int_st = READ_REG(UART_INT_ST(0)); + while (1) { + uint32_t fifo_len = READ_REG(UART_STATUS(0)) & 0xff; + if (fifo_len == 0) { + break; + } + while (fifo_len-- > 0) { + uint8_t byte = READ_REG(UART_FIFO(0)) & 0xff; + uart_isr_receive(byte); + } + } + WRITE_REG(UART_INT_CLR(0), int_st); +} + +static esp_command_error verify_data_len(esp_command_req_t *command, uint8_t len) +{ + return (command->data_len == len) ? ESP_OK : ESP_BAD_DATA_LEN; +} + +void cmd_loop() { + while(1) { + /* Wait for a command */ + while(ub.command == NULL) { } + esp_command_req_t *command = ub.command; + ub.command = NULL; + /* provide easy access for 32-bit data words */ + uint32_t *data_words = (uint32_t *)command->data_buf; + + /* Send command response header */ + esp_command_response_t resp = { + .resp = 1, + .op_ret = command->op, + .len_ret = 2, /* esptool.py ignores this value, but other users of the stub may not */ + .value = 0, + }; + + /* Some commands need to set resp.len_ret or resp.value before it is sent back */ + switch(command->op) { + case ESP_READ_REG: + if (command->data_len == 4) { + resp.value = READ_REG(data_words[0]); + } + break; + case ESP_FLASH_VERIFY_MD5: + resp.len_ret = 16 + 2; /* Will sent 16 bytes of data with MD5 value */ + break; + default: + break; + } + + /* Send the command response */ + SLIP_send_frame_delimiter(); + SLIP_send_frame_data_buf(&resp, sizeof(esp_command_response_t)); + + if(command->data_len > MAX_WRITE_BLOCK+16) { + SLIP_send_frame_data(ESP_BAD_DATA_LEN); + SLIP_send_frame_data(0xEE); + SLIP_send_frame_delimiter(); + continue; + } + + /* ... ESP_FLASH_VERIFY_MD5 will insert in-frame response data + between here and when we send the status bytes at the + end of the frame */ + + esp_command_error error = ESP_CMD_NOT_IMPLEMENTED; + int status = 0; + + /* First stage of command processing - before sending error/status */ + switch (command->op) { + case ESP_ERASE_FLASH: + error = verify_data_len(command, 0) || SPIEraseChip(); + break; + case ESP_ERASE_REGION: + /* Params for ERASE_REGION are addr, len */ + error = verify_data_len(command, 8) || handle_flash_erase(data_words[0], data_words[1]); + break; + case ESP_SET_BAUD: + /* ESP_SET_BAUD sends two args, new and old baud rates */ + error = verify_data_len(command, 8); + /* actual baud setting happens after we send the reply */ + break; + case ESP_READ_FLASH: + error = verify_data_len(command, 16); + /* actual data is sent after we send the reply */ + break; + case ESP_FLASH_VERIFY_MD5: + /* unsure why the MD5 command has 4 params but we only pass 2 of them, + but this is in ESP32 ROM so we can't mess with it. + */ + error = verify_data_len(command, 16) || handle_flash_get_md5sum(data_words[0], data_words[1]); + break; + case ESP_FLASH_BEGIN: + /* parameters (interpreted differently to ROM flasher): + 0 - erase_size (used as total size to write) + 1 - num_blocks (ignored) + 2 - block_size (should be MAX_WRITE_BLOCK, relies on num_blocks * block_size >= erase_size) + 3 - offset (used as-is) + */ + if (command->data_len == 16 && data_words[2] != MAX_WRITE_BLOCK) { + error = ESP_BAD_BLOCKSIZE; + } else { + error = verify_data_len(command, 16) || handle_flash_begin(data_words[0], data_words[3]); + } + break; + case ESP_FLASH_DEFLATED_BEGIN: + /* parameters: + 0 - uncompressed size + 1 - num_blocks (based on compressed size) + 2 - block_size (should be MAX_WRITE_BLOCK, total bytes over serial = num_blocks * block_size) + 3 - offset (used as-is) + */ + if (command->data_len == 16 && data_words[2] != MAX_WRITE_BLOCK) { + error = ESP_BAD_BLOCKSIZE; + } else { + error = verify_data_len(command, 16) || handle_flash_deflated_begin(data_words[0], data_words[1] * data_words[2], data_words[3]); } + break; + case ESP_FLASH_DATA: + case ESP_FLASH_DEFLATED_DATA: +#ifdef ESP32 + case ESP_FLASH_ENCRYPT_DATA: +#endif + /* ACK DATA commands immediately, then process them a few lines down, + allowing next command to buffer */ + if(is_in_flash_mode()) { + error = get_flash_error(); + int payload_len = command->data_len - 16; + if (data_words[0] != payload_len) { + /* First byte of data payload header is length (repeated) as a word */ + error = ESP_BAD_DATA_LEN; + } + uint8_t data_checksum = calculate_checksum(command->data_buf + 16, payload_len); + if (data_checksum != command->checksum) { + error = ESP_BAD_DATA_CHECKSUM; + } + } + else { + error = ESP_NOT_IN_FLASH_MODE; + } + break; + case ESP_FLASH_END: + case ESP_FLASH_DEFLATED_END: + error = handle_flash_end(); + break; + case ESP_SPI_SET_PARAMS: + /* data params: fl_id, total_size, block_size, sector_Size, page_size, status_mask */ + error = verify_data_len(command, 24) || handle_spi_set_params(data_words, &status); + break; + case ESP_SPI_ATTACH: + /* parameter is 'hspi mode' (0, 1 or a pin mask for ESP32. Ignored on ESP8266.) */ + error = verify_data_len(command, 4) || handle_spi_attach(data_words[0]); + break; + case ESP_WRITE_REG: + /* params are addr, value, mask (ignored), delay_us (ignored) */ + error = verify_data_len(command, 16); + if (error == ESP_OK) { + WRITE_REG(data_words[0], data_words[1]); + } + break; + case ESP_READ_REG: + /* actual READ_REG operation happens higher up */ + error = verify_data_len(command, 4); + break; + case ESP_MEM_BEGIN: + error = verify_data_len(command, 16) || handle_mem_begin(data_words[0], data_words[3]); + break; + case ESP_MEM_DATA: + error = handle_mem_data(command->data_buf + 16, command->data_len - 16); + break; + case ESP_MEM_END: + error = verify_data_len(command, 8) || handle_mem_finish(); + break; + case ESP_RUN_USER_CODE: + /* Returning from here will run user code, ie standard boot process + + This command does not send a response. + */ + return; + } + + SLIP_send_frame_data(error); + SLIP_send_frame_data(status); + SLIP_send_frame_delimiter(); + + /* Some commands need to do things after after sending this response */ + if (error == ESP_OK) { + switch(command->op) { + case ESP_SET_BAUD: + ets_delay_us(10000); + uart_div_modify(0, get_new_uart_divider(data_words[1], data_words[0])); + ets_delay_us(1000); + break; + case ESP_READ_FLASH: + /* args are: offset, length, block_size, max_in_flight */ + handle_flash_read(data_words[0], data_words[1], data_words[2], + data_words[3]); + break; + case ESP_FLASH_DATA: + /* drop into flashing mode, discard 16 byte payload header */ + handle_flash_data(command->data_buf + 16, command->data_len - 16); + break; +#ifdef ESP32 + case ESP_FLASH_ENCRYPT_DATA: + /* write encrypted data */ + handle_flash_encrypt_data(command->data_buf + 16, command->data_len -16); + break; +#endif + case ESP_FLASH_DEFLATED_DATA: + handle_flash_deflated_data(command->data_buf + 16, command->data_len - 16); + break; + case ESP_FLASH_DEFLATED_END: + case ESP_FLASH_END: + /* passing 0 as parameter for ESP_FLASH_END means reboot now */ + if (data_words[0] == 0) { + /* Flush the FLASH_END response before rebooting */ +#ifdef ESP32 + uart_tx_flush(0); +#endif + ets_delay_us(10000); + software_reset(); + } + break; + case ESP_MEM_END: + if (data_words[1] != 0) { + void (*entrypoint_fn)(void) = (void (*))data_words[1]; + /* Make sure the command response has been flushed out + of the UART before we run the new code */ +#ifdef ESP32 + uart_tx_flush(0); +#endif + ets_delay_us(1000); + /* this is a little different from the ROM loader, + which exits the loader routine and _then_ calls this + function. But for our purposes so far, having a bit of + extra stuff on the stack doesn't really matter. + */ + entrypoint_fn(); + } + break; + } + } + } +} + + +extern uint32_t _bss_start; +extern uint32_t _bss_end; + +void __attribute__((used)) stub_main(); + + +#ifdef ESP8266 +__asm__ ( + ".global stub_main_8266\n" + ".literal_position\n" + ".align 4\n" + "stub_main_8266:\n" +/* ESP8266 wrapper for "stub_main()" manipulates the return address in + * a0, so 'return' from here runs user code. + * + * After setting a0, we jump directly to stub_main_inner() which is a + * normal C function + * + * Adapted from similar approach used by Cesanta Software for ESP8266 + * flasher stub. + * + */ + "movi a0, 0x400010a8;" + "j stub_main;"); +#endif + +/* This function is called from stub_main, with return address + reset to point to user code. */ +void stub_main() +{ + const uint32_t greeting = 0x4941484f; /* OHAI */ + + /* this points to stub_main now, clear for next boot */ + ets_set_user_start(0); + + /* zero bss */ + for(uint32_t *p = &_bss_start; p < &_bss_end; p++) { + *p = 0; + } + + SLIP_send(&greeting, 4); + + /* All UART reads come via uart_isr */ + ub.reading_buf = ub.buf_a; + ets_isr_attach(ETS_UART0_INUM, uart_isr, NULL); + REG_SET_MASK(UART_INT_ENA(0), UART_RX_INTS); + ets_isr_unmask(1 << ETS_UART0_INUM); + + /* Configure default SPI flash functionality. + Can be overriden later by esptool.py. */ +#ifdef ESP8266 + SelectSpiFunction(); + + spi_flash_attach(); +#else + uint32_t spiconfig = ets_efuse_get_spiconfig(); + uint32_t strapping = READ_REG(GPIO_STRAP_REG); + /* If GPIO1 (U0TXD) is pulled low and no other boot mode is + set in efuse, assume HSPI flash mode (same as normal boot) + */ + if (spiconfig == 0 && (strapping & 0x1c) == 0x08) { + spiconfig = 1; /* HSPI flash mode */ + } + spi_flash_attach(spiconfig, 0); +#endif + SPIParamCfg(0, 16*1024*1024, FLASH_BLOCK_SIZE, FLASH_SECTOR_SIZE, + FLASH_PAGE_SIZE, FLASH_STATUS_MASK); + + cmd_loop(); + + /* if cmd_loop returns, it's due to ESP_RUN_USER_CODE command. */ + + return; +} diff --git a/dist/bin/esptool/flasher_stub/stub_write_flash.c b/dist/bin/esptool/flasher_stub/stub_write_flash.c new file mode 100644 index 000000000..05616146b --- /dev/null +++ b/dist/bin/esptool/flasher_stub/stub_write_flash.c @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2016-2019 Espressif Systems (Shanghai) PTE LTD & Cesanta Software Limited + * All rights reserved + * + * This file is part of the esptool.py binary flasher stub. + * + * 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. + */ +#include "soc_support.h" +#include "stub_write_flash.h" +#include "stub_flasher.h" +#include "rom_functions.h" +#include "miniz.h" + +/* local flashing state + + This is wrapped in a structure because gcc 4.8 + generates significantly more code for ESP32 + if they are static variables (literal pool, I think!) +*/ +static struct { + /* set by flash_begin, cleared by flash_end */ + bool in_flash_mode; + /* offset of next SPI write */ + uint32_t next_write; + /* sector number for next erase */ + int next_erase_sector; + /* number of output bytes remaining to write */ + uint32_t remaining; + /* number of sectors remaining to erase */ + int remaining_erase_sector; + /* last error generated by a data packet */ + esp_command_error last_error; + + /* inflator state for deflate write */ + tinfl_decompressor inflator; + /* number of compressed bytes remaining to read */ + uint32_t remaining_compressed; +} fs; + +/* SPI status bits */ +static const uint32_t STATUS_WIP_BIT = (1 << 0); +static const uint32_t STATUS_CMP_BIT = (1 << 14); /* Complement Protect */ +static const uint32_t STATUS_QIE_BIT = (1 << 9); /* Quad Enable */ + +bool is_in_flash_mode(void) +{ + return fs.in_flash_mode; +} + +esp_command_error get_flash_error(void) +{ + return fs.last_error; +} + +/* Wait for the SPI state machine to be ready, + ie no command in progress in the internal host. +*/ +inline static void spi_wait_ready(void) +{ + /* Wait for SPI state machine ready */ + while((READ_REG(SPI_EXT2_REG) & SPI_ST)) + { } +#ifdef ESP32 + while(READ_REG(SPI0_EXT2_REG) & SPI_ST) + { } +#endif +} + +/* Returns true if the spiflash is ready for its next write + operation. + + Doesn't block, except for the SPI state machine to finish + any previous SPI host operation. +*/ +static bool spiflash_is_ready(void) +{ + spi_wait_ready(); + WRITE_REG(SPI_RD_STATUS_REG, 0); + /* Issue read status command */ + WRITE_REG(SPI_CMD_REG, SPI_FLASH_RDSR); + while(READ_REG(SPI_CMD_REG) != 0) + { } + uint32_t status_value = READ_REG(SPI_RD_STATUS_REG); + return (status_value & STATUS_WIP_BIT) == 0; +} + +static void spi_write_enable(void) +{ + while(!spiflash_is_ready()) + { } + WRITE_REG(SPI_CMD_REG, SPI_FLASH_WREN); + while(READ_REG(SPI_CMD_REG) != 0) + { } +} + +#ifdef ESP32 +static esp_rom_spiflash_chip_t *flashchip = (esp_rom_spiflash_chip_t *)0x3ffae270; + +/* Stub version of SPIUnlock() that replaces version in ROM. + + This works around a bug where SPIUnlock sometimes reads the wrong + high status byte (RDSR2 result) and then copies it back to the + flash status, causing lock bit CMP or Status Register Protect ` to + become set. + */ +SpiFlashOpResult SPIUnlock(void) +{ + uint32_t status; + + spi_wait_ready(); /* ROM SPI_read_status_high() doesn't wait for this */ + if (SPI_read_status_high(&status) != SPI_FLASH_RESULT_OK) { + return SPI_FLASH_RESULT_ERR; + } + + /* Clear all bits except QIE, if it is set. + (This is different from ROM SPIUnlock, which keeps all bits as-is.) + */ + status &= STATUS_QIE_BIT; + + spi_write_enable(); + + REG_SET_MASK(SPI_CTRL_REG, SPI_WRSR_2B); + if (SPI_write_status(flashchip, status) != SPI_FLASH_RESULT_OK) { + return SPI_FLASH_RESULT_ERR; + } + + return SPI_FLASH_RESULT_OK; +} +#endif + +esp_command_error handle_flash_begin(uint32_t total_size, uint32_t offset) { + fs.in_flash_mode = true; + fs.next_write = offset; + fs.next_erase_sector = offset / FLASH_SECTOR_SIZE; + fs.remaining = total_size; + fs.remaining_erase_sector = ((offset % FLASH_SECTOR_SIZE) + total_size + FLASH_SECTOR_SIZE - 1) / FLASH_SECTOR_SIZE; + fs.last_error = ESP_OK; + + if (SPIUnlock() != 0) { + return ESP_FAILED_SPI_UNLOCK; + } + + return ESP_OK; +} + +esp_command_error handle_flash_deflated_begin(uint32_t uncompressed_size, uint32_t compressed_size, uint32_t offset) { + esp_command_error err = handle_flash_begin(uncompressed_size, offset); + tinfl_init(&fs.inflator); + fs.remaining_compressed = compressed_size; + return err; +} + +/* Erase the next sector or block (depending if we're at a block boundary). + + Updates fs.next_erase_sector & fs.remaining_erase_sector on success. + + If nothing left to erase, returns immediately. + + Returns immediately if SPI flash not yet ready for a write operation. + + Does not wait for the erase to complete - the next SPI operation + should check if a write operation is currently in progress. + */ +static void start_next_erase(void) +{ + if(fs.remaining_erase_sector == 0) + return; /* nothing left to erase */ + if(!spiflash_is_ready()) + return; /* don't wait for flash to be ready, caller will call again if needed */ + + spi_write_enable(); + + uint32_t command = SPI_FLASH_SE; /* sector erase, 4KB */ + uint32_t sectors_to_erase = 1; + if(fs.remaining_erase_sector >= SECTORS_PER_BLOCK + && fs.next_erase_sector % SECTORS_PER_BLOCK == 0) { + /* perform a 64KB block erase if we have space for it */ + command = SPI_FLASH_BE; + sectors_to_erase = SECTORS_PER_BLOCK; + } + + uint32_t addr = fs.next_erase_sector * FLASH_SECTOR_SIZE; + spi_wait_ready(); + WRITE_REG(SPI_ADDR_REG, addr & 0xffffff); + WRITE_REG(SPI_CMD_REG, command); + while(READ_REG(SPI_CMD_REG) != 0) + { } + fs.remaining_erase_sector -= sectors_to_erase; + fs.next_erase_sector += sectors_to_erase; +} + +/* Write data to flash (either direct for non-compressed upload, or + freshly decompressed.) Erases as it goes. + + Updates fs.remaining_erase_sector, fs.next_write, and fs.remaining +*/ +void handle_flash_data(void *data_buf, uint32_t length) { + int last_sector; + + if (length > fs.remaining) { + /* Trim the final block, as it may have padding beyond + the length we are writing */ + length = fs.remaining; + } + + if (length == 0) { + return; + } + + /* what sector is this write going to end in? + make sure we've erased at least that far. + */ + last_sector = (fs.next_write + length) / FLASH_SECTOR_SIZE; + while(fs.remaining_erase_sector > 0 && fs.next_erase_sector <= last_sector) { + start_next_erase(); + } + while(!spiflash_is_ready()) + {} + + /* do the actual write */ + if (SPIWrite(fs.next_write, data_buf, length)) { + fs.last_error = ESP_FAILED_SPI_OP; + } + fs.next_write += length; + fs.remaining -= length; +} + + +#ifdef ESP32 +/* Write encrypted data to flash (either direct for non-compressed upload, or + freshly decompressed.) Erases as it goes. + + Updates fs.remaining_erase_sector, fs.next_write, and fs.remaining +*/ +void handle_flash_encrypt_data(void *data_buf, uint32_t length) { + int last_sector; + + if (length > fs.remaining) { + /* Trim the final block, as it may have padding beyond + the length we are writing */ + length = fs.remaining; + } + + if (length == 0) { + return; + } + + /* what sector is this write going to end in? + make sure we've erased at least that far. + */ + last_sector = (fs.next_write + length) / FLASH_SECTOR_SIZE; + while(fs.remaining_erase_sector > 0 && fs.next_erase_sector <= last_sector) { + start_next_erase(); + } + while(!spiflash_is_ready()) + {} + + /* do the actual write */ + if (esp_rom_spiflash_write_encrypted(fs.next_write, data_buf, length)) { + fs.last_error = ESP_FAILED_SPI_OP; + } + fs.next_write += length; + fs.remaining -= length; +} +#endif + +void handle_flash_deflated_data(void *data_buf, uint32_t length) { + static uint8_t out_buf[32768]; + static uint8_t *next_out = out_buf; + int status = TINFL_STATUS_NEEDS_MORE_INPUT; + + while(length > 0 && fs.remaining > 0 && status > TINFL_STATUS_DONE) { + size_t in_bytes = length; /* input remaining */ + size_t out_bytes = out_buf + sizeof(out_buf) - next_out; /* output space remaining */ + int flags = TINFL_FLAG_PARSE_ZLIB_HEADER; + if(fs.remaining_compressed > length) { + flags |= TINFL_FLAG_HAS_MORE_INPUT; + } + + /* start an opportunistic erase: decompressing takes time, so might as + well be running a SPI erase in the background. */ + start_next_erase(); + + status = tinfl_decompress(&fs.inflator, data_buf, &in_bytes, + out_buf, next_out, &out_bytes, + flags); + + fs.remaining_compressed -= in_bytes; + length -= in_bytes; + data_buf += in_bytes; + + next_out += out_bytes; + size_t bytes_in_out_buf = next_out - out_buf; + if (status <= TINFL_STATUS_DONE || bytes_in_out_buf == sizeof(out_buf)) { + // Output buffer full, or done + handle_flash_data(out_buf, bytes_in_out_buf); + next_out = out_buf; + } + } // while + + if (status < TINFL_STATUS_DONE) { + /* error won't get sent back to esptool.py until next block is sent */ + fs.last_error = ESP_INFLATE_ERROR; + } + + if (status == TINFL_STATUS_DONE && fs.remaining > 0) { + fs.last_error = ESP_NOT_ENOUGH_DATA; + } + if (status != TINFL_STATUS_DONE && fs.remaining == 0) { + fs.last_error = ESP_TOO_MUCH_DATA; + } +} + +esp_command_error handle_flash_end(void) +{ + if (!fs.in_flash_mode) { + return ESP_NOT_IN_FLASH_MODE; + } + + if (fs.remaining > 0) { + return ESP_NOT_ENOUGH_DATA; + } + + fs.in_flash_mode = false; + return fs.last_error; +} diff --git a/dist/bin/esptool/flasher_stub/wrap_stub.py b/dist/bin/esptool/flasher_stub/wrap_stub.py new file mode 100644 index 000000000..203eaa5c3 --- /dev/null +++ b/dist/bin/esptool/flasher_stub/wrap_stub.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +# +# Stub has to be generated via Python 3, for correct repr() output +# +# Copyright (c) 2016 Cesanta Software Limited & Copyright (c) 2016-2019 Espressif Systems (Shanghai) PTE LTD +# All rights reserved +# +# 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. + +import base64 +import os +import os.path +import sys +import zlib + +sys.path.append('..') +import esptool + +def wrap_stub(elf_file): + """ Wrap an ELF file into a stub 'dict' """ + print('Wrapping ELF file %s...' % elf_file) + e = esptool.ELFFile(elf_file) + + text_section = e.get_section('.text') + try: + data_section = e.get_section('.data') + except ValueError: + data_section = None + stub = { + 'text': text_section.data, + 'text_start': text_section.addr, + 'entry': e.entrypoint, + } + if data_section is not None: + stub['data'] = data_section.data + stub['data_start'] = data_section.addr + + # Pad text with NOPs to mod 4. + if len(stub['text']) % 4 != 0: + stub['text'] += (4 - (len(stub['text']) % 4)) * '\0' + + print('Stub text: %d @ 0x%08x, data: %d @ 0x%08x, entry @ 0x%x' % ( + len(stub['text']), stub['text_start'], + len(stub.get('data', '')), stub.get('data_start', 0), + stub['entry']), file=sys.stderr) + return stub + +PYTHON_TEMPLATE = """\ +ESP%sROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b\"\"\" +%s\"\"\"))) +""" + +def write_python_snippet(stubs): + with open(sys.argv[-1], 'w') as f: + f.write("# Binary stub code (see flasher_stub dir for source & details)\n") + for key in "8266", "32": + stub_data = stubs["stub_flasher_%s" % key] + encoded = base64.b64encode(zlib.compress(repr(stub_data).encode("utf-8"), 9)).decode("utf-8") + in_lines = "" + # split encoded data into 160 character lines + LINE_LEN=160 + for c in range(0, len(encoded), LINE_LEN): + in_lines += encoded[c:c+LINE_LEN] + "\\\n" + f.write(PYTHON_TEMPLATE % (key, in_lines)) + print("Python snippet is %d bytes" % f.tell()) + +def stub_name(filename): + """ Return a dictionary key for the stub with filename 'filename' """ + return os.path.splitext(os.path.basename(filename))[0] + +if __name__ == '__main__': + stubs = dict( (stub_name(elf_file),wrap_stub(elf_file)) for elf_file in sys.argv[1:-1] ) + print('Dumping to Python snippet file %s.' % sys.argv[-1]) + write_python_snippet(stubs) diff --git a/dist/bin/esptool/pyaes/__init__.py b/dist/bin/esptool/pyaes/__init__.py new file mode 100644 index 000000000..5712f794a --- /dev/null +++ b/dist/bin/esptool/pyaes/__init__.py @@ -0,0 +1,53 @@ +# The MIT License (MIT) +# +# Copyright (c) 2014 Richard Moore +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +# This is a pure-Python implementation of the AES algorithm and AES common +# modes of operation. + +# See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard +# See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation + + +# Supported key sizes: +# 128-bit +# 192-bit +# 256-bit + + +# Supported modes of operation: +# ECB - Electronic Codebook +# CBC - Cipher-Block Chaining +# CFB - Cipher Feedback +# OFB - Output Feedback +# CTR - Counter + +# See the README.md for API details and general information. + +# Also useful, PyCrypto, a crypto library implemented in C with Python bindings: +# https://www.dlitz.net/software/pycrypto/ + + +VERSION = [1, 3, 0] + +from .aes import AES, AESModeOfOperationCTR, AESModeOfOperationCBC, AESModeOfOperationCFB, AESModeOfOperationECB, AESModeOfOperationOFB, AESModesOfOperation, Counter +from .blockfeeder import decrypt_stream, Decrypter, encrypt_stream, Encrypter +from .blockfeeder import PADDING_NONE, PADDING_DEFAULT diff --git a/dist/bin/esptool/pyaes/aes.py b/dist/bin/esptool/pyaes/aes.py new file mode 100644 index 000000000..c6e8bc02a --- /dev/null +++ b/dist/bin/esptool/pyaes/aes.py @@ -0,0 +1,589 @@ +# The MIT License (MIT) +# +# Copyright (c) 2014 Richard Moore +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +# This is a pure-Python implementation of the AES algorithm and AES common +# modes of operation. + +# See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard + +# Honestly, the best description of the modes of operations are the wonderful +# diagrams on Wikipedia. They explain in moments what my words could never +# achieve. Hence the inline documentation here is sparer than I'd prefer. +# See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation + +# Also useful, PyCrypto, a crypto library implemented in C with Python bindings: +# https://www.dlitz.net/software/pycrypto/ + + +# Supported key sizes: +# 128-bit +# 192-bit +# 256-bit + + +# Supported modes of operation: +# ECB - Electronic Codebook +# CBC - Cipher-Block Chaining +# CFB - Cipher Feedback +# OFB - Output Feedback +# CTR - Counter + + +# See the README.md for API details and general information. + + +import copy +import struct + +__all__ = ["AES", "AESModeOfOperationCTR", "AESModeOfOperationCBC", "AESModeOfOperationCFB", + "AESModeOfOperationECB", "AESModeOfOperationOFB", "AESModesOfOperation", "Counter"] + + +def _compact_word(word): + return (word[0] << 24) | (word[1] << 16) | (word[2] << 8) | word[3] + +def _string_to_bytes(text): + return list(ord(c) for c in text) + +def _bytes_to_string(binary): + return "".join(chr(b) for b in binary) + +def _concat_list(a, b): + return a + b + + +# Python 3 compatibility +try: + xrange +except Exception: + xrange = range + + # Python 3 supports bytes, which is already an array of integers + def _string_to_bytes(text): + if isinstance(text, bytes): + return text + return [ord(c) for c in text] + + # In Python 3, we return bytes + def _bytes_to_string(binary): + return bytes(binary) + + # Python 3 cannot concatenate a list onto a bytes, so we bytes-ify it first + def _concat_list(a, b): + return a + bytes(b) + + +# Based *largely* on the Rijndael implementation +# See: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf +class AES(object): + '''Encapsulates the AES block cipher. + + You generally should not need this. Use the AESModeOfOperation classes + below instead.''' + + # Number of rounds by keysize + number_of_rounds = {16: 10, 24: 12, 32: 14} + + # Round constant words + rcon = [ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ] + + # S-box and Inverse S-box (S is for Substitution) + S = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ] + Si =[ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d ] + + # Transformations for encryption + T1 = [ 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a ] + T2 = [ 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616 ] + T3 = [ 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16 ] + T4 = [ 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c ] + + # Transformations for decryption + T5 = [ 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742 ] + T6 = [ 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857 ] + T7 = [ 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8 ] + T8 = [ 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0 ] + + # Transformations for decryption key expansion + U1 = [ 0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3 ] + U2 = [ 0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697 ] + U3 = [ 0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46 ] + U4 = [ 0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d ] + + def __init__(self, key): + + if len(key) not in (16, 24, 32): + raise ValueError('Invalid key size') + + rounds = self.number_of_rounds[len(key)] + + # Encryption round keys + self._Ke = [[0] * 4 for i in xrange(rounds + 1)] + + # Decryption round keys + self._Kd = [[0] * 4 for i in xrange(rounds + 1)] + + round_key_count = (rounds + 1) * 4 + KC = len(key) // 4 + + # Convert the key into ints + tk = [ struct.unpack('>i', key[i:i + 4])[0] for i in xrange(0, len(key), 4) ] + + # Copy values into round key arrays + for i in xrange(0, KC): + self._Ke[i // 4][i % 4] = tk[i] + self._Kd[rounds - (i // 4)][i % 4] = tk[i] + + # Key expansion (fips-197 section 5.2) + rconpointer = 0 + t = KC + while t < round_key_count: + + tt = tk[KC - 1] + tk[0] ^= ((self.S[(tt >> 16) & 0xFF] << 24) ^ + (self.S[(tt >> 8) & 0xFF] << 16) ^ + (self.S[ tt & 0xFF] << 8) ^ + self.S[(tt >> 24) & 0xFF] ^ + (self.rcon[rconpointer] << 24)) + rconpointer += 1 + + if KC != 8: + for i in xrange(1, KC): + tk[i] ^= tk[i - 1] + + # Key expansion for 256-bit keys is "slightly different" (fips-197) + else: + for i in xrange(1, KC // 2): + tk[i] ^= tk[i - 1] + tt = tk[KC // 2 - 1] + + tk[KC // 2] ^= (self.S[ tt & 0xFF] ^ + (self.S[(tt >> 8) & 0xFF] << 8) ^ + (self.S[(tt >> 16) & 0xFF] << 16) ^ + (self.S[(tt >> 24) & 0xFF] << 24)) + + for i in xrange(KC // 2 + 1, KC): + tk[i] ^= tk[i - 1] + + # Copy values into round key arrays + j = 0 + while j < KC and t < round_key_count: + self._Ke[t // 4][t % 4] = tk[j] + self._Kd[rounds - (t // 4)][t % 4] = tk[j] + j += 1 + t += 1 + + # Inverse-Cipher-ify the decryption round key (fips-197 section 5.3) + for r in xrange(1, rounds): + for j in xrange(0, 4): + tt = self._Kd[r][j] + self._Kd[r][j] = (self.U1[(tt >> 24) & 0xFF] ^ + self.U2[(tt >> 16) & 0xFF] ^ + self.U3[(tt >> 8) & 0xFF] ^ + self.U4[ tt & 0xFF]) + + def encrypt(self, plaintext): + 'Encrypt a block of plain text using the AES block cipher.' + + if len(plaintext) != 16: + raise ValueError('wrong block length') + + rounds = len(self._Ke) - 1 + (s1, s2, s3) = [1, 2, 3] + a = [0, 0, 0, 0] + + # Convert plaintext to (ints ^ key) + t = [(_compact_word(plaintext[4 * i:4 * i + 4]) ^ self._Ke[0][i]) for i in xrange(0, 4)] + + # Apply round transforms + for r in xrange(1, rounds): + for i in xrange(0, 4): + a[i] = (self.T1[(t[ i ] >> 24) & 0xFF] ^ + self.T2[(t[(i + s1) % 4] >> 16) & 0xFF] ^ + self.T3[(t[(i + s2) % 4] >> 8) & 0xFF] ^ + self.T4[ t[(i + s3) % 4] & 0xFF] ^ + self._Ke[r][i]) + t = copy.copy(a) + + # The last round is special + result = [ ] + for i in xrange(0, 4): + tt = self._Ke[rounds][i] + result.append((self.S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) + result.append((self.S[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) + result.append((self.S[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) + result.append((self.S[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) + + return result + + def decrypt(self, ciphertext): + 'Decrypt a block of cipher text using the AES block cipher.' + + if len(ciphertext) != 16: + raise ValueError('wrong block length') + + rounds = len(self._Kd) - 1 + (s1, s2, s3) = [3, 2, 1] + a = [0, 0, 0, 0] + + # Convert ciphertext to (ints ^ key) + t = [(_compact_word(ciphertext[4 * i:4 * i + 4]) ^ self._Kd[0][i]) for i in xrange(0, 4)] + + # Apply round transforms + for r in xrange(1, rounds): + for i in xrange(0, 4): + a[i] = (self.T5[(t[ i ] >> 24) & 0xFF] ^ + self.T6[(t[(i + s1) % 4] >> 16) & 0xFF] ^ + self.T7[(t[(i + s2) % 4] >> 8) & 0xFF] ^ + self.T8[ t[(i + s3) % 4] & 0xFF] ^ + self._Kd[r][i]) + t = copy.copy(a) + + # The last round is special + result = [ ] + for i in xrange(0, 4): + tt = self._Kd[rounds][i] + result.append((self.Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) + result.append((self.Si[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) + result.append((self.Si[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) + result.append((self.Si[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) + + return result + + +class Counter(object): + '''A counter object for the Counter (CTR) mode of operation. + + To create a custom counter, you can usually just override the + increment method.''' + + def __init__(self, initial_value = 1): + + # Convert the value into an array of bytes long + self._counter = [ ((initial_value >> i) % 256) for i in xrange(128 - 8, -1, -8) ] + + value = property(lambda s: s._counter) + + def increment(self): + '''Increment the counter (overflow rolls back to 0).''' + + for i in xrange(len(self._counter) - 1, -1, -1): + self._counter[i] += 1 + + if self._counter[i] < 256: break + + # Carry the one + self._counter[i] = 0 + + # Overflow + else: + self._counter = [ 0 ] * len(self._counter) + + +class AESBlockModeOfOperation(object): + '''Super-class for AES modes of operation that require blocks.''' + def __init__(self, key): + self._aes = AES(key) + + def decrypt(self, ciphertext): + raise Exception('not implemented') + + def encrypt(self, plaintext): + raise Exception('not implemented') + + +class AESStreamModeOfOperation(AESBlockModeOfOperation): + '''Super-class for AES modes of operation that are stream-ciphers.''' + +class AESSegmentModeOfOperation(AESStreamModeOfOperation): + '''Super-class for AES modes of operation that segment data.''' + + segment_bytes = 16 + + + +class AESModeOfOperationECB(AESBlockModeOfOperation): + '''AES Electronic Codebook Mode of Operation. + + o Block-cipher, so data must be padded to 16 byte boundaries + + Security Notes: + o This mode is not recommended + o Any two identical blocks produce identical encrypted values, + exposing data patterns. (See the image of Tux on wikipedia) + + Also see: + o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_.28ECB.29 + o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.1''' + + + name = "Electronic Codebook (ECB)" + + def encrypt(self, plaintext): + if len(plaintext) != 16: + raise ValueError('plaintext block must be 16 bytes') + + plaintext = _string_to_bytes(plaintext) + return _bytes_to_string(self._aes.encrypt(plaintext)) + + def decrypt(self, ciphertext): + if len(ciphertext) != 16: + raise ValueError('ciphertext block must be 16 bytes') + + ciphertext = _string_to_bytes(ciphertext) + return _bytes_to_string(self._aes.decrypt(ciphertext)) + + + +class AESModeOfOperationCBC(AESBlockModeOfOperation): + '''AES Cipher-Block Chaining Mode of Operation. + + o The Initialization Vector (IV) + o Block-cipher, so data must be padded to 16 byte boundaries + o An incorrect initialization vector will only cause the first + block to be corrupt; all other blocks will be intact + o A corrupt bit in the cipher text will cause a block to be + corrupted, and the next block to be inverted, but all other + blocks will be intact. + + Security Notes: + o This method (and CTR) ARE recommended. + + Also see: + o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29 + o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.2''' + + + name = "Cipher-Block Chaining (CBC)" + + def __init__(self, key, iv = None): + if iv is None: + self._last_cipherblock = [ 0 ] * 16 + elif len(iv) != 16: + raise ValueError('initialization vector must be 16 bytes') + else: + self._last_cipherblock = _string_to_bytes(iv) + + AESBlockModeOfOperation.__init__(self, key) + + def encrypt(self, plaintext): + if len(plaintext) != 16: + raise ValueError('plaintext block must be 16 bytes') + + plaintext = _string_to_bytes(plaintext) + precipherblock = [ (p ^ l) for (p, l) in zip(plaintext, self._last_cipherblock) ] + self._last_cipherblock = self._aes.encrypt(precipherblock) + + return _bytes_to_string(self._last_cipherblock) + + def decrypt(self, ciphertext): + if len(ciphertext) != 16: + raise ValueError('ciphertext block must be 16 bytes') + + cipherblock = _string_to_bytes(ciphertext) + plaintext = [ (p ^ l) for (p, l) in zip(self._aes.decrypt(cipherblock), self._last_cipherblock) ] + self._last_cipherblock = cipherblock + + return _bytes_to_string(plaintext) + + + +class AESModeOfOperationCFB(AESSegmentModeOfOperation): + '''AES Cipher Feedback Mode of Operation. + + o A stream-cipher, so input does not need to be padded to blocks, + but does need to be padded to segment_size + + Also see: + o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_.28CFB.29 + o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.3''' + + + name = "Cipher Feedback (CFB)" + + def __init__(self, key, iv, segment_size = 1): + if segment_size == 0: segment_size = 1 + + if iv is None: + self._shift_register = [ 0 ] * 16 + elif len(iv) != 16: + raise ValueError('initialization vector must be 16 bytes') + else: + self._shift_register = _string_to_bytes(iv) + + self._segment_bytes = segment_size + + AESBlockModeOfOperation.__init__(self, key) + + segment_bytes = property(lambda s: s._segment_bytes) + + def encrypt(self, plaintext): + if len(plaintext) % self._segment_bytes != 0: + raise ValueError('plaintext block must be a multiple of segment_size') + + plaintext = _string_to_bytes(plaintext) + + # Break block into segments + encrypted = [ ] + for i in xrange(0, len(plaintext), self._segment_bytes): + plaintext_segment = plaintext[i: i + self._segment_bytes] + xor_segment = self._aes.encrypt(self._shift_register)[:len(plaintext_segment)] + cipher_segment = [ (p ^ x) for (p, x) in zip(plaintext_segment, xor_segment) ] + + # Shift the top bits out and the ciphertext in + self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment) + + encrypted.extend(cipher_segment) + + return _bytes_to_string(encrypted) + + def decrypt(self, ciphertext): + if len(ciphertext) % self._segment_bytes != 0: + raise ValueError('ciphertext block must be a multiple of segment_size') + + ciphertext = _string_to_bytes(ciphertext) + + # Break block into segments + decrypted = [ ] + for i in xrange(0, len(ciphertext), self._segment_bytes): + cipher_segment = ciphertext[i: i + self._segment_bytes] + xor_segment = self._aes.encrypt(self._shift_register)[:len(cipher_segment)] + plaintext_segment = [ (p ^ x) for (p, x) in zip(cipher_segment, xor_segment) ] + + # Shift the top bits out and the ciphertext in + self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment) + + decrypted.extend(plaintext_segment) + + return _bytes_to_string(decrypted) + + + +class AESModeOfOperationOFB(AESStreamModeOfOperation): + '''AES Output Feedback Mode of Operation. + + o A stream-cipher, so input does not need to be padded to blocks, + allowing arbitrary length data. + o A bit twiddled in the cipher text, twiddles the same bit in the + same bit in the plain text, which can be useful for error + correction techniques. + + Also see: + o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_.28OFB.29 + o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.4''' + + + name = "Output Feedback (OFB)" + + def __init__(self, key, iv = None): + if iv is None: + self._last_precipherblock = [ 0 ] * 16 + elif len(iv) != 16: + raise ValueError('initialization vector must be 16 bytes') + else: + self._last_precipherblock = _string_to_bytes(iv) + + self._remaining_block = [ ] + + AESBlockModeOfOperation.__init__(self, key) + + def encrypt(self, plaintext): + encrypted = [ ] + for p in _string_to_bytes(plaintext): + if len(self._remaining_block) == 0: + self._remaining_block = self._aes.encrypt(self._last_precipherblock) + self._last_precipherblock = [ ] + precipherbyte = self._remaining_block.pop(0) + self._last_precipherblock.append(precipherbyte) + cipherbyte = p ^ precipherbyte + encrypted.append(cipherbyte) + + return _bytes_to_string(encrypted) + + def decrypt(self, ciphertext): + # AES-OFB is symetric + return self.encrypt(ciphertext) + + + +class AESModeOfOperationCTR(AESStreamModeOfOperation): + '''AES Counter Mode of Operation. + + o A stream-cipher, so input does not need to be padded to blocks, + allowing arbitrary length data. + o The counter must be the same size as the key size (ie. len(key)) + o Each block independant of the other, so a corrupt byte will not + damage future blocks. + o Each block has a uniue counter value associated with it, which + contributes to the encrypted value, so no data patterns are + leaked. + o Also known as: Counter Mode (CM), Integer Counter Mode (ICM) and + Segmented Integer Counter (SIC + + Security Notes: + o This method (and CBC) ARE recommended. + o Each message block is associated with a counter value which must be + unique for ALL messages with the same key. Otherwise security may be + compromised. + + Also see: + + o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29 + o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.5 + and Appendix B for managing the initial counter''' + + + name = "Counter (CTR)" + + def __init__(self, key, counter = None): + AESBlockModeOfOperation.__init__(self, key) + + if counter is None: + counter = Counter() + + self._counter = counter + self._remaining_counter = [ ] + + def encrypt(self, plaintext): + while len(self._remaining_counter) < len(plaintext): + self._remaining_counter += self._aes.encrypt(self._counter.value) + self._counter.increment() + + plaintext = _string_to_bytes(plaintext) + + encrypted = [ (p ^ c) for (p, c) in zip(plaintext, self._remaining_counter) ] + self._remaining_counter = self._remaining_counter[len(encrypted):] + + return _bytes_to_string(encrypted) + + def decrypt(self, crypttext): + # AES-CTR is symetric + return self.encrypt(crypttext) + + +# Simple lookup table for each mode +AESModesOfOperation = dict( + ctr = AESModeOfOperationCTR, + cbc = AESModeOfOperationCBC, + cfb = AESModeOfOperationCFB, + ecb = AESModeOfOperationECB, + ofb = AESModeOfOperationOFB, +) diff --git a/dist/bin/esptool/pyaes/blockfeeder.py b/dist/bin/esptool/pyaes/blockfeeder.py new file mode 100644 index 000000000..f4113c37f --- /dev/null +++ b/dist/bin/esptool/pyaes/blockfeeder.py @@ -0,0 +1,227 @@ +# The MIT License (MIT) +# +# Copyright (c) 2014 Richard Moore +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + + +from .aes import AESBlockModeOfOperation, AESSegmentModeOfOperation, AESStreamModeOfOperation +from .util import append_PKCS7_padding, strip_PKCS7_padding, to_bufferable + + +# First we inject three functions to each of the modes of operations +# +# _can_consume(size) +# - Given a size, determine how many bytes could be consumed in +# a single call to either the decrypt or encrypt method +# +# _final_encrypt(data, padding = PADDING_DEFAULT) +# - call and return encrypt on this (last) chunk of data, +# padding as necessary; this will always be at least 16 +# bytes unless the total incoming input was less than 16 +# bytes +# +# _final_decrypt(data, padding = PADDING_DEFAULT) +# - same as _final_encrypt except for decrypt, for +# stripping off padding +# + +PADDING_NONE = 'none' +PADDING_DEFAULT = 'default' + +# @TODO: Ciphertext stealing and explicit PKCS#7 +# PADDING_CIPHERTEXT_STEALING +# PADDING_PKCS7 + +# ECB and CBC are block-only ciphers + +def _block_can_consume(self, size): + if size >= 16: return 16 + return 0 + +# After padding, we may have more than one block +def _block_final_encrypt(self, data, padding = PADDING_DEFAULT): + if padding == PADDING_DEFAULT: + data = append_PKCS7_padding(data) + + elif padding == PADDING_NONE: + if len(data) != 16: + raise Exception('invalid data length for final block') + else: + raise Exception('invalid padding option') + + if len(data) == 32: + return self.encrypt(data[:16]) + self.encrypt(data[16:]) + + return self.encrypt(data) + + +def _block_final_decrypt(self, data, padding = PADDING_DEFAULT): + if padding == PADDING_DEFAULT: + return strip_PKCS7_padding(self.decrypt(data)) + + if padding == PADDING_NONE: + if len(data) != 16: + raise Exception('invalid data length for final block') + return self.decrypt(data) + + raise Exception('invalid padding option') + +AESBlockModeOfOperation._can_consume = _block_can_consume +AESBlockModeOfOperation._final_encrypt = _block_final_encrypt +AESBlockModeOfOperation._final_decrypt = _block_final_decrypt + + + +# CFB is a segment cipher + +def _segment_can_consume(self, size): + return self.segment_bytes * int(size // self.segment_bytes) + +# CFB can handle a non-segment-sized block at the end using the remaining cipherblock +def _segment_final_encrypt(self, data, padding = PADDING_DEFAULT): + if padding != PADDING_DEFAULT: + raise Exception('invalid padding option') + + faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes))) + padded = data + to_bufferable(faux_padding) + return self.encrypt(padded)[:len(data)] + +# CFB can handle a non-segment-sized block at the end using the remaining cipherblock +def _segment_final_decrypt(self, data, padding = PADDING_DEFAULT): + if padding != PADDING_DEFAULT: + raise Exception('invalid padding option') + + faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes))) + padded = data + to_bufferable(faux_padding) + return self.decrypt(padded)[:len(data)] + +AESSegmentModeOfOperation._can_consume = _segment_can_consume +AESSegmentModeOfOperation._final_encrypt = _segment_final_encrypt +AESSegmentModeOfOperation._final_decrypt = _segment_final_decrypt + + + +# OFB and CTR are stream ciphers + +def _stream_can_consume(self, size): + return size + +def _stream_final_encrypt(self, data, padding = PADDING_DEFAULT): + if padding not in [PADDING_NONE, PADDING_DEFAULT]: + raise Exception('invalid padding option') + + return self.encrypt(data) + +def _stream_final_decrypt(self, data, padding = PADDING_DEFAULT): + if padding not in [PADDING_NONE, PADDING_DEFAULT]: + raise Exception('invalid padding option') + + return self.decrypt(data) + +AESStreamModeOfOperation._can_consume = _stream_can_consume +AESStreamModeOfOperation._final_encrypt = _stream_final_encrypt +AESStreamModeOfOperation._final_decrypt = _stream_final_decrypt + + + +class BlockFeeder(object): + '''The super-class for objects to handle chunking a stream of bytes + into the appropriate block size for the underlying mode of operation + and applying (or stripping) padding, as necessary.''' + + def __init__(self, mode, feed, final, padding = PADDING_DEFAULT): + self._mode = mode + self._feed = feed + self._final = final + self._buffer = to_bufferable("") + self._padding = padding + + def feed(self, data = None): + '''Provide bytes to encrypt (or decrypt), returning any bytes + possible from this or any previous calls to feed. + + Call with None or an empty string to flush the mode of + operation and return any final bytes; no further calls to + feed may be made.''' + + if self._buffer is None: + raise ValueError('already finished feeder') + + # Finalize; process the spare bytes we were keeping + if not data: + result = self._final(self._buffer, self._padding) + self._buffer = None + return result + + self._buffer += to_bufferable(data) + + # We keep 16 bytes around so we can determine padding + result = to_bufferable('') + while len(self._buffer) > 16: + can_consume = self._mode._can_consume(len(self._buffer) - 16) + if can_consume == 0: break + result += self._feed(self._buffer[:can_consume]) + self._buffer = self._buffer[can_consume:] + + return result + + +class Encrypter(BlockFeeder): + 'Accepts bytes of plaintext and returns encrypted ciphertext.' + + def __init__(self, mode, padding = PADDING_DEFAULT): + BlockFeeder.__init__(self, mode, mode.encrypt, mode._final_encrypt, padding) + + +class Decrypter(BlockFeeder): + 'Accepts bytes of ciphertext and returns decrypted plaintext.' + + def __init__(self, mode, padding = PADDING_DEFAULT): + BlockFeeder.__init__(self, mode, mode.decrypt, mode._final_decrypt, padding) + + +# 8kb blocks +BLOCK_SIZE = (1 << 13) + +def _feed_stream(feeder, in_stream, out_stream, block_size = BLOCK_SIZE): + 'Uses feeder to read and convert from in_stream and write to out_stream.' + + while True: + chunk = in_stream.read(block_size) + if not chunk: + break + converted = feeder.feed(chunk) + out_stream.write(converted) + converted = feeder.feed() + out_stream.write(converted) + + +def encrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT): + 'Encrypts a stream of bytes from in_stream to out_stream using mode.' + + encrypter = Encrypter(mode, padding = padding) + _feed_stream(encrypter, in_stream, out_stream, block_size) + + +def decrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT): + 'Decrypts a stream of bytes from in_stream to out_stream using mode.' + + decrypter = Decrypter(mode, padding = padding) + _feed_stream(decrypter, in_stream, out_stream, block_size) diff --git a/dist/bin/esptool/pyaes/util.py b/dist/bin/esptool/pyaes/util.py new file mode 100644 index 000000000..081a37594 --- /dev/null +++ b/dist/bin/esptool/pyaes/util.py @@ -0,0 +1,60 @@ +# The MIT License (MIT) +# +# Copyright (c) 2014 Richard Moore +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +# Why to_bufferable? +# Python 3 is very different from Python 2.x when it comes to strings of text +# and strings of bytes; in Python 3, strings of bytes do not exist, instead to +# represent arbitrary binary data, we must use the "bytes" object. This method +# ensures the object behaves as we need it to. + +def to_bufferable(binary): + return binary + +def _get_byte(c): + return ord(c) + +try: + xrange +except: + + def to_bufferable(binary): + if isinstance(binary, bytes): + return binary + return bytes(ord(b) for b in binary) + + def _get_byte(c): + return c + +def append_PKCS7_padding(data): + pad = 16 - (len(data) % 16) + return data + to_bufferable(chr(pad) * pad) + +def strip_PKCS7_padding(data): + if len(data) % 16 != 0: + raise ValueError("invalid length") + + pad = _get_byte(data[-1]) + + if pad > 16: + raise ValueError("invalid padding byte") + + return data[:-pad] diff --git a/dist/bin/esptool/serial/__init__.py b/dist/bin/esptool/serial/__init__.py new file mode 100644 index 000000000..c24ced885 --- /dev/null +++ b/dist/bin/esptool/serial/__init__.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# +# This is a wrapper module for different platform implementations +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2001-2017 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +import sys +import importlib + +from serial.serialutil import * +#~ SerialBase, SerialException, to_bytes, iterbytes + +__version__ = '3.4' + +VERSION = __version__ + +# pylint: disable=wrong-import-position +if sys.platform == 'cli': + from serial.serialcli import Serial +else: + import os + # chose an implementation, depending on os + if os.name == 'nt': # sys.platform == 'win32': + from serial.serialwin32 import Serial + elif os.name == 'posix': + from serial.serialposix import Serial, PosixPollSerial, VTIMESerial # noqa + elif os.name == 'java': + from serial.serialjava import Serial + else: + raise ImportError("Sorry: no implementation for your platform ('{}') available".format(os.name)) + + +protocol_handler_packages = [ + 'serial.urlhandler', +] + + +def serial_for_url(url, *args, **kwargs): + """\ + Get an instance of the Serial class, depending on port/url. The port is not + opened when the keyword parameter 'do_not_open' is true, by default it + is. All other parameters are directly passed to the __init__ method when + the port is instantiated. + + The list of package names that is searched for protocol handlers is kept in + ``protocol_handler_packages``. + + e.g. we want to support a URL ``foobar://``. A module + ``my_handlers.protocol_foobar`` is provided by the user. Then + ``protocol_handler_packages.append("my_handlers")`` would extend the search + path so that ``serial_for_url("foobar://"))`` would work. + """ + # check and remove extra parameter to not confuse the Serial class + do_open = not kwargs.pop('do_not_open', False) + # the default is to use the native implementation + klass = Serial + try: + url_lowercase = url.lower() + except AttributeError: + # it's not a string, use default + pass + else: + # if it is an URL, try to import the handler module from the list of possible packages + if '://' in url_lowercase: + protocol = url_lowercase.split('://', 1)[0] + module_name = '.protocol_{}'.format(protocol) + for package_name in protocol_handler_packages: + try: + importlib.import_module(package_name) + handler_module = importlib.import_module(module_name, package_name) + except ImportError: + continue + else: + if hasattr(handler_module, 'serial_class_for_url'): + url, klass = handler_module.serial_class_for_url(url) + else: + klass = handler_module.Serial + break + else: + raise ValueError('invalid URL, protocol {!r} not known'.format(protocol)) + # instantiate and open when desired + instance = klass(None, *args, **kwargs) + instance.port = url + if do_open: + instance.open() + return instance diff --git a/dist/bin/esptool/serial/__pycache__/__init__.cpython-39.pyc b/dist/bin/esptool/serial/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 000000000..826998f33 Binary files /dev/null and b/dist/bin/esptool/serial/__pycache__/__init__.cpython-39.pyc differ diff --git a/dist/bin/esptool/serial/__pycache__/serialutil.cpython-39.pyc b/dist/bin/esptool/serial/__pycache__/serialutil.cpython-39.pyc new file mode 100644 index 000000000..9c4fb3d82 Binary files /dev/null and b/dist/bin/esptool/serial/__pycache__/serialutil.cpython-39.pyc differ diff --git a/dist/bin/esptool/serial/__pycache__/serialwin32.cpython-39.pyc b/dist/bin/esptool/serial/__pycache__/serialwin32.cpython-39.pyc new file mode 100644 index 000000000..d3f266f4e Binary files /dev/null and b/dist/bin/esptool/serial/__pycache__/serialwin32.cpython-39.pyc differ diff --git a/dist/bin/esptool/serial/__pycache__/win32.cpython-39.pyc b/dist/bin/esptool/serial/__pycache__/win32.cpython-39.pyc new file mode 100644 index 000000000..90aca91be Binary files /dev/null and b/dist/bin/esptool/serial/__pycache__/win32.cpython-39.pyc differ diff --git a/dist/bin/esptool/serial/rfc2217.py b/dist/bin/esptool/serial/rfc2217.py new file mode 100644 index 000000000..e8ddb7f2e --- /dev/null +++ b/dist/bin/esptool/serial/rfc2217.py @@ -0,0 +1,1345 @@ +#! python +# +# This module implements a RFC2217 compatible client. RF2217 descibes a +# protocol to access serial ports over TCP/IP and allows setting the baud rate, +# modem control lines etc. +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2001-2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +# TODO: +# - setting control line -> answer is not checked (had problems with one of the +# severs). consider implementing a compatibility mode flag to make check +# conditional +# - write timeout not implemented at all + +# ########################################################################### +# observations and issues with servers +# =========================================================================== +# sredird V2.2.1 +# - http://www.ibiblio.org/pub/Linux/system/serial/ sredird-2.2.2.tar.gz +# - does not acknowledge SET_CONTROL (RTS/DTR) correctly, always responding +# [105 1] instead of the actual value. +# - SET_BAUDRATE answer contains 4 extra null bytes -> probably for larger +# numbers than 2**32? +# - To get the signature [COM_PORT_OPTION 0] has to be sent. +# - run a server: while true; do nc -l -p 7000 -c "sredird debug /dev/ttyUSB0 /var/lock/sredir"; done +# =========================================================================== +# telnetcpcd (untested) +# - http://ftp.wayne.edu/kermit/sredird/telnetcpcd-1.09.tar.gz +# - To get the signature [COM_PORT_OPTION] w/o data has to be sent. +# =========================================================================== +# ser2net +# - does not negotiate BINARY or COM_PORT_OPTION for his side but at least +# acknowledges that the client activates these options +# - The configuration may be that the server prints a banner. As this client +# implementation does a flushInput on connect, this banner is hidden from +# the user application. +# - NOTIFY_MODEMSTATE: the poll interval of the server seems to be one +# second. +# - To get the signature [COM_PORT_OPTION 0] has to be sent. +# - run a server: run ser2net daemon, in /etc/ser2net.conf: +# 2000:telnet:0:/dev/ttyS0:9600 remctl banner +# ########################################################################### + +# How to identify ports? pySerial might want to support other protocols in the +# future, so lets use an URL scheme. +# for RFC2217 compliant servers we will use this: +# rfc2217://:[?option[&option...]] +# +# options: +# - "logging" set log level print diagnostic messages (e.g. "logging=debug") +# - "ign_set_control": do not look at the answers to SET_CONTROL +# - "poll_modem": issue NOTIFY_MODEMSTATE requests when CTS/DTR/RI/CD is read. +# Without this option it expects that the server sends notifications +# automatically on change (which most servers do and is according to the +# RFC). +# the order of the options is not relevant + +import logging +import socket +import struct +import threading +import time +try: + import urlparse +except ImportError: + import urllib.parse as urlparse +try: + import Queue +except ImportError: + import queue as Queue + +import serial +from serial.serialutil import SerialBase, SerialException, to_bytes, \ + iterbytes, portNotOpenError, Timeout + +# port string is expected to be something like this: +# rfc2217://host:port +# host may be an IP or including domain, whatever. +# port is 0...65535 + +# map log level names to constants. used in from_url() +LOGGER_LEVELS = { + 'debug': logging.DEBUG, + 'info': logging.INFO, + 'warning': logging.WARNING, + 'error': logging.ERROR, +} + + +# telnet protocol characters +SE = b'\xf0' # Subnegotiation End +NOP = b'\xf1' # No Operation +DM = b'\xf2' # Data Mark +BRK = b'\xf3' # Break +IP = b'\xf4' # Interrupt process +AO = b'\xf5' # Abort output +AYT = b'\xf6' # Are You There +EC = b'\xf7' # Erase Character +EL = b'\xf8' # Erase Line +GA = b'\xf9' # Go Ahead +SB = b'\xfa' # Subnegotiation Begin +WILL = b'\xfb' +WONT = b'\xfc' +DO = b'\xfd' +DONT = b'\xfe' +IAC = b'\xff' # Interpret As Command +IAC_DOUBLED = b'\xff\xff' + +# selected telnet options +BINARY = b'\x00' # 8-bit data path +ECHO = b'\x01' # echo +SGA = b'\x03' # suppress go ahead + +# RFC2217 +COM_PORT_OPTION = b'\x2c' + +# Client to Access Server +SET_BAUDRATE = b'\x01' +SET_DATASIZE = b'\x02' +SET_PARITY = b'\x03' +SET_STOPSIZE = b'\x04' +SET_CONTROL = b'\x05' +NOTIFY_LINESTATE = b'\x06' +NOTIFY_MODEMSTATE = b'\x07' +FLOWCONTROL_SUSPEND = b'\x08' +FLOWCONTROL_RESUME = b'\x09' +SET_LINESTATE_MASK = b'\x0a' +SET_MODEMSTATE_MASK = b'\x0b' +PURGE_DATA = b'\x0c' + +SERVER_SET_BAUDRATE = b'\x65' +SERVER_SET_DATASIZE = b'\x66' +SERVER_SET_PARITY = b'\x67' +SERVER_SET_STOPSIZE = b'\x68' +SERVER_SET_CONTROL = b'\x69' +SERVER_NOTIFY_LINESTATE = b'\x6a' +SERVER_NOTIFY_MODEMSTATE = b'\x6b' +SERVER_FLOWCONTROL_SUSPEND = b'\x6c' +SERVER_FLOWCONTROL_RESUME = b'\x6d' +SERVER_SET_LINESTATE_MASK = b'\x6e' +SERVER_SET_MODEMSTATE_MASK = b'\x6f' +SERVER_PURGE_DATA = b'\x70' + +RFC2217_ANSWER_MAP = { + SET_BAUDRATE: SERVER_SET_BAUDRATE, + SET_DATASIZE: SERVER_SET_DATASIZE, + SET_PARITY: SERVER_SET_PARITY, + SET_STOPSIZE: SERVER_SET_STOPSIZE, + SET_CONTROL: SERVER_SET_CONTROL, + NOTIFY_LINESTATE: SERVER_NOTIFY_LINESTATE, + NOTIFY_MODEMSTATE: SERVER_NOTIFY_MODEMSTATE, + FLOWCONTROL_SUSPEND: SERVER_FLOWCONTROL_SUSPEND, + FLOWCONTROL_RESUME: SERVER_FLOWCONTROL_RESUME, + SET_LINESTATE_MASK: SERVER_SET_LINESTATE_MASK, + SET_MODEMSTATE_MASK: SERVER_SET_MODEMSTATE_MASK, + PURGE_DATA: SERVER_PURGE_DATA, +} + +SET_CONTROL_REQ_FLOW_SETTING = b'\x00' # Request Com Port Flow Control Setting (outbound/both) +SET_CONTROL_USE_NO_FLOW_CONTROL = b'\x01' # Use No Flow Control (outbound/both) +SET_CONTROL_USE_SW_FLOW_CONTROL = b'\x02' # Use XON/XOFF Flow Control (outbound/both) +SET_CONTROL_USE_HW_FLOW_CONTROL = b'\x03' # Use HARDWARE Flow Control (outbound/both) +SET_CONTROL_REQ_BREAK_STATE = b'\x04' # Request BREAK State +SET_CONTROL_BREAK_ON = b'\x05' # Set BREAK State ON +SET_CONTROL_BREAK_OFF = b'\x06' # Set BREAK State OFF +SET_CONTROL_REQ_DTR = b'\x07' # Request DTR Signal State +SET_CONTROL_DTR_ON = b'\x08' # Set DTR Signal State ON +SET_CONTROL_DTR_OFF = b'\x09' # Set DTR Signal State OFF +SET_CONTROL_REQ_RTS = b'\x0a' # Request RTS Signal State +SET_CONTROL_RTS_ON = b'\x0b' # Set RTS Signal State ON +SET_CONTROL_RTS_OFF = b'\x0c' # Set RTS Signal State OFF +SET_CONTROL_REQ_FLOW_SETTING_IN = b'\x0d' # Request Com Port Flow Control Setting (inbound) +SET_CONTROL_USE_NO_FLOW_CONTROL_IN = b'\x0e' # Use No Flow Control (inbound) +SET_CONTROL_USE_SW_FLOW_CONTOL_IN = b'\x0f' # Use XON/XOFF Flow Control (inbound) +SET_CONTROL_USE_HW_FLOW_CONTOL_IN = b'\x10' # Use HARDWARE Flow Control (inbound) +SET_CONTROL_USE_DCD_FLOW_CONTROL = b'\x11' # Use DCD Flow Control (outbound/both) +SET_CONTROL_USE_DTR_FLOW_CONTROL = b'\x12' # Use DTR Flow Control (inbound) +SET_CONTROL_USE_DSR_FLOW_CONTROL = b'\x13' # Use DSR Flow Control (outbound/both) + +LINESTATE_MASK_TIMEOUT = 128 # Time-out Error +LINESTATE_MASK_SHIFTREG_EMPTY = 64 # Transfer Shift Register Empty +LINESTATE_MASK_TRANSREG_EMPTY = 32 # Transfer Holding Register Empty +LINESTATE_MASK_BREAK_DETECT = 16 # Break-detect Error +LINESTATE_MASK_FRAMING_ERROR = 8 # Framing Error +LINESTATE_MASK_PARTIY_ERROR = 4 # Parity Error +LINESTATE_MASK_OVERRUN_ERROR = 2 # Overrun Error +LINESTATE_MASK_DATA_READY = 1 # Data Ready + +MODEMSTATE_MASK_CD = 128 # Receive Line Signal Detect (also known as Carrier Detect) +MODEMSTATE_MASK_RI = 64 # Ring Indicator +MODEMSTATE_MASK_DSR = 32 # Data-Set-Ready Signal State +MODEMSTATE_MASK_CTS = 16 # Clear-To-Send Signal State +MODEMSTATE_MASK_CD_CHANGE = 8 # Delta Receive Line Signal Detect +MODEMSTATE_MASK_RI_CHANGE = 4 # Trailing-edge Ring Detector +MODEMSTATE_MASK_DSR_CHANGE = 2 # Delta Data-Set-Ready +MODEMSTATE_MASK_CTS_CHANGE = 1 # Delta Clear-To-Send + +PURGE_RECEIVE_BUFFER = b'\x01' # Purge access server receive data buffer +PURGE_TRANSMIT_BUFFER = b'\x02' # Purge access server transmit data buffer +PURGE_BOTH_BUFFERS = b'\x03' # Purge both the access server receive data + # buffer and the access server transmit data buffer + + +RFC2217_PARITY_MAP = { + serial.PARITY_NONE: 1, + serial.PARITY_ODD: 2, + serial.PARITY_EVEN: 3, + serial.PARITY_MARK: 4, + serial.PARITY_SPACE: 5, +} +RFC2217_REVERSE_PARITY_MAP = dict((v, k) for k, v in RFC2217_PARITY_MAP.items()) + +RFC2217_STOPBIT_MAP = { + serial.STOPBITS_ONE: 1, + serial.STOPBITS_ONE_POINT_FIVE: 3, + serial.STOPBITS_TWO: 2, +} +RFC2217_REVERSE_STOPBIT_MAP = dict((v, k) for k, v in RFC2217_STOPBIT_MAP.items()) + +# Telnet filter states +M_NORMAL = 0 +M_IAC_SEEN = 1 +M_NEGOTIATE = 2 + +# TelnetOption and TelnetSubnegotiation states +REQUESTED = 'REQUESTED' +ACTIVE = 'ACTIVE' +INACTIVE = 'INACTIVE' +REALLY_INACTIVE = 'REALLY_INACTIVE' + + +class TelnetOption(object): + """Manage a single telnet option, keeps track of DO/DONT WILL/WONT.""" + + def __init__(self, connection, name, option, send_yes, send_no, ack_yes, + ack_no, initial_state, activation_callback=None): + """\ + Initialize option. + :param connection: connection used to transmit answers + :param name: a readable name for debug outputs + :param send_yes: what to send when option is to be enabled. + :param send_no: what to send when option is to be disabled. + :param ack_yes: what to expect when remote agrees on option. + :param ack_no: what to expect when remote disagrees on option. + :param initial_state: options initialized with REQUESTED are tried to + be enabled on startup. use INACTIVE for all others. + """ + self.connection = connection + self.name = name + self.option = option + self.send_yes = send_yes + self.send_no = send_no + self.ack_yes = ack_yes + self.ack_no = ack_no + self.state = initial_state + self.active = False + self.activation_callback = activation_callback + + def __repr__(self): + """String for debug outputs""" + return "{o.name}:{o.active}({o.state})".format(o=self) + + def process_incoming(self, command): + """\ + A DO/DONT/WILL/WONT was received for this option, update state and + answer when needed. + """ + if command == self.ack_yes: + if self.state is REQUESTED: + self.state = ACTIVE + self.active = True + if self.activation_callback is not None: + self.activation_callback() + elif self.state is ACTIVE: + pass + elif self.state is INACTIVE: + self.state = ACTIVE + self.connection.telnet_send_option(self.send_yes, self.option) + self.active = True + if self.activation_callback is not None: + self.activation_callback() + elif self.state is REALLY_INACTIVE: + self.connection.telnet_send_option(self.send_no, self.option) + else: + raise ValueError('option in illegal state {!r}'.format(self)) + elif command == self.ack_no: + if self.state is REQUESTED: + self.state = INACTIVE + self.active = False + elif self.state is ACTIVE: + self.state = INACTIVE + self.connection.telnet_send_option(self.send_no, self.option) + self.active = False + elif self.state is INACTIVE: + pass + elif self.state is REALLY_INACTIVE: + pass + else: + raise ValueError('option in illegal state {!r}'.format(self)) + + +class TelnetSubnegotiation(object): + """\ + A object to handle subnegotiation of options. In this case actually + sub-sub options for RFC 2217. It is used to track com port options. + """ + + def __init__(self, connection, name, option, ack_option=None): + if ack_option is None: + ack_option = option + self.connection = connection + self.name = name + self.option = option + self.value = None + self.ack_option = ack_option + self.state = INACTIVE + + def __repr__(self): + """String for debug outputs.""" + return "{sn.name}:{sn.state}".format(sn=self) + + def set(self, value): + """\ + Request a change of the value. a request is sent to the server. if + the client needs to know if the change is performed he has to check the + state of this object. + """ + self.value = value + self.state = REQUESTED + self.connection.rfc2217_send_subnegotiation(self.option, self.value) + if self.connection.logger: + self.connection.logger.debug("SB Requesting {} -> {!r}".format(self.name, self.value)) + + def is_ready(self): + """\ + Check if answer from server has been received. when server rejects + the change, raise a ValueError. + """ + if self.state == REALLY_INACTIVE: + raise ValueError("remote rejected value for option {!r}".format(self.name)) + return self.state == ACTIVE + # add property to have a similar interface as TelnetOption + active = property(is_ready) + + def wait(self, timeout=3): + """\ + Wait until the subnegotiation has been acknowledged or timeout. It + can also throw a value error when the answer from the server does not + match the value sent. + """ + timeout_timer = Timeout(timeout) + while not timeout_timer.expired(): + time.sleep(0.05) # prevent 100% CPU load + if self.is_ready(): + break + else: + raise SerialException("timeout while waiting for option {!r}".format(self.name)) + + def check_answer(self, suboption): + """\ + Check an incoming subnegotiation block. The parameter already has + cut off the header like sub option number and com port option value. + """ + if self.value == suboption[:len(self.value)]: + self.state = ACTIVE + else: + # error propagation done in is_ready + self.state = REALLY_INACTIVE + if self.connection.logger: + self.connection.logger.debug("SB Answer {} -> {!r} -> {}".format(self.name, suboption, self.state)) + + +class Serial(SerialBase): + """Serial port implementation for RFC 2217 remote serial ports.""" + + BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200) + + def __init__(self, *args, **kwargs): + self._thread = None + self._socket = None + self._linestate = 0 + self._modemstate = None + self._modemstate_timeout = Timeout(-1) + self._remote_suspend_flow = False + self._write_lock = None + self.logger = None + self._ignore_set_control_answer = False + self._poll_modem_state = False + self._network_timeout = 3 + self._telnet_options = None + self._rfc2217_port_settings = None + self._rfc2217_options = None + self._read_buffer = None + super(Serial, self).__init__(*args, **kwargs) # must be last call in case of auto-open + + def open(self): + """\ + Open port with current settings. This may throw a SerialException + if the port cannot be opened. + """ + self.logger = None + self._ignore_set_control_answer = False + self._poll_modem_state = False + self._network_timeout = 3 + if self._port is None: + raise SerialException("Port must be configured before it can be used.") + if self.is_open: + raise SerialException("Port is already open.") + try: + self._socket = socket.create_connection(self.from_url(self.portstr), timeout=5) # XXX good value? + self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + except Exception as msg: + self._socket = None + raise SerialException("Could not open port {}: {}".format(self.portstr, msg)) + + # use a thread save queue as buffer. it also simplifies implementing + # the read timeout + self._read_buffer = Queue.Queue() + # to ensure that user writes does not interfere with internal + # telnet/rfc2217 options establish a lock + self._write_lock = threading.Lock() + # name the following separately so that, below, a check can be easily done + mandadory_options = [ + TelnetOption(self, 'we-BINARY', BINARY, WILL, WONT, DO, DONT, INACTIVE), + TelnetOption(self, 'we-RFC2217', COM_PORT_OPTION, WILL, WONT, DO, DONT, REQUESTED), + ] + # all supported telnet options + self._telnet_options = [ + TelnetOption(self, 'ECHO', ECHO, DO, DONT, WILL, WONT, REQUESTED), + TelnetOption(self, 'we-SGA', SGA, WILL, WONT, DO, DONT, REQUESTED), + TelnetOption(self, 'they-SGA', SGA, DO, DONT, WILL, WONT, REQUESTED), + TelnetOption(self, 'they-BINARY', BINARY, DO, DONT, WILL, WONT, INACTIVE), + TelnetOption(self, 'they-RFC2217', COM_PORT_OPTION, DO, DONT, WILL, WONT, REQUESTED), + ] + mandadory_options + # RFC 2217 specific states + # COM port settings + self._rfc2217_port_settings = { + 'baudrate': TelnetSubnegotiation(self, 'baudrate', SET_BAUDRATE, SERVER_SET_BAUDRATE), + 'datasize': TelnetSubnegotiation(self, 'datasize', SET_DATASIZE, SERVER_SET_DATASIZE), + 'parity': TelnetSubnegotiation(self, 'parity', SET_PARITY, SERVER_SET_PARITY), + 'stopsize': TelnetSubnegotiation(self, 'stopsize', SET_STOPSIZE, SERVER_SET_STOPSIZE), + } + # There are more subnegotiation objects, combine all in one dictionary + # for easy access + self._rfc2217_options = { + 'purge': TelnetSubnegotiation(self, 'purge', PURGE_DATA, SERVER_PURGE_DATA), + 'control': TelnetSubnegotiation(self, 'control', SET_CONTROL, SERVER_SET_CONTROL), + } + self._rfc2217_options.update(self._rfc2217_port_settings) + # cache for line and modem states that the server sends to us + self._linestate = 0 + self._modemstate = None + self._modemstate_timeout = Timeout(-1) + # RFC 2217 flow control between server and client + self._remote_suspend_flow = False + + self.is_open = True + self._thread = threading.Thread(target=self._telnet_read_loop) + self._thread.setDaemon(True) + self._thread.setName('pySerial RFC 2217 reader thread for {}'.format(self._port)) + self._thread.start() + + try: # must clean-up if open fails + # negotiate Telnet/RFC 2217 -> send initial requests + for option in self._telnet_options: + if option.state is REQUESTED: + self.telnet_send_option(option.send_yes, option.option) + # now wait until important options are negotiated + timeout = Timeout(self._network_timeout) + while not timeout.expired(): + time.sleep(0.05) # prevent 100% CPU load + if sum(o.active for o in mandadory_options) == sum(o.state != INACTIVE for o in mandadory_options): + break + else: + raise SerialException( + "Remote does not seem to support RFC2217 or BINARY mode {!r}".format(mandadory_options)) + if self.logger: + self.logger.info("Negotiated options: {}".format(self._telnet_options)) + + # fine, go on, set RFC 2271 specific things + self._reconfigure_port() + # all things set up get, now a clean start + if not self._dsrdtr: + self._update_dtr_state() + if not self._rtscts: + self._update_rts_state() + self.reset_input_buffer() + self.reset_output_buffer() + except: + self.close() + raise + + def _reconfigure_port(self): + """Set communication parameters on opened port.""" + if self._socket is None: + raise SerialException("Can only operate on open ports") + + # if self._timeout != 0 and self._interCharTimeout is not None: + # XXX + + if self._write_timeout is not None: + raise NotImplementedError('write_timeout is currently not supported') + # XXX + + # Setup the connection + # to get good performance, all parameter changes are sent first... + if not 0 < self._baudrate < 2 ** 32: + raise ValueError("invalid baudrate: {!r}".format(self._baudrate)) + self._rfc2217_port_settings['baudrate'].set(struct.pack(b'!I', self._baudrate)) + self._rfc2217_port_settings['datasize'].set(struct.pack(b'!B', self._bytesize)) + self._rfc2217_port_settings['parity'].set(struct.pack(b'!B', RFC2217_PARITY_MAP[self._parity])) + self._rfc2217_port_settings['stopsize'].set(struct.pack(b'!B', RFC2217_STOPBIT_MAP[self._stopbits])) + + # and now wait until parameters are active + items = self._rfc2217_port_settings.values() + if self.logger: + self.logger.debug("Negotiating settings: {}".format(items)) + timeout = Timeout(self._network_timeout) + while not timeout.expired(): + time.sleep(0.05) # prevent 100% CPU load + if sum(o.active for o in items) == len(items): + break + else: + raise SerialException("Remote does not accept parameter change (RFC2217): {!r}".format(items)) + if self.logger: + self.logger.info("Negotiated settings: {}".format(items)) + + if self._rtscts and self._xonxoff: + raise ValueError('xonxoff and rtscts together are not supported') + elif self._rtscts: + self.rfc2217_set_control(SET_CONTROL_USE_HW_FLOW_CONTROL) + elif self._xonxoff: + self.rfc2217_set_control(SET_CONTROL_USE_SW_FLOW_CONTROL) + else: + self.rfc2217_set_control(SET_CONTROL_USE_NO_FLOW_CONTROL) + + def close(self): + """Close port""" + self.is_open = False + if self._socket: + try: + self._socket.shutdown(socket.SHUT_RDWR) + self._socket.close() + except: + # ignore errors. + pass + if self._thread: + self._thread.join(7) # XXX more than socket timeout + self._thread = None + # in case of quick reconnects, give the server some time + time.sleep(0.3) + self._socket = None + + def from_url(self, url): + """\ + extract host and port from an URL string, other settings are extracted + an stored in instance + """ + parts = urlparse.urlsplit(url) + if parts.scheme != "rfc2217": + raise SerialException( + 'expected a string in the form ' + '"rfc2217://:[?option[&option...]]": ' + 'not starting with rfc2217:// ({!r})'.format(parts.scheme)) + try: + # process options now, directly altering self + for option, values in urlparse.parse_qs(parts.query, True).items(): + if option == 'logging': + logging.basicConfig() # XXX is that good to call it here? + self.logger = logging.getLogger('pySerial.rfc2217') + self.logger.setLevel(LOGGER_LEVELS[values[0]]) + self.logger.debug('enabled logging') + elif option == 'ign_set_control': + self._ignore_set_control_answer = True + elif option == 'poll_modem': + self._poll_modem_state = True + elif option == 'timeout': + self._network_timeout = float(values[0]) + else: + raise ValueError('unknown option: {!r}'.format(option)) + if not 0 <= parts.port < 65536: + raise ValueError("port not in range 0...65535") + except ValueError as e: + raise SerialException( + 'expected a string in the form ' + '"rfc2217://:[?option[&option...]]": {}'.format(e)) + return (parts.hostname, parts.port) + + # - - - - - - - - - - - - - - - - - - - - - - - - + + @property + def in_waiting(self): + """Return the number of bytes currently in the input buffer.""" + if not self.is_open: + raise portNotOpenError + return self._read_buffer.qsize() + + def read(self, size=1): + """\ + Read size bytes from the serial port. If a timeout is set it may + return less characters as requested. With no timeout it will block + until the requested number of bytes is read. + """ + if not self.is_open: + raise portNotOpenError + data = bytearray() + try: + timeout = Timeout(self._timeout) + while len(data) < size: + if self._thread is None: + raise SerialException('connection failed (reader thread died)') + data += self._read_buffer.get(True, timeout.time_left()) + if timeout.expired(): + break + except Queue.Empty: # -> timeout + pass + return bytes(data) + + def write(self, data): + """\ + Output the given byte string over the serial port. Can block if the + connection is blocked. May raise SerialException if the connection is + closed. + """ + if not self.is_open: + raise portNotOpenError + with self._write_lock: + try: + self._socket.sendall(to_bytes(data).replace(IAC, IAC_DOUBLED)) + except socket.error as e: + raise SerialException("connection failed (socket error): {}".format(e)) + return len(data) + + def reset_input_buffer(self): + """Clear input buffer, discarding all that is in the buffer.""" + if not self.is_open: + raise portNotOpenError + self.rfc2217_send_purge(PURGE_RECEIVE_BUFFER) + # empty read buffer + while self._read_buffer.qsize(): + self._read_buffer.get(False) + + def reset_output_buffer(self): + """\ + Clear output buffer, aborting the current output and + discarding all that is in the buffer. + """ + if not self.is_open: + raise portNotOpenError + self.rfc2217_send_purge(PURGE_TRANSMIT_BUFFER) + + def _update_break_state(self): + """\ + Set break: Controls TXD. When active, to transmitting is + possible. + """ + if not self.is_open: + raise portNotOpenError + if self.logger: + self.logger.info('set BREAK to {}'.format('active' if self._break_state else 'inactive')) + if self._break_state: + self.rfc2217_set_control(SET_CONTROL_BREAK_ON) + else: + self.rfc2217_set_control(SET_CONTROL_BREAK_OFF) + + def _update_rts_state(self): + """Set terminal status line: Request To Send.""" + if not self.is_open: + raise portNotOpenError + if self.logger: + self.logger.info('set RTS to {}'.format('active' if self._rts_state else 'inactive')) + if self._rts_state: + self.rfc2217_set_control(SET_CONTROL_RTS_ON) + else: + self.rfc2217_set_control(SET_CONTROL_RTS_OFF) + + def _update_dtr_state(self): + """Set terminal status line: Data Terminal Ready.""" + if not self.is_open: + raise portNotOpenError + if self.logger: + self.logger.info('set DTR to {}'.format('active' if self._dtr_state else 'inactive')) + if self._dtr_state: + self.rfc2217_set_control(SET_CONTROL_DTR_ON) + else: + self.rfc2217_set_control(SET_CONTROL_DTR_OFF) + + @property + def cts(self): + """Read terminal status line: Clear To Send.""" + if not self.is_open: + raise portNotOpenError + return bool(self.get_modem_state() & MODEMSTATE_MASK_CTS) + + @property + def dsr(self): + """Read terminal status line: Data Set Ready.""" + if not self.is_open: + raise portNotOpenError + return bool(self.get_modem_state() & MODEMSTATE_MASK_DSR) + + @property + def ri(self): + """Read terminal status line: Ring Indicator.""" + if not self.is_open: + raise portNotOpenError + return bool(self.get_modem_state() & MODEMSTATE_MASK_RI) + + @property + def cd(self): + """Read terminal status line: Carrier Detect.""" + if not self.is_open: + raise portNotOpenError + return bool(self.get_modem_state() & MODEMSTATE_MASK_CD) + + # - - - platform specific - - - + # None so far + + # - - - RFC2217 specific - - - + + def _telnet_read_loop(self): + """Read loop for the socket.""" + mode = M_NORMAL + suboption = None + try: + while self.is_open: + try: + data = self._socket.recv(1024) + except socket.timeout: + # just need to get out of recv form time to time to check if + # still alive + continue + except socket.error as e: + # connection fails -> terminate loop + if self.logger: + self.logger.debug("socket error in reader thread: {}".format(e)) + break + if not data: + break # lost connection + for byte in iterbytes(data): + if mode == M_NORMAL: + # interpret as command or as data + if byte == IAC: + mode = M_IAC_SEEN + else: + # store data in read buffer or sub option buffer + # depending on state + if suboption is not None: + suboption += byte + else: + self._read_buffer.put(byte) + elif mode == M_IAC_SEEN: + if byte == IAC: + # interpret as command doubled -> insert character + # itself + if suboption is not None: + suboption += IAC + else: + self._read_buffer.put(IAC) + mode = M_NORMAL + elif byte == SB: + # sub option start + suboption = bytearray() + mode = M_NORMAL + elif byte == SE: + # sub option end -> process it now + self._telnet_process_subnegotiation(bytes(suboption)) + suboption = None + mode = M_NORMAL + elif byte in (DO, DONT, WILL, WONT): + # negotiation + telnet_command = byte + mode = M_NEGOTIATE + else: + # other telnet commands + self._telnet_process_command(byte) + mode = M_NORMAL + elif mode == M_NEGOTIATE: # DO, DONT, WILL, WONT was received, option now following + self._telnet_negotiate_option(telnet_command, byte) + mode = M_NORMAL + finally: + self._thread = None + if self.logger: + self.logger.debug("read thread terminated") + + # - incoming telnet commands and options + + def _telnet_process_command(self, command): + """Process commands other than DO, DONT, WILL, WONT.""" + # Currently none. RFC2217 only uses negotiation and subnegotiation. + if self.logger: + self.logger.warning("ignoring Telnet command: {!r}".format(command)) + + def _telnet_negotiate_option(self, command, option): + """Process incoming DO, DONT, WILL, WONT.""" + # check our registered telnet options and forward command to them + # they know themselves if they have to answer or not + known = False + for item in self._telnet_options: + # can have more than one match! as some options are duplicated for + # 'us' and 'them' + if item.option == option: + item.process_incoming(command) + known = True + if not known: + # handle unknown options + # only answer to positive requests and deny them + if command == WILL or command == DO: + self.telnet_send_option((DONT if command == WILL else WONT), option) + if self.logger: + self.logger.warning("rejected Telnet option: {!r}".format(option)) + + def _telnet_process_subnegotiation(self, suboption): + """Process subnegotiation, the data between IAC SB and IAC SE.""" + if suboption[0:1] == COM_PORT_OPTION: + if suboption[1:2] == SERVER_NOTIFY_LINESTATE and len(suboption) >= 3: + self._linestate = ord(suboption[2:3]) # ensure it is a number + if self.logger: + self.logger.info("NOTIFY_LINESTATE: {}".format(self._linestate)) + elif suboption[1:2] == SERVER_NOTIFY_MODEMSTATE and len(suboption) >= 3: + self._modemstate = ord(suboption[2:3]) # ensure it is a number + if self.logger: + self.logger.info("NOTIFY_MODEMSTATE: {}".format(self._modemstate)) + # update time when we think that a poll would make sense + self._modemstate_timeout.restart(0.3) + elif suboption[1:2] == FLOWCONTROL_SUSPEND: + self._remote_suspend_flow = True + elif suboption[1:2] == FLOWCONTROL_RESUME: + self._remote_suspend_flow = False + else: + for item in self._rfc2217_options.values(): + if item.ack_option == suboption[1:2]: + #~ print "processing COM_PORT_OPTION: %r" % list(suboption[1:]) + item.check_answer(bytes(suboption[2:])) + break + else: + if self.logger: + self.logger.warning("ignoring COM_PORT_OPTION: {!r}".format(suboption)) + else: + if self.logger: + self.logger.warning("ignoring subnegotiation: {!r}".format(suboption)) + + # - outgoing telnet commands and options + + def _internal_raw_write(self, data): + """internal socket write with no data escaping. used to send telnet stuff.""" + with self._write_lock: + self._socket.sendall(data) + + def telnet_send_option(self, action, option): + """Send DO, DONT, WILL, WONT.""" + self._internal_raw_write(IAC + action + option) + + def rfc2217_send_subnegotiation(self, option, value=b''): + """Subnegotiation of RFC2217 parameters.""" + value = value.replace(IAC, IAC_DOUBLED) + self._internal_raw_write(IAC + SB + COM_PORT_OPTION + option + value + IAC + SE) + + def rfc2217_send_purge(self, value): + """\ + Send purge request to the remote. + (PURGE_RECEIVE_BUFFER / PURGE_TRANSMIT_BUFFER / PURGE_BOTH_BUFFERS) + """ + item = self._rfc2217_options['purge'] + item.set(value) # transmit desired purge type + item.wait(self._network_timeout) # wait for acknowledge from the server + + def rfc2217_set_control(self, value): + """transmit change of control line to remote""" + item = self._rfc2217_options['control'] + item.set(value) # transmit desired control type + if self._ignore_set_control_answer: + # answers are ignored when option is set. compatibility mode for + # servers that answer, but not the expected one... (or no answer + # at all) i.e. sredird + time.sleep(0.1) # this helps getting the unit tests passed + else: + item.wait(self._network_timeout) # wait for acknowledge from the server + + def rfc2217_flow_server_ready(self): + """\ + check if server is ready to receive data. block for some time when + not. + """ + #~ if self._remote_suspend_flow: + #~ wait--- + + def get_modem_state(self): + """\ + get last modem state (cached value. If value is "old", request a new + one. This cache helps that we don't issue to many requests when e.g. all + status lines, one after the other is queried by the user (CTS, DSR + etc.) + """ + # active modem state polling enabled? is the value fresh enough? + if self._poll_modem_state and self._modemstate_timeout.expired(): + if self.logger: + self.logger.debug('polling modem state') + # when it is older, request an update + self.rfc2217_send_subnegotiation(NOTIFY_MODEMSTATE) + timeout = Timeout(self._network_timeout) + while not timeout.expired(): + time.sleep(0.05) # prevent 100% CPU load + # when expiration time is updated, it means that there is a new + # value + if not self._modemstate_timeout.expired(): + break + else: + if self.logger: + self.logger.warning('poll for modem state failed') + # even when there is a timeout, do not generate an error just + # return the last known value. this way we can support buggy + # servers that do not respond to polls, but send automatic + # updates. + if self._modemstate is not None: + if self.logger: + self.logger.debug('using cached modem state') + return self._modemstate + else: + # never received a notification from the server + raise SerialException("remote sends no NOTIFY_MODEMSTATE") + + +############################################################################# +# The following is code that helps implementing an RFC 2217 server. + +class PortManager(object): + """\ + This class manages the state of Telnet and RFC 2217. It needs a serial + instance and a connection to work with. Connection is expected to implement + a (thread safe) write function, that writes the string to the network. + """ + + def __init__(self, serial_port, connection, logger=None): + self.serial = serial_port + self.connection = connection + self.logger = logger + self._client_is_rfc2217 = False + + # filter state machine + self.mode = M_NORMAL + self.suboption = None + self.telnet_command = None + + # states for modem/line control events + self.modemstate_mask = 255 + self.last_modemstate = None + self.linstate_mask = 0 + + # all supported telnet options + self._telnet_options = [ + TelnetOption(self, 'ECHO', ECHO, WILL, WONT, DO, DONT, REQUESTED), + TelnetOption(self, 'we-SGA', SGA, WILL, WONT, DO, DONT, REQUESTED), + TelnetOption(self, 'they-SGA', SGA, DO, DONT, WILL, WONT, INACTIVE), + TelnetOption(self, 'we-BINARY', BINARY, WILL, WONT, DO, DONT, INACTIVE), + TelnetOption(self, 'they-BINARY', BINARY, DO, DONT, WILL, WONT, REQUESTED), + TelnetOption(self, 'we-RFC2217', COM_PORT_OPTION, WILL, WONT, DO, DONT, REQUESTED, self._client_ok), + TelnetOption(self, 'they-RFC2217', COM_PORT_OPTION, DO, DONT, WILL, WONT, INACTIVE, self._client_ok), + ] + + # negotiate Telnet/RFC2217 -> send initial requests + if self.logger: + self.logger.debug("requesting initial Telnet/RFC 2217 options") + for option in self._telnet_options: + if option.state is REQUESTED: + self.telnet_send_option(option.send_yes, option.option) + # issue 1st modem state notification + + def _client_ok(self): + """\ + callback of telnet option. It gets called when option is activated. + This one here is used to detect when the client agrees on RFC 2217. A + flag is set so that other functions like check_modem_lines know if the + client is OK. + """ + # The callback is used for we and they so if one party agrees, we're + # already happy. it seems not all servers do the negotiation correctly + # and i guess there are incorrect clients too.. so be happy if client + # answers one or the other positively. + self._client_is_rfc2217 = True + if self.logger: + self.logger.info("client accepts RFC 2217") + # this is to ensure that the client gets a notification, even if there + # was no change + self.check_modem_lines(force_notification=True) + + # - outgoing telnet commands and options + + def telnet_send_option(self, action, option): + """Send DO, DONT, WILL, WONT.""" + self.connection.write(IAC + action + option) + + def rfc2217_send_subnegotiation(self, option, value=b''): + """Subnegotiation of RFC 2217 parameters.""" + value = value.replace(IAC, IAC_DOUBLED) + self.connection.write(IAC + SB + COM_PORT_OPTION + option + value + IAC + SE) + + # - check modem lines, needs to be called periodically from user to + # establish polling + + def check_modem_lines(self, force_notification=False): + """\ + read control lines from serial port and compare the last value sent to remote. + send updates on changes. + """ + modemstate = ( + (self.serial.cts and MODEMSTATE_MASK_CTS) | + (self.serial.dsr and MODEMSTATE_MASK_DSR) | + (self.serial.ri and MODEMSTATE_MASK_RI) | + (self.serial.cd and MODEMSTATE_MASK_CD)) + # check what has changed + deltas = modemstate ^ (self.last_modemstate or 0) # when last is None -> 0 + if deltas & MODEMSTATE_MASK_CTS: + modemstate |= MODEMSTATE_MASK_CTS_CHANGE + if deltas & MODEMSTATE_MASK_DSR: + modemstate |= MODEMSTATE_MASK_DSR_CHANGE + if deltas & MODEMSTATE_MASK_RI: + modemstate |= MODEMSTATE_MASK_RI_CHANGE + if deltas & MODEMSTATE_MASK_CD: + modemstate |= MODEMSTATE_MASK_CD_CHANGE + # if new state is different and the mask allows this change, send + # notification. suppress notifications when client is not rfc2217 + if modemstate != self.last_modemstate or force_notification: + if (self._client_is_rfc2217 and (modemstate & self.modemstate_mask)) or force_notification: + self.rfc2217_send_subnegotiation( + SERVER_NOTIFY_MODEMSTATE, + to_bytes([modemstate & self.modemstate_mask])) + if self.logger: + self.logger.info("NOTIFY_MODEMSTATE: {}".format(modemstate)) + # save last state, but forget about deltas. + # otherwise it would also notify about changing deltas which is + # probably not very useful + self.last_modemstate = modemstate & 0xf0 + + # - outgoing data escaping + + def escape(self, data): + """\ + This generator function is for the user. All outgoing data has to be + properly escaped, so that no IAC character in the data stream messes up + the Telnet state machine in the server. + + socket.sendall(escape(data)) + """ + for byte in iterbytes(data): + if byte == IAC: + yield IAC + yield IAC + else: + yield byte + + # - incoming data filter + + def filter(self, data): + """\ + Handle a bunch of incoming bytes. This is a generator. It will yield + all characters not of interest for Telnet/RFC 2217. + + The idea is that the reader thread pushes data from the socket through + this filter: + + for byte in filter(socket.recv(1024)): + # do things like CR/LF conversion/whatever + # and write data to the serial port + serial.write(byte) + + (socket error handling code left as exercise for the reader) + """ + for byte in iterbytes(data): + if self.mode == M_NORMAL: + # interpret as command or as data + if byte == IAC: + self.mode = M_IAC_SEEN + else: + # store data in sub option buffer or pass it to our + # consumer depending on state + if self.suboption is not None: + self.suboption += byte + else: + yield byte + elif self.mode == M_IAC_SEEN: + if byte == IAC: + # interpret as command doubled -> insert character + # itself + if self.suboption is not None: + self.suboption += byte + else: + yield byte + self.mode = M_NORMAL + elif byte == SB: + # sub option start + self.suboption = bytearray() + self.mode = M_NORMAL + elif byte == SE: + # sub option end -> process it now + self._telnet_process_subnegotiation(bytes(self.suboption)) + self.suboption = None + self.mode = M_NORMAL + elif byte in (DO, DONT, WILL, WONT): + # negotiation + self.telnet_command = byte + self.mode = M_NEGOTIATE + else: + # other telnet commands + self._telnet_process_command(byte) + self.mode = M_NORMAL + elif self.mode == M_NEGOTIATE: # DO, DONT, WILL, WONT was received, option now following + self._telnet_negotiate_option(self.telnet_command, byte) + self.mode = M_NORMAL + + # - incoming telnet commands and options + + def _telnet_process_command(self, command): + """Process commands other than DO, DONT, WILL, WONT.""" + # Currently none. RFC2217 only uses negotiation and subnegotiation. + if self.logger: + self.logger.warning("ignoring Telnet command: {!r}".format(command)) + + def _telnet_negotiate_option(self, command, option): + """Process incoming DO, DONT, WILL, WONT.""" + # check our registered telnet options and forward command to them + # they know themselves if they have to answer or not + known = False + for item in self._telnet_options: + # can have more than one match! as some options are duplicated for + # 'us' and 'them' + if item.option == option: + item.process_incoming(command) + known = True + if not known: + # handle unknown options + # only answer to positive requests and deny them + if command == WILL or command == DO: + self.telnet_send_option((DONT if command == WILL else WONT), option) + if self.logger: + self.logger.warning("rejected Telnet option: {!r}".format(option)) + + def _telnet_process_subnegotiation(self, suboption): + """Process subnegotiation, the data between IAC SB and IAC SE.""" + if suboption[0:1] == COM_PORT_OPTION: + if self.logger: + self.logger.debug('received COM_PORT_OPTION: {!r}'.format(suboption)) + if suboption[1:2] == SET_BAUDRATE: + backup = self.serial.baudrate + try: + (baudrate,) = struct.unpack(b"!I", suboption[2:6]) + if baudrate != 0: + self.serial.baudrate = baudrate + except ValueError as e: + if self.logger: + self.logger.error("failed to set baud rate: {}".format(e)) + self.serial.baudrate = backup + else: + if self.logger: + self.logger.info("{} baud rate: {}".format('set' if baudrate else 'get', self.serial.baudrate)) + self.rfc2217_send_subnegotiation(SERVER_SET_BAUDRATE, struct.pack(b"!I", self.serial.baudrate)) + elif suboption[1:2] == SET_DATASIZE: + backup = self.serial.bytesize + try: + (datasize,) = struct.unpack(b"!B", suboption[2:3]) + if datasize != 0: + self.serial.bytesize = datasize + except ValueError as e: + if self.logger: + self.logger.error("failed to set data size: {}".format(e)) + self.serial.bytesize = backup + else: + if self.logger: + self.logger.info("{} data size: {}".format('set' if datasize else 'get', self.serial.bytesize)) + self.rfc2217_send_subnegotiation(SERVER_SET_DATASIZE, struct.pack(b"!B", self.serial.bytesize)) + elif suboption[1:2] == SET_PARITY: + backup = self.serial.parity + try: + parity = struct.unpack(b"!B", suboption[2:3])[0] + if parity != 0: + self.serial.parity = RFC2217_REVERSE_PARITY_MAP[parity] + except ValueError as e: + if self.logger: + self.logger.error("failed to set parity: {}".format(e)) + self.serial.parity = backup + else: + if self.logger: + self.logger.info("{} parity: {}".format('set' if parity else 'get', self.serial.parity)) + self.rfc2217_send_subnegotiation( + SERVER_SET_PARITY, + struct.pack(b"!B", RFC2217_PARITY_MAP[self.serial.parity])) + elif suboption[1:2] == SET_STOPSIZE: + backup = self.serial.stopbits + try: + stopbits = struct.unpack(b"!B", suboption[2:3])[0] + if stopbits != 0: + self.serial.stopbits = RFC2217_REVERSE_STOPBIT_MAP[stopbits] + except ValueError as e: + if self.logger: + self.logger.error("failed to set stop bits: {}".format(e)) + self.serial.stopbits = backup + else: + if self.logger: + self.logger.info("{} stop bits: {}".format('set' if stopbits else 'get', self.serial.stopbits)) + self.rfc2217_send_subnegotiation( + SERVER_SET_STOPSIZE, + struct.pack(b"!B", RFC2217_STOPBIT_MAP[self.serial.stopbits])) + elif suboption[1:2] == SET_CONTROL: + if suboption[2:3] == SET_CONTROL_REQ_FLOW_SETTING: + if self.serial.xonxoff: + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_SW_FLOW_CONTROL) + elif self.serial.rtscts: + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_HW_FLOW_CONTROL) + else: + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_NO_FLOW_CONTROL) + elif suboption[2:3] == SET_CONTROL_USE_NO_FLOW_CONTROL: + self.serial.xonxoff = False + self.serial.rtscts = False + if self.logger: + self.logger.info("changed flow control to None") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_NO_FLOW_CONTROL) + elif suboption[2:3] == SET_CONTROL_USE_SW_FLOW_CONTROL: + self.serial.xonxoff = True + if self.logger: + self.logger.info("changed flow control to XON/XOFF") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_SW_FLOW_CONTROL) + elif suboption[2:3] == SET_CONTROL_USE_HW_FLOW_CONTROL: + self.serial.rtscts = True + if self.logger: + self.logger.info("changed flow control to RTS/CTS") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_HW_FLOW_CONTROL) + elif suboption[2:3] == SET_CONTROL_REQ_BREAK_STATE: + if self.logger: + self.logger.warning("requested break state - not implemented") + pass # XXX needs cached value + elif suboption[2:3] == SET_CONTROL_BREAK_ON: + self.serial.break_condition = True + if self.logger: + self.logger.info("changed BREAK to active") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_BREAK_ON) + elif suboption[2:3] == SET_CONTROL_BREAK_OFF: + self.serial.break_condition = False + if self.logger: + self.logger.info("changed BREAK to inactive") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_BREAK_OFF) + elif suboption[2:3] == SET_CONTROL_REQ_DTR: + if self.logger: + self.logger.warning("requested DTR state - not implemented") + pass # XXX needs cached value + elif suboption[2:3] == SET_CONTROL_DTR_ON: + self.serial.dtr = True + if self.logger: + self.logger.info("changed DTR to active") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_DTR_ON) + elif suboption[2:3] == SET_CONTROL_DTR_OFF: + self.serial.dtr = False + if self.logger: + self.logger.info("changed DTR to inactive") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_DTR_OFF) + elif suboption[2:3] == SET_CONTROL_REQ_RTS: + if self.logger: + self.logger.warning("requested RTS state - not implemented") + pass # XXX needs cached value + #~ self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_ON) + elif suboption[2:3] == SET_CONTROL_RTS_ON: + self.serial.rts = True + if self.logger: + self.logger.info("changed RTS to active") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_ON) + elif suboption[2:3] == SET_CONTROL_RTS_OFF: + self.serial.rts = False + if self.logger: + self.logger.info("changed RTS to inactive") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_OFF) + #~ elif suboption[2:3] == SET_CONTROL_REQ_FLOW_SETTING_IN: + #~ elif suboption[2:3] == SET_CONTROL_USE_NO_FLOW_CONTROL_IN: + #~ elif suboption[2:3] == SET_CONTROL_USE_SW_FLOW_CONTOL_IN: + #~ elif suboption[2:3] == SET_CONTROL_USE_HW_FLOW_CONTOL_IN: + #~ elif suboption[2:3] == SET_CONTROL_USE_DCD_FLOW_CONTROL: + #~ elif suboption[2:3] == SET_CONTROL_USE_DTR_FLOW_CONTROL: + #~ elif suboption[2:3] == SET_CONTROL_USE_DSR_FLOW_CONTROL: + elif suboption[1:2] == NOTIFY_LINESTATE: + # client polls for current state + self.rfc2217_send_subnegotiation( + SERVER_NOTIFY_LINESTATE, + to_bytes([0])) # sorry, nothing like that implemented + elif suboption[1:2] == NOTIFY_MODEMSTATE: + if self.logger: + self.logger.info("request for modem state") + # client polls for current state + self.check_modem_lines(force_notification=True) + elif suboption[1:2] == FLOWCONTROL_SUSPEND: + if self.logger: + self.logger.info("suspend") + self._remote_suspend_flow = True + elif suboption[1:2] == FLOWCONTROL_RESUME: + if self.logger: + self.logger.info("resume") + self._remote_suspend_flow = False + elif suboption[1:2] == SET_LINESTATE_MASK: + self.linstate_mask = ord(suboption[2:3]) # ensure it is a number + if self.logger: + self.logger.info("line state mask: 0x{:02x}".format(self.linstate_mask)) + elif suboption[1:2] == SET_MODEMSTATE_MASK: + self.modemstate_mask = ord(suboption[2:3]) # ensure it is a number + if self.logger: + self.logger.info("modem state mask: 0x{:02x}".format(self.modemstate_mask)) + elif suboption[1:2] == PURGE_DATA: + if suboption[2:3] == PURGE_RECEIVE_BUFFER: + self.serial.reset_input_buffer() + if self.logger: + self.logger.info("purge in") + self.rfc2217_send_subnegotiation(SERVER_PURGE_DATA, PURGE_RECEIVE_BUFFER) + elif suboption[2:3] == PURGE_TRANSMIT_BUFFER: + self.serial.reset_output_buffer() + if self.logger: + self.logger.info("purge out") + self.rfc2217_send_subnegotiation(SERVER_PURGE_DATA, PURGE_TRANSMIT_BUFFER) + elif suboption[2:3] == PURGE_BOTH_BUFFERS: + self.serial.reset_input_buffer() + self.serial.reset_output_buffer() + if self.logger: + self.logger.info("purge both") + self.rfc2217_send_subnegotiation(SERVER_PURGE_DATA, PURGE_BOTH_BUFFERS) + else: + if self.logger: + self.logger.error("undefined PURGE_DATA: {!r}".format(list(suboption[2:]))) + else: + if self.logger: + self.logger.error("undefined COM_PORT_OPTION: {!r}".format(list(suboption[1:]))) + else: + if self.logger: + self.logger.warning("unknown subnegotiation: {!r}".format(suboption)) + + +# simple client test +if __name__ == '__main__': + import sys + s = Serial('rfc2217://localhost:7000', 115200) + sys.stdout.write('{}\n'.format(s)) + + sys.stdout.write("write...\n") + s.write(b"hello\n") + s.flush() + sys.stdout.write("read: {}\n".format(s.read(5))) + s.close() diff --git a/dist/bin/esptool/serial/rs485.py b/dist/bin/esptool/serial/rs485.py new file mode 100644 index 000000000..293935071 --- /dev/null +++ b/dist/bin/esptool/serial/rs485.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python + +# RS485 support +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +"""\ +The settings for RS485 are stored in a dedicated object that can be applied to +serial ports (where supported). +NOTE: Some implementations may only support a subset of the settings. +""" + +import time +import serial + + +class RS485Settings(object): + def __init__( + self, + rts_level_for_tx=True, + rts_level_for_rx=False, + loopback=False, + delay_before_tx=None, + delay_before_rx=None): + self.rts_level_for_tx = rts_level_for_tx + self.rts_level_for_rx = rts_level_for_rx + self.loopback = loopback + self.delay_before_tx = delay_before_tx + self.delay_before_rx = delay_before_rx + + +class RS485(serial.Serial): + """\ + A subclass that replaces the write method with one that toggles RTS + according to the RS485 settings. + + NOTE: This may work unreliably on some serial ports (control signals not + synchronized or delayed compared to data). Using delays may be + unreliable (varying times, larger than expected) as the OS may not + support very fine grained delays (no smaller than in the order of + tens of milliseconds). + + NOTE: Some implementations support this natively. Better performance + can be expected when the native version is used. + + NOTE: The loopback property is ignored by this implementation. The actual + behavior depends on the used hardware. + + Usage: + + ser = RS485(...) + ser.rs485_mode = RS485Settings(...) + ser.write(b'hello') + """ + + def __init__(self, *args, **kwargs): + super(RS485, self).__init__(*args, **kwargs) + self._alternate_rs485_settings = None + + def write(self, b): + """Write to port, controlling RTS before and after transmitting.""" + if self._alternate_rs485_settings is not None: + # apply level for TX and optional delay + self.setRTS(self._alternate_rs485_settings.rts_level_for_tx) + if self._alternate_rs485_settings.delay_before_tx is not None: + time.sleep(self._alternate_rs485_settings.delay_before_tx) + # write and wait for data to be written + super(RS485, self).write(b) + super(RS485, self).flush() + # optional delay and apply level for RX + if self._alternate_rs485_settings.delay_before_rx is not None: + time.sleep(self._alternate_rs485_settings.delay_before_rx) + self.setRTS(self._alternate_rs485_settings.rts_level_for_rx) + else: + super(RS485, self).write(b) + + # redirect where the property stores the settings so that underlying Serial + # instance does not see them + @property + def rs485_mode(self): + """\ + Enable RS485 mode and apply new settings, set to None to disable. + See serial.rs485.RS485Settings for more info about the value. + """ + return self._alternate_rs485_settings + + @rs485_mode.setter + def rs485_mode(self, rs485_settings): + self._alternate_rs485_settings = rs485_settings diff --git a/dist/bin/esptool/serial/serialcli.py b/dist/bin/esptool/serial/serialcli.py new file mode 100644 index 000000000..0727a5256 --- /dev/null +++ b/dist/bin/esptool/serial/serialcli.py @@ -0,0 +1,251 @@ +#! python +# +# Backend for .NET/Mono (IronPython), .NET >= 2 +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2008-2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +import System +import System.IO.Ports +from serial.serialutil import * + +# must invoke function with byte array, make a helper to convert strings +# to byte arrays +sab = System.Array[System.Byte] + + +def as_byte_array(string): + return sab([ord(x) for x in string]) # XXX will require adaption when run with a 3.x compatible IronPython + + +class Serial(SerialBase): + """Serial port implementation for .NET/Mono.""" + + BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200) + + def open(self): + """\ + Open port with current settings. This may throw a SerialException + if the port cannot be opened. + """ + if self._port is None: + raise SerialException("Port must be configured before it can be used.") + if self.is_open: + raise SerialException("Port is already open.") + try: + self._port_handle = System.IO.Ports.SerialPort(self.portstr) + except Exception as msg: + self._port_handle = None + raise SerialException("could not open port %s: %s" % (self.portstr, msg)) + + # if RTS and/or DTR are not set before open, they default to True + if self._rts_state is None: + self._rts_state = True + if self._dtr_state is None: + self._dtr_state = True + + self._reconfigure_port() + self._port_handle.Open() + self.is_open = True + if not self._dsrdtr: + self._update_dtr_state() + if not self._rtscts: + self._update_rts_state() + self.reset_input_buffer() + + def _reconfigure_port(self): + """Set communication parameters on opened port.""" + if not self._port_handle: + raise SerialException("Can only operate on a valid port handle") + + #~ self._port_handle.ReceivedBytesThreshold = 1 + + if self._timeout is None: + self._port_handle.ReadTimeout = System.IO.Ports.SerialPort.InfiniteTimeout + else: + self._port_handle.ReadTimeout = int(self._timeout * 1000) + + # if self._timeout != 0 and self._interCharTimeout is not None: + # timeouts = (int(self._interCharTimeout * 1000),) + timeouts[1:] + + if self._write_timeout is None: + self._port_handle.WriteTimeout = System.IO.Ports.SerialPort.InfiniteTimeout + else: + self._port_handle.WriteTimeout = int(self._write_timeout * 1000) + + # Setup the connection info. + try: + self._port_handle.BaudRate = self._baudrate + except IOError as e: + # catch errors from illegal baudrate settings + raise ValueError(str(e)) + + if self._bytesize == FIVEBITS: + self._port_handle.DataBits = 5 + elif self._bytesize == SIXBITS: + self._port_handle.DataBits = 6 + elif self._bytesize == SEVENBITS: + self._port_handle.DataBits = 7 + elif self._bytesize == EIGHTBITS: + self._port_handle.DataBits = 8 + else: + raise ValueError("Unsupported number of data bits: %r" % self._bytesize) + + if self._parity == PARITY_NONE: + self._port_handle.Parity = getattr(System.IO.Ports.Parity, 'None') # reserved keyword in Py3k + elif self._parity == PARITY_EVEN: + self._port_handle.Parity = System.IO.Ports.Parity.Even + elif self._parity == PARITY_ODD: + self._port_handle.Parity = System.IO.Ports.Parity.Odd + elif self._parity == PARITY_MARK: + self._port_handle.Parity = System.IO.Ports.Parity.Mark + elif self._parity == PARITY_SPACE: + self._port_handle.Parity = System.IO.Ports.Parity.Space + else: + raise ValueError("Unsupported parity mode: %r" % self._parity) + + if self._stopbits == STOPBITS_ONE: + self._port_handle.StopBits = System.IO.Ports.StopBits.One + elif self._stopbits == STOPBITS_ONE_POINT_FIVE: + self._port_handle.StopBits = System.IO.Ports.StopBits.OnePointFive + elif self._stopbits == STOPBITS_TWO: + self._port_handle.StopBits = System.IO.Ports.StopBits.Two + else: + raise ValueError("Unsupported number of stop bits: %r" % self._stopbits) + + if self._rtscts and self._xonxoff: + self._port_handle.Handshake = System.IO.Ports.Handshake.RequestToSendXOnXOff + elif self._rtscts: + self._port_handle.Handshake = System.IO.Ports.Handshake.RequestToSend + elif self._xonxoff: + self._port_handle.Handshake = System.IO.Ports.Handshake.XOnXOff + else: + self._port_handle.Handshake = getattr(System.IO.Ports.Handshake, 'None') # reserved keyword in Py3k + + #~ def __del__(self): + #~ self.close() + + def close(self): + """Close port""" + if self.is_open: + if self._port_handle: + try: + self._port_handle.Close() + except System.IO.Ports.InvalidOperationException: + # ignore errors. can happen for unplugged USB serial devices + pass + self._port_handle = None + self.is_open = False + + # - - - - - - - - - - - - - - - - - - - - - - - - + + @property + def in_waiting(self): + """Return the number of characters currently in the input buffer.""" + if not self.is_open: + raise portNotOpenError + return self._port_handle.BytesToRead + + def read(self, size=1): + """\ + Read size bytes from the serial port. If a timeout is set it may + return less characters as requested. With no timeout it will block + until the requested number of bytes is read. + """ + if not self.is_open: + raise portNotOpenError + # must use single byte reads as this is the only way to read + # without applying encodings + data = bytearray() + while size: + try: + data.append(self._port_handle.ReadByte()) + except System.TimeoutException: + break + else: + size -= 1 + return bytes(data) + + def write(self, data): + """Output the given string over the serial port.""" + if not self.is_open: + raise portNotOpenError + #~ if not isinstance(data, (bytes, bytearray)): + #~ raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data))) + try: + # must call overloaded method with byte array argument + # as this is the only one not applying encodings + self._port_handle.Write(as_byte_array(data), 0, len(data)) + except System.TimeoutException: + raise writeTimeoutError + return len(data) + + def reset_input_buffer(self): + """Clear input buffer, discarding all that is in the buffer.""" + if not self.is_open: + raise portNotOpenError + self._port_handle.DiscardInBuffer() + + def reset_output_buffer(self): + """\ + Clear output buffer, aborting the current output and + discarding all that is in the buffer. + """ + if not self.is_open: + raise portNotOpenError + self._port_handle.DiscardOutBuffer() + + def _update_break_state(self): + """ + Set break: Controls TXD. When active, to transmitting is possible. + """ + if not self.is_open: + raise portNotOpenError + self._port_handle.BreakState = bool(self._break_state) + + def _update_rts_state(self): + """Set terminal status line: Request To Send""" + if not self.is_open: + raise portNotOpenError + self._port_handle.RtsEnable = bool(self._rts_state) + + def _update_dtr_state(self): + """Set terminal status line: Data Terminal Ready""" + if not self.is_open: + raise portNotOpenError + self._port_handle.DtrEnable = bool(self._dtr_state) + + @property + def cts(self): + """Read terminal status line: Clear To Send""" + if not self.is_open: + raise portNotOpenError + return self._port_handle.CtsHolding + + @property + def dsr(self): + """Read terminal status line: Data Set Ready""" + if not self.is_open: + raise portNotOpenError + return self._port_handle.DsrHolding + + @property + def ri(self): + """Read terminal status line: Ring Indicator""" + if not self.is_open: + raise portNotOpenError + #~ return self._port_handle.XXX + return False # XXX an error would be better + + @property + def cd(self): + """Read terminal status line: Carrier Detect""" + if not self.is_open: + raise portNotOpenError + return self._port_handle.CDHolding + + # - - platform specific - - - - + # none diff --git a/dist/bin/esptool/serial/serialjava.py b/dist/bin/esptool/serial/serialjava.py new file mode 100644 index 000000000..7bd5b3e08 --- /dev/null +++ b/dist/bin/esptool/serial/serialjava.py @@ -0,0 +1,249 @@ +#!jython +# +# Backend Jython with JavaComm +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2002-2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +from serial.serialutil import * + + +def my_import(name): + mod = __import__(name) + components = name.split('.') + for comp in components[1:]: + mod = getattr(mod, comp) + return mod + + +def detect_java_comm(names): + """try given list of modules and return that imports""" + for name in names: + try: + mod = my_import(name) + mod.SerialPort + return mod + except (ImportError, AttributeError): + pass + raise ImportError("No Java Communications API implementation found") + + +# Java Communications API implementations +# http://mho.republika.pl/java/comm/ + +comm = detect_java_comm([ + 'javax.comm', # Sun/IBM + 'gnu.io', # RXTX +]) + + +def device(portnumber): + """Turn a port number into a device name""" + enum = comm.CommPortIdentifier.getPortIdentifiers() + ports = [] + while enum.hasMoreElements(): + el = enum.nextElement() + if el.getPortType() == comm.CommPortIdentifier.PORT_SERIAL: + ports.append(el) + return ports[portnumber].getName() + + +class Serial(SerialBase): + """\ + Serial port class, implemented with Java Communications API and + thus usable with jython and the appropriate java extension. + """ + + def open(self): + """\ + Open port with current settings. This may throw a SerialException + if the port cannot be opened. + """ + if self._port is None: + raise SerialException("Port must be configured before it can be used.") + if self.is_open: + raise SerialException("Port is already open.") + if type(self._port) == type(''): # strings are taken directly + portId = comm.CommPortIdentifier.getPortIdentifier(self._port) + else: + portId = comm.CommPortIdentifier.getPortIdentifier(device(self._port)) # numbers are transformed to a comport id obj + try: + self.sPort = portId.open("python serial module", 10) + except Exception as msg: + self.sPort = None + raise SerialException("Could not open port: %s" % msg) + self._reconfigurePort() + self._instream = self.sPort.getInputStream() + self._outstream = self.sPort.getOutputStream() + self.is_open = True + + def _reconfigurePort(self): + """Set communication parameters on opened port.""" + if not self.sPort: + raise SerialException("Can only operate on a valid port handle") + + self.sPort.enableReceiveTimeout(30) + if self._bytesize == FIVEBITS: + jdatabits = comm.SerialPort.DATABITS_5 + elif self._bytesize == SIXBITS: + jdatabits = comm.SerialPort.DATABITS_6 + elif self._bytesize == SEVENBITS: + jdatabits = comm.SerialPort.DATABITS_7 + elif self._bytesize == EIGHTBITS: + jdatabits = comm.SerialPort.DATABITS_8 + else: + raise ValueError("unsupported bytesize: %r" % self._bytesize) + + if self._stopbits == STOPBITS_ONE: + jstopbits = comm.SerialPort.STOPBITS_1 + elif self._stopbits == STOPBITS_ONE_POINT_FIVE: + jstopbits = comm.SerialPort.STOPBITS_1_5 + elif self._stopbits == STOPBITS_TWO: + jstopbits = comm.SerialPort.STOPBITS_2 + else: + raise ValueError("unsupported number of stopbits: %r" % self._stopbits) + + if self._parity == PARITY_NONE: + jparity = comm.SerialPort.PARITY_NONE + elif self._parity == PARITY_EVEN: + jparity = comm.SerialPort.PARITY_EVEN + elif self._parity == PARITY_ODD: + jparity = comm.SerialPort.PARITY_ODD + elif self._parity == PARITY_MARK: + jparity = comm.SerialPort.PARITY_MARK + elif self._parity == PARITY_SPACE: + jparity = comm.SerialPort.PARITY_SPACE + else: + raise ValueError("unsupported parity type: %r" % self._parity) + + jflowin = jflowout = 0 + if self._rtscts: + jflowin |= comm.SerialPort.FLOWCONTROL_RTSCTS_IN + jflowout |= comm.SerialPort.FLOWCONTROL_RTSCTS_OUT + if self._xonxoff: + jflowin |= comm.SerialPort.FLOWCONTROL_XONXOFF_IN + jflowout |= comm.SerialPort.FLOWCONTROL_XONXOFF_OUT + + self.sPort.setSerialPortParams(self._baudrate, jdatabits, jstopbits, jparity) + self.sPort.setFlowControlMode(jflowin | jflowout) + + if self._timeout >= 0: + self.sPort.enableReceiveTimeout(int(self._timeout*1000)) + else: + self.sPort.disableReceiveTimeout() + + def close(self): + """Close port""" + if self.is_open: + if self.sPort: + self._instream.close() + self._outstream.close() + self.sPort.close() + self.sPort = None + self.is_open = False + + # - - - - - - - - - - - - - - - - - - - - - - - - + + @property + def in_waiting(self): + """Return the number of characters currently in the input buffer.""" + if not self.sPort: + raise portNotOpenError + return self._instream.available() + + def read(self, size=1): + """\ + Read size bytes from the serial port. If a timeout is set it may + return less characters as requested. With no timeout it will block + until the requested number of bytes is read. + """ + if not self.sPort: + raise portNotOpenError + read = bytearray() + if size > 0: + while len(read) < size: + x = self._instream.read() + if x == -1: + if self.timeout >= 0: + break + else: + read.append(x) + return bytes(read) + + def write(self, data): + """Output the given string over the serial port.""" + if not self.sPort: + raise portNotOpenError + if not isinstance(data, (bytes, bytearray)): + raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data))) + self._outstream.write(data) + return len(data) + + def reset_input_buffer(self): + """Clear input buffer, discarding all that is in the buffer.""" + if not self.sPort: + raise portNotOpenError + self._instream.skip(self._instream.available()) + + def reset_output_buffer(self): + """\ + Clear output buffer, aborting the current output and + discarding all that is in the buffer. + """ + if not self.sPort: + raise portNotOpenError + self._outstream.flush() + + def send_break(self, duration=0.25): + """Send break condition. Timed, returns to idle state after given duration.""" + if not self.sPort: + raise portNotOpenError + self.sPort.sendBreak(duration*1000.0) + + def _update_break_state(self): + """Set break: Controls TXD. When active, to transmitting is possible.""" + if self.fd is None: + raise portNotOpenError + raise SerialException("The _update_break_state function is not implemented in java.") + + def _update_rts_state(self): + """Set terminal status line: Request To Send""" + if not self.sPort: + raise portNotOpenError + self.sPort.setRTS(self._rts_state) + + def _update_dtr_state(self): + """Set terminal status line: Data Terminal Ready""" + if not self.sPort: + raise portNotOpenError + self.sPort.setDTR(self._dtr_state) + + @property + def cts(self): + """Read terminal status line: Clear To Send""" + if not self.sPort: + raise portNotOpenError + self.sPort.isCTS() + + @property + def dsr(self): + """Read terminal status line: Data Set Ready""" + if not self.sPort: + raise portNotOpenError + self.sPort.isDSR() + + @property + def ri(self): + """Read terminal status line: Ring Indicator""" + if not self.sPort: + raise portNotOpenError + self.sPort.isRI() + + @property + def cd(self): + """Read terminal status line: Carrier Detect""" + if not self.sPort: + raise portNotOpenError + self.sPort.isCD() diff --git a/dist/bin/esptool/serial/serialposix.py b/dist/bin/esptool/serial/serialposix.py new file mode 100644 index 000000000..afe506223 --- /dev/null +++ b/dist/bin/esptool/serial/serialposix.py @@ -0,0 +1,811 @@ +#!/usr/bin/env python +# +# backend for serial IO for POSIX compatible systems, like Linux, OSX +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2001-2016 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause +# +# parts based on code from Grant B. Edwards : +# ftp://ftp.visi.com/users/grante/python/PosixSerial.py +# +# references: http://www.easysw.com/~mike/serial/serial.html + +# Collection of port names (was previously used by number_to_device which was +# removed. +# - Linux /dev/ttyS%d (confirmed) +# - cygwin/win32 /dev/com%d (confirmed) +# - openbsd (OpenBSD) /dev/cua%02d +# - bsd*, freebsd* /dev/cuad%d +# - darwin (OS X) /dev/cuad%d +# - netbsd /dev/dty%02d (NetBSD 1.6 testing by Erk) +# - irix (IRIX) /dev/ttyf%d (partially tested) names depending on flow control +# - hp (HP-UX) /dev/tty%dp0 (not tested) +# - sunos (Solaris/SunOS) /dev/tty%c (letters, 'a'..'z') (confirmed) +# - aix (AIX) /dev/tty%d + + +# pylint: disable=abstract-method +import errno +import fcntl +import os +import select +import struct +import sys +import termios + +import serial +from serial.serialutil import SerialBase, SerialException, to_bytes, \ + portNotOpenError, writeTimeoutError, Timeout + + +class PlatformSpecificBase(object): + BAUDRATE_CONSTANTS = {} + + def _set_special_baudrate(self, baudrate): + raise NotImplementedError('non-standard baudrates are not supported on this platform') + + def _set_rs485_mode(self, rs485_settings): + raise NotImplementedError('RS485 not supported on this platform') + + +# some systems support an extra flag to enable the two in POSIX unsupported +# paritiy settings for MARK and SPACE +CMSPAR = 0 # default, for unsupported platforms, override below + +# try to detect the OS so that a device can be selected... +# this code block should supply a device() and set_special_baudrate() function +# for the platform +plat = sys.platform.lower() + +if plat[:5] == 'linux': # Linux (confirmed) # noqa + import array + + # extra termios flags + CMSPAR = 0o10000000000 # Use "stick" (mark/space) parity + + # baudrate ioctls + TCGETS2 = 0x802C542A + TCSETS2 = 0x402C542B + BOTHER = 0o010000 + + # RS485 ioctls + TIOCGRS485 = 0x542E + TIOCSRS485 = 0x542F + SER_RS485_ENABLED = 0b00000001 + SER_RS485_RTS_ON_SEND = 0b00000010 + SER_RS485_RTS_AFTER_SEND = 0b00000100 + SER_RS485_RX_DURING_TX = 0b00010000 + + class PlatformSpecific(PlatformSpecificBase): + BAUDRATE_CONSTANTS = { + 0: 0o000000, # hang up + 50: 0o000001, + 75: 0o000002, + 110: 0o000003, + 134: 0o000004, + 150: 0o000005, + 200: 0o000006, + 300: 0o000007, + 600: 0o000010, + 1200: 0o000011, + 1800: 0o000012, + 2400: 0o000013, + 4800: 0o000014, + 9600: 0o000015, + 19200: 0o000016, + 38400: 0o000017, + 57600: 0o010001, + 115200: 0o010002, + 230400: 0o010003, + 460800: 0o010004, + 500000: 0o010005, + 576000: 0o010006, + 921600: 0o010007, + 1000000: 0o010010, + 1152000: 0o010011, + 1500000: 0o010012, + 2000000: 0o010013, + 2500000: 0o010014, + 3000000: 0o010015, + 3500000: 0o010016, + 4000000: 0o010017 + } + + def _set_special_baudrate(self, baudrate): + # right size is 44 on x86_64, allow for some growth + buf = array.array('i', [0] * 64) + try: + # get serial_struct + fcntl.ioctl(self.fd, TCGETS2, buf) + # set custom speed + buf[2] &= ~termios.CBAUD + buf[2] |= BOTHER + buf[9] = buf[10] = baudrate + + # set serial_struct + fcntl.ioctl(self.fd, TCSETS2, buf) + except IOError as e: + raise ValueError('Failed to set custom baud rate ({}): {}'.format(baudrate, e)) + + def _set_rs485_mode(self, rs485_settings): + buf = array.array('i', [0] * 8) # flags, delaytx, delayrx, padding + try: + fcntl.ioctl(self.fd, TIOCGRS485, buf) + buf[0] |= SER_RS485_ENABLED + if rs485_settings is not None: + if rs485_settings.loopback: + buf[0] |= SER_RS485_RX_DURING_TX + else: + buf[0] &= ~SER_RS485_RX_DURING_TX + if rs485_settings.rts_level_for_tx: + buf[0] |= SER_RS485_RTS_ON_SEND + else: + buf[0] &= ~SER_RS485_RTS_ON_SEND + if rs485_settings.rts_level_for_rx: + buf[0] |= SER_RS485_RTS_AFTER_SEND + else: + buf[0] &= ~SER_RS485_RTS_AFTER_SEND + if rs485_settings.delay_before_tx is not None: + buf[1] = int(rs485_settings.delay_before_tx * 1000) + if rs485_settings.delay_before_rx is not None: + buf[2] = int(rs485_settings.delay_before_rx * 1000) + else: + buf[0] = 0 # clear SER_RS485_ENABLED + fcntl.ioctl(self.fd, TIOCSRS485, buf) + except IOError as e: + raise ValueError('Failed to set RS485 mode: {}'.format(e)) + + +elif plat == 'cygwin': # cygwin/win32 (confirmed) + + class PlatformSpecific(PlatformSpecificBase): + BAUDRATE_CONSTANTS = { + 128000: 0x01003, + 256000: 0x01005, + 500000: 0x01007, + 576000: 0x01008, + 921600: 0x01009, + 1000000: 0x0100a, + 1152000: 0x0100b, + 1500000: 0x0100c, + 2000000: 0x0100d, + 2500000: 0x0100e, + 3000000: 0x0100f + } + + +elif plat[:6] == 'darwin': # OS X + import array + IOSSIOSPEED = 0x80045402 # _IOW('T', 2, speed_t) + + class PlatformSpecific(PlatformSpecificBase): + osx_version = os.uname()[2].split('.') + # Tiger or above can support arbitrary serial speeds + if int(osx_version[0]) >= 8: + def _set_special_baudrate(self, baudrate): + # use IOKit-specific call to set up high speeds + buf = array.array('i', [baudrate]) + fcntl.ioctl(self.fd, IOSSIOSPEED, buf, 1) + +elif plat[:3] == 'bsd' or \ + plat[:7] == 'freebsd' or \ + plat[:6] == 'netbsd' or \ + plat[:7] == 'openbsd': + + class ReturnBaudrate(object): + def __getitem__(self, key): + return key + + class PlatformSpecific(PlatformSpecificBase): + # Only tested on FreeBSD: + # The baud rate may be passed in as + # a literal value. + BAUDRATE_CONSTANTS = ReturnBaudrate() + +else: + class PlatformSpecific(PlatformSpecificBase): + pass + + +# load some constants for later use. +# try to use values from termios, use defaults from linux otherwise +TIOCMGET = getattr(termios, 'TIOCMGET', 0x5415) +TIOCMBIS = getattr(termios, 'TIOCMBIS', 0x5416) +TIOCMBIC = getattr(termios, 'TIOCMBIC', 0x5417) +TIOCMSET = getattr(termios, 'TIOCMSET', 0x5418) + +# TIOCM_LE = getattr(termios, 'TIOCM_LE', 0x001) +TIOCM_DTR = getattr(termios, 'TIOCM_DTR', 0x002) +TIOCM_RTS = getattr(termios, 'TIOCM_RTS', 0x004) +# TIOCM_ST = getattr(termios, 'TIOCM_ST', 0x008) +# TIOCM_SR = getattr(termios, 'TIOCM_SR', 0x010) + +TIOCM_CTS = getattr(termios, 'TIOCM_CTS', 0x020) +TIOCM_CAR = getattr(termios, 'TIOCM_CAR', 0x040) +TIOCM_RNG = getattr(termios, 'TIOCM_RNG', 0x080) +TIOCM_DSR = getattr(termios, 'TIOCM_DSR', 0x100) +TIOCM_CD = getattr(termios, 'TIOCM_CD', TIOCM_CAR) +TIOCM_RI = getattr(termios, 'TIOCM_RI', TIOCM_RNG) +# TIOCM_OUT1 = getattr(termios, 'TIOCM_OUT1', 0x2000) +# TIOCM_OUT2 = getattr(termios, 'TIOCM_OUT2', 0x4000) +if hasattr(termios, 'TIOCINQ'): + TIOCINQ = termios.TIOCINQ +else: + TIOCINQ = getattr(termios, 'FIONREAD', 0x541B) +TIOCOUTQ = getattr(termios, 'TIOCOUTQ', 0x5411) + +TIOCM_zero_str = struct.pack('I', 0) +TIOCM_RTS_str = struct.pack('I', TIOCM_RTS) +TIOCM_DTR_str = struct.pack('I', TIOCM_DTR) + +TIOCSBRK = getattr(termios, 'TIOCSBRK', 0x5427) +TIOCCBRK = getattr(termios, 'TIOCCBRK', 0x5428) + + +class Serial(SerialBase, PlatformSpecific): + """\ + Serial port class POSIX implementation. Serial port configuration is + done with termios and fcntl. Runs on Linux and many other Un*x like + systems. + """ + + def open(self): + """\ + Open port with current settings. This may throw a SerialException + if the port cannot be opened.""" + if self._port is None: + raise SerialException("Port must be configured before it can be used.") + if self.is_open: + raise SerialException("Port is already open.") + self.fd = None + # open + try: + self.fd = os.open(self.portstr, os.O_RDWR | os.O_NOCTTY | os.O_NONBLOCK) + except OSError as msg: + self.fd = None + raise SerialException(msg.errno, "could not open port {}: {}".format(self._port, msg)) + #~ fcntl.fcntl(self.fd, fcntl.F_SETFL, 0) # set blocking + + try: + self._reconfigure_port(force_update=True) + except: + try: + os.close(self.fd) + except: + # ignore any exception when closing the port + # also to keep original exception that happened when setting up + pass + self.fd = None + raise + else: + self.is_open = True + try: + if not self._dsrdtr: + self._update_dtr_state() + if not self._rtscts: + self._update_rts_state() + except IOError as e: + if e.errno in (errno.EINVAL, errno.ENOTTY): + # ignore Invalid argument and Inappropriate ioctl + pass + else: + raise + self.reset_input_buffer() + self.pipe_abort_read_r, self.pipe_abort_read_w = os.pipe() + self.pipe_abort_write_r, self.pipe_abort_write_w = os.pipe() + fcntl.fcntl(self.pipe_abort_read_r, fcntl.F_SETFL, os.O_NONBLOCK) + fcntl.fcntl(self.pipe_abort_write_r, fcntl.F_SETFL, os.O_NONBLOCK) + + def _reconfigure_port(self, force_update=False): + """Set communication parameters on opened port.""" + if self.fd is None: + raise SerialException("Can only operate on a valid file descriptor") + + # if exclusive lock is requested, create it before we modify anything else + if self._exclusive is not None: + if self._exclusive: + try: + fcntl.flock(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + except IOError as msg: + raise SerialException(msg.errno, "Could not exclusively lock port {}: {}".format(self._port, msg)) + else: + fcntl.flock(self.fd, fcntl.LOCK_UN) + + custom_baud = None + + vmin = vtime = 0 # timeout is done via select + if self._inter_byte_timeout is not None: + vmin = 1 + vtime = int(self._inter_byte_timeout * 10) + try: + orig_attr = termios.tcgetattr(self.fd) + iflag, oflag, cflag, lflag, ispeed, ospeed, cc = orig_attr + except termios.error as msg: # if a port is nonexistent but has a /dev file, it'll fail here + raise SerialException("Could not configure port: {}".format(msg)) + # set up raw mode / no echo / binary + cflag |= (termios.CLOCAL | termios.CREAD) + lflag &= ~(termios.ICANON | termios.ECHO | termios.ECHOE | + termios.ECHOK | termios.ECHONL | + termios.ISIG | termios.IEXTEN) # |termios.ECHOPRT + for flag in ('ECHOCTL', 'ECHOKE'): # netbsd workaround for Erk + if hasattr(termios, flag): + lflag &= ~getattr(termios, flag) + + oflag &= ~(termios.OPOST | termios.ONLCR | termios.OCRNL) + iflag &= ~(termios.INLCR | termios.IGNCR | termios.ICRNL | termios.IGNBRK) + if hasattr(termios, 'IUCLC'): + iflag &= ~termios.IUCLC + if hasattr(termios, 'PARMRK'): + iflag &= ~termios.PARMRK + + # setup baud rate + try: + ispeed = ospeed = getattr(termios, 'B{}'.format(self._baudrate)) + except AttributeError: + try: + ispeed = ospeed = self.BAUDRATE_CONSTANTS[self._baudrate] + except KeyError: + #~ raise ValueError('Invalid baud rate: %r' % self._baudrate) + # may need custom baud rate, it isn't in our list. + ispeed = ospeed = getattr(termios, 'B38400') + try: + custom_baud = int(self._baudrate) # store for later + except ValueError: + raise ValueError('Invalid baud rate: {!r}'.format(self._baudrate)) + else: + if custom_baud < 0: + raise ValueError('Invalid baud rate: {!r}'.format(self._baudrate)) + + # setup char len + cflag &= ~termios.CSIZE + if self._bytesize == 8: + cflag |= termios.CS8 + elif self._bytesize == 7: + cflag |= termios.CS7 + elif self._bytesize == 6: + cflag |= termios.CS6 + elif self._bytesize == 5: + cflag |= termios.CS5 + else: + raise ValueError('Invalid char len: {!r}'.format(self._bytesize)) + # setup stop bits + if self._stopbits == serial.STOPBITS_ONE: + cflag &= ~(termios.CSTOPB) + elif self._stopbits == serial.STOPBITS_ONE_POINT_FIVE: + cflag |= (termios.CSTOPB) # XXX same as TWO.. there is no POSIX support for 1.5 + elif self._stopbits == serial.STOPBITS_TWO: + cflag |= (termios.CSTOPB) + else: + raise ValueError('Invalid stop bit specification: {!r}'.format(self._stopbits)) + # setup parity + iflag &= ~(termios.INPCK | termios.ISTRIP) + if self._parity == serial.PARITY_NONE: + cflag &= ~(termios.PARENB | termios.PARODD | CMSPAR) + elif self._parity == serial.PARITY_EVEN: + cflag &= ~(termios.PARODD | CMSPAR) + cflag |= (termios.PARENB) + elif self._parity == serial.PARITY_ODD: + cflag &= ~CMSPAR + cflag |= (termios.PARENB | termios.PARODD) + elif self._parity == serial.PARITY_MARK and CMSPAR: + cflag |= (termios.PARENB | CMSPAR | termios.PARODD) + elif self._parity == serial.PARITY_SPACE and CMSPAR: + cflag |= (termios.PARENB | CMSPAR) + cflag &= ~(termios.PARODD) + else: + raise ValueError('Invalid parity: {!r}'.format(self._parity)) + # setup flow control + # xonxoff + if hasattr(termios, 'IXANY'): + if self._xonxoff: + iflag |= (termios.IXON | termios.IXOFF) # |termios.IXANY) + else: + iflag &= ~(termios.IXON | termios.IXOFF | termios.IXANY) + else: + if self._xonxoff: + iflag |= (termios.IXON | termios.IXOFF) + else: + iflag &= ~(termios.IXON | termios.IXOFF) + # rtscts + if hasattr(termios, 'CRTSCTS'): + if self._rtscts: + cflag |= (termios.CRTSCTS) + else: + cflag &= ~(termios.CRTSCTS) + elif hasattr(termios, 'CNEW_RTSCTS'): # try it with alternate constant name + if self._rtscts: + cflag |= (termios.CNEW_RTSCTS) + else: + cflag &= ~(termios.CNEW_RTSCTS) + # XXX should there be a warning if setting up rtscts (and xonxoff etc) fails?? + + # buffer + # vmin "minimal number of characters to be read. 0 for non blocking" + if vmin < 0 or vmin > 255: + raise ValueError('Invalid vmin: {!r}'.format(vmin)) + cc[termios.VMIN] = vmin + # vtime + if vtime < 0 or vtime > 255: + raise ValueError('Invalid vtime: {!r}'.format(vtime)) + cc[termios.VTIME] = vtime + # activate settings + if force_update or [iflag, oflag, cflag, lflag, ispeed, ospeed, cc] != orig_attr: + termios.tcsetattr( + self.fd, + termios.TCSANOW, + [iflag, oflag, cflag, lflag, ispeed, ospeed, cc]) + + # apply custom baud rate, if any + if custom_baud is not None: + self._set_special_baudrate(custom_baud) + + if self._rs485_mode is not None: + self._set_rs485_mode(self._rs485_mode) + + def close(self): + """Close port""" + if self.is_open: + if self.fd is not None: + os.close(self.fd) + self.fd = None + os.close(self.pipe_abort_read_w) + os.close(self.pipe_abort_read_r) + os.close(self.pipe_abort_write_w) + os.close(self.pipe_abort_write_r) + self.pipe_abort_read_r, self.pipe_abort_read_w = None, None + self.pipe_abort_write_r, self.pipe_abort_write_w = None, None + self.is_open = False + + # - - - - - - - - - - - - - - - - - - - - - - - - + + @property + def in_waiting(self): + """Return the number of bytes currently in the input buffer.""" + #~ s = fcntl.ioctl(self.fd, termios.FIONREAD, TIOCM_zero_str) + s = fcntl.ioctl(self.fd, TIOCINQ, TIOCM_zero_str) + return struct.unpack('I', s)[0] + + # select based implementation, proved to work on many systems + def read(self, size=1): + """\ + Read size bytes from the serial port. If a timeout is set it may + return less characters as requested. With no timeout it will block + until the requested number of bytes is read. + """ + if not self.is_open: + raise portNotOpenError + read = bytearray() + timeout = Timeout(self._timeout) + while len(read) < size: + try: + ready, _, _ = select.select([self.fd, self.pipe_abort_read_r], [], [], timeout.time_left()) + if self.pipe_abort_read_r in ready: + os.read(self.pipe_abort_read_r, 1000) + break + # If select was used with a timeout, and the timeout occurs, it + # returns with empty lists -> thus abort read operation. + # For timeout == 0 (non-blocking operation) also abort when + # there is nothing to read. + if not ready: + break # timeout + buf = os.read(self.fd, size - len(read)) + # read should always return some data as select reported it was + # ready to read when we get to this point. + if not buf: + # Disconnected devices, at least on Linux, show the + # behavior that they are always ready to read immediately + # but reading returns nothing. + raise SerialException( + 'device reports readiness to read but returned no data ' + '(device disconnected or multiple access on port?)') + read.extend(buf) + except OSError as e: + # this is for Python 3.x where select.error is a subclass of + # OSError ignore BlockingIOErrors and EINTR. other errors are shown + # https://www.python.org/dev/peps/pep-0475. + if e.errno not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): + raise SerialException('read failed: {}'.format(e)) + except select.error as e: + # this is for Python 2.x + # ignore BlockingIOErrors and EINTR. all errors are shown + # see also http://www.python.org/dev/peps/pep-3151/#select + if e[0] not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): + raise SerialException('read failed: {}'.format(e)) + if timeout.expired(): + break + return bytes(read) + + def cancel_read(self): + if self.is_open: + os.write(self.pipe_abort_read_w, b"x") + + def cancel_write(self): + if self.is_open: + os.write(self.pipe_abort_write_w, b"x") + + def write(self, data): + """Output the given byte string over the serial port.""" + if not self.is_open: + raise portNotOpenError + d = to_bytes(data) + tx_len = length = len(d) + timeout = Timeout(self._write_timeout) + while tx_len > 0: + try: + n = os.write(self.fd, d) + if timeout.is_non_blocking: + # Zero timeout indicates non-blocking - simply return the + # number of bytes of data actually written + return n + elif not timeout.is_infinite: + # when timeout is set, use select to wait for being ready + # with the time left as timeout + if timeout.expired(): + raise writeTimeoutError + abort, ready, _ = select.select([self.pipe_abort_write_r], [self.fd], [], timeout.time_left()) + if abort: + os.read(self.pipe_abort_write_r, 1000) + break + if not ready: + raise writeTimeoutError + else: + assert timeout.time_left() is None + # wait for write operation + abort, ready, _ = select.select([self.pipe_abort_write_r], [self.fd], [], None) + if abort: + os.read(self.pipe_abort_write_r, 1) + break + if not ready: + raise SerialException('write failed (select)') + d = d[n:] + tx_len -= n + except SerialException: + raise + except OSError as e: + # this is for Python 3.x where select.error is a subclass of + # OSError ignore BlockingIOErrors and EINTR. other errors are shown + # https://www.python.org/dev/peps/pep-0475. + if e.errno not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): + raise SerialException('write failed: {}'.format(e)) + except select.error as e: + # this is for Python 2.x + # ignore BlockingIOErrors and EINTR. all errors are shown + # see also http://www.python.org/dev/peps/pep-3151/#select + if e[0] not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): + raise SerialException('write failed: {}'.format(e)) + if not timeout.is_non_blocking and timeout.expired(): + raise writeTimeoutError + return length - len(d) + + def flush(self): + """\ + Flush of file like objects. In this case, wait until all data + is written. + """ + if not self.is_open: + raise portNotOpenError + termios.tcdrain(self.fd) + + def reset_input_buffer(self): + """Clear input buffer, discarding all that is in the buffer.""" + if not self.is_open: + raise portNotOpenError + termios.tcflush(self.fd, termios.TCIFLUSH) + + def reset_output_buffer(self): + """\ + Clear output buffer, aborting the current output and discarding all + that is in the buffer. + """ + if not self.is_open: + raise portNotOpenError + termios.tcflush(self.fd, termios.TCOFLUSH) + + def send_break(self, duration=0.25): + """\ + Send break condition. Timed, returns to idle state after given + duration. + """ + if not self.is_open: + raise portNotOpenError + termios.tcsendbreak(self.fd, int(duration / 0.25)) + + def _update_break_state(self): + """\ + Set break: Controls TXD. When active, no transmitting is possible. + """ + if self._break_state: + fcntl.ioctl(self.fd, TIOCSBRK) + else: + fcntl.ioctl(self.fd, TIOCCBRK) + + def _update_rts_state(self): + """Set terminal status line: Request To Send""" + if self._rts_state: + fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_RTS_str) + else: + fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_RTS_str) + + def _update_dtr_state(self): + """Set terminal status line: Data Terminal Ready""" + if self._dtr_state: + fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_DTR_str) + else: + fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_DTR_str) + + @property + def cts(self): + """Read terminal status line: Clear To Send""" + if not self.is_open: + raise portNotOpenError + s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str) + return struct.unpack('I', s)[0] & TIOCM_CTS != 0 + + @property + def dsr(self): + """Read terminal status line: Data Set Ready""" + if not self.is_open: + raise portNotOpenError + s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str) + return struct.unpack('I', s)[0] & TIOCM_DSR != 0 + + @property + def ri(self): + """Read terminal status line: Ring Indicator""" + if not self.is_open: + raise portNotOpenError + s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str) + return struct.unpack('I', s)[0] & TIOCM_RI != 0 + + @property + def cd(self): + """Read terminal status line: Carrier Detect""" + if not self.is_open: + raise portNotOpenError + s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str) + return struct.unpack('I', s)[0] & TIOCM_CD != 0 + + # - - platform specific - - - - + + @property + def out_waiting(self): + """Return the number of bytes currently in the output buffer.""" + #~ s = fcntl.ioctl(self.fd, termios.FIONREAD, TIOCM_zero_str) + s = fcntl.ioctl(self.fd, TIOCOUTQ, TIOCM_zero_str) + return struct.unpack('I', s)[0] + + def fileno(self): + """\ + For easier use of the serial port instance with select. + WARNING: this function is not portable to different platforms! + """ + if not self.is_open: + raise portNotOpenError + return self.fd + + def set_input_flow_control(self, enable=True): + """\ + Manually control flow - when software flow control is enabled. + This will send XON (true) or XOFF (false) to the other device. + WARNING: this function is not portable to different platforms! + """ + if not self.is_open: + raise portNotOpenError + if enable: + termios.tcflow(self.fd, termios.TCION) + else: + termios.tcflow(self.fd, termios.TCIOFF) + + def set_output_flow_control(self, enable=True): + """\ + Manually control flow of outgoing data - when hardware or software flow + control is enabled. + WARNING: this function is not portable to different platforms! + """ + if not self.is_open: + raise portNotOpenError + if enable: + termios.tcflow(self.fd, termios.TCOON) + else: + termios.tcflow(self.fd, termios.TCOOFF) + + def nonblocking(self): + """DEPRECATED - has no use""" + import warnings + warnings.warn("nonblocking() has no effect, already nonblocking", DeprecationWarning) + + +class PosixPollSerial(Serial): + """\ + Poll based read implementation. Not all systems support poll properly. + However this one has better handling of errors, such as a device + disconnecting while it's in use (e.g. USB-serial unplugged). + """ + + def read(self, size=1): + """\ + Read size bytes from the serial port. If a timeout is set it may + return less characters as requested. With no timeout it will block + until the requested number of bytes is read. + """ + if not self.is_open: + raise portNotOpenError + read = bytearray() + poll = select.poll() + poll.register(self.fd, select.POLLIN | select.POLLERR | select.POLLHUP | select.POLLNVAL) + if size > 0: + while len(read) < size: + # print "\tread(): size",size, "have", len(read) #debug + # wait until device becomes ready to read (or something fails) + for fd, event in poll.poll(self._timeout * 1000): + if event & (select.POLLERR | select.POLLHUP | select.POLLNVAL): + raise SerialException('device reports error (poll)') + # we don't care if it is select.POLLIN or timeout, that's + # handled below + buf = os.read(self.fd, size - len(read)) + read.extend(buf) + if ((self._timeout is not None and self._timeout >= 0) or + (self._inter_byte_timeout is not None and self._inter_byte_timeout > 0)) and not buf: + break # early abort on timeout + return bytes(read) + + +class VTIMESerial(Serial): + """\ + Implement timeout using vtime of tty device instead of using select. + This means that no inter character timeout can be specified and that + the error handling is degraded. + + Overall timeout is disabled when inter-character timeout is used. + """ + + def _reconfigure_port(self, force_update=True): + """Set communication parameters on opened port.""" + super(VTIMESerial, self)._reconfigure_port() + fcntl.fcntl(self.fd, fcntl.F_SETFL, 0) # clear O_NONBLOCK + + if self._inter_byte_timeout is not None: + vmin = 1 + vtime = int(self._inter_byte_timeout * 10) + elif self._timeout is None: + vmin = 1 + vtime = 0 + else: + vmin = 0 + vtime = int(self._timeout * 10) + try: + orig_attr = termios.tcgetattr(self.fd) + iflag, oflag, cflag, lflag, ispeed, ospeed, cc = orig_attr + except termios.error as msg: # if a port is nonexistent but has a /dev file, it'll fail here + raise serial.SerialException("Could not configure port: {}".format(msg)) + + if vtime < 0 or vtime > 255: + raise ValueError('Invalid vtime: {!r}'.format(vtime)) + cc[termios.VTIME] = vtime + cc[termios.VMIN] = vmin + + termios.tcsetattr( + self.fd, + termios.TCSANOW, + [iflag, oflag, cflag, lflag, ispeed, ospeed, cc]) + + def read(self, size=1): + """\ + Read size bytes from the serial port. If a timeout is set it may + return less characters as requested. With no timeout it will block + until the requested number of bytes is read. + """ + if not self.is_open: + raise portNotOpenError + read = bytearray() + while len(read) < size: + buf = os.read(self.fd, size - len(read)) + if not buf: + break + read.extend(buf) + return bytes(read) + + # hack to make hasattr return false + cancel_read = property() diff --git a/dist/bin/esptool/serial/serialutil.py b/dist/bin/esptool/serial/serialutil.py new file mode 100644 index 000000000..7d51752ff --- /dev/null +++ b/dist/bin/esptool/serial/serialutil.py @@ -0,0 +1,693 @@ +#! python +# +# Base class and support functions used by various backends. +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2001-2016 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +import io +import time + +# ``memoryview`` was introduced in Python 2.7 and ``bytes(some_memoryview)`` +# isn't returning the contents (very unfortunate). Therefore we need special +# cases and test for it. Ensure that there is a ``memoryview`` object for older +# Python versions. This is easier than making every test dependent on its +# existence. +try: + memoryview +except (NameError, AttributeError): + # implementation does not matter as we do not really use it. + # it just must not inherit from something else we might care for. + class memoryview(object): # pylint: disable=redefined-builtin,invalid-name + pass + +try: + unicode +except (NameError, AttributeError): + unicode = str # for Python 3, pylint: disable=redefined-builtin,invalid-name + +try: + basestring +except (NameError, AttributeError): + basestring = (str,) # for Python 3, pylint: disable=redefined-builtin,invalid-name + + +# "for byte in data" fails for python3 as it returns ints instead of bytes +def iterbytes(b): + """Iterate over bytes, returning bytes instead of ints (python3)""" + if isinstance(b, memoryview): + b = b.tobytes() + i = 0 + while True: + a = b[i:i + 1] + i += 1 + if a: + yield a + else: + break + + +# all Python versions prior 3.x convert ``str([17])`` to '[17]' instead of '\x11' +# so a simple ``bytes(sequence)`` doesn't work for all versions +def to_bytes(seq): + """convert a sequence to a bytes type""" + if isinstance(seq, bytes): + return seq + elif isinstance(seq, bytearray): + return bytes(seq) + elif isinstance(seq, memoryview): + return seq.tobytes() + elif isinstance(seq, unicode): + raise TypeError('unicode strings are not supported, please encode to bytes: {!r}'.format(seq)) + else: + # handle list of integers and bytes (one or more items) for Python 2 and 3 + return bytes(bytearray(seq)) + + +# create control bytes +XON = to_bytes([17]) +XOFF = to_bytes([19]) + +CR = to_bytes([13]) +LF = to_bytes([10]) + + +PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE = 'N', 'E', 'O', 'M', 'S' +STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO = (1, 1.5, 2) +FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS = (5, 6, 7, 8) + +PARITY_NAMES = { + PARITY_NONE: 'None', + PARITY_EVEN: 'Even', + PARITY_ODD: 'Odd', + PARITY_MARK: 'Mark', + PARITY_SPACE: 'Space', +} + + +class SerialException(IOError): + """Base class for serial port related exceptions.""" + + +class SerialTimeoutException(SerialException): + """Write timeouts give an exception""" + + +writeTimeoutError = SerialTimeoutException('Write timeout') +portNotOpenError = SerialException('Attempting to use a port that is not open') + + +class Timeout(object): + """\ + Abstraction for timeout operations. Using time.monotonic() if available + or time.time() in all other cases. + + The class can also be initialized with 0 or None, in order to support + non-blocking and fully blocking I/O operations. The attributes + is_non_blocking and is_infinite are set accordingly. + """ + if hasattr(time, 'monotonic'): + # Timeout implementation with time.monotonic(). This function is only + # supported by Python 3.3 and above. It returns a time in seconds + # (float) just as time.time(), but is not affected by system clock + # adjustments. + TIME = time.monotonic + else: + # Timeout implementation with time.time(). This is compatible with all + # Python versions but has issues if the clock is adjusted while the + # timeout is running. + TIME = time.time + + def __init__(self, duration): + """Initialize a timeout with given duration""" + self.is_infinite = (duration is None) + self.is_non_blocking = (duration == 0) + self.duration = duration + if duration is not None: + self.target_time = self.TIME() + duration + else: + self.target_time = None + + def expired(self): + """Return a boolean, telling if the timeout has expired""" + return self.target_time is not None and self.time_left() <= 0 + + def time_left(self): + """Return how many seconds are left until the timeout expires""" + if self.is_non_blocking: + return 0 + elif self.is_infinite: + return None + else: + delta = self.target_time - self.TIME() + if delta > self.duration: + # clock jumped, recalculate + self.target_time = self.TIME() + self.duration + return self.duration + else: + return max(0, delta) + + def restart(self, duration): + """\ + Restart a timeout, only supported if a timeout was already set up + before. + """ + self.duration = duration + self.target_time = self.TIME() + duration + + +class SerialBase(io.RawIOBase): + """\ + Serial port base class. Provides __init__ function and properties to + get/set port settings. + """ + + # default values, may be overridden in subclasses that do not support all values + BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200, 230400, 460800, 500000, + 576000, 921600, 1000000, 1152000, 1500000, 2000000, 2500000, + 3000000, 3500000, 4000000) + BYTESIZES = (FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS) + PARITIES = (PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE) + STOPBITS = (STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO) + + def __init__(self, + port=None, + baudrate=9600, + bytesize=EIGHTBITS, + parity=PARITY_NONE, + stopbits=STOPBITS_ONE, + timeout=None, + xonxoff=False, + rtscts=False, + write_timeout=None, + dsrdtr=False, + inter_byte_timeout=None, + exclusive=None, + **kwargs): + """\ + Initialize comm port object. If a "port" is given, then the port will be + opened immediately. Otherwise a Serial port object in closed state + is returned. + """ + + self.is_open = False + self.portstr = None + self.name = None + # correct values are assigned below through properties + self._port = None + self._baudrate = None + self._bytesize = None + self._parity = None + self._stopbits = None + self._timeout = None + self._write_timeout = None + self._xonxoff = None + self._rtscts = None + self._dsrdtr = None + self._inter_byte_timeout = None + self._rs485_mode = None # disabled by default + self._rts_state = True + self._dtr_state = True + self._break_state = False + self._exclusive = None + + # assign values using get/set methods using the properties feature + self.port = port + self.baudrate = baudrate + self.bytesize = bytesize + self.parity = parity + self.stopbits = stopbits + self.timeout = timeout + self.write_timeout = write_timeout + self.xonxoff = xonxoff + self.rtscts = rtscts + self.dsrdtr = dsrdtr + self.inter_byte_timeout = inter_byte_timeout + self.exclusive = exclusive + + # watch for backward compatible kwargs + if 'writeTimeout' in kwargs: + self.write_timeout = kwargs.pop('writeTimeout') + if 'interCharTimeout' in kwargs: + self.inter_byte_timeout = kwargs.pop('interCharTimeout') + if kwargs: + raise ValueError('unexpected keyword arguments: {!r}'.format(kwargs)) + + if port is not None: + self.open() + + # - - - - - - - - - - - - - - - - - - - - - - - - + + # to be implemented by subclasses: + # def open(self): + # def close(self): + + # - - - - - - - - - - - - - - - - - - - - - - - - + + @property + def port(self): + """\ + Get the current port setting. The value that was passed on init or using + setPort() is passed back. + """ + return self._port + + @port.setter + def port(self, port): + """\ + Change the port. + """ + if port is not None and not isinstance(port, basestring): + raise ValueError('"port" must be None or a string, not {}'.format(type(port))) + was_open = self.is_open + if was_open: + self.close() + self.portstr = port + self._port = port + self.name = self.portstr + if was_open: + self.open() + + @property + def baudrate(self): + """Get the current baud rate setting.""" + return self._baudrate + + @baudrate.setter + def baudrate(self, baudrate): + """\ + Change baud rate. It raises a ValueError if the port is open and the + baud rate is not possible. If the port is closed, then the value is + accepted and the exception is raised when the port is opened. + """ + try: + b = int(baudrate) + except TypeError: + raise ValueError("Not a valid baudrate: {!r}".format(baudrate)) + else: + if b < 0: + raise ValueError("Not a valid baudrate: {!r}".format(baudrate)) + self._baudrate = b + if self.is_open: + self._reconfigure_port() + + @property + def bytesize(self): + """Get the current byte size setting.""" + return self._bytesize + + @bytesize.setter + def bytesize(self, bytesize): + """Change byte size.""" + if bytesize not in self.BYTESIZES: + raise ValueError("Not a valid byte size: {!r}".format(bytesize)) + self._bytesize = bytesize + if self.is_open: + self._reconfigure_port() + + @property + def exclusive(self): + """Get the current exclusive access setting.""" + return self._exclusive + + @exclusive.setter + def exclusive(self, exclusive): + """Change the exclusive access setting.""" + self._exclusive = exclusive + if self.is_open: + self._reconfigure_port() + + @property + def parity(self): + """Get the current parity setting.""" + return self._parity + + @parity.setter + def parity(self, parity): + """Change parity setting.""" + if parity not in self.PARITIES: + raise ValueError("Not a valid parity: {!r}".format(parity)) + self._parity = parity + if self.is_open: + self._reconfigure_port() + + @property + def stopbits(self): + """Get the current stop bits setting.""" + return self._stopbits + + @stopbits.setter + def stopbits(self, stopbits): + """Change stop bits size.""" + if stopbits not in self.STOPBITS: + raise ValueError("Not a valid stop bit size: {!r}".format(stopbits)) + self._stopbits = stopbits + if self.is_open: + self._reconfigure_port() + + @property + def timeout(self): + """Get the current timeout setting.""" + return self._timeout + + @timeout.setter + def timeout(self, timeout): + """Change timeout setting.""" + if timeout is not None: + try: + timeout + 1 # test if it's a number, will throw a TypeError if not... + except TypeError: + raise ValueError("Not a valid timeout: {!r}".format(timeout)) + if timeout < 0: + raise ValueError("Not a valid timeout: {!r}".format(timeout)) + self._timeout = timeout + if self.is_open: + self._reconfigure_port() + + @property + def write_timeout(self): + """Get the current timeout setting.""" + return self._write_timeout + + @write_timeout.setter + def write_timeout(self, timeout): + """Change timeout setting.""" + if timeout is not None: + if timeout < 0: + raise ValueError("Not a valid timeout: {!r}".format(timeout)) + try: + timeout + 1 # test if it's a number, will throw a TypeError if not... + except TypeError: + raise ValueError("Not a valid timeout: {!r}".format(timeout)) + + self._write_timeout = timeout + if self.is_open: + self._reconfigure_port() + + @property + def inter_byte_timeout(self): + """Get the current inter-character timeout setting.""" + return self._inter_byte_timeout + + @inter_byte_timeout.setter + def inter_byte_timeout(self, ic_timeout): + """Change inter-byte timeout setting.""" + if ic_timeout is not None: + if ic_timeout < 0: + raise ValueError("Not a valid timeout: {!r}".format(ic_timeout)) + try: + ic_timeout + 1 # test if it's a number, will throw a TypeError if not... + except TypeError: + raise ValueError("Not a valid timeout: {!r}".format(ic_timeout)) + + self._inter_byte_timeout = ic_timeout + if self.is_open: + self._reconfigure_port() + + @property + def xonxoff(self): + """Get the current XON/XOFF setting.""" + return self._xonxoff + + @xonxoff.setter + def xonxoff(self, xonxoff): + """Change XON/XOFF setting.""" + self._xonxoff = xonxoff + if self.is_open: + self._reconfigure_port() + + @property + def rtscts(self): + """Get the current RTS/CTS flow control setting.""" + return self._rtscts + + @rtscts.setter + def rtscts(self, rtscts): + """Change RTS/CTS flow control setting.""" + self._rtscts = rtscts + if self.is_open: + self._reconfigure_port() + + @property + def dsrdtr(self): + """Get the current DSR/DTR flow control setting.""" + return self._dsrdtr + + @dsrdtr.setter + def dsrdtr(self, dsrdtr=None): + """Change DsrDtr flow control setting.""" + if dsrdtr is None: + # if not set, keep backwards compatibility and follow rtscts setting + self._dsrdtr = self._rtscts + else: + # if defined independently, follow its value + self._dsrdtr = dsrdtr + if self.is_open: + self._reconfigure_port() + + @property + def rts(self): + return self._rts_state + + @rts.setter + def rts(self, value): + self._rts_state = value + if self.is_open: + self._update_rts_state() + + @property + def dtr(self): + return self._dtr_state + + @dtr.setter + def dtr(self, value): + self._dtr_state = value + if self.is_open: + self._update_dtr_state() + + @property + def break_condition(self): + return self._break_state + + @break_condition.setter + def break_condition(self, value): + self._break_state = value + if self.is_open: + self._update_break_state() + + # - - - - - - - - - - - - - - - - - - - - - - - - + # functions useful for RS-485 adapters + + @property + def rs485_mode(self): + """\ + Enable RS485 mode and apply new settings, set to None to disable. + See serial.rs485.RS485Settings for more info about the value. + """ + return self._rs485_mode + + @rs485_mode.setter + def rs485_mode(self, rs485_settings): + self._rs485_mode = rs485_settings + if self.is_open: + self._reconfigure_port() + + # - - - - - - - - - - - - - - - - - - - - - - - - + + _SAVED_SETTINGS = ('baudrate', 'bytesize', 'parity', 'stopbits', 'xonxoff', + 'dsrdtr', 'rtscts', 'timeout', 'write_timeout', + 'inter_byte_timeout') + + def get_settings(self): + """\ + Get current port settings as a dictionary. For use with + apply_settings(). + """ + return dict([(key, getattr(self, '_' + key)) for key in self._SAVED_SETTINGS]) + + def apply_settings(self, d): + """\ + Apply stored settings from a dictionary returned from + get_settings(). It's allowed to delete keys from the dictionary. These + values will simply left unchanged. + """ + for key in self._SAVED_SETTINGS: + if key in d and d[key] != getattr(self, '_' + key): # check against internal "_" value + setattr(self, key, d[key]) # set non "_" value to use properties write function + + # - - - - - - - - - - - - - - - - - - - - - - - - + + def __repr__(self): + """String representation of the current port settings and its state.""" + return '{name}(port={p.portstr!r}, ' \ + 'baudrate={p.baudrate!r}, bytesize={p.bytesize!r}, parity={p.parity!r}, ' \ + 'stopbits={p.stopbits!r}, timeout={p.timeout!r}, xonxoff={p.xonxoff!r}, ' \ + 'rtscts={p.rtscts!r}, dsrdtr={p.dsrdtr!r})'.format( + name=self.__class__.__name__, id=id(self), p=self) + + # - - - - - - - - - - - - - - - - - - - - - - - - + # compatibility with io library + # pylint: disable=invalid-name,missing-docstring + + def readable(self): + return True + + def writable(self): + return True + + def seekable(self): + return False + + def readinto(self, b): + data = self.read(len(b)) + n = len(data) + try: + b[:n] = data + except TypeError as err: + import array + if not isinstance(b, array.array): + raise err + b[:n] = array.array('b', data) + return n + + # - - - - - - - - - - - - - - - - - - - - - - - - + # context manager + + def __enter__(self): + if not self.is_open: + self.open() + return self + + def __exit__(self, *args, **kwargs): + self.close() + + # - - - - - - - - - - - - - - - - - - - - - - - - + + def send_break(self, duration=0.25): + """\ + Send break condition. Timed, returns to idle state after given + duration. + """ + if not self.is_open: + raise portNotOpenError + self.break_condition = True + time.sleep(duration) + self.break_condition = False + + # - - - - - - - - - - - - - - - - - - - - - - - - + # backwards compatibility / deprecated functions + + def flushInput(self): + self.reset_input_buffer() + + def flushOutput(self): + self.reset_output_buffer() + + def inWaiting(self): + return self.in_waiting + + def sendBreak(self, duration=0.25): + self.send_break(duration) + + def setRTS(self, value=1): + self.rts = value + + def setDTR(self, value=1): + self.dtr = value + + def getCTS(self): + return self.cts + + def getDSR(self): + return self.dsr + + def getRI(self): + return self.ri + + def getCD(self): + return self.cd + + def setPort(self, port): + self.port = port + + @property + def writeTimeout(self): + return self.write_timeout + + @writeTimeout.setter + def writeTimeout(self, timeout): + self.write_timeout = timeout + + @property + def interCharTimeout(self): + return self.inter_byte_timeout + + @interCharTimeout.setter + def interCharTimeout(self, interCharTimeout): + self.inter_byte_timeout = interCharTimeout + + def getSettingsDict(self): + return self.get_settings() + + def applySettingsDict(self, d): + self.apply_settings(d) + + def isOpen(self): + return self.is_open + + # - - - - - - - - - - - - - - - - - - - - - - - - + # additional functionality + + def read_all(self): + """\ + Read all bytes currently available in the buffer of the OS. + """ + return self.read(self.in_waiting) + + def read_until(self, terminator=LF, size=None): + """\ + Read until a termination sequence is found ('\n' by default), the size + is exceeded or until timeout occurs. + """ + lenterm = len(terminator) + line = bytearray() + timeout = Timeout(self._timeout) + while True: + c = self.read(1) + if c: + line += c + if line[-lenterm:] == terminator: + break + if size is not None and len(line) >= size: + break + else: + break + if timeout.expired(): + break + return bytes(line) + + def iread_until(self, *args, **kwargs): + """\ + Read lines, implemented as generator. It will raise StopIteration on + timeout (empty read). + """ + while True: + line = self.read_until(*args, **kwargs) + if not line: + break + yield line + + +# - - - - - - - - - - - - - - - - - - - - - - - - - +if __name__ == '__main__': + import sys + s = SerialBase() + sys.stdout.write('port name: {}\n'.format(s.name)) + sys.stdout.write('baud rates: {}\n'.format(s.BAUDRATES)) + sys.stdout.write('byte sizes: {}\n'.format(s.BYTESIZES)) + sys.stdout.write('parities: {}\n'.format(s.PARITIES)) + sys.stdout.write('stop bits: {}\n'.format(s.STOPBITS)) + sys.stdout.write('{}\n'.format(s)) diff --git a/dist/bin/esptool/serial/serialwin32.py b/dist/bin/esptool/serial/serialwin32.py new file mode 100644 index 000000000..7b889993b --- /dev/null +++ b/dist/bin/esptool/serial/serialwin32.py @@ -0,0 +1,475 @@ +#! python +# +# backend for Windows ("win32" incl. 32/64 bit support) +# +# (C) 2001-2015 Chris Liechti +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# SPDX-License-Identifier: BSD-3-Clause +# +# Initial patch to use ctypes by Giovanni Bajo + +# pylint: disable=invalid-name,too-few-public-methods +import ctypes +import time +from serial import win32 + +import serial +from serial.serialutil import SerialBase, SerialException, to_bytes, portNotOpenError, writeTimeoutError + + +class Serial(SerialBase): + """Serial port implementation for Win32 based on ctypes.""" + + BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200) + + def __init__(self, *args, **kwargs): + self._port_handle = None + self._overlapped_read = None + self._overlapped_write = None + super(Serial, self).__init__(*args, **kwargs) + + def open(self): + """\ + Open port with current settings. This may throw a SerialException + if the port cannot be opened. + """ + if self._port is None: + raise SerialException("Port must be configured before it can be used.") + if self.is_open: + raise SerialException("Port is already open.") + # the "\\.\COMx" format is required for devices other than COM1-COM8 + # not all versions of windows seem to support this properly + # so that the first few ports are used with the DOS device name + port = self.name + try: + if port.upper().startswith('COM') and int(port[3:]) > 8: + port = '\\\\.\\' + port + except ValueError: + # for like COMnotanumber + pass + self._port_handle = win32.CreateFile( + port, + win32.GENERIC_READ | win32.GENERIC_WRITE, + 0, # exclusive access + None, # no security + win32.OPEN_EXISTING, + win32.FILE_ATTRIBUTE_NORMAL | win32.FILE_FLAG_OVERLAPPED, + 0) + if self._port_handle == win32.INVALID_HANDLE_VALUE: + self._port_handle = None # 'cause __del__ is called anyway + raise SerialException("could not open port {!r}: {!r}".format(self.portstr, ctypes.WinError())) + + try: + self._overlapped_read = win32.OVERLAPPED() + self._overlapped_read.hEvent = win32.CreateEvent(None, 1, 0, None) + self._overlapped_write = win32.OVERLAPPED() + #~ self._overlapped_write.hEvent = win32.CreateEvent(None, 1, 0, None) + self._overlapped_write.hEvent = win32.CreateEvent(None, 0, 0, None) + + # Setup a 4k buffer + win32.SetupComm(self._port_handle, 4096, 4096) + + # Save original timeout values: + self._orgTimeouts = win32.COMMTIMEOUTS() + win32.GetCommTimeouts(self._port_handle, ctypes.byref(self._orgTimeouts)) + + self._reconfigure_port() + + # Clear buffers: + # Remove anything that was there + win32.PurgeComm( + self._port_handle, + win32.PURGE_TXCLEAR | win32.PURGE_TXABORT | + win32.PURGE_RXCLEAR | win32.PURGE_RXABORT) + except: + try: + self._close() + except: + # ignore any exception when closing the port + # also to keep original exception that happened when setting up + pass + self._port_handle = None + raise + else: + self.is_open = True + + def _reconfigure_port(self): + """Set communication parameters on opened port.""" + if not self._port_handle: + raise SerialException("Can only operate on a valid port handle") + + # Set Windows timeout values + # timeouts is a tuple with the following items: + # (ReadIntervalTimeout,ReadTotalTimeoutMultiplier, + # ReadTotalTimeoutConstant,WriteTotalTimeoutMultiplier, + # WriteTotalTimeoutConstant) + timeouts = win32.COMMTIMEOUTS() + if self._timeout is None: + pass # default of all zeros is OK + elif self._timeout == 0: + timeouts.ReadIntervalTimeout = win32.MAXDWORD + else: + timeouts.ReadTotalTimeoutConstant = max(int(self._timeout * 1000), 1) + if self._timeout != 0 and self._inter_byte_timeout is not None: + timeouts.ReadIntervalTimeout = max(int(self._inter_byte_timeout * 1000), 1) + + if self._write_timeout is None: + pass + elif self._write_timeout == 0: + timeouts.WriteTotalTimeoutConstant = win32.MAXDWORD + else: + timeouts.WriteTotalTimeoutConstant = max(int(self._write_timeout * 1000), 1) + win32.SetCommTimeouts(self._port_handle, ctypes.byref(timeouts)) + + win32.SetCommMask(self._port_handle, win32.EV_ERR) + + # Setup the connection info. + # Get state and modify it: + comDCB = win32.DCB() + win32.GetCommState(self._port_handle, ctypes.byref(comDCB)) + comDCB.BaudRate = self._baudrate + + if self._bytesize == serial.FIVEBITS: + comDCB.ByteSize = 5 + elif self._bytesize == serial.SIXBITS: + comDCB.ByteSize = 6 + elif self._bytesize == serial.SEVENBITS: + comDCB.ByteSize = 7 + elif self._bytesize == serial.EIGHTBITS: + comDCB.ByteSize = 8 + else: + raise ValueError("Unsupported number of data bits: {!r}".format(self._bytesize)) + + if self._parity == serial.PARITY_NONE: + comDCB.Parity = win32.NOPARITY + comDCB.fParity = 0 # Disable Parity Check + elif self._parity == serial.PARITY_EVEN: + comDCB.Parity = win32.EVENPARITY + comDCB.fParity = 1 # Enable Parity Check + elif self._parity == serial.PARITY_ODD: + comDCB.Parity = win32.ODDPARITY + comDCB.fParity = 1 # Enable Parity Check + elif self._parity == serial.PARITY_MARK: + comDCB.Parity = win32.MARKPARITY + comDCB.fParity = 1 # Enable Parity Check + elif self._parity == serial.PARITY_SPACE: + comDCB.Parity = win32.SPACEPARITY + comDCB.fParity = 1 # Enable Parity Check + else: + raise ValueError("Unsupported parity mode: {!r}".format(self._parity)) + + if self._stopbits == serial.STOPBITS_ONE: + comDCB.StopBits = win32.ONESTOPBIT + elif self._stopbits == serial.STOPBITS_ONE_POINT_FIVE: + comDCB.StopBits = win32.ONE5STOPBITS + elif self._stopbits == serial.STOPBITS_TWO: + comDCB.StopBits = win32.TWOSTOPBITS + else: + raise ValueError("Unsupported number of stop bits: {!r}".format(self._stopbits)) + + comDCB.fBinary = 1 # Enable Binary Transmission + # Char. w/ Parity-Err are replaced with 0xff (if fErrorChar is set to TRUE) + if self._rs485_mode is None: + if self._rtscts: + comDCB.fRtsControl = win32.RTS_CONTROL_HANDSHAKE + else: + comDCB.fRtsControl = win32.RTS_CONTROL_ENABLE if self._rts_state else win32.RTS_CONTROL_DISABLE + comDCB.fOutxCtsFlow = self._rtscts + else: + # checks for unsupported settings + # XXX verify if platform really does not have a setting for those + if not self._rs485_mode.rts_level_for_tx: + raise ValueError( + 'Unsupported value for RS485Settings.rts_level_for_tx: {!r}'.format( + self._rs485_mode.rts_level_for_tx,)) + if self._rs485_mode.rts_level_for_rx: + raise ValueError( + 'Unsupported value for RS485Settings.rts_level_for_rx: {!r}'.format( + self._rs485_mode.rts_level_for_rx,)) + if self._rs485_mode.delay_before_tx is not None: + raise ValueError( + 'Unsupported value for RS485Settings.delay_before_tx: {!r}'.format( + self._rs485_mode.delay_before_tx,)) + if self._rs485_mode.delay_before_rx is not None: + raise ValueError( + 'Unsupported value for RS485Settings.delay_before_rx: {!r}'.format( + self._rs485_mode.delay_before_rx,)) + if self._rs485_mode.loopback: + raise ValueError( + 'Unsupported value for RS485Settings.loopback: {!r}'.format( + self._rs485_mode.loopback,)) + comDCB.fRtsControl = win32.RTS_CONTROL_TOGGLE + comDCB.fOutxCtsFlow = 0 + + if self._dsrdtr: + comDCB.fDtrControl = win32.DTR_CONTROL_HANDSHAKE + else: + comDCB.fDtrControl = win32.DTR_CONTROL_ENABLE if self._dtr_state else win32.DTR_CONTROL_DISABLE + comDCB.fOutxDsrFlow = self._dsrdtr + comDCB.fOutX = self._xonxoff + comDCB.fInX = self._xonxoff + comDCB.fNull = 0 + comDCB.fErrorChar = 0 + comDCB.fAbortOnError = 0 + comDCB.XonChar = serial.XON + comDCB.XoffChar = serial.XOFF + + if not win32.SetCommState(self._port_handle, ctypes.byref(comDCB)): + raise SerialException( + 'Cannot configure port, something went wrong. ' + 'Original message: {!r}'.format(ctypes.WinError())) + + #~ def __del__(self): + #~ self.close() + + def _close(self): + """internal close port helper""" + if self._port_handle is not None: + # Restore original timeout values: + win32.SetCommTimeouts(self._port_handle, self._orgTimeouts) + if self._overlapped_read is not None: + self.cancel_read() + win32.CloseHandle(self._overlapped_read.hEvent) + self._overlapped_read = None + if self._overlapped_write is not None: + self.cancel_write() + win32.CloseHandle(self._overlapped_write.hEvent) + self._overlapped_write = None + win32.CloseHandle(self._port_handle) + self._port_handle = None + + def close(self): + """Close port""" + if self.is_open: + self._close() + self.is_open = False + + # - - - - - - - - - - - - - - - - - - - - - - - - + + @property + def in_waiting(self): + """Return the number of bytes currently in the input buffer.""" + flags = win32.DWORD() + comstat = win32.COMSTAT() + if not win32.ClearCommError(self._port_handle, ctypes.byref(flags), ctypes.byref(comstat)): + raise SerialException("ClearCommError failed ({!r})".format(ctypes.WinError())) + return comstat.cbInQue + + def read(self, size=1): + """\ + Read size bytes from the serial port. If a timeout is set it may + return less characters as requested. With no timeout it will block + until the requested number of bytes is read. + """ + if not self.is_open: + raise portNotOpenError + if size > 0: + win32.ResetEvent(self._overlapped_read.hEvent) + flags = win32.DWORD() + comstat = win32.COMSTAT() + if not win32.ClearCommError(self._port_handle, ctypes.byref(flags), ctypes.byref(comstat)): + raise SerialException("ClearCommError failed ({!r})".format(ctypes.WinError())) + n = min(comstat.cbInQue, size) if self.timeout == 0 else size + if n > 0: + buf = ctypes.create_string_buffer(n) + rc = win32.DWORD() + read_ok = win32.ReadFile( + self._port_handle, + buf, + n, + ctypes.byref(rc), + ctypes.byref(self._overlapped_read)) + if not read_ok and win32.GetLastError() not in (win32.ERROR_SUCCESS, win32.ERROR_IO_PENDING): + raise SerialException("ReadFile failed ({!r})".format(ctypes.WinError())) + result_ok = win32.GetOverlappedResult( + self._port_handle, + ctypes.byref(self._overlapped_read), + ctypes.byref(rc), + True) + if not result_ok: + if win32.GetLastError() != win32.ERROR_OPERATION_ABORTED: + raise SerialException("GetOverlappedResult failed ({!r})".format(ctypes.WinError())) + read = buf.raw[:rc.value] + else: + read = bytes() + else: + read = bytes() + return bytes(read) + + def write(self, data): + """Output the given byte string over the serial port.""" + if not self.is_open: + raise portNotOpenError + #~ if not isinstance(data, (bytes, bytearray)): + #~ raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data))) + # convert data (needed in case of memoryview instance: Py 3.1 io lib), ctypes doesn't like memoryview + data = to_bytes(data) + if data: + #~ win32event.ResetEvent(self._overlapped_write.hEvent) + n = win32.DWORD() + success = win32.WriteFile(self._port_handle, data, len(data), ctypes.byref(n), self._overlapped_write) + if self._write_timeout != 0: # if blocking (None) or w/ write timeout (>0) + if not success and win32.GetLastError() not in (win32.ERROR_SUCCESS, win32.ERROR_IO_PENDING): + raise SerialException("WriteFile failed ({!r})".format(ctypes.WinError())) + + # Wait for the write to complete. + #~ win32.WaitForSingleObject(self._overlapped_write.hEvent, win32.INFINITE) + win32.GetOverlappedResult(self._port_handle, self._overlapped_write, ctypes.byref(n), True) + if win32.GetLastError() == win32.ERROR_OPERATION_ABORTED: + return n.value # canceled IO is no error + if n.value != len(data): + raise writeTimeoutError + return n.value + else: + errorcode = win32.ERROR_SUCCESS if success else win32.GetLastError() + if errorcode in (win32.ERROR_INVALID_USER_BUFFER, win32.ERROR_NOT_ENOUGH_MEMORY, + win32.ERROR_OPERATION_ABORTED): + return 0 + elif errorcode in (win32.ERROR_SUCCESS, win32.ERROR_IO_PENDING): + # no info on true length provided by OS function in async mode + return len(data) + else: + raise SerialException("WriteFile failed ({!r})".format(ctypes.WinError())) + else: + return 0 + + def flush(self): + """\ + Flush of file like objects. In this case, wait until all data + is written. + """ + while self.out_waiting: + time.sleep(0.05) + # XXX could also use WaitCommEvent with mask EV_TXEMPTY, but it would + # require overlapped IO and it's also only possible to set a single mask + # on the port--- + + def reset_input_buffer(self): + """Clear input buffer, discarding all that is in the buffer.""" + if not self.is_open: + raise portNotOpenError + win32.PurgeComm(self._port_handle, win32.PURGE_RXCLEAR | win32.PURGE_RXABORT) + + def reset_output_buffer(self): + """\ + Clear output buffer, aborting the current output and discarding all + that is in the buffer. + """ + if not self.is_open: + raise portNotOpenError + win32.PurgeComm(self._port_handle, win32.PURGE_TXCLEAR | win32.PURGE_TXABORT) + + def _update_break_state(self): + """Set break: Controls TXD. When active, to transmitting is possible.""" + if not self.is_open: + raise portNotOpenError + if self._break_state: + win32.SetCommBreak(self._port_handle) + else: + win32.ClearCommBreak(self._port_handle) + + def _update_rts_state(self): + """Set terminal status line: Request To Send""" + if self._rts_state: + win32.EscapeCommFunction(self._port_handle, win32.SETRTS) + else: + win32.EscapeCommFunction(self._port_handle, win32.CLRRTS) + + def _update_dtr_state(self): + """Set terminal status line: Data Terminal Ready""" + if self._dtr_state: + win32.EscapeCommFunction(self._port_handle, win32.SETDTR) + else: + win32.EscapeCommFunction(self._port_handle, win32.CLRDTR) + + def _GetCommModemStatus(self): + if not self.is_open: + raise portNotOpenError + stat = win32.DWORD() + win32.GetCommModemStatus(self._port_handle, ctypes.byref(stat)) + return stat.value + + @property + def cts(self): + """Read terminal status line: Clear To Send""" + return win32.MS_CTS_ON & self._GetCommModemStatus() != 0 + + @property + def dsr(self): + """Read terminal status line: Data Set Ready""" + return win32.MS_DSR_ON & self._GetCommModemStatus() != 0 + + @property + def ri(self): + """Read terminal status line: Ring Indicator""" + return win32.MS_RING_ON & self._GetCommModemStatus() != 0 + + @property + def cd(self): + """Read terminal status line: Carrier Detect""" + return win32.MS_RLSD_ON & self._GetCommModemStatus() != 0 + + # - - platform specific - - - - + + def set_buffer_size(self, rx_size=4096, tx_size=None): + """\ + Recommend a buffer size to the driver (device driver can ignore this + value). Must be called before the port is opened. + """ + if tx_size is None: + tx_size = rx_size + win32.SetupComm(self._port_handle, rx_size, tx_size) + + def set_output_flow_control(self, enable=True): + """\ + Manually control flow - when software flow control is enabled. + This will do the same as if XON (true) or XOFF (false) are received + from the other device and control the transmission accordingly. + WARNING: this function is not portable to different platforms! + """ + if not self.is_open: + raise portNotOpenError + if enable: + win32.EscapeCommFunction(self._port_handle, win32.SETXON) + else: + win32.EscapeCommFunction(self._port_handle, win32.SETXOFF) + + @property + def out_waiting(self): + """Return how many bytes the in the outgoing buffer""" + flags = win32.DWORD() + comstat = win32.COMSTAT() + if not win32.ClearCommError(self._port_handle, ctypes.byref(flags), ctypes.byref(comstat)): + raise SerialException("ClearCommError failed ({!r})".format(ctypes.WinError())) + return comstat.cbOutQue + + def _cancel_overlapped_io(self, overlapped): + """Cancel a blocking read operation, may be called from other thread""" + # check if read operation is pending + rc = win32.DWORD() + err = win32.GetOverlappedResult( + self._port_handle, + ctypes.byref(overlapped), + ctypes.byref(rc), + False) + if not err and win32.GetLastError() in (win32.ERROR_IO_PENDING, win32.ERROR_IO_INCOMPLETE): + # cancel, ignoring any errors (e.g. it may just have finished on its own) + win32.CancelIoEx(self._port_handle, overlapped) + + def cancel_read(self): + """Cancel a blocking read operation, may be called from other thread""" + self._cancel_overlapped_io(self._overlapped_read) + + def cancel_write(self): + """Cancel a blocking write operation, may be called from other thread""" + self._cancel_overlapped_io(self._overlapped_write) + + @SerialBase.exclusive.setter + def exclusive(self, exclusive): + """Change the exclusive access setting.""" + if exclusive is not None and not exclusive: + raise ValueError('win32 only supports exclusive access (not: {})'.format(exclusive)) + else: + serial.SerialBase.exclusive.__set__(self, exclusive) diff --git a/dist/bin/esptool/serial/threaded/__init__.py b/dist/bin/esptool/serial/threaded/__init__.py new file mode 100644 index 000000000..74b692494 --- /dev/null +++ b/dist/bin/esptool/serial/threaded/__init__.py @@ -0,0 +1,295 @@ +#!/usr/bin/env python3 +# +# Working with threading and pySerial +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2015-2016 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause +"""\ +Support threading with serial ports. +""" +import serial +import threading + + +class Protocol(object): + """\ + Protocol as used by the ReaderThread. This base class provides empty + implementations of all methods. + """ + + def connection_made(self, transport): + """Called when reader thread is started""" + + def data_received(self, data): + """Called with snippets received from the serial port""" + + def connection_lost(self, exc): + """\ + Called when the serial port is closed or the reader loop terminated + otherwise. + """ + if isinstance(exc, Exception): + raise exc + + +class Packetizer(Protocol): + """ + Read binary packets from serial port. Packets are expected to be terminated + with a TERMINATOR byte (null byte by default). + + The class also keeps track of the transport. + """ + + TERMINATOR = b'\0' + + def __init__(self): + self.buffer = bytearray() + self.transport = None + + def connection_made(self, transport): + """Store transport""" + self.transport = transport + + def connection_lost(self, exc): + """Forget transport""" + self.transport = None + super(Packetizer, self).connection_lost(exc) + + def data_received(self, data): + """Buffer received data, find TERMINATOR, call handle_packet""" + self.buffer.extend(data) + while self.TERMINATOR in self.buffer: + packet, self.buffer = self.buffer.split(self.TERMINATOR, 1) + self.handle_packet(packet) + + def handle_packet(self, packet): + """Process packets - to be overridden by subclassing""" + raise NotImplementedError('please implement functionality in handle_packet') + + +class FramedPacket(Protocol): + """ + Read binary packets. Packets are expected to have a start and stop marker. + + The class also keeps track of the transport. + """ + + START = b'(' + STOP = b')' + + def __init__(self): + self.packet = bytearray() + self.in_packet = False + self.transport = None + + def connection_made(self, transport): + """Store transport""" + self.transport = transport + + def connection_lost(self, exc): + """Forget transport""" + self.transport = None + self.in_packet = False + del self.packet[:] + super(FramedPacket, self).connection_lost(exc) + + def data_received(self, data): + """Find data enclosed in START/STOP, call handle_packet""" + for byte in serial.iterbytes(data): + if byte == self.START: + self.in_packet = True + elif byte == self.STOP: + self.in_packet = False + self.handle_packet(bytes(self.packet)) # make read-only copy + del self.packet[:] + elif self.in_packet: + self.packet.extend(byte) + else: + self.handle_out_of_packet_data(byte) + + def handle_packet(self, packet): + """Process packets - to be overridden by subclassing""" + raise NotImplementedError('please implement functionality in handle_packet') + + def handle_out_of_packet_data(self, data): + """Process data that is received outside of packets""" + pass + + +class LineReader(Packetizer): + """ + Read and write (Unicode) lines from/to serial port. + The encoding is applied. + """ + + TERMINATOR = b'\r\n' + ENCODING = 'utf-8' + UNICODE_HANDLING = 'replace' + + def handle_packet(self, packet): + self.handle_line(packet.decode(self.ENCODING, self.UNICODE_HANDLING)) + + def handle_line(self, line): + """Process one line - to be overridden by subclassing""" + raise NotImplementedError('please implement functionality in handle_line') + + def write_line(self, text): + """ + Write text to the transport. ``text`` is a Unicode string and the encoding + is applied before sending ans also the newline is append. + """ + # + is not the best choice but bytes does not support % or .format in py3 and we want a single write call + self.transport.write(text.encode(self.ENCODING, self.UNICODE_HANDLING) + self.TERMINATOR) + + +class ReaderThread(threading.Thread): + """\ + Implement a serial port read loop and dispatch to a Protocol instance (like + the asyncio.Protocol) but do it with threads. + + Calls to close() will close the serial port but it is also possible to just + stop() this thread and continue the serial port instance otherwise. + """ + + def __init__(self, serial_instance, protocol_factory): + """\ + Initialize thread. + + Note that the serial_instance' timeout is set to one second! + Other settings are not changed. + """ + super(ReaderThread, self).__init__() + self.daemon = True + self.serial = serial_instance + self.protocol_factory = protocol_factory + self.alive = True + self._lock = threading.Lock() + self._connection_made = threading.Event() + self.protocol = None + + def stop(self): + """Stop the reader thread""" + self.alive = False + if hasattr(self.serial, 'cancel_read'): + self.serial.cancel_read() + self.join(2) + + def run(self): + """Reader loop""" + if not hasattr(self.serial, 'cancel_read'): + self.serial.timeout = 1 + self.protocol = self.protocol_factory() + try: + self.protocol.connection_made(self) + except Exception as e: + self.alive = False + self.protocol.connection_lost(e) + self._connection_made.set() + return + error = None + self._connection_made.set() + while self.alive and self.serial.is_open: + try: + # read all that is there or wait for one byte (blocking) + data = self.serial.read(self.serial.in_waiting or 1) + except serial.SerialException as e: + # probably some I/O problem such as disconnected USB serial + # adapters -> exit + error = e + break + else: + if data: + # make a separated try-except for called used code + try: + self.protocol.data_received(data) + except Exception as e: + error = e + break + self.alive = False + self.protocol.connection_lost(error) + self.protocol = None + + def write(self, data): + """Thread safe writing (uses lock)""" + with self._lock: + self.serial.write(data) + + def close(self): + """Close the serial port and exit reader thread (uses lock)""" + # use the lock to let other threads finish writing + with self._lock: + # first stop reading, so that closing can be done on idle port + self.stop() + self.serial.close() + + def connect(self): + """ + Wait until connection is set up and return the transport and protocol + instances. + """ + if self.alive: + self._connection_made.wait() + if not self.alive: + raise RuntimeError('connection_lost already called') + return (self, self.protocol) + else: + raise RuntimeError('already stopped') + + # - - context manager, returns protocol + + def __enter__(self): + """\ + Enter context handler. May raise RuntimeError in case the connection + could not be created. + """ + self.start() + self._connection_made.wait() + if not self.alive: + raise RuntimeError('connection_lost already called') + return self.protocol + + def __exit__(self, exc_type, exc_val, exc_tb): + """Leave context: close port""" + self.close() + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# test +if __name__ == '__main__': + # pylint: disable=wrong-import-position + import sys + import time + import traceback + + #~ PORT = 'spy:///dev/ttyUSB0' + PORT = 'loop://' + + class PrintLines(LineReader): + def connection_made(self, transport): + super(PrintLines, self).connection_made(transport) + sys.stdout.write('port opened\n') + self.write_line('hello world') + + def handle_line(self, data): + sys.stdout.write('line received: {!r}\n'.format(data)) + + def connection_lost(self, exc): + if exc: + traceback.print_exc(exc) + sys.stdout.write('port closed\n') + + ser = serial.serial_for_url(PORT, baudrate=115200, timeout=1) + with ReaderThread(ser, PrintLines) as protocol: + protocol.write_line('hello') + time.sleep(2) + + # alternative usage + ser = serial.serial_for_url(PORT, baudrate=115200, timeout=1) + t = ReaderThread(ser, PrintLines) + t.start() + transport, protocol = t.connect() + protocol.write_line('hello') + time.sleep(2) + t.close() diff --git a/dist/bin/esptool/serial/tools/__init__.py b/dist/bin/esptool/serial/tools/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/dist/bin/esptool/serial/tools/__pycache__/__init__.cpython-39.pyc b/dist/bin/esptool/serial/tools/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 000000000..5ba42d1f1 Binary files /dev/null and b/dist/bin/esptool/serial/tools/__pycache__/__init__.cpython-39.pyc differ diff --git a/dist/bin/esptool/serial/tools/__pycache__/list_ports.cpython-39.pyc b/dist/bin/esptool/serial/tools/__pycache__/list_ports.cpython-39.pyc new file mode 100644 index 000000000..e9738144c Binary files /dev/null and b/dist/bin/esptool/serial/tools/__pycache__/list_ports.cpython-39.pyc differ diff --git a/dist/bin/esptool/serial/tools/__pycache__/list_ports_common.cpython-39.pyc b/dist/bin/esptool/serial/tools/__pycache__/list_ports_common.cpython-39.pyc new file mode 100644 index 000000000..e0bd5cebe Binary files /dev/null and b/dist/bin/esptool/serial/tools/__pycache__/list_ports_common.cpython-39.pyc differ diff --git a/dist/bin/esptool/serial/tools/__pycache__/list_ports_windows.cpython-39.pyc b/dist/bin/esptool/serial/tools/__pycache__/list_ports_windows.cpython-39.pyc new file mode 100644 index 000000000..9a133f1fb Binary files /dev/null and b/dist/bin/esptool/serial/tools/__pycache__/list_ports_windows.cpython-39.pyc differ diff --git a/dist/bin/esptool/serial/tools/hexlify_codec.py b/dist/bin/esptool/serial/tools/hexlify_codec.py new file mode 100644 index 000000000..1371da2cc --- /dev/null +++ b/dist/bin/esptool/serial/tools/hexlify_codec.py @@ -0,0 +1,124 @@ +#! python +# +# This is a codec to create and decode hexdumps with spaces between characters. used by miniterm. +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2015-2016 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause +"""\ +Python 'hex' Codec - 2-digit hex with spaces content transfer encoding. + +Encode and decode may be a bit missleading at first sight... + +The textual representation is a hex dump: e.g. "40 41" +The "encoded" data of this is the binary form, e.g. b"@A" + +Therefore decoding is binary to text and thus converting binary data to hex dump. + +""" + +import codecs +import serial + + +try: + unicode +except (NameError, AttributeError): + unicode = str # for Python 3, pylint: disable=redefined-builtin,invalid-name + + +HEXDIGITS = '0123456789ABCDEF' + + +# Codec APIs + +def hex_encode(data, errors='strict'): + """'40 41 42' -> b'@ab'""" + return (serial.to_bytes([int(h, 16) for h in data.split()]), len(data)) + + +def hex_decode(data, errors='strict'): + """b'@ab' -> '40 41 42'""" + return (unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data))), len(data)) + + +class Codec(codecs.Codec): + def encode(self, data, errors='strict'): + """'40 41 42' -> b'@ab'""" + return serial.to_bytes([int(h, 16) for h in data.split()]) + + def decode(self, data, errors='strict'): + """b'@ab' -> '40 41 42'""" + return unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data))) + + +class IncrementalEncoder(codecs.IncrementalEncoder): + """Incremental hex encoder""" + + def __init__(self, errors='strict'): + self.errors = errors + self.state = 0 + + def reset(self): + self.state = 0 + + def getstate(self): + return self.state + + def setstate(self, state): + self.state = state + + def encode(self, data, final=False): + """\ + Incremental encode, keep track of digits and emit a byte when a pair + of hex digits is found. The space is optional unless the error + handling is defined to be 'strict'. + """ + state = self.state + encoded = [] + for c in data.upper(): + if c in HEXDIGITS: + z = HEXDIGITS.index(c) + if state: + encoded.append(z + (state & 0xf0)) + state = 0 + else: + state = 0x100 + (z << 4) + elif c == ' ': # allow spaces to separate values + if state and self.errors == 'strict': + raise UnicodeError('odd number of hex digits') + state = 0 + else: + if self.errors == 'strict': + raise UnicodeError('non-hex digit found: {!r}'.format(c)) + self.state = state + return serial.to_bytes(encoded) + + +class IncrementalDecoder(codecs.IncrementalDecoder): + """Incremental decoder""" + def decode(self, data, final=False): + return unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data))) + + +class StreamWriter(Codec, codecs.StreamWriter): + """Combination of hexlify codec and StreamWriter""" + + +class StreamReader(Codec, codecs.StreamReader): + """Combination of hexlify codec and StreamReader""" + + +def getregentry(): + """encodings module API""" + return codecs.CodecInfo( + name='hexlify', + encode=hex_encode, + decode=hex_decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamwriter=StreamWriter, + streamreader=StreamReader, + #~ _is_text_encoding=True, + ) diff --git a/dist/bin/esptool/serial/tools/list_ports.py b/dist/bin/esptool/serial/tools/list_ports.py new file mode 100644 index 000000000..827e81f95 --- /dev/null +++ b/dist/bin/esptool/serial/tools/list_ports.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python +# +# Serial port enumeration. Console tool and backend selection. +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2011-2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +"""\ +This module will provide a function called comports that returns an +iterable (generator or list) that will enumerate available com ports. Note that +on some systems non-existent ports may be listed. + +Additionally a grep function is supplied that can be used to search for ports +based on their descriptions or hardware ID. +""" + +import sys +import os +import re + +# chose an implementation, depending on os +#~ if sys.platform == 'cli': +#~ else: +if os.name == 'nt': # sys.platform == 'win32': + from serial.tools.list_ports_windows import comports +elif os.name == 'posix': + from serial.tools.list_ports_posix import comports +#~ elif os.name == 'java': +else: + raise ImportError("Sorry: no implementation for your platform ('{}') available".format(os.name)) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +def grep(regexp, include_links=False): + """\ + Search for ports using a regular expression. Port name, description and + hardware ID are searched. The function returns an iterable that returns the + same tuples as comport() would do. + """ + r = re.compile(regexp, re.I) + for info in comports(include_links): + port, desc, hwid = info + if r.search(port) or r.search(desc) or r.search(hwid): + yield info + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +def main(): + import argparse + + parser = argparse.ArgumentParser(description='Serial port enumeration') + + parser.add_argument( + 'regexp', + nargs='?', + help='only show ports that match this regex') + + parser.add_argument( + '-v', '--verbose', + action='store_true', + help='show more messages') + + parser.add_argument( + '-q', '--quiet', + action='store_true', + help='suppress all messages') + + parser.add_argument( + '-n', + type=int, + help='only output the N-th entry') + + parser.add_argument( + '-s', '--include-links', + action='store_true', + help='include entries that are symlinks to real devices') + + args = parser.parse_args() + + hits = 0 + # get iteraror w/ or w/o filter + if args.regexp: + if not args.quiet: + sys.stderr.write("Filtered list with regexp: {!r}\n".format(args.regexp)) + iterator = sorted(grep(args.regexp, include_links=args.include_links)) + else: + iterator = sorted(comports(include_links=args.include_links)) + # list them + for n, (port, desc, hwid) in enumerate(iterator, 1): + if args.n is None or args.n == n: + sys.stdout.write("{:20}\n".format(port)) + if args.verbose: + sys.stdout.write(" desc: {}\n".format(desc)) + sys.stdout.write(" hwid: {}\n".format(hwid)) + hits += 1 + if not args.quiet: + if hits: + sys.stderr.write("{} ports found\n".format(hits)) + else: + sys.stderr.write("no ports found\n") + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# test +if __name__ == '__main__': + main() diff --git a/dist/bin/esptool/serial/tools/list_ports_common.py b/dist/bin/esptool/serial/tools/list_ports_common.py new file mode 100644 index 000000000..145e63eec --- /dev/null +++ b/dist/bin/esptool/serial/tools/list_ports_common.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python +# +# This is a helper module for the various platform dependent list_port +# implementations. +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause +import re +import glob +import os + + +def numsplit(text): + """\ + Convert string into a list of texts and numbers in order to support a + natural sorting. + """ + result = [] + for group in re.split(r'(\d+)', text): + if group: + try: + group = int(group) + except ValueError: + pass + result.append(group) + return result + + +class ListPortInfo(object): + """Info collection base class for serial ports""" + + def __init__(self, device=None): + self.device = device + self.name = None + self.description = 'n/a' + self.hwid = 'n/a' + # USB specific data + self.vid = None + self.pid = None + self.serial_number = None + self.location = None + self.manufacturer = None + self.product = None + self.interface = None + # special handling for links + if device is not None and os.path.islink(device): + self.hwid = 'LINK={}'.format(os.path.realpath(device)) + + def usb_description(self): + """return a short string to name the port based on USB info""" + if self.interface is not None: + return '{} - {}'.format(self.product, self.interface) + elif self.product is not None: + return self.product + else: + return self.name + + def usb_info(self): + """return a string with USB related information about device""" + return 'USB VID:PID={:04X}:{:04X}{}{}'.format( + self.vid or 0, + self.pid or 0, + ' SER={}'.format(self.serial_number) if self.serial_number is not None else '', + ' LOCATION={}'.format(self.location) if self.location is not None else '') + + def apply_usb_info(self): + """update description and hwid from USB data""" + self.description = self.usb_description() + self.hwid = self.usb_info() + + def __eq__(self, other): + return self.device == other.device + + def __lt__(self, other): + return numsplit(self.device) < numsplit(other.device) + + def __str__(self): + return '{} - {}'.format(self.device, self.description) + + def __getitem__(self, index): + """Item access: backwards compatible -> (port, desc, hwid)""" + if index == 0: + return self.device + elif index == 1: + return self.description + elif index == 2: + return self.hwid + else: + raise IndexError('{} > 2'.format(index)) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +def list_links(devices): + """\ + search all /dev devices and look for symlinks to known ports already + listed in devices. + """ + links = [] + for device in glob.glob('/dev/*'): + if os.path.islink(device) and os.path.realpath(device) in devices: + links.append(device) + return links + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# test +if __name__ == '__main__': + print(ListPortInfo('dummy')) diff --git a/dist/bin/esptool/serial/tools/list_ports_linux.py b/dist/bin/esptool/serial/tools/list_ports_linux.py new file mode 100644 index 000000000..4be27cdf0 --- /dev/null +++ b/dist/bin/esptool/serial/tools/list_ports_linux.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python +# +# This is a module that gathers a list of serial ports including details on +# GNU/Linux systems. +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2011-2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +import glob +import os +from serial.tools import list_ports_common + + +class SysFS(list_ports_common.ListPortInfo): + """Wrapper for easy sysfs access and device info""" + + def __init__(self, device): + super(SysFS, self).__init__(device) + # special handling for links + if device is not None and os.path.islink(device): + device = os.path.realpath(device) + is_link = True + else: + is_link = False + self.name = os.path.basename(device) + self.usb_device_path = None + if os.path.exists('/sys/class/tty/{}/device'.format(self.name)): + self.device_path = os.path.realpath('/sys/class/tty/{}/device'.format(self.name)) + self.subsystem = os.path.basename(os.path.realpath(os.path.join(self.device_path, 'subsystem'))) + else: + self.device_path = None + self.subsystem = None + # check device type + if self.subsystem == 'usb-serial': + self.usb_interface_path = os.path.dirname(self.device_path) + elif self.subsystem == 'usb': + self.usb_interface_path = self.device_path + else: + self.usb_interface_path = None + # fill-in info for USB devices + if self.usb_interface_path is not None: + self.usb_device_path = os.path.dirname(self.usb_interface_path) + + try: + num_if = int(self.read_line(self.usb_device_path, 'bNumInterfaces')) + except ValueError: + num_if = 1 + + self.vid = int(self.read_line(self.usb_device_path, 'idVendor'), 16) + self.pid = int(self.read_line(self.usb_device_path, 'idProduct'), 16) + self.serial_number = self.read_line(self.usb_device_path, 'serial') + if num_if > 1: # multi interface devices like FT4232 + self.location = os.path.basename(self.usb_interface_path) + else: + self.location = os.path.basename(self.usb_device_path) + + self.manufacturer = self.read_line(self.usb_device_path, 'manufacturer') + self.product = self.read_line(self.usb_device_path, 'product') + self.interface = self.read_line(self.device_path, 'interface') + + if self.subsystem in ('usb', 'usb-serial'): + self.apply_usb_info() + #~ elif self.subsystem in ('pnp', 'amba'): # PCI based devices, raspi + elif self.subsystem == 'pnp': # PCI based devices + self.description = self.name + self.hwid = self.read_line(self.device_path, 'id') + elif self.subsystem == 'amba': # raspi + self.description = self.name + self.hwid = os.path.basename(self.device_path) + + if is_link: + self.hwid += ' LINK={}'.format(device) + + def read_line(self, *args): + """\ + Helper function to read a single line from a file. + One or more parameters are allowed, they are joined with os.path.join. + Returns None on errors.. + """ + try: + with open(os.path.join(*args)) as f: + line = f.readline().strip() + return line + except IOError: + return None + + +def comports(include_links=False): + devices = glob.glob('/dev/ttyS*') # built-in serial ports + devices.extend(glob.glob('/dev/ttyUSB*')) # usb-serial with own driver + devices.extend(glob.glob('/dev/ttyACM*')) # usb-serial with CDC-ACM profile + devices.extend(glob.glob('/dev/ttyAMA*')) # ARM internal port (raspi) + devices.extend(glob.glob('/dev/rfcomm*')) # BT serial devices + devices.extend(glob.glob('/dev/ttyAP*')) # Advantech multi-port serial controllers + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [info + for info in [SysFS(d) for d in devices] + if info.subsystem != "platform"] # hide non-present internal serial ports + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# test +if __name__ == '__main__': + for port, desc, hwid in sorted(comports()): + print("{}: {} [{}]".format(port, desc, hwid)) diff --git a/dist/bin/esptool/serial/tools/list_ports_osx.py b/dist/bin/esptool/serial/tools/list_ports_osx.py new file mode 100644 index 000000000..79ce4f1de --- /dev/null +++ b/dist/bin/esptool/serial/tools/list_ports_osx.py @@ -0,0 +1,260 @@ +#!/usr/bin/env python +# +# This is a module that gathers a list of serial ports including details on OSX +# +# code originally from https://github.com/makerbot/pyserial/tree/master/serial/tools +# with contributions from cibomahto, dgs3, FarMcKon, tedbrandston +# and modifications by cliechti, hoihu, hardkrash +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2013-2015 +# +# SPDX-License-Identifier: BSD-3-Clause + + +# List all of the callout devices in OS/X by querying IOKit. + +# See the following for a reference of how to do this: +# http://developer.apple.com/library/mac/#documentation/DeviceDrivers/Conceptual/WorkingWSerial/WWSerial_SerialDevs/SerialDevices.html#//apple_ref/doc/uid/TP30000384-CIHGEAFD + +# More help from darwin_hid.py + +# Also see the 'IORegistryExplorer' for an idea of what we are actually searching + +import ctypes +import ctypes.util + +from serial.tools import list_ports_common + +iokit = ctypes.cdll.LoadLibrary(ctypes.util.find_library('IOKit')) +cf = ctypes.cdll.LoadLibrary(ctypes.util.find_library('CoreFoundation')) + +kIOMasterPortDefault = ctypes.c_void_p.in_dll(iokit, "kIOMasterPortDefault") +kCFAllocatorDefault = ctypes.c_void_p.in_dll(cf, "kCFAllocatorDefault") + +kCFStringEncodingMacRoman = 0 + +iokit.IOServiceMatching.restype = ctypes.c_void_p + +iokit.IOServiceGetMatchingServices.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p] +iokit.IOServiceGetMatchingServices.restype = ctypes.c_void_p + +iokit.IORegistryEntryGetParentEntry.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p] + +iokit.IORegistryEntryCreateCFProperty.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint32] +iokit.IORegistryEntryCreateCFProperty.restype = ctypes.c_void_p + +iokit.IORegistryEntryGetPath.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p] +iokit.IORegistryEntryGetPath.restype = ctypes.c_void_p + +iokit.IORegistryEntryGetName.argtypes = [ctypes.c_void_p, ctypes.c_void_p] +iokit.IORegistryEntryGetName.restype = ctypes.c_void_p + +iokit.IOObjectGetClass.argtypes = [ctypes.c_void_p, ctypes.c_void_p] +iokit.IOObjectGetClass.restype = ctypes.c_void_p + +iokit.IOObjectRelease.argtypes = [ctypes.c_void_p] + + +cf.CFStringCreateWithCString.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int32] +cf.CFStringCreateWithCString.restype = ctypes.c_void_p + +cf.CFStringGetCStringPtr.argtypes = [ctypes.c_void_p, ctypes.c_uint32] +cf.CFStringGetCStringPtr.restype = ctypes.c_char_p + +cf.CFNumberGetValue.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.c_void_p] +cf.CFNumberGetValue.restype = ctypes.c_void_p + +# void CFRelease ( CFTypeRef cf ); +cf.CFRelease.argtypes = [ctypes.c_void_p] +cf.CFRelease.restype = None + +# CFNumber type defines +kCFNumberSInt8Type = 1 +kCFNumberSInt16Type = 2 +kCFNumberSInt32Type = 3 +kCFNumberSInt64Type = 4 + + +def get_string_property(device_type, property): + """ + Search the given device for the specified string property + + @param device_type Type of Device + @param property String to search for + @return Python string containing the value, or None if not found. + """ + key = cf.CFStringCreateWithCString( + kCFAllocatorDefault, + property.encode("mac_roman"), + kCFStringEncodingMacRoman) + + CFContainer = iokit.IORegistryEntryCreateCFProperty( + device_type, + key, + kCFAllocatorDefault, + 0) + output = None + + if CFContainer: + output = cf.CFStringGetCStringPtr(CFContainer, 0) + if output is not None: + output = output.decode('mac_roman') + cf.CFRelease(CFContainer) + return output + + +def get_int_property(device_type, property, cf_number_type): + """ + Search the given device for the specified string property + + @param device_type Device to search + @param property String to search for + @param cf_number_type CFType number + + @return Python string containing the value, or None if not found. + """ + key = cf.CFStringCreateWithCString( + kCFAllocatorDefault, + property.encode("mac_roman"), + kCFStringEncodingMacRoman) + + CFContainer = iokit.IORegistryEntryCreateCFProperty( + device_type, + key, + kCFAllocatorDefault, + 0) + + if CFContainer: + if (cf_number_type == kCFNumberSInt32Type): + number = ctypes.c_uint32() + elif (cf_number_type == kCFNumberSInt16Type): + number = ctypes.c_uint16() + cf.CFNumberGetValue(CFContainer, cf_number_type, ctypes.byref(number)) + cf.CFRelease(CFContainer) + return number.value + return None + + +def IORegistryEntryGetName(device): + pathname = ctypes.create_string_buffer(100) # TODO: Is this ok? + iokit.IOObjectGetClass(device, ctypes.byref(pathname)) + return pathname.value + + +def GetParentDeviceByType(device, parent_type): + """ Find the first parent of a device that implements the parent_type + @param IOService Service to inspect + @return Pointer to the parent type, or None if it was not found. + """ + # First, try to walk up the IOService tree to find a parent of this device that is a IOUSBDevice. + parent_type = parent_type.encode('mac_roman') + while IORegistryEntryGetName(device) != parent_type: + parent = ctypes.c_void_p() + response = iokit.IORegistryEntryGetParentEntry( + device, + "IOService".encode("mac_roman"), + ctypes.byref(parent)) + # If we weren't able to find a parent for the device, we're done. + if response != 0: + return None + device = parent + return device + + +def GetIOServicesByType(service_type): + """ + returns iterator over specified service_type + """ + serial_port_iterator = ctypes.c_void_p() + + iokit.IOServiceGetMatchingServices( + kIOMasterPortDefault, + iokit.IOServiceMatching(service_type.encode('mac_roman')), + ctypes.byref(serial_port_iterator)) + + services = [] + while iokit.IOIteratorIsValid(serial_port_iterator): + service = iokit.IOIteratorNext(serial_port_iterator) + if not service: + break + services.append(service) + iokit.IOObjectRelease(serial_port_iterator) + return services + + +def location_to_string(locationID): + """ + helper to calculate port and bus number from locationID + """ + loc = ['{}-'.format(locationID >> 24)] + while locationID & 0xf00000: + if len(loc) > 1: + loc.append('.') + loc.append('{}'.format((locationID >> 20) & 0xf)) + locationID <<= 4 + return ''.join(loc) + + +class SuitableSerialInterface(object): + pass + + +def scan_interfaces(): + """ + helper function to scan USB interfaces + returns a list of SuitableSerialInterface objects with name and id attributes + """ + interfaces = [] + for service in GetIOServicesByType('IOSerialBSDClient'): + device = get_string_property(service, "IOCalloutDevice") + if device: + usb_device = GetParentDeviceByType(service, "IOUSBInterface") + if usb_device: + name = get_string_property(usb_device, "USB Interface Name") or None + locationID = get_int_property(usb_device, "locationID", kCFNumberSInt32Type) or '' + i = SuitableSerialInterface() + i.id = locationID + i.name = name + interfaces.append(i) + return interfaces + + +def search_for_locationID_in_interfaces(serial_interfaces, locationID): + for interface in serial_interfaces: + if (interface.id == locationID): + return interface.name + return None + + +def comports(include_links=False): + # XXX include_links is currently ignored. are links in /dev even supported here? + # Scan for all iokit serial ports + services = GetIOServicesByType('IOSerialBSDClient') + ports = [] + serial_interfaces = scan_interfaces() + for service in services: + # First, add the callout device file. + device = get_string_property(service, "IOCalloutDevice") + if device: + info = list_ports_common.ListPortInfo(device) + # If the serial port is implemented by IOUSBDevice + usb_device = GetParentDeviceByType(service, "IOUSBDevice") + if usb_device: + # fetch some useful informations from properties + info.vid = get_int_property(usb_device, "idVendor", kCFNumberSInt16Type) + info.pid = get_int_property(usb_device, "idProduct", kCFNumberSInt16Type) + info.serial_number = get_string_property(usb_device, "USB Serial Number") + info.product = get_string_property(usb_device, "USB Product Name") or 'n/a' + info.manufacturer = get_string_property(usb_device, "USB Vendor Name") + locationID = get_int_property(usb_device, "locationID", kCFNumberSInt32Type) + info.location = location_to_string(locationID) + info.interface = search_for_locationID_in_interfaces(serial_interfaces, locationID) + info.apply_usb_info() + ports.append(info) + return ports + +# test +if __name__ == '__main__': + for port, desc, hwid in sorted(comports()): + print("{}: {} [{}]".format(port, desc, hwid)) diff --git a/dist/bin/esptool/serial/tools/list_ports_posix.py b/dist/bin/esptool/serial/tools/list_ports_posix.py new file mode 100644 index 000000000..0d580b021 --- /dev/null +++ b/dist/bin/esptool/serial/tools/list_ports_posix.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +# +# This is a module that gathers a list of serial ports on POSIXy systems. +# For some specific implementations, see also list_ports_linux, list_ports_osx +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2011-2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +"""\ +The ``comports`` function is expected to return an iterable that yields tuples +of 3 strings: port name, human readable description and a hardware ID. + +As currently no method is known to get the second two strings easily, they are +currently just identical to the port name. +""" + +import glob +import sys +import os +from serial.tools import list_ports_common + +# try to detect the OS so that a device can be selected... +plat = sys.platform.lower() + +if plat[:5] == 'linux': # Linux (confirmed) # noqa + from serial.tools.list_ports_linux import comports + +elif plat[:6] == 'darwin': # OS X (confirmed) + from serial.tools.list_ports_osx import comports + +elif plat == 'cygwin': # cygwin/win32 + # cygwin accepts /dev/com* in many contexts + # (such as 'open' call, explicit 'ls'), but 'glob.glob' + # and bare 'ls' do not; so use /dev/ttyS* instead + def comports(include_links=False): + devices = glob.glob('/dev/ttyS*') + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [list_ports_common.ListPortInfo(d) for d in devices] + +elif plat[:7] == 'openbsd': # OpenBSD + def comports(include_links=False): + devices = glob.glob('/dev/cua*') + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [list_ports_common.ListPortInfo(d) for d in devices] + +elif plat[:3] == 'bsd' or plat[:7] == 'freebsd': + def comports(include_links=False): + devices = glob.glob('/dev/cua*[!.init][!.lock]') + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [list_ports_common.ListPortInfo(d) for d in devices] + +elif plat[:6] == 'netbsd': # NetBSD + def comports(include_links=False): + """scan for available ports. return a list of device names.""" + devices = glob.glob('/dev/dty*') + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [list_ports_common.ListPortInfo(d) for d in devices] + +elif plat[:4] == 'irix': # IRIX + def comports(include_links=False): + """scan for available ports. return a list of device names.""" + devices = glob.glob('/dev/ttyf*') + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [list_ports_common.ListPortInfo(d) for d in devices] + +elif plat[:2] == 'hp': # HP-UX (not tested) + def comports(include_links=False): + """scan for available ports. return a list of device names.""" + devices = glob.glob('/dev/tty*p0') + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [list_ports_common.ListPortInfo(d) for d in devices] + +elif plat[:5] == 'sunos': # Solaris/SunOS + def comports(include_links=False): + """scan for available ports. return a list of device names.""" + devices = glob.glob('/dev/tty*c') + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [list_ports_common.ListPortInfo(d) for d in devices] + +elif plat[:3] == 'aix': # AIX + def comports(include_links=False): + """scan for available ports. return a list of device names.""" + devices = glob.glob('/dev/tty*') + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [list_ports_common.ListPortInfo(d) for d in devices] + +else: + # platform detection has failed... + import serial + sys.stderr.write("""\ +don't know how to enumerate ttys on this system. +! I you know how the serial ports are named send this information to +! the author of this module: + +sys.platform = {!r} +os.name = {!r} +pySerial version = {} + +also add the naming scheme of the serial ports and with a bit luck you can get +this module running... +""".format(sys.platform, os.name, serial.VERSION)) + raise ImportError("Sorry: no implementation for your platform ('{}') available".format(os.name)) + +# test +if __name__ == '__main__': + for port, desc, hwid in sorted(comports()): + print("{}: {} [{}]".format(port, desc, hwid)) diff --git a/dist/bin/esptool/serial/tools/list_ports_windows.py b/dist/bin/esptool/serial/tools/list_ports_windows.py new file mode 100644 index 000000000..f28047be2 --- /dev/null +++ b/dist/bin/esptool/serial/tools/list_ports_windows.py @@ -0,0 +1,305 @@ +#! python +# +# Enumerate serial ports on Windows including a human readable description +# and hardware information. +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2001-2016 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +# pylint: disable=invalid-name,too-few-public-methods +import re +import ctypes +from ctypes.wintypes import BOOL +from ctypes.wintypes import HWND +from ctypes.wintypes import DWORD +from ctypes.wintypes import WORD +from ctypes.wintypes import LONG +from ctypes.wintypes import ULONG +from ctypes.wintypes import HKEY +from ctypes.wintypes import BYTE +import serial +from serial.win32 import ULONG_PTR +from serial.tools import list_ports_common + + +def ValidHandle(value, func, arguments): + if value == 0: + raise ctypes.WinError() + return value + + +NULL = 0 +HDEVINFO = ctypes.c_void_p +LPCTSTR = ctypes.c_wchar_p +PCTSTR = ctypes.c_wchar_p +PTSTR = ctypes.c_wchar_p +LPDWORD = PDWORD = ctypes.POINTER(DWORD) +#~ LPBYTE = PBYTE = ctypes.POINTER(BYTE) +LPBYTE = PBYTE = ctypes.c_void_p # XXX avoids error about types + +ACCESS_MASK = DWORD +REGSAM = ACCESS_MASK + + +class GUID(ctypes.Structure): + _fields_ = [ + ('Data1', DWORD), + ('Data2', WORD), + ('Data3', WORD), + ('Data4', BYTE * 8), + ] + + def __str__(self): + return "{{{:08x}-{:04x}-{:04x}-{}-{}}}".format( + self.Data1, + self.Data2, + self.Data3, + ''.join(["{:02x}".format(d) for d in self.Data4[:2]]), + ''.join(["{:02x}".format(d) for d in self.Data4[2:]]), + ) + + +class SP_DEVINFO_DATA(ctypes.Structure): + _fields_ = [ + ('cbSize', DWORD), + ('ClassGuid', GUID), + ('DevInst', DWORD), + ('Reserved', ULONG_PTR), + ] + + def __str__(self): + return "ClassGuid:{} DevInst:{}".format(self.ClassGuid, self.DevInst) + + +PSP_DEVINFO_DATA = ctypes.POINTER(SP_DEVINFO_DATA) + +PSP_DEVICE_INTERFACE_DETAIL_DATA = ctypes.c_void_p + +setupapi = ctypes.windll.LoadLibrary("setupapi") +SetupDiDestroyDeviceInfoList = setupapi.SetupDiDestroyDeviceInfoList +SetupDiDestroyDeviceInfoList.argtypes = [HDEVINFO] +SetupDiDestroyDeviceInfoList.restype = BOOL + +SetupDiClassGuidsFromName = setupapi.SetupDiClassGuidsFromNameW +SetupDiClassGuidsFromName.argtypes = [PCTSTR, ctypes.POINTER(GUID), DWORD, PDWORD] +SetupDiClassGuidsFromName.restype = BOOL + +SetupDiEnumDeviceInfo = setupapi.SetupDiEnumDeviceInfo +SetupDiEnumDeviceInfo.argtypes = [HDEVINFO, DWORD, PSP_DEVINFO_DATA] +SetupDiEnumDeviceInfo.restype = BOOL + +SetupDiGetClassDevs = setupapi.SetupDiGetClassDevsW +SetupDiGetClassDevs.argtypes = [ctypes.POINTER(GUID), PCTSTR, HWND, DWORD] +SetupDiGetClassDevs.restype = HDEVINFO +SetupDiGetClassDevs.errcheck = ValidHandle + +SetupDiGetDeviceRegistryProperty = setupapi.SetupDiGetDeviceRegistryPropertyW +SetupDiGetDeviceRegistryProperty.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD] +SetupDiGetDeviceRegistryProperty.restype = BOOL + +SetupDiGetDeviceInstanceId = setupapi.SetupDiGetDeviceInstanceIdW +SetupDiGetDeviceInstanceId.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, PTSTR, DWORD, PDWORD] +SetupDiGetDeviceInstanceId.restype = BOOL + +SetupDiOpenDevRegKey = setupapi.SetupDiOpenDevRegKey +SetupDiOpenDevRegKey.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM] +SetupDiOpenDevRegKey.restype = HKEY + +advapi32 = ctypes.windll.LoadLibrary("Advapi32") +RegCloseKey = advapi32.RegCloseKey +RegCloseKey.argtypes = [HKEY] +RegCloseKey.restype = LONG + +RegQueryValueEx = advapi32.RegQueryValueExW +RegQueryValueEx.argtypes = [HKEY, LPCTSTR , LPDWORD, LPDWORD, LPBYTE, LPDWORD] +RegQueryValueEx.restype = LONG + + +DIGCF_PRESENT = 2 +DIGCF_DEVICEINTERFACE = 16 +INVALID_HANDLE_VALUE = 0 +ERROR_INSUFFICIENT_BUFFER = 122 +SPDRP_HARDWAREID = 1 +SPDRP_FRIENDLYNAME = 12 +SPDRP_LOCATION_PATHS = 35 +SPDRP_MFG = 11 +DICS_FLAG_GLOBAL = 1 +DIREG_DEV = 0x00000001 +KEY_READ = 0x20019 + + +def iterate_comports(): + """Return a generator that yields descriptions for serial ports""" + GUIDs = (GUID * 8)() # so far only seen one used, so hope 8 are enough... + guids_size = DWORD() + if not SetupDiClassGuidsFromName( + "Ports", + GUIDs, + ctypes.sizeof(GUIDs), + ctypes.byref(guids_size)): + raise ctypes.WinError() + + # repeat for all possible GUIDs + for index in range(guids_size.value): + bInterfaceNumber = None + g_hdi = SetupDiGetClassDevs( + ctypes.byref(GUIDs[index]), + None, + NULL, + DIGCF_PRESENT) # was DIGCF_PRESENT|DIGCF_DEVICEINTERFACE which misses CDC ports + + devinfo = SP_DEVINFO_DATA() + devinfo.cbSize = ctypes.sizeof(devinfo) + index = 0 + while SetupDiEnumDeviceInfo(g_hdi, index, ctypes.byref(devinfo)): + index += 1 + + # get the real com port name + hkey = SetupDiOpenDevRegKey( + g_hdi, + ctypes.byref(devinfo), + DICS_FLAG_GLOBAL, + 0, + DIREG_DEV, # DIREG_DRV for SW info + KEY_READ) + port_name_buffer = ctypes.create_unicode_buffer(250) + port_name_length = ULONG(ctypes.sizeof(port_name_buffer)) + RegQueryValueEx( + hkey, + "PortName", + None, + None, + ctypes.byref(port_name_buffer), + ctypes.byref(port_name_length)) + RegCloseKey(hkey) + + # unfortunately does this method also include parallel ports. + # we could check for names starting with COM or just exclude LPT + # and hope that other "unknown" names are serial ports... + if port_name_buffer.value.startswith('LPT'): + continue + + # hardware ID + szHardwareID = ctypes.create_unicode_buffer(250) + # try to get ID that includes serial number + if not SetupDiGetDeviceInstanceId( + g_hdi, + ctypes.byref(devinfo), + #~ ctypes.byref(szHardwareID), + szHardwareID, + ctypes.sizeof(szHardwareID) - 1, + None): + # fall back to more generic hardware ID if that would fail + if not SetupDiGetDeviceRegistryProperty( + g_hdi, + ctypes.byref(devinfo), + SPDRP_HARDWAREID, + None, + ctypes.byref(szHardwareID), + ctypes.sizeof(szHardwareID) - 1, + None): + # Ignore ERROR_INSUFFICIENT_BUFFER + if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER: + raise ctypes.WinError() + # stringify + szHardwareID_str = szHardwareID.value + + info = list_ports_common.ListPortInfo(port_name_buffer.value) + + # in case of USB, make a more readable string, similar to that form + # that we also generate on other platforms + if szHardwareID_str.startswith('USB'): + m = re.search(r'VID_([0-9a-f]{4})(&PID_([0-9a-f]{4}))?(&MI_(\d{2}))?(\\(\w+))?', szHardwareID_str, re.I) + if m: + info.vid = int(m.group(1), 16) + if m.group(3): + info.pid = int(m.group(3), 16) + if m.group(5): + bInterfaceNumber = int(m.group(5)) + if m.group(7): + info.serial_number = m.group(7) + # calculate a location string + loc_path_str = ctypes.create_unicode_buffer(250) + if SetupDiGetDeviceRegistryProperty( + g_hdi, + ctypes.byref(devinfo), + SPDRP_LOCATION_PATHS, + None, + ctypes.byref(loc_path_str), + ctypes.sizeof(loc_path_str) - 1, + None): + m = re.finditer(r'USBROOT\((\w+)\)|#USB\((\w+)\)', loc_path_str.value) + location = [] + for g in m: + if g.group(1): + location.append('{:d}'.format(int(g.group(1)) + 1)) + else: + if len(location) > 1: + location.append('.') + else: + location.append('-') + location.append(g.group(2)) + if bInterfaceNumber is not None: + location.append(':{}.{}'.format( + 'x', # XXX how to determine correct bConfigurationValue? + bInterfaceNumber)) + if location: + info.location = ''.join(location) + info.hwid = info.usb_info() + elif szHardwareID_str.startswith('FTDIBUS'): + m = re.search(r'VID_([0-9a-f]{4})\+PID_([0-9a-f]{4})(\+(\w+))?', szHardwareID_str, re.I) + if m: + info.vid = int(m.group(1), 16) + info.pid = int(m.group(2), 16) + if m.group(4): + info.serial_number = m.group(4) + # USB location is hidden by FDTI driver :( + info.hwid = info.usb_info() + else: + info.hwid = szHardwareID_str + + # friendly name + szFriendlyName = ctypes.create_unicode_buffer(250) + if SetupDiGetDeviceRegistryProperty( + g_hdi, + ctypes.byref(devinfo), + SPDRP_FRIENDLYNAME, + #~ SPDRP_DEVICEDESC, + None, + ctypes.byref(szFriendlyName), + ctypes.sizeof(szFriendlyName) - 1, + None): + info.description = szFriendlyName.value + #~ else: + # Ignore ERROR_INSUFFICIENT_BUFFER + #~ if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER: + #~ raise IOError("failed to get details for %s (%s)" % (devinfo, szHardwareID.value)) + # ignore errors and still include the port in the list, friendly name will be same as port name + + # manufacturer + szManufacturer = ctypes.create_unicode_buffer(250) + if SetupDiGetDeviceRegistryProperty( + g_hdi, + ctypes.byref(devinfo), + SPDRP_MFG, + #~ SPDRP_DEVICEDESC, + None, + ctypes.byref(szManufacturer), + ctypes.sizeof(szManufacturer) - 1, + None): + info.manufacturer = szManufacturer.value + yield info + SetupDiDestroyDeviceInfoList(g_hdi) + + +def comports(include_links=False): + """Return a list of info objects about serial ports""" + return list(iterate_comports()) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# test +if __name__ == '__main__': + for port, desc, hwid in sorted(comports()): + print("{}: {} [{}]".format(port, desc, hwid)) diff --git a/dist/bin/esptool/serial/tools/miniterm.py b/dist/bin/esptool/serial/tools/miniterm.py new file mode 100644 index 000000000..88307c6e8 --- /dev/null +++ b/dist/bin/esptool/serial/tools/miniterm.py @@ -0,0 +1,976 @@ +#!/usr/bin/env python +# +# Very simple serial terminal +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C)2002-2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +import codecs +import os +import sys +import threading + +import serial +from serial.tools.list_ports import comports +from serial.tools import hexlify_codec + +# pylint: disable=wrong-import-order,wrong-import-position + +codecs.register(lambda c: hexlify_codec.getregentry() if c == 'hexlify' else None) + +try: + raw_input +except NameError: + # pylint: disable=redefined-builtin,invalid-name + raw_input = input # in python3 it's "raw" + unichr = chr + + +def key_description(character): + """generate a readable description for a key""" + ascii_code = ord(character) + if ascii_code < 32: + return 'Ctrl+{:c}'.format(ord('@') + ascii_code) + else: + return repr(character) + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +class ConsoleBase(object): + """OS abstraction for console (input/output codec, no echo)""" + + def __init__(self): + if sys.version_info >= (3, 0): + self.byte_output = sys.stdout.buffer + else: + self.byte_output = sys.stdout + self.output = sys.stdout + + def setup(self): + """Set console to read single characters, no echo""" + + def cleanup(self): + """Restore default console settings""" + + def getkey(self): + """Read a single key from the console""" + return None + + def write_bytes(self, byte_string): + """Write bytes (already encoded)""" + self.byte_output.write(byte_string) + self.byte_output.flush() + + def write(self, text): + """Write string""" + self.output.write(text) + self.output.flush() + + def cancel(self): + """Cancel getkey operation""" + + # - - - - - - - - - - - - - - - - - - - - - - - - + # context manager: + # switch terminal temporary to normal mode (e.g. to get user input) + + def __enter__(self): + self.cleanup() + return self + + def __exit__(self, *args, **kwargs): + self.setup() + + +if os.name == 'nt': # noqa + import msvcrt + import ctypes + + class Out(object): + """file-like wrapper that uses os.write""" + + def __init__(self, fd): + self.fd = fd + + def flush(self): + pass + + def write(self, s): + os.write(self.fd, s) + + class Console(ConsoleBase): + def __init__(self): + super(Console, self).__init__() + self._saved_ocp = ctypes.windll.kernel32.GetConsoleOutputCP() + self._saved_icp = ctypes.windll.kernel32.GetConsoleCP() + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + ctypes.windll.kernel32.SetConsoleCP(65001) + self.output = codecs.getwriter('UTF-8')(Out(sys.stdout.fileno()), 'replace') + # the change of the code page is not propagated to Python, manually fix it + sys.stderr = codecs.getwriter('UTF-8')(Out(sys.stderr.fileno()), 'replace') + sys.stdout = self.output + self.output.encoding = 'UTF-8' # needed for input + + def __del__(self): + ctypes.windll.kernel32.SetConsoleOutputCP(self._saved_ocp) + ctypes.windll.kernel32.SetConsoleCP(self._saved_icp) + + def getkey(self): + while True: + z = msvcrt.getwch() + if z == unichr(13): + return unichr(10) + elif z in (unichr(0), unichr(0x0e)): # functions keys, ignore + msvcrt.getwch() + else: + return z + + def cancel(self): + # CancelIo, CancelSynchronousIo do not seem to work when using + # getwch, so instead, send a key to the window with the console + hwnd = ctypes.windll.kernel32.GetConsoleWindow() + ctypes.windll.user32.PostMessageA(hwnd, 0x100, 0x0d, 0) + +elif os.name == 'posix': + import atexit + import termios + import fcntl + + class Console(ConsoleBase): + def __init__(self): + super(Console, self).__init__() + self.fd = sys.stdin.fileno() + self.old = termios.tcgetattr(self.fd) + atexit.register(self.cleanup) + if sys.version_info < (3, 0): + self.enc_stdin = codecs.getreader(sys.stdin.encoding)(sys.stdin) + else: + self.enc_stdin = sys.stdin + + def setup(self): + new = termios.tcgetattr(self.fd) + new[3] = new[3] & ~termios.ICANON & ~termios.ECHO & ~termios.ISIG + new[6][termios.VMIN] = 1 + new[6][termios.VTIME] = 0 + termios.tcsetattr(self.fd, termios.TCSANOW, new) + + def getkey(self): + c = self.enc_stdin.read(1) + if c == unichr(0x7f): + c = unichr(8) # map the BS key (which yields DEL) to backspace + return c + + def cancel(self): + fcntl.ioctl(self.fd, termios.TIOCSTI, b'\0') + + def cleanup(self): + termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old) + +else: + raise NotImplementedError( + 'Sorry no implementation for your platform ({}) available.'.format(sys.platform)) + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +class Transform(object): + """do-nothing: forward all data unchanged""" + def rx(self, text): + """text received from serial port""" + return text + + def tx(self, text): + """text to be sent to serial port""" + return text + + def echo(self, text): + """text to be sent but displayed on console""" + return text + + +class CRLF(Transform): + """ENTER sends CR+LF""" + + def tx(self, text): + return text.replace('\n', '\r\n') + + +class CR(Transform): + """ENTER sends CR""" + + def rx(self, text): + return text.replace('\r', '\n') + + def tx(self, text): + return text.replace('\n', '\r') + + +class LF(Transform): + """ENTER sends LF""" + + +class NoTerminal(Transform): + """remove typical terminal control codes from input""" + + REPLACEMENT_MAP = dict((x, 0x2400 + x) for x in range(32) if unichr(x) not in '\r\n\b\t') + REPLACEMENT_MAP.update( + { + 0x7F: 0x2421, # DEL + 0x9B: 0x2425, # CSI + }) + + def rx(self, text): + return text.translate(self.REPLACEMENT_MAP) + + echo = rx + + +class NoControls(NoTerminal): + """Remove all control codes, incl. CR+LF""" + + REPLACEMENT_MAP = dict((x, 0x2400 + x) for x in range(32)) + REPLACEMENT_MAP.update( + { + 0x20: 0x2423, # visual space + 0x7F: 0x2421, # DEL + 0x9B: 0x2425, # CSI + }) + + +class Printable(Transform): + """Show decimal code for all non-ASCII characters and replace most control codes""" + + def rx(self, text): + r = [] + for c in text: + if ' ' <= c < '\x7f' or c in '\r\n\b\t': + r.append(c) + elif c < ' ': + r.append(unichr(0x2400 + ord(c))) + else: + r.extend(unichr(0x2080 + ord(d) - 48) for d in '{:d}'.format(ord(c))) + r.append(' ') + return ''.join(r) + + echo = rx + + +class Colorize(Transform): + """Apply different colors for received and echo""" + + def __init__(self): + # XXX make it configurable, use colorama? + self.input_color = '\x1b[37m' + self.echo_color = '\x1b[31m' + + def rx(self, text): + return self.input_color + text + + def echo(self, text): + return self.echo_color + text + + +class DebugIO(Transform): + """Print what is sent and received""" + + def rx(self, text): + sys.stderr.write(' [RX:{}] '.format(repr(text))) + sys.stderr.flush() + return text + + def tx(self, text): + sys.stderr.write(' [TX:{}] '.format(repr(text))) + sys.stderr.flush() + return text + + +# other ideas: +# - add date/time for each newline +# - insert newline after: a) timeout b) packet end character + +EOL_TRANSFORMATIONS = { + 'crlf': CRLF, + 'cr': CR, + 'lf': LF, +} + +TRANSFORMATIONS = { + 'direct': Transform, # no transformation + 'default': NoTerminal, + 'nocontrol': NoControls, + 'printable': Printable, + 'colorize': Colorize, + 'debug': DebugIO, +} + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +def ask_for_port(): + """\ + Show a list of ports and ask the user for a choice. To make selection + easier on systems with long device names, also allow the input of an + index. + """ + sys.stderr.write('\n--- Available ports:\n') + ports = [] + for n, (port, desc, hwid) in enumerate(sorted(comports()), 1): + sys.stderr.write('--- {:2}: {:20} {!r}\n'.format(n, port, desc)) + ports.append(port) + while True: + port = raw_input('--- Enter port index or full name: ') + try: + index = int(port) - 1 + if not 0 <= index < len(ports): + sys.stderr.write('--- Invalid index!\n') + continue + except ValueError: + pass + else: + port = ports[index] + return port + + +class Miniterm(object): + """\ + Terminal application. Copy data from serial port to console and vice versa. + Handle special keys from the console to show menu etc. + """ + + def __init__(self, serial_instance, echo=False, eol='crlf', filters=()): + self.console = Console() + self.serial = serial_instance + self.echo = echo + self.raw = False + self.input_encoding = 'UTF-8' + self.output_encoding = 'UTF-8' + self.eol = eol + self.filters = filters + self.update_transformations() + self.exit_character = 0x1d # GS/CTRL+] + self.menu_character = 0x14 # Menu: CTRL+T + self.alive = None + self._reader_alive = None + self.receiver_thread = None + self.rx_decoder = None + self.tx_decoder = None + + def _start_reader(self): + """Start reader thread""" + self._reader_alive = True + # start serial->console thread + self.receiver_thread = threading.Thread(target=self.reader, name='rx') + self.receiver_thread.daemon = True + self.receiver_thread.start() + + def _stop_reader(self): + """Stop reader thread only, wait for clean exit of thread""" + self._reader_alive = False + if hasattr(self.serial, 'cancel_read'): + self.serial.cancel_read() + self.receiver_thread.join() + + def start(self): + """start worker threads""" + self.alive = True + self._start_reader() + # enter console->serial loop + self.transmitter_thread = threading.Thread(target=self.writer, name='tx') + self.transmitter_thread.daemon = True + self.transmitter_thread.start() + self.console.setup() + + def stop(self): + """set flag to stop worker threads""" + self.alive = False + + def join(self, transmit_only=False): + """wait for worker threads to terminate""" + self.transmitter_thread.join() + if not transmit_only: + if hasattr(self.serial, 'cancel_read'): + self.serial.cancel_read() + self.receiver_thread.join() + + def close(self): + self.serial.close() + + def update_transformations(self): + """take list of transformation classes and instantiate them for rx and tx""" + transformations = [EOL_TRANSFORMATIONS[self.eol]] + [TRANSFORMATIONS[f] + for f in self.filters] + self.tx_transformations = [t() for t in transformations] + self.rx_transformations = list(reversed(self.tx_transformations)) + + def set_rx_encoding(self, encoding, errors='replace'): + """set encoding for received data""" + self.input_encoding = encoding + self.rx_decoder = codecs.getincrementaldecoder(encoding)(errors) + + def set_tx_encoding(self, encoding, errors='replace'): + """set encoding for transmitted data""" + self.output_encoding = encoding + self.tx_encoder = codecs.getincrementalencoder(encoding)(errors) + + def dump_port_settings(self): + """Write current settings to sys.stderr""" + sys.stderr.write("\n--- Settings: {p.name} {p.baudrate},{p.bytesize},{p.parity},{p.stopbits}\n".format( + p=self.serial)) + sys.stderr.write('--- RTS: {:8} DTR: {:8} BREAK: {:8}\n'.format( + ('active' if self.serial.rts else 'inactive'), + ('active' if self.serial.dtr else 'inactive'), + ('active' if self.serial.break_condition else 'inactive'))) + try: + sys.stderr.write('--- CTS: {:8} DSR: {:8} RI: {:8} CD: {:8}\n'.format( + ('active' if self.serial.cts else 'inactive'), + ('active' if self.serial.dsr else 'inactive'), + ('active' if self.serial.ri else 'inactive'), + ('active' if self.serial.cd else 'inactive'))) + except serial.SerialException: + # on RFC 2217 ports, it can happen if no modem state notification was + # yet received. ignore this error. + pass + sys.stderr.write('--- software flow control: {}\n'.format('active' if self.serial.xonxoff else 'inactive')) + sys.stderr.write('--- hardware flow control: {}\n'.format('active' if self.serial.rtscts else 'inactive')) + sys.stderr.write('--- serial input encoding: {}\n'.format(self.input_encoding)) + sys.stderr.write('--- serial output encoding: {}\n'.format(self.output_encoding)) + sys.stderr.write('--- EOL: {}\n'.format(self.eol.upper())) + sys.stderr.write('--- filters: {}\n'.format(' '.join(self.filters))) + + def reader(self): + """loop and copy serial->console""" + try: + while self.alive and self._reader_alive: + # read all that is there or wait for one byte + data = self.serial.read(self.serial.in_waiting or 1) + if data: + if self.raw: + self.console.write_bytes(data) + else: + text = self.rx_decoder.decode(data) + for transformation in self.rx_transformations: + text = transformation.rx(text) + self.console.write(text) + except serial.SerialException: + self.alive = False + self.console.cancel() + raise # XXX handle instead of re-raise? + + def writer(self): + """\ + Loop and copy console->serial until self.exit_character character is + found. When self.menu_character is found, interpret the next key + locally. + """ + menu_active = False + try: + while self.alive: + try: + c = self.console.getkey() + except KeyboardInterrupt: + c = '\x03' + if not self.alive: + break + if menu_active: + self.handle_menu_key(c) + menu_active = False + elif c == self.menu_character: + menu_active = True # next char will be for menu + elif c == self.exit_character: + self.stop() # exit app + break + else: + #~ if self.raw: + text = c + for transformation in self.tx_transformations: + text = transformation.tx(text) + self.serial.write(self.tx_encoder.encode(text)) + if self.echo: + echo_text = c + for transformation in self.tx_transformations: + echo_text = transformation.echo(echo_text) + self.console.write(echo_text) + except: + self.alive = False + raise + + def handle_menu_key(self, c): + """Implement a simple menu / settings""" + if c == self.menu_character or c == self.exit_character: + # Menu/exit character again -> send itself + self.serial.write(self.tx_encoder.encode(c)) + if self.echo: + self.console.write(c) + elif c == '\x15': # CTRL+U -> upload file + self.upload_file() + elif c in '\x08hH?': # CTRL+H, h, H, ? -> Show help + sys.stderr.write(self.get_help_text()) + elif c == '\x12': # CTRL+R -> Toggle RTS + self.serial.rts = not self.serial.rts + sys.stderr.write('--- RTS {} ---\n'.format('active' if self.serial.rts else 'inactive')) + elif c == '\x04': # CTRL+D -> Toggle DTR + self.serial.dtr = not self.serial.dtr + sys.stderr.write('--- DTR {} ---\n'.format('active' if self.serial.dtr else 'inactive')) + elif c == '\x02': # CTRL+B -> toggle BREAK condition + self.serial.break_condition = not self.serial.break_condition + sys.stderr.write('--- BREAK {} ---\n'.format('active' if self.serial.break_condition else 'inactive')) + elif c == '\x05': # CTRL+E -> toggle local echo + self.echo = not self.echo + sys.stderr.write('--- local echo {} ---\n'.format('active' if self.echo else 'inactive')) + elif c == '\x06': # CTRL+F -> edit filters + self.change_filter() + elif c == '\x0c': # CTRL+L -> EOL mode + modes = list(EOL_TRANSFORMATIONS) # keys + eol = modes.index(self.eol) + 1 + if eol >= len(modes): + eol = 0 + self.eol = modes[eol] + sys.stderr.write('--- EOL: {} ---\n'.format(self.eol.upper())) + self.update_transformations() + elif c == '\x01': # CTRL+A -> set encoding + self.change_encoding() + elif c == '\x09': # CTRL+I -> info + self.dump_port_settings() + #~ elif c == '\x01': # CTRL+A -> cycle escape mode + #~ elif c == '\x0c': # CTRL+L -> cycle linefeed mode + elif c in 'pP': # P -> change port + self.change_port() + elif c in 'sS': # S -> suspend / open port temporarily + self.suspend_port() + elif c in 'bB': # B -> change baudrate + self.change_baudrate() + elif c == '8': # 8 -> change to 8 bits + self.serial.bytesize = serial.EIGHTBITS + self.dump_port_settings() + elif c == '7': # 7 -> change to 8 bits + self.serial.bytesize = serial.SEVENBITS + self.dump_port_settings() + elif c in 'eE': # E -> change to even parity + self.serial.parity = serial.PARITY_EVEN + self.dump_port_settings() + elif c in 'oO': # O -> change to odd parity + self.serial.parity = serial.PARITY_ODD + self.dump_port_settings() + elif c in 'mM': # M -> change to mark parity + self.serial.parity = serial.PARITY_MARK + self.dump_port_settings() + elif c in 'sS': # S -> change to space parity + self.serial.parity = serial.PARITY_SPACE + self.dump_port_settings() + elif c in 'nN': # N -> change to no parity + self.serial.parity = serial.PARITY_NONE + self.dump_port_settings() + elif c == '1': # 1 -> change to 1 stop bits + self.serial.stopbits = serial.STOPBITS_ONE + self.dump_port_settings() + elif c == '2': # 2 -> change to 2 stop bits + self.serial.stopbits = serial.STOPBITS_TWO + self.dump_port_settings() + elif c == '3': # 3 -> change to 1.5 stop bits + self.serial.stopbits = serial.STOPBITS_ONE_POINT_FIVE + self.dump_port_settings() + elif c in 'xX': # X -> change software flow control + self.serial.xonxoff = (c == 'X') + self.dump_port_settings() + elif c in 'rR': # R -> change hardware flow control + self.serial.rtscts = (c == 'R') + self.dump_port_settings() + else: + sys.stderr.write('--- unknown menu character {} --\n'.format(key_description(c))) + + def upload_file(self): + """Ask user for filenname and send its contents""" + sys.stderr.write('\n--- File to upload: ') + sys.stderr.flush() + with self.console: + filename = sys.stdin.readline().rstrip('\r\n') + if filename: + try: + with open(filename, 'rb') as f: + sys.stderr.write('--- Sending file {} ---\n'.format(filename)) + while True: + block = f.read(1024) + if not block: + break + self.serial.write(block) + # Wait for output buffer to drain. + self.serial.flush() + sys.stderr.write('.') # Progress indicator. + sys.stderr.write('\n--- File {} sent ---\n'.format(filename)) + except IOError as e: + sys.stderr.write('--- ERROR opening file {}: {} ---\n'.format(filename, e)) + + def change_filter(self): + """change the i/o transformations""" + sys.stderr.write('\n--- Available Filters:\n') + sys.stderr.write('\n'.join( + '--- {:<10} = {.__doc__}'.format(k, v) + for k, v in sorted(TRANSFORMATIONS.items()))) + sys.stderr.write('\n--- Enter new filter name(s) [{}]: '.format(' '.join(self.filters))) + with self.console: + new_filters = sys.stdin.readline().lower().split() + if new_filters: + for f in new_filters: + if f not in TRANSFORMATIONS: + sys.stderr.write('--- unknown filter: {}\n'.format(repr(f))) + break + else: + self.filters = new_filters + self.update_transformations() + sys.stderr.write('--- filters: {}\n'.format(' '.join(self.filters))) + + def change_encoding(self): + """change encoding on the serial port""" + sys.stderr.write('\n--- Enter new encoding name [{}]: '.format(self.input_encoding)) + with self.console: + new_encoding = sys.stdin.readline().strip() + if new_encoding: + try: + codecs.lookup(new_encoding) + except LookupError: + sys.stderr.write('--- invalid encoding name: {}\n'.format(new_encoding)) + else: + self.set_rx_encoding(new_encoding) + self.set_tx_encoding(new_encoding) + sys.stderr.write('--- serial input encoding: {}\n'.format(self.input_encoding)) + sys.stderr.write('--- serial output encoding: {}\n'.format(self.output_encoding)) + + def change_baudrate(self): + """change the baudrate""" + sys.stderr.write('\n--- Baudrate: ') + sys.stderr.flush() + with self.console: + backup = self.serial.baudrate + try: + self.serial.baudrate = int(sys.stdin.readline().strip()) + except ValueError as e: + sys.stderr.write('--- ERROR setting baudrate: {} ---\n'.format(e)) + self.serial.baudrate = backup + else: + self.dump_port_settings() + + def change_port(self): + """Have a conversation with the user to change the serial port""" + with self.console: + try: + port = ask_for_port() + except KeyboardInterrupt: + port = None + if port and port != self.serial.port: + # reader thread needs to be shut down + self._stop_reader() + # save settings + settings = self.serial.getSettingsDict() + try: + new_serial = serial.serial_for_url(port, do_not_open=True) + # restore settings and open + new_serial.applySettingsDict(settings) + new_serial.rts = self.serial.rts + new_serial.dtr = self.serial.dtr + new_serial.open() + new_serial.break_condition = self.serial.break_condition + except Exception as e: + sys.stderr.write('--- ERROR opening new port: {} ---\n'.format(e)) + new_serial.close() + else: + self.serial.close() + self.serial = new_serial + sys.stderr.write('--- Port changed to: {} ---\n'.format(self.serial.port)) + # and restart the reader thread + self._start_reader() + + def suspend_port(self): + """\ + open port temporarily, allow reconnect, exit and port change to get + out of the loop + """ + # reader thread needs to be shut down + self._stop_reader() + self.serial.close() + sys.stderr.write('\n--- Port closed: {} ---\n'.format(self.serial.port)) + do_change_port = False + while not self.serial.is_open: + sys.stderr.write('--- Quit: {exit} | p: port change | any other key to reconnect ---\n'.format( + exit=key_description(self.exit_character))) + k = self.console.getkey() + if k == self.exit_character: + self.stop() # exit app + break + elif k in 'pP': + do_change_port = True + break + try: + self.serial.open() + except Exception as e: + sys.stderr.write('--- ERROR opening port: {} ---\n'.format(e)) + if do_change_port: + self.change_port() + else: + # and restart the reader thread + self._start_reader() + sys.stderr.write('--- Port opened: {} ---\n'.format(self.serial.port)) + + def get_help_text(self): + """return the help text""" + # help text, starts with blank line! + return """ +--- pySerial ({version}) - miniterm - help +--- +--- {exit:8} Exit program +--- {menu:8} Menu escape key, followed by: +--- Menu keys: +--- {menu:7} Send the menu character itself to remote +--- {exit:7} Send the exit character itself to remote +--- {info:7} Show info +--- {upload:7} Upload file (prompt will be shown) +--- {repr:7} encoding +--- {filter:7} edit filters +--- Toggles: +--- {rts:7} RTS {dtr:7} DTR {brk:7} BREAK +--- {echo:7} echo {eol:7} EOL +--- +--- Port settings ({menu} followed by the following): +--- p change port +--- 7 8 set data bits +--- N E O S M change parity (None, Even, Odd, Space, Mark) +--- 1 2 3 set stop bits (1, 2, 1.5) +--- b change baud rate +--- x X disable/enable software flow control +--- r R disable/enable hardware flow control +""".format(version=getattr(serial, 'VERSION', 'unknown version'), + exit=key_description(self.exit_character), + menu=key_description(self.menu_character), + rts=key_description('\x12'), + dtr=key_description('\x04'), + brk=key_description('\x02'), + echo=key_description('\x05'), + info=key_description('\x09'), + upload=key_description('\x15'), + repr=key_description('\x01'), + filter=key_description('\x06'), + eol=key_description('\x0c')) + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# default args can be used to override when calling main() from an other script +# e.g to create a miniterm-my-device.py +def main(default_port=None, default_baudrate=9600, default_rts=None, default_dtr=None): + """Command line tool, entry point""" + + import argparse + + parser = argparse.ArgumentParser( + description="Miniterm - A simple terminal program for the serial port.") + + parser.add_argument( + "port", + nargs='?', + help="serial port name ('-' to show port list)", + default=default_port) + + parser.add_argument( + "baudrate", + nargs='?', + type=int, + help="set baud rate, default: %(default)s", + default=default_baudrate) + + group = parser.add_argument_group("port settings") + + group.add_argument( + "--parity", + choices=['N', 'E', 'O', 'S', 'M'], + type=lambda c: c.upper(), + help="set parity, one of {N E O S M}, default: N", + default='N') + + group.add_argument( + "--rtscts", + action="store_true", + help="enable RTS/CTS flow control (default off)", + default=False) + + group.add_argument( + "--xonxoff", + action="store_true", + help="enable software flow control (default off)", + default=False) + + group.add_argument( + "--rts", + type=int, + help="set initial RTS line state (possible values: 0, 1)", + default=default_rts) + + group.add_argument( + "--dtr", + type=int, + help="set initial DTR line state (possible values: 0, 1)", + default=default_dtr) + + group.add_argument( + "--ask", + action="store_true", + help="ask again for port when open fails", + default=False) + + group = parser.add_argument_group("data handling") + + group.add_argument( + "-e", "--echo", + action="store_true", + help="enable local echo (default off)", + default=False) + + group.add_argument( + "--encoding", + dest="serial_port_encoding", + metavar="CODEC", + help="set the encoding for the serial port (e.g. hexlify, Latin1, UTF-8), default: %(default)s", + default='UTF-8') + + group.add_argument( + "-f", "--filter", + action="append", + metavar="NAME", + help="add text transformation", + default=[]) + + group.add_argument( + "--eol", + choices=['CR', 'LF', 'CRLF'], + type=lambda c: c.upper(), + help="end of line mode", + default='CRLF') + + group.add_argument( + "--raw", + action="store_true", + help="Do no apply any encodings/transformations", + default=False) + + group = parser.add_argument_group("hotkeys") + + group.add_argument( + "--exit-char", + type=int, + metavar='NUM', + help="Unicode of special character that is used to exit the application, default: %(default)s", + default=0x1d) # GS/CTRL+] + + group.add_argument( + "--menu-char", + type=int, + metavar='NUM', + help="Unicode code of special character that is used to control miniterm (menu), default: %(default)s", + default=0x14) # Menu: CTRL+T + + group = parser.add_argument_group("diagnostics") + + group.add_argument( + "-q", "--quiet", + action="store_true", + help="suppress non-error messages", + default=False) + + group.add_argument( + "--develop", + action="store_true", + help="show Python traceback on error", + default=False) + + args = parser.parse_args() + + if args.menu_char == args.exit_char: + parser.error('--exit-char can not be the same as --menu-char') + + if args.filter: + if 'help' in args.filter: + sys.stderr.write('Available filters:\n') + sys.stderr.write('\n'.join( + '{:<10} = {.__doc__}'.format(k, v) + for k, v in sorted(TRANSFORMATIONS.items()))) + sys.stderr.write('\n') + sys.exit(1) + filters = args.filter + else: + filters = ['default'] + + while True: + # no port given on command line -> ask user now + if args.port is None or args.port == '-': + try: + args.port = ask_for_port() + except KeyboardInterrupt: + sys.stderr.write('\n') + parser.error('user aborted and port is not given') + else: + if not args.port: + parser.error('port is not given') + try: + serial_instance = serial.serial_for_url( + args.port, + args.baudrate, + parity=args.parity, + rtscts=args.rtscts, + xonxoff=args.xonxoff, + do_not_open=True) + + if not hasattr(serial_instance, 'cancel_read'): + # enable timeout for alive flag polling if cancel_read is not available + serial_instance.timeout = 1 + + if args.dtr is not None: + if not args.quiet: + sys.stderr.write('--- forcing DTR {}\n'.format('active' if args.dtr else 'inactive')) + serial_instance.dtr = args.dtr + if args.rts is not None: + if not args.quiet: + sys.stderr.write('--- forcing RTS {}\n'.format('active' if args.rts else 'inactive')) + serial_instance.rts = args.rts + + serial_instance.open() + except serial.SerialException as e: + sys.stderr.write('could not open port {}: {}\n'.format(repr(args.port), e)) + if args.develop: + raise + if not args.ask: + sys.exit(1) + else: + args.port = '-' + else: + break + + miniterm = Miniterm( + serial_instance, + echo=args.echo, + eol=args.eol.lower(), + filters=filters) + miniterm.exit_character = unichr(args.exit_char) + miniterm.menu_character = unichr(args.menu_char) + miniterm.raw = args.raw + miniterm.set_rx_encoding(args.serial_port_encoding) + miniterm.set_tx_encoding(args.serial_port_encoding) + + if not args.quiet: + sys.stderr.write('--- Miniterm on {p.name} {p.baudrate},{p.bytesize},{p.parity},{p.stopbits} ---\n'.format( + p=miniterm.serial)) + sys.stderr.write('--- Quit: {} | Menu: {} | Help: {} followed by {} ---\n'.format( + key_description(miniterm.exit_character), + key_description(miniterm.menu_character), + key_description(miniterm.menu_character), + key_description('\x08'))) + + miniterm.start() + try: + miniterm.join(True) + except KeyboardInterrupt: + pass + if not args.quiet: + sys.stderr.write("\n--- exit ---\n") + miniterm.join() + miniterm.close() + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +if __name__ == '__main__': + main() diff --git a/dist/bin/esptool/serial/urlhandler/__init__.py b/dist/bin/esptool/serial/urlhandler/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/dist/bin/esptool/serial/urlhandler/protocol_alt.py b/dist/bin/esptool/serial/urlhandler/protocol_alt.py new file mode 100644 index 000000000..c14a87e4e --- /dev/null +++ b/dist/bin/esptool/serial/urlhandler/protocol_alt.py @@ -0,0 +1,55 @@ +#! python +# +# This module implements a special URL handler that allows selecting an +# alternate implementation provided by some backends. +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause +# +# URL format: alt://port[?option[=value][&option[=value]]] +# options: +# - class=X used class named X instead of Serial +# +# example: +# use poll based implementation on Posix (Linux): +# python -m serial.tools.miniterm alt:///dev/ttyUSB0?class=PosixPollSerial + +try: + import urlparse +except ImportError: + import urllib.parse as urlparse + +import serial + + +def serial_class_for_url(url): + """extract host and port from an URL string""" + parts = urlparse.urlsplit(url) + if parts.scheme != 'alt': + raise serial.SerialException( + 'expected a string in the form "alt://port[?option[=value][&option[=value]]]": ' + 'not starting with alt:// ({!r})'.format(parts.scheme)) + class_name = 'Serial' + try: + for option, values in urlparse.parse_qs(parts.query, True).items(): + if option == 'class': + class_name = values[0] + else: + raise ValueError('unknown option: {!r}'.format(option)) + except ValueError as e: + raise serial.SerialException( + 'expected a string in the form ' + '"alt://port[?option[=value][&option[=value]]]": {!r}'.format(e)) + if not hasattr(serial, class_name): + raise ValueError('unknown class: {!r}'.format(class_name)) + cls = getattr(serial, class_name) + if not issubclass(cls, serial.Serial): + raise ValueError('class {!r} is not an instance of Serial'.format(class_name)) + return (''.join([parts.netloc, parts.path]), cls) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +if __name__ == '__main__': + s = serial.serial_for_url('alt:///dev/ttyS0?class=PosixPollSerial') + print(s) diff --git a/dist/bin/esptool/serial/urlhandler/protocol_hwgrep.py b/dist/bin/esptool/serial/urlhandler/protocol_hwgrep.py new file mode 100644 index 000000000..49bbebe33 --- /dev/null +++ b/dist/bin/esptool/serial/urlhandler/protocol_hwgrep.py @@ -0,0 +1,89 @@ +#! python +# +# This module implements a special URL handler that uses the port listing to +# find ports by searching the string descriptions. +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2011-2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause +# +# URL format: hwgrep://&