diff --git a/.github/workflows/build_arm.yml b/.github/workflows/build_arm.yml index bbdd48b..a3d6368 100644 --- a/.github/workflows/build_arm.yml +++ b/.github/workflows/build_arm.yml @@ -29,7 +29,7 @@ jobs: - 'imxrt1020_evk' - 'imxrt1060_evk' - 'metro_m7_1011' - #stm32f3 + # stm32f3 - 'stm32f303disco' # stm32f4 - 'feather_stm32f405_express' @@ -111,54 +111,3 @@ jobs: asset_path: ${{ env.ENV_PORT }}/_bin/${{ matrix.board }}/update-tinyuf2-${{ matrix.board }}-${{ github.event.release.tag_name }}.uf2 asset_name: update-tinyuf2-${{ matrix.board }}-${{ github.event.release.tag_name }}.uf2 asset_content_type: application/x-binary - - # --------------------------------------- - # Unit testing with ghostfat - # --------------------------------------- - GHOSTFAT: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - board: - # Alphabetical order - # test_ghostfat - - 'test_4k_cluster' - - 'test_32k_cluster' - - 'test_base' - - 'test_huge' - - steps: - - name: Setup Python - uses: actions/setup-python@v2 - - - name: Checkout code - uses: actions/checkout@v2 - - - name: Build - run: | - make --directory ports/test_ghostfat/ BOARD=${{ matrix.board }} all - - - name: Copy known good filesystem image - # NOTE: test_huge's knowngood.img file starts as 1.5 GiB - # Compressing once with GZip results gives 85 MiB - # Compressing it a second time gives 10 MiB ... - # Therefore, store known good images double-compressed... - run: | - pushd ./ports/test_ghostfat/ - cp ./boards/${{ matrix.board }}/knowngood.img.gz.gz ./_build/${{ matrix.board }}/ - popd - - - name: Decompress known good filesystem image - run: | - pushd ./ports/test_ghostfat/ - gzip --decompress ./_build/${{ matrix.board }}/knowngood.img.gz.gz - gzip --decompress ./_build/${{ matrix.board }}/knowngood.img.gz - popd - - - name: Execute native self-test - run: | - pushd ./ports/test_ghostfat/_build/${{ matrix.board }}/ - chmod +x ./tinyuf2-${{ matrix.board }}.elf - ./tinyuf2-${{ matrix.board }}.elf - popd diff --git a/.github/workflows/build_selftest.yml b/.github/workflows/build_selftest.yml new file mode 100644 index 0000000..0eef92f --- /dev/null +++ b/.github/workflows/build_selftest.yml @@ -0,0 +1,82 @@ +name: GhostFAT Selftest + +on: + pull_request: + push: + repository_dispatch: + release: + types: + - created + +jobs: + + # --------------------------------------- + # Unit testing with ghostfat + # --------------------------------------- + GHOSTFAT: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + board: + # Alphabetical order + # test_ghostfat + - '4k' + - '4k_favicon' + - '32k' + - '32k_favicon' + - '512b' + - '512b_favicon' + - 'huge' + + steps: + - name: Setup Python + uses: actions/setup-python@v2 + + - name: Checkout code + uses: actions/checkout@v2 + + - name: Build + run: | + make --directory ports/test_ghostfat/ BOARD=${{ matrix.board }} all + + - name: Copy known good filesystem image + # NOTE: test_huge's knowngood.img file starts as 1.5 GiB + # Compressing once with GZip results gives 85 MiB + # Compressing it a second time gives 10 MiB ... + # Therefore, store known good images double-compressed... + run: | + pushd ./ports/test_ghostfat/ + cp ./boards/${{ matrix.board }}/knowngood.img.gz.gz ./_build/${{ matrix.board }}/ + popd + + - name: Decompress known good filesystem image + run: | + pushd ./ports/test_ghostfat/ + gzip --decompress ./_build/${{ matrix.board }}/knowngood.img.gz.gz + gzip --decompress ./_build/${{ matrix.board }}/knowngood.img.gz + popd + + - name: Execute native self-test + if: always() + run: | + pushd ./ports/test_ghostfat/_build/${{ matrix.board }}/ + chmod +x ./tinyuf2-${{ matrix.board }}.elf + ./tinyuf2-${{ matrix.board }}.elf + popd + + - name: Compress newly generated self-test images + if: always() + run: | + pushd ./ports/test_ghostfat/_build/${{ matrix.board }}/ + mv ghostfat.img ghostfat_${{ matrix.board }}.img + gzip --keep ghostfat_${{ matrix.board }}.img + gzip --keep --force --best ghostfat_${{ matrix.board }}.img.gz + popd + + - name: Save newly generated self-test images as CI artifacts + if: always() + uses: actions/upload-artifact@v2 + with: + name: ghostfat_selftest_images + path: ./ports/test_ghostfat/_build/${{ matrix.board }}/ghostfat_${{ matrix.board }}.img.gz.gz diff --git a/ports/espressif/boards/adafruit_camera_esp32s2/board.h b/ports/espressif/boards/adafruit_camera_esp32s2/board.h index e5e9fa8..56c5bbb 100644 --- a/ports/espressif/boards/adafruit_camera_esp32s2/board.h +++ b/ports/espressif/boards/adafruit_camera_esp32s2/board.h @@ -89,14 +89,17 @@ // USB UF2 //--------------------------------------------------------------------+ -#define USB_VID 0x239A -#define USB_PID 0x0117 -#define USB_MANUFACTURER "Adafruit" -#define USB_PRODUCT "Camera" +#define USB_VID 0x239A +#define USB_PID 0x0117 +#define USB_MANUFACTURER "Adafruit" +#define USB_PRODUCT "Camera" -#define UF2_PRODUCT_NAME USB_MANUFACTURER " " USB_PRODUCT -#define UF2_BOARD_ID "ESP32S2-Camera-revA" -#define UF2_VOLUME_LABEL "CAMERABOOT" -#define UF2_INDEX_URL "https://www.adafruit.com/" +#define UF2_PRODUCT_NAME USB_MANUFACTURER " " USB_PRODUCT +#define UF2_BOARD_ID "ESP32S2-Camera-revA" +#define UF2_VOLUME_LABEL "CAMERABOOT" +#define UF2_INDEX_URL "https://www.adafruit.com/" + +// Use favicon +#define TINYUF2_FAVICON_HEADER "favicon_adafruit_256.h" #endif diff --git a/ports/espressif/boards/adafruit_feather_esp32s2/board.h b/ports/espressif/boards/adafruit_feather_esp32s2/board.h index 9f52914..1a0a634 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s2/board.h +++ b/ports/espressif/boards/adafruit_feather_esp32s2/board.h @@ -63,14 +63,17 @@ // USB UF2 //--------------------------------------------------------------------+ -#define USB_VID 0x239A -#define USB_PID 0x00EB -#define USB_MANUFACTURER "Adafruit" -#define USB_PRODUCT "Feather ESP32-S2" +#define USB_VID 0x239A +#define USB_PID 0x00EB +#define USB_MANUFACTURER "Adafruit" +#define USB_PRODUCT "Feather ESP32-S2" -#define UF2_PRODUCT_NAME USB_MANUFACTURER " " USB_PRODUCT -#define UF2_BOARD_ID "ESP32S2-Feather-revA" -#define UF2_VOLUME_LABEL "FTHRS2BOOT" -#define UF2_INDEX_URL "https://www.adafruit.com/product/pid" // TODO update link +#define UF2_PRODUCT_NAME USB_MANUFACTURER " " USB_PRODUCT +#define UF2_BOARD_ID "ESP32S2-Feather-revA" +#define UF2_VOLUME_LABEL "FTHRS2BOOT" +#define UF2_INDEX_URL "https://www.adafruit.com/product/pid" // TODO update link + +// Use favicon +#define TINYUF2_FAVICON_HEADER "favicon_adafruit_256.h" #endif diff --git a/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/board.h b/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/board.h index 15486ce..f44d922 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/board.h +++ b/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/board.h @@ -95,15 +95,18 @@ // USB UF2 //--------------------------------------------------------------------+ -#define USB_VID 0x239A -#define USB_PID 0x00ED +#define USB_VID 0x239A +#define USB_PID 0x00ED -#define USB_MANUFACTURER "Adafruit" -#define USB_PRODUCT "Feather ESP32-S2 Reverse TFT" +#define USB_MANUFACTURER "Adafruit" +#define USB_PRODUCT "Feather ESP32-S2 Reverse TFT" -#define UF2_PRODUCT_NAME USB_MANUFACTURER " " USB_PRODUCT -#define UF2_BOARD_ID "ESP32S2-FeatherRevTFT-revB" -#define UF2_VOLUME_LABEL "FTHRS2BOOT" -#define UF2_INDEX_URL "https://www.adafruit.com/product/5345" +#define UF2_PRODUCT_NAME USB_MANUFACTURER " " USB_PRODUCT +#define UF2_BOARD_ID "ESP32S2-FeatherRevTFT-revB" +#define UF2_VOLUME_LABEL "FTHRS2BOOT" +#define UF2_INDEX_URL "https://www.adafruit.com/product/5345" + +// Use favicon +#define TINYUF2_FAVICON_HEADER "favicon_adafruit_256.h" #endif diff --git a/ports/espressif/boards/adafruit_feather_esp32s2_tft/board.h b/ports/espressif/boards/adafruit_feather_esp32s2_tft/board.h index cb8e351..b040f7a 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s2_tft/board.h +++ b/ports/espressif/boards/adafruit_feather_esp32s2_tft/board.h @@ -95,15 +95,18 @@ // USB UF2 //--------------------------------------------------------------------+ -#define USB_VID 0x239A -#define USB_PID 0x010F +#define USB_VID 0x239A +#define USB_PID 0x010F -#define USB_MANUFACTURER "Adafruit" -#define USB_PRODUCT "Feather ESP32-S2 TFT" +#define USB_MANUFACTURER "Adafruit" +#define USB_PRODUCT "Feather ESP32-S2 TFT" -#define UF2_PRODUCT_NAME USB_MANUFACTURER " " USB_PRODUCT -#define UF2_BOARD_ID "ESP32S2-FeatherTFT-revA" -#define UF2_VOLUME_LABEL "FTHRS2BOOT" -#define UF2_INDEX_URL "https://www.adafruit.com/product/5300" // TODO update link +#define UF2_PRODUCT_NAME USB_MANUFACTURER " " USB_PRODUCT +#define UF2_BOARD_ID "ESP32S2-FeatherTFT-revA" +#define UF2_VOLUME_LABEL "FTHRS2BOOT" +#define UF2_INDEX_URL "https://www.adafruit.com/product/5300" + +// Use favicon +#define TINYUF2_FAVICON_HEADER "favicon_adafruit_256.h" #endif diff --git a/ports/espressif/boards/adafruit_funhouse_esp32s2/board.h b/ports/espressif/boards/adafruit_funhouse_esp32s2/board.h index 59122d6..b2946db 100644 --- a/ports/espressif/boards/adafruit_funhouse_esp32s2/board.h +++ b/ports/espressif/boards/adafruit_funhouse_esp32s2/board.h @@ -92,14 +92,17 @@ // USB UF2 //--------------------------------------------------------------------+ -#define USB_VID 0x239A -#define USB_PID 0x00F9 -#define USB_MANUFACTURER "Adafruit" -#define USB_PRODUCT "FunHouse" +#define USB_VID 0x239A +#define USB_PID 0x00F9 +#define USB_MANUFACTURER "Adafruit" +#define USB_PRODUCT "FunHouse" -#define UF2_PRODUCT_NAME USB_MANUFACTURER " " USB_PRODUCT -#define UF2_BOARD_ID "ESP32S2-FunHouse-revB" -#define UF2_VOLUME_LABEL "HOUSEBOOT" -#define UF2_INDEX_URL "https://www.adafruit.com/" +#define UF2_PRODUCT_NAME USB_MANUFACTURER " " USB_PRODUCT +#define UF2_BOARD_ID "ESP32S2-FunHouse-revB" +#define UF2_VOLUME_LABEL "HOUSEBOOT" +#define UF2_INDEX_URL "https://www.adafruit.com/" + +// Use favicon +#define TINYUF2_FAVICON_HEADER "favicon_adafruit_256.h" #endif diff --git a/ports/espressif/boards/adafruit_magtag_29gray/board.h b/ports/espressif/boards/adafruit_magtag_29gray/board.h index 458cc8d..bcb83e5 100644 --- a/ports/espressif/boards/adafruit_magtag_29gray/board.h +++ b/ports/espressif/boards/adafruit_magtag_29gray/board.h @@ -64,14 +64,17 @@ // USB UF2 //--------------------------------------------------------------------+ -#define USB_VID 0x239A -#define USB_PID 0x00E5 -#define USB_MANUFACTURER "Adafruit" -#define USB_PRODUCT "MagTag 2.9 Grayscale" +#define USB_VID 0x239A +#define USB_PID 0x00E5 +#define USB_MANUFACTURER "Adafruit" +#define USB_PRODUCT "MagTag 2.9 Grayscale" -#define UF2_PRODUCT_NAME USB_MANUFACTURER " " USB_PRODUCT -#define UF2_BOARD_ID "ESP32S2-MagTag_29gray-revC" -#define UF2_VOLUME_LABEL "MAGTAGBOOT" -#define UF2_INDEX_URL "https://www.adafruit.com/product/4800" +#define UF2_PRODUCT_NAME USB_MANUFACTURER " " USB_PRODUCT +#define UF2_BOARD_ID "ESP32S2-MagTag_29gray-revC" +#define UF2_VOLUME_LABEL "MAGTAGBOOT" +#define UF2_INDEX_URL "https://www.adafruit.com/product/4800" + +// Use favicon +#define TINYUF2_FAVICON_HEADER "favicon_adafruit_256.h" #endif diff --git a/ports/espressif/boards/adafruit_metro_esp32s2/board.h b/ports/espressif/boards/adafruit_metro_esp32s2/board.h index bedad33..0c4527b 100644 --- a/ports/espressif/boards/adafruit_metro_esp32s2/board.h +++ b/ports/espressif/boards/adafruit_metro_esp32s2/board.h @@ -60,14 +60,17 @@ // USB UF2 //--------------------------------------------------------------------+ -#define USB_VID 0x239A -#define USB_PID 0x00DF -#define USB_MANUFACTURER "Adafruit" -#define USB_PRODUCT "Metro ESP32-S2" +#define USB_VID 0x239A +#define USB_PID 0x00DF +#define USB_MANUFACTURER "Adafruit" +#define USB_PRODUCT "Metro ESP32-S2" -#define UF2_PRODUCT_NAME USB_MANUFACTURER " " USB_PRODUCT -#define UF2_BOARD_ID "ESP32S2-Metro-revC" -#define UF2_VOLUME_LABEL "METROS2BOOT" -#define UF2_INDEX_URL "https://www.adafruit.com/product/4775" +#define UF2_PRODUCT_NAME USB_MANUFACTURER " " USB_PRODUCT +#define UF2_BOARD_ID "ESP32S2-Metro-revC" +#define UF2_VOLUME_LABEL "METROS2BOOT" +#define UF2_INDEX_URL "https://www.adafruit.com/product/4775" + +// Use favicon +#define TINYUF2_FAVICON_HEADER "favicon_adafruit_256.h" #endif diff --git a/ports/espressif/boards/adafruit_qtpy_esp32s2/board.h b/ports/espressif/boards/adafruit_qtpy_esp32s2/board.h index 8bf3648..547ba55 100644 --- a/ports/espressif/boards/adafruit_qtpy_esp32s2/board.h +++ b/ports/espressif/boards/adafruit_qtpy_esp32s2/board.h @@ -58,14 +58,17 @@ // USB UF2 //--------------------------------------------------------------------+ -#define USB_VID 0x239A -#define USB_PID 0x0111 -#define USB_MANUFACTURER "Adafruit" -#define USB_PRODUCT "QT Py ESP32-S2" +#define USB_VID 0x239A +#define USB_PID 0x0111 +#define USB_MANUFACTURER "Adafruit" +#define USB_PRODUCT "QT Py ESP32-S2" -#define UF2_PRODUCT_NAME USB_MANUFACTURER " " USB_PRODUCT -#define UF2_BOARD_ID "ESP32S2-QTPY-revB" -#define UF2_VOLUME_LABEL "QTPYS2BOOT" -#define UF2_INDEX_URL "https://www.adafruit.com/product/5325" // TODO update link +#define UF2_PRODUCT_NAME USB_MANUFACTURER " " USB_PRODUCT +#define UF2_BOARD_ID "ESP32S2-QTPY-revB" +#define UF2_VOLUME_LABEL "QTPYS2BOOT" +#define UF2_INDEX_URL "https://www.adafruit.com/product/5325" + +// Use favicon +#define TINYUF2_FAVICON_HEADER "favicon_adafruit_256.h" #endif diff --git a/ports/espressif/boards/adafruit_qtpy_esp32s3/board.h b/ports/espressif/boards/adafruit_qtpy_esp32s3/board.h index 8ee3915..9a1b581 100644 --- a/ports/espressif/boards/adafruit_qtpy_esp32s3/board.h +++ b/ports/espressif/boards/adafruit_qtpy_esp32s3/board.h @@ -64,15 +64,17 @@ // USB UF2 //--------------------------------------------------------------------+ -#define USB_VID 0x239A -#define USB_PID 0x0119 +#define USB_VID 0x239A +#define USB_PID 0x0119 -#define USB_MANUFACTURER "Adafruit" -#define USB_PRODUCT "QT Py ESP32-S3" +#define USB_MANUFACTURER "Adafruit" +#define USB_PRODUCT "QT Py ESP32-S3" -#define UF2_PRODUCT_NAME USB_MANUFACTURER " " USB_PRODUCT -#define UF2_BOARD_ID "ESP32S3-QTPy-A" -#define UF2_VOLUME_LABEL "QTPYS3BOOT" -#define UF2_INDEX_URL "https://adafruit.com" +#define UF2_PRODUCT_NAME USB_MANUFACTURER " " USB_PRODUCT +#define UF2_BOARD_ID "ESP32S3-QTPy-A" +#define UF2_VOLUME_LABEL "QTPYS3BOOT" +#define UF2_INDEX_URL "https://adafruit.com" // TODO udate product link + +#define TINYUF2_FAVICON_HEADER "favicon_adafruit_256.h" #endif diff --git a/ports/espressif/boards/espressif_saola_1_wrover/board.h b/ports/espressif/boards/espressif_saola_1_wrover/board.h index 9bedd90..d0d4c38 100644 --- a/ports/espressif/boards/espressif_saola_1_wrover/board.h +++ b/ports/espressif/boards/espressif_saola_1_wrover/board.h @@ -73,4 +73,7 @@ #define UF2_VOLUME_LABEL "SAOLA1RBOOT" #define UF2_INDEX_URL "https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/hw-reference/esp32s2/user-guide-saola-1-v1.2.html" +// Use favicon +#define TINYUF2_FAVICON_HEADER "favicon_adafruit_256.h" + #endif diff --git a/ports/make.mk b/ports/make.mk index 7dfefa0..dde4074 100644 --- a/ports/make.mk +++ b/ports/make.mk @@ -74,12 +74,18 @@ else # Bootloader src, board folder and TinyUSB stack SRC_C += \ - $(subst $(TOP)/,,$(wildcard $(TOP)/src/*.c)) \ + src/ghostfat.c \ + src/images.c \ + src/main.c \ + src/msc.c \ + src/screen.c \ + src/usb_descriptors.c \ $(subst $(TOP)/,,$(wildcard $(TOP)/$(BOARD_DIR)/*.c)) # Include INC += \ $(TOP)/src \ + $(TOP)/src/favicon \ $(TOP)/$(PORT_DIR) \ $(TOP)/$(BOARD_DIR) diff --git a/ports/test_ghostfat/Makefile b/ports/test_ghostfat/Makefile index 2826eda..a69955b 100644 --- a/ports/test_ghostfat/Makefile +++ b/ports/test_ghostfat/Makefile @@ -29,6 +29,7 @@ SRC_S += # Port include INC += \ $(TOP)/src \ + $(TOP)/src/favicon \ $(TOP)/$(PORT_DIR) \ $(TOP)/$(BOARD_DIR) \ diff --git a/ports/test_ghostfat/boards/test_4k_cluster/board.h b/ports/test_ghostfat/boards/32k/board.h similarity index 95% rename from ports/test_ghostfat/boards/test_4k_cluster/board.h rename to ports/test_ghostfat/boards/32k/board.h index d42b4a1..29e6fe5 100644 --- a/ports/test_ghostfat/boards/test_4k_cluster/board.h +++ b/ports/test_ghostfat/boards/32k/board.h @@ -35,8 +35,8 @@ #define USB_PRODUCT "SELFTEST" #define UF2_PRODUCT_NAME USB_MANUFACTURER " " USB_PRODUCT -#define UF2_BOARD_ID "Test_4k" -#define UF2_VOLUME_LABEL "Test_4k" +#define UF2_BOARD_ID "32k" +#define UF2_VOLUME_LABEL "32k" #define UF2_INDEX_URL "https://www.adafruit.com" #endif diff --git a/ports/test_ghostfat/boards/test_32k_cluster/board.mk b/ports/test_ghostfat/boards/32k/board.mk similarity index 100% rename from ports/test_ghostfat/boards/test_32k_cluster/board.mk rename to ports/test_ghostfat/boards/32k/board.mk diff --git a/ports/test_ghostfat/boards/32k/knowngood.img.gz.gz b/ports/test_ghostfat/boards/32k/knowngood.img.gz.gz new file mode 100644 index 0000000..b704ed8 Binary files /dev/null and b/ports/test_ghostfat/boards/32k/knowngood.img.gz.gz differ diff --git a/ports/test_ghostfat/boards/test_32k_cluster/board.h b/ports/test_ghostfat/boards/32k_favicon/board.h similarity index 95% rename from ports/test_ghostfat/boards/test_32k_cluster/board.h rename to ports/test_ghostfat/boards/32k_favicon/board.h index 9c45769..a38ed92 100644 --- a/ports/test_ghostfat/boards/test_32k_cluster/board.h +++ b/ports/test_ghostfat/boards/32k_favicon/board.h @@ -35,8 +35,8 @@ #define USB_PRODUCT "SELFTEST" #define UF2_PRODUCT_NAME USB_MANUFACTURER " " USB_PRODUCT -#define UF2_BOARD_ID "Test_32k" -#define UF2_VOLUME_LABEL "Test_32k" +#define UF2_BOARD_ID "32k_fav" +#define UF2_VOLUME_LABEL "32k_fav" #define UF2_INDEX_URL "https://www.adafruit.com" #endif diff --git a/ports/test_ghostfat/boards/32k_favicon/board.mk b/ports/test_ghostfat/boards/32k_favicon/board.mk new file mode 100644 index 0000000..8fc9c40 --- /dev/null +++ b/ports/test_ghostfat/boards/32k_favicon/board.mk @@ -0,0 +1,6 @@ +CFLAGS += \ + -DTINYUF2_FAVICON_HEADER=\"favicon_adafruit_256.h\" \ + -DCFG_UF2_NUM_BLOCKS=265000 \ + -DCFG_UF2_SECTORS_PER_CLUSTER=64 \ + -DCOMPILE_DATE=\"Mar\ 11\ 2020\" \ + -DCOMPILE_TIME=\"17:35:07\" diff --git a/ports/test_ghostfat/boards/32k_favicon/knowngood.img.gz.gz b/ports/test_ghostfat/boards/32k_favicon/knowngood.img.gz.gz new file mode 100644 index 0000000..efedd0d Binary files /dev/null and b/ports/test_ghostfat/boards/32k_favicon/knowngood.img.gz.gz differ diff --git a/ports/test_ghostfat/boards/test_base/board.h b/ports/test_ghostfat/boards/4k/board.h similarity index 95% rename from ports/test_ghostfat/boards/test_base/board.h rename to ports/test_ghostfat/boards/4k/board.h index e5c5496..c207657 100644 --- a/ports/test_ghostfat/boards/test_base/board.h +++ b/ports/test_ghostfat/boards/4k/board.h @@ -35,8 +35,8 @@ #define USB_PRODUCT "SELFTEST" #define UF2_PRODUCT_NAME USB_MANUFACTURER " " USB_PRODUCT -#define UF2_BOARD_ID "TestBase" -#define UF2_VOLUME_LABEL "TestBase" +#define UF2_BOARD_ID "4k" +#define UF2_VOLUME_LABEL "4k" #define UF2_INDEX_URL "https://www.adafruit.com" #endif diff --git a/ports/test_ghostfat/boards/test_4k_cluster/board.mk b/ports/test_ghostfat/boards/4k/board.mk similarity index 100% rename from ports/test_ghostfat/boards/test_4k_cluster/board.mk rename to ports/test_ghostfat/boards/4k/board.mk diff --git a/ports/test_ghostfat/boards/4k/knowngood.img.gz.gz b/ports/test_ghostfat/boards/4k/knowngood.img.gz.gz new file mode 100644 index 0000000..61d9942 Binary files /dev/null and b/ports/test_ghostfat/boards/4k/knowngood.img.gz.gz differ diff --git a/ports/test_ghostfat/boards/4k_favicon/board.h b/ports/test_ghostfat/boards/4k_favicon/board.h new file mode 100644 index 0000000..9cbdf0d --- /dev/null +++ b/ports/test_ghostfat/boards/4k_favicon/board.h @@ -0,0 +1,42 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Ha Thach for Adafruit Industries + * + * 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. + */ + +#ifndef BOARD_H_ +#define BOARD_H_ + +//--------------------------------------------------------------------+ +// USB UF2 +//--------------------------------------------------------------------+ + +#define USB_VID 0x0000 +#define USB_PID 0x0000 +#define USB_MANUFACTURER "Adafruit" +#define USB_PRODUCT "SELFTEST" + +#define UF2_PRODUCT_NAME USB_MANUFACTURER " " USB_PRODUCT +#define UF2_BOARD_ID "4k_fav" +#define UF2_VOLUME_LABEL "4k_fav" +#define UF2_INDEX_URL "https://www.adafruit.com" + +#endif diff --git a/ports/test_ghostfat/boards/4k_favicon/board.mk b/ports/test_ghostfat/boards/4k_favicon/board.mk new file mode 100644 index 0000000..48a4395 --- /dev/null +++ b/ports/test_ghostfat/boards/4k_favicon/board.mk @@ -0,0 +1,5 @@ +CFLAGS += \ + -DTINYUF2_FAVICON_HEADER=\"favicon_adafruit_256.h\" \ + -DCFG_UF2_SECTORS_PER_CLUSTER=8 \ + -DCOMPILE_DATE=\"Mar\ 11\ 2020\" \ + -DCOMPILE_TIME=\"17:35:07\" diff --git a/ports/test_ghostfat/boards/4k_favicon/knowngood.img.gz.gz b/ports/test_ghostfat/boards/4k_favicon/knowngood.img.gz.gz new file mode 100644 index 0000000..b6d8f7d Binary files /dev/null and b/ports/test_ghostfat/boards/4k_favicon/knowngood.img.gz.gz differ diff --git a/ports/test_ghostfat/boards/512b/board.h b/ports/test_ghostfat/boards/512b/board.h new file mode 100644 index 0000000..cf7056f --- /dev/null +++ b/ports/test_ghostfat/boards/512b/board.h @@ -0,0 +1,42 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Ha Thach for Adafruit Industries + * + * 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. + */ + +#ifndef BOARD_H_ +#define BOARD_H_ + +//--------------------------------------------------------------------+ +// USB UF2 +//--------------------------------------------------------------------+ + +#define USB_VID 0x0000 +#define USB_PID 0x0000 +#define USB_MANUFACTURER "Adafruit" +#define USB_PRODUCT "SELFTEST" + +#define UF2_PRODUCT_NAME USB_MANUFACTURER " " USB_PRODUCT +#define UF2_BOARD_ID "512b" +#define UF2_VOLUME_LABEL "512b" +#define UF2_INDEX_URL "https://www.adafruit.com" + +#endif diff --git a/ports/test_ghostfat/boards/test_base/board.mk b/ports/test_ghostfat/boards/512b/board.mk similarity index 100% rename from ports/test_ghostfat/boards/test_base/board.mk rename to ports/test_ghostfat/boards/512b/board.mk diff --git a/ports/test_ghostfat/boards/512b/knowngood.img.gz.gz b/ports/test_ghostfat/boards/512b/knowngood.img.gz.gz new file mode 100644 index 0000000..56cb908 Binary files /dev/null and b/ports/test_ghostfat/boards/512b/knowngood.img.gz.gz differ diff --git a/ports/test_ghostfat/boards/512b_favicon/board.h b/ports/test_ghostfat/boards/512b_favicon/board.h new file mode 100644 index 0000000..7a68aef --- /dev/null +++ b/ports/test_ghostfat/boards/512b_favicon/board.h @@ -0,0 +1,42 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Ha Thach for Adafruit Industries + * + * 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. + */ + +#ifndef BOARD_H_ +#define BOARD_H_ + +//--------------------------------------------------------------------+ +// USB UF2 +//--------------------------------------------------------------------+ + +#define USB_VID 0x0000 +#define USB_PID 0x0000 +#define USB_MANUFACTURER "Adafruit" +#define USB_PRODUCT "SELFTEST" + +#define UF2_PRODUCT_NAME USB_MANUFACTURER " " USB_PRODUCT +#define UF2_BOARD_ID "512b_Fav" +#define UF2_VOLUME_LABEL "512b_Fav" +#define UF2_INDEX_URL "https://www.adafruit.com" + +#endif diff --git a/ports/test_ghostfat/boards/512b_favicon/board.mk b/ports/test_ghostfat/boards/512b_favicon/board.mk new file mode 100644 index 0000000..6401ab9 --- /dev/null +++ b/ports/test_ghostfat/boards/512b_favicon/board.mk @@ -0,0 +1,4 @@ +CFLAGS += \ + -DTINYUF2_FAVICON_HEADER=\"favicon_adafruit_256.h\" \ + -DCOMPILE_DATE=\"Mar\ 11\ 2020\" \ + -DCOMPILE_TIME=\"17:35:07\" diff --git a/ports/test_ghostfat/boards/512b_favicon/knowngood.img.gz.gz b/ports/test_ghostfat/boards/512b_favicon/knowngood.img.gz.gz new file mode 100644 index 0000000..b67be2f Binary files /dev/null and b/ports/test_ghostfat/boards/512b_favicon/knowngood.img.gz.gz differ diff --git a/ports/test_ghostfat/boards/test_huge/board.h b/ports/test_ghostfat/boards/huge/board.h similarity index 100% rename from ports/test_ghostfat/boards/test_huge/board.h rename to ports/test_ghostfat/boards/huge/board.h diff --git a/ports/test_ghostfat/boards/test_huge/board.mk b/ports/test_ghostfat/boards/huge/board.mk similarity index 100% rename from ports/test_ghostfat/boards/test_huge/board.mk rename to ports/test_ghostfat/boards/huge/board.mk diff --git a/ports/test_ghostfat/boards/test_huge/knowngood.img.gz.gz b/ports/test_ghostfat/boards/huge/knowngood.img.gz.gz similarity index 56% rename from ports/test_ghostfat/boards/test_huge/knowngood.img.gz.gz rename to ports/test_ghostfat/boards/huge/knowngood.img.gz.gz index 257d45b..bff6246 100644 Binary files a/ports/test_ghostfat/boards/test_huge/knowngood.img.gz.gz and b/ports/test_ghostfat/boards/huge/knowngood.img.gz.gz differ diff --git a/ports/test_ghostfat/boards/test_32k_cluster/knowngood.img.gz.gz b/ports/test_ghostfat/boards/test_32k_cluster/knowngood.img.gz.gz deleted file mode 100644 index 17959f5..0000000 Binary files a/ports/test_ghostfat/boards/test_32k_cluster/knowngood.img.gz.gz and /dev/null differ diff --git a/ports/test_ghostfat/boards/test_4k_cluster/knowngood.img.gz.gz b/ports/test_ghostfat/boards/test_4k_cluster/knowngood.img.gz.gz deleted file mode 100644 index dad9834..0000000 Binary files a/ports/test_ghostfat/boards/test_4k_cluster/knowngood.img.gz.gz and /dev/null differ diff --git a/ports/test_ghostfat/boards/test_base/knowngood.img.gz.gz b/ports/test_ghostfat/boards/test_base/knowngood.img.gz.gz deleted file mode 100644 index 554c661..0000000 Binary files a/ports/test_ghostfat/boards/test_base/knowngood.img.gz.gz and /dev/null differ diff --git a/ports/test_ghostfat/main.c b/ports/test_ghostfat/main.c index 5b97785..e91e2cd 100644 --- a/ports/test_ghostfat/main.c +++ b/ports/test_ghostfat/main.c @@ -373,11 +373,14 @@ int main(void) { int r; + printf("initializing UF2\n"); fflush(stdout); uf2_init(); + printf("generating new disk image\n"); fflush(stdout); r = DumpDiskImage(); if (r) { goto errorExit; } + printf("comparing against known good disk image\n"); fflush(stdout); r = CompareDiskImages(); if (r) { goto errorExit; } @@ -385,7 +388,7 @@ int main(void) return ERR_NONE; errorExit: - printf("Failed -- %s\n", GetErrorString(r)); + printf("FAIL: (%d) %s\n", r, GetErrorString(r)); return r; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f505708..489b334 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,5 @@ idf_component_register(SRCS ghostfat.c images.c main.c msc.c screen.c usb_descriptors.c - INCLUDE_DIRS "." + INCLUDE_DIRS "." "favicon" REQUIRES boards) target_sources(${COMPONENT_TARGET} PUBLIC diff --git a/src/board_api.h b/src/board_api.h index 5c00c2d..fcef9fb 100644 --- a/src/board_api.h +++ b/src/board_api.h @@ -54,6 +54,13 @@ #define TINYUF2_DISPLAY 0 #endif +// Use favicon.ico + autorun.inf (only works with windows) +// define TINYUF2_FAVICON_HEADER to enable this feature + +//--------------------------------------------------------------------+ +// Constant +//--------------------------------------------------------------------+ + #define DBL_TAP_MAGIC 0xf01669ef // Enter DFU magic #define DBL_TAP_MAGIC_QUICK_BOOT 0xf02669ef // Skip double tap delay detection #define DBL_TAP_MAGIC_ERASE_APP 0xf5e80ab4 // Erase entire application !! diff --git a/src/favicon/favicon_adafruit_256.h b/src/favicon/favicon_adafruit_256.h new file mode 100644 index 0000000..86da7c5 --- /dev/null +++ b/src/favicon/favicon_adafruit_256.h @@ -0,0 +1,490 @@ +#ifndef FAVICON_H +#define FAVICON_H + +const unsigned long favicon_len = 7705; +const unsigned char favicon_data[] __attribute__((aligned(16))) = { +0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x03, 0x1e, +0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, +0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x04, +0x00, 0x00, 0x00, 0xf6, 0x7b, 0x60, 0xed, 0x00, 0x00, 0x1d, 0xca, 0x49, 0x44, 0x41, 0x54, 0x78, +0xda, 0xed, 0x9d, 0x77, 0x7c, 0x95, 0xd5, 0xdd, 0xc0, 0xbf, 0xf7, 0x66, 0xef, 0x84, 0x24, 0x8c, +0x10, 0x32, 0x20, 0x0c, 0x09, 0x33, 0x40, 0x00, 0x95, 0xe1, 0xc2, 0x89, 0x82, 0x56, 0xaa, 0x6f, +0xa5, 0x56, 0x41, 0xab, 0x62, 0x07, 0xd5, 0x4a, 0x6d, 0xad, 0x55, 0xdf, 0xb6, 0x1f, 0x79, 0xd5, +0xea, 0x5b, 0xf4, 0xb5, 0x75, 0xb4, 0x56, 0x69, 0x1d, 0xe0, 0xa8, 0x13, 0x11, 0xab, 0x40, 0xd8, +0x7b, 0xaf, 0x00, 0x61, 0x85, 0x10, 0xc8, 0x24, 0xeb, 0x26, 0xb9, 0xf7, 0xbe, 0x7f, 0x30, 0xcc, +0xb8, 0xe3, 0x99, 0xf7, 0x9c, 0xe7, 0x72, 0xbf, 0xe7, 0x1f, 0xc8, 0x7d, 0x9e, 0xf3, 0xfc, 0xce, +0x73, 0x7e, 0xcf, 0x19, 0xbf, 0xf3, 0x3b, 0xbf, 0x63, 0x23, 0x44, 0x7b, 0x22, 0x29, 0xe4, 0x7b, +0x5c, 0x4c, 0x37, 0xa0, 0x99, 0x03, 0x6c, 0xe1, 0x1b, 0x56, 0x51, 0x2d, 0x5a, 0xac, 0x10, 0x81, +0xa1, 0x0b, 0x73, 0x28, 0xa6, 0x15, 0xf7, 0xf9, 0xe4, 0xa4, 0x8c, 0x7f, 0x31, 0x86, 0x30, 0xd1, +0xa2, 0x85, 0x30, 0x9f, 0x74, 0x5e, 0xa0, 0xbe, 0x4d, 0xe5, 0x7f, 0x97, 0xb6, 0x73, 0x3d, 0x36, +0xd1, 0xe2, 0x85, 0x30, 0x97, 0x54, 0x5e, 0xa0, 0xc1, 0x63, 0xf5, 0xbb, 0x71, 0xb3, 0x8e, 0x61, +0xa2, 0x05, 0x0c, 0x61, 0x26, 0x71, 0xfc, 0x9a, 0x1a, 0xaf, 0xd5, 0xef, 0xa6, 0x95, 0x97, 0x89, +0x17, 0x2d, 0x64, 0x08, 0xb3, 0x08, 0xe3, 0x76, 0x8e, 0xf8, 0xa8, 0x7e, 0x37, 0x6e, 0x8e, 0x70, +0x95, 0x68, 0x31, 0x43, 0x98, 0xc5, 0x48, 0xd6, 0xf8, 0xa9, 0x7e, 0x37, 0x4e, 0x5e, 0x24, 0x5a, +0xb4, 0xa0, 0x21, 0xcc, 0xa0, 0x3b, 0x6f, 0xd1, 0xe2, 0x57, 0x01, 0xdc, 0x6c, 0x63, 0xb0, 0x68, +0x51, 0x43, 0x18, 0x4f, 0x34, 0x8f, 0x50, 0xab, 0xa0, 0xfa, 0xdd, 0xd4, 0x73, 0x7f, 0x68, 0x2e, +0x10, 0x6c, 0xd8, 0xb8, 0x9e, 0x62, 0x45, 0xd5, 0xef, 0xc6, 0xcd, 0x42, 0xba, 0x88, 0x16, 0x38, +0x84, 0xb1, 0xf4, 0xe7, 0x4b, 0xc5, 0xd5, 0xef, 0x66, 0x37, 0x85, 0xa2, 0x05, 0x36, 0x16, 0xbb, +0x68, 0x01, 0x04, 0x93, 0xc2, 0xbd, 0x8c, 0x57, 0x71, 0x7d, 0x26, 0x05, 0xa1, 0x4e, 0x20, 0x78, +0x88, 0x60, 0x06, 0x27, 0x54, 0x7c, 0xff, 0x6e, 0x5c, 0xfc, 0x8b, 0x24, 0xd1, 0x62, 0x87, 0x30, +0x8a, 0x4b, 0xd9, 0xa4, 0xaa, 0xfa, 0xdd, 0xb8, 0xd9, 0xc5, 0x28, 0xd1, 0x62, 0x87, 0x30, 0x86, +0x5e, 0xbc, 0xdb, 0x6e, 0xd9, 0x47, 0x59, 0xaa, 0xe3, 0x5e, 0xd1, 0x82, 0x1b, 0xc9, 0x85, 0x3b, +0x06, 0x88, 0xe5, 0x87, 0x5c, 0xab, 0x61, 0x8d, 0x2f, 0x96, 0x31, 0x24, 0x88, 0x16, 0x3e, 0x84, +0x5e, 0xec, 0xdc, 0xc8, 0x01, 0xd5, 0x5f, 0xff, 0x99, 0xb4, 0x95, 0x41, 0xa2, 0xc5, 0x0f, 0xa1, +0x97, 0x7c, 0xbe, 0xd1, 0x58, 0xfd, 0x6e, 0x6a, 0xb8, 0x5b, 0xb4, 0xf8, 0xc6, 0x71, 0x61, 0x76, +0x01, 0x69, 0xdc, 0xcf, 0x25, 0x9a, 0xef, 0x4e, 0xa4, 0x90, 0x38, 0xd1, 0x45, 0x08, 0xa1, 0x9d, +0x48, 0xee, 0xe3, 0xa4, 0xe6, 0xef, 0xdf, 0x8d, 0x9b, 0xf5, 0xf4, 0x17, 0x5d, 0x88, 0x10, 0xda, +0x19, 0xcf, 0x36, 0x5d, 0xd5, 0xef, 0xa6, 0x82, 0xdb, 0x44, 0x17, 0x22, 0x84, 0x56, 0x7a, 0xf2, +0x21, 0x2e, 0x9d, 0x0a, 0xe0, 0x62, 0x1e, 0x31, 0xa2, 0x0b, 0x62, 0x0c, 0x17, 0xda, 0x18, 0x20, +0x86, 0x7b, 0xb8, 0x46, 0xb7, 0x31, 0xd7, 0x46, 0x01, 0x19, 0xa2, 0x8b, 0x12, 0x42, 0x3d, 0x76, +0xa6, 0x72, 0x4c, 0xe7, 0xd7, 0x7f, 0x26, 0x9d, 0xe4, 0x26, 0xd1, 0x85, 0x09, 0xa1, 0x9e, 0x7c, +0x56, 0x18, 0x52, 0xfd, 0x6e, 0x5a, 0x79, 0x96, 0x28, 0xd1, 0xc5, 0x09, 0xa1, 0x8e, 0x14, 0x5e, +0xd1, 0x60, 0xfa, 0xf5, 0x96, 0x96, 0xd2, 0x5b, 0x74, 0x81, 0x42, 0xa8, 0x21, 0x8c, 0x59, 0x3e, +0xbd, 0x7e, 0xd5, 0xa6, 0x32, 0xae, 0x11, 0x5d, 0xa4, 0x10, 0x6a, 0x18, 0xaf, 0xc2, 0xef, 0x47, +0x49, 0x6a, 0xe6, 0x0f, 0x44, 0x8a, 0x2e, 0x54, 0x08, 0xa5, 0xf4, 0xe2, 0x0b, 0x43, 0xab, 0xdf, +0x8d, 0x9b, 0xff, 0x90, 0x25, 0xba, 0x58, 0xfa, 0xb9, 0x30, 0xa6, 0x81, 0xd1, 0xdc, 0xcf, 0x95, +0x86, 0xe7, 0xda, 0x9f, 0x01, 0xa2, 0x0b, 0xa6, 0x9f, 0x0b, 0x43, 0x01, 0x6e, 0xe0, 0x1e, 0xc2, +0x0d, 0xcf, 0x35, 0x9d, 0x51, 0x44, 0x88, 0x2e, 0x5a, 0x08, 0xff, 0x0c, 0x66, 0x9d, 0xe1, 0xcd, +0xff, 0x99, 0xb4, 0x88, 0xee, 0xa2, 0x0b, 0x17, 0xc2, 0x1f, 0xa9, 0xfc, 0x43, 0xb7, 0xe9, 0xd7, +0x5b, 0x3a, 0xcc, 0xe5, 0xa2, 0x8b, 0xa7, 0x97, 0x60, 0xef, 0x02, 0xc2, 0xb9, 0x93, 0x5b, 0x4c, +0xf3, 0xe3, 0xed, 0xce, 0xf0, 0x50, 0xdc, 0x00, 0xb9, 0xb9, 0x82, 0x83, 0x26, 0x7d, 0xfd, 0x67, +0xd2, 0x87, 0xa4, 0x89, 0x2e, 0x62, 0x08, 0xef, 0xe4, 0xb0, 0xd8, 0xd4, 0xea, 0x77, 0x73, 0x80, +0xb1, 0xa2, 0x0b, 0xa9, 0x8f, 0x60, 0xee, 0x02, 0xe2, 0x79, 0x90, 0x89, 0x26, 0x3f, 0xa3, 0x27, +0x85, 0xd6, 0xde, 0x28, 0x12, 0xbc, 0x0a, 0x60, 0x67, 0x2a, 0xd3, 0x4d, 0x9f, 0xa6, 0x45, 0x52, +0x48, 0xb2, 0xe8, 0xa2, 0x86, 0xf0, 0xc4, 0x28, 0x36, 0x9a, 0xdc, 0xfc, 0x9f, 0x49, 0x7b, 0x19, +0x21, 0xba, 0xa8, 0x7a, 0x08, 0xd6, 0x16, 0xa0, 0x3b, 0x3f, 0x67, 0x68, 0x40, 0x9e, 0xd4, 0x93, +0x51, 0x56, 0xee, 0x04, 0x82, 0x53, 0x01, 0xa2, 0xb8, 0x93, 0xc9, 0x01, 0x2a, 0x5b, 0x2c, 0xa3, +0x43, 0x1b, 0x45, 0xe4, 0xc2, 0xc6, 0x64, 0xf6, 0x07, 0xa4, 0xf9, 0x3f, 0x93, 0x42, 0x71, 0x43, +0x24, 0x63, 0xa0, 0x8e, 0x4d, 0x1f, 0x5a, 0xd2, 0x69, 0xee, 0xb6, 0x6e, 0x27, 0x10, 0x7c, 0x5d, +0x40, 0x32, 0x0f, 0xe8, 0xd8, 0xf4, 0xa1, 0x85, 0x78, 0xc6, 0x12, 0x2b, 0xba, 0xd8, 0x5a, 0x09, +0x36, 0x05, 0xb0, 0x33, 0x99, 0x5b, 0x03, 0xbe, 0x46, 0x57, 0x40, 0x2f, 0xd1, 0x05, 0xd7, 0x4a, +0xb0, 0x29, 0x40, 0x7f, 0x66, 0xd0, 0x35, 0xe0, 0x4f, 0xed, 0x6b, 0xdd, 0x28, 0xa2, 0xc1, 0xa5, +0x00, 0xb1, 0xfc, 0x80, 0xd1, 0x02, 0x9e, 0x9b, 0xc0, 0x04, 0xab, 0x6e, 0x14, 0x09, 0x2e, 0x05, +0x18, 0xcb, 0x34, 0x41, 0xa1, 0x1c, 0x87, 0xd3, 0x43, 0x74, 0xe1, 0xb5, 0x11, 0x4c, 0x0a, 0x90, +0xc2, 0x74, 0x61, 0xae, 0xda, 0x03, 0x18, 0x22, 0xba, 0xf8, 0xda, 0x08, 0x26, 0x05, 0xb8, 0x8c, +0x49, 0xc2, 0x56, 0xe7, 0x93, 0x18, 0x6f, 0xcd, 0x8d, 0x22, 0xc1, 0xa3, 0x00, 0xe9, 0xdc, 0x2e, +0xb4, 0x19, 0xbe, 0xc4, 0x9a, 0xee, 0x61, 0xc1, 0xa3, 0x00, 0x97, 0x30, 0x4e, 0xe8, 0xf3, 0xfb, +0x31, 0x5c, 0xf4, 0x2b, 0xd0, 0x42, 0xb0, 0x28, 0x40, 0x0a, 0x53, 0x04, 0x4c, 0xff, 0xda, 0x92, +0xcc, 0x68, 0x2b, 0x6e, 0x14, 0x09, 0x16, 0x05, 0x18, 0xce, 0xe5, 0xc2, 0xcd, 0xb1, 0x17, 0x5b, +0x31, 0x84, 0x64, 0x70, 0x28, 0x40, 0x0c, 0xd7, 0x4a, 0x30, 0x0d, 0xcb, 0xb3, 0x62, 0xe0, 0x98, +0xe0, 0x50, 0x80, 0xde, 0x5c, 0x66, 0xc2, 0xc6, 0x0f, 0xb5, 0xa4, 0x5b, 0x71, 0x55, 0x30, 0x18, +0x14, 0xc0, 0xce, 0xc5, 0xf4, 0x13, 0x2d, 0x04, 0x10, 0x41, 0xbe, 0xf5, 0x4e, 0x14, 0x09, 0x06, +0x05, 0x48, 0x60, 0x82, 0x24, 0x2e, 0x19, 0xc3, 0xac, 0xe7, 0x1f, 0x18, 0x0c, 0x0a, 0xd0, 0x57, +0x9a, 0x09, 0x58, 0xb6, 0xf5, 0x76, 0x09, 0x58, 0x5f, 0x01, 0x6c, 0x14, 0x90, 0x2d, 0x5a, 0x88, +0xb3, 0xc4, 0x93, 0x23, 0x5a, 0x04, 0xb5, 0x58, 0x5f, 0x01, 0x64, 0x72, 0xc7, 0x88, 0xa3, 0x8f, +0x68, 0x11, 0xd4, 0x62, 0x7d, 0x05, 0xe8, 0x46, 0xbe, 0x70, 0x0b, 0xc0, 0x39, 0x22, 0xc8, 0xb2, +0xda, 0x1b, 0xb5, 0x98, 0xb8, 0x1e, 0xc8, 0x23, 0x53, 0xb4, 0x08, 0x6d, 0xc8, 0x90, 0xa6, 0x35, +0x52, 0x48, 0x30, 0x28, 0x80, 0x4c, 0x03, 0xaf, 0x74, 0xab, 0x85, 0x91, 0xb6, 0xba, 0x02, 0xc4, +0xd1, 0x4f, 0xaa, 0x28, 0x1d, 0xa9, 0xa1, 0x16, 0x20, 0xb0, 0xc4, 0x92, 0x27, 0x5a, 0x84, 0x76, +0x24, 0x5a, 0xcd, 0x2b, 0xa0, 0xb3, 0x01, 0xd5, 0x46, 0x18, 0xe1, 0xaa, 0x15, 0xc3, 0x89, 0x53, +0xd5, 0xf5, 0x2e, 0x5c, 0x86, 0xc8, 0x1f, 0x2f, 0xc1, 0x1a, 0x40, 0x5b, 0x92, 0xac, 0xac, 0x00, +0xf1, 0xe4, 0x92, 0x43, 0x77, 0x52, 0xe9, 0xa2, 0x7a, 0x61, 0xb3, 0x9e, 0x1a, 0xdc, 0x2a, 0xae, +0x6f, 0xa6, 0x82, 0x66, 0x4e, 0x53, 0x4b, 0x35, 0x15, 0xd4, 0xd1, 0x4c, 0x8b, 0xaa, 0xfb, 0xcf, +0x91, 0x4e, 0x62, 0xe0, 0x5f, 0x9a, 0x0f, 0xc2, 0xac, 0xd6, 0xa6, 0x9e, 0x53, 0x80, 0x58, 0xc6, +0x72, 0x2b, 0x63, 0xc8, 0x24, 0x01, 0xbb, 0x86, 0x42, 0xb8, 0x55, 0x56, 0x9f, 0x1b, 0x37, 0xad, +0xd4, 0x53, 0x4b, 0x2d, 0x55, 0x94, 0xb1, 0x9f, 0x7d, 0x94, 0x50, 0x4a, 0x39, 0x0d, 0xaa, 0xda, +0x92, 0x2e, 0xd2, 0xf5, 0xb9, 0xb2, 0x4c, 0x49, 0x15, 0x72, 0x46, 0x01, 0xe2, 0x99, 0xc9, 0x83, +0xe4, 0x06, 0x58, 0x7b, 0x23, 0x89, 0x25, 0xfd, 0xec, 0xbf, 0x5b, 0x70, 0x50, 0xc9, 0x21, 0x0e, +0xb0, 0x89, 0x4d, 0xec, 0xa7, 0x9c, 0x16, 0x45, 0x79, 0x74, 0x91, 0xcc, 0x1d, 0x3b, 0x8c, 0x14, +0xd1, 0x22, 0xa8, 0x23, 0x1c, 0x88, 0xe0, 0x87, 0xfc, 0x46, 0xf0, 0x64, 0x2a, 0x82, 0x08, 0xe2, +0xc9, 0xe2, 0x52, 0xa6, 0x71, 0x8a, 0x9d, 0xac, 0xa1, 0x88, 0x1d, 0x9c, 0xf0, 0xdb, 0x1a, 0x44, +0x4b, 0x35, 0x07, 0x00, 0x0c, 0x1a, 0xdb, 0x04, 0x8c, 0x70, 0x60, 0x2c, 0x0f, 0x49, 0x33, 0x97, +0xb6, 0x11, 0x43, 0x2f, 0x7a, 0x71, 0x39, 0x27, 0xd9, 0xc2, 0xd7, 0x2c, 0x61, 0x2f, 0x8d, 0x3e, +0xef, 0xd0, 0x32, 0x72, 0x30, 0x0f, 0x27, 0x35, 0xa2, 0x45, 0x50, 0x47, 0x38, 0xc9, 0xdc, 0x27, +0x61, 0xe0, 0xf3, 0x08, 0x32, 0xc8, 0x60, 0x22, 0x77, 0xf2, 0x15, 0x9f, 0xb0, 0x9e, 0x3a, 0xd1, +0x02, 0x29, 0x46, 0x2e, 0x85, 0xf4, 0x4b, 0x38, 0xe3, 0xb8, 0x4c, 0xb4, 0x10, 0x5e, 0x89, 0x61, +0x30, 0x03, 0x99, 0xca, 0xe7, 0xcc, 0x67, 0x33, 0x4d, 0x1e, 0xaf, 0x91, 0x6b, 0xd0, 0xd5, 0xa8, +0x70, 0xec, 0x22, 0x0d, 0x76, 0x6e, 0x10, 0xec, 0x4d, 0xeb, 0x8f, 0x30, 0x72, 0xb9, 0x97, 0xd7, +0x98, 0x43, 0x9e, 0xc7, 0xca, 0x96, 0xeb, 0x8b, 0xab, 0xf6, 0xa2, 0xa6, 0xd2, 0x12, 0xce, 0x78, +0x0b, 0xcc, 0x5c, 0x23, 0xc8, 0xa7, 0x37, 0x13, 0x79, 0x91, 0x2f, 0x68, 0x68, 0xf7, 0x4b, 0x1d, +0xcd, 0x52, 0x59, 0xdf, 0x2d, 0xa7, 0x00, 0x76, 0x72, 0x45, 0x8b, 0xa0, 0x90, 0x18, 0x26, 0x32, +0x8f, 0xdf, 0x75, 0x70, 0xfe, 0xa8, 0xf6, 0x33, 0x44, 0x0c, 0x34, 0xe5, 0xd4, 0x8b, 0x16, 0x41, +0x1d, 0x76, 0xc9, 0x9a, 0x50, 0xdf, 0xf4, 0xe0, 0x67, 0xcc, 0x63, 0x4c, 0x9b, 0xae, 0xe0, 0x84, +0x64, 0xc3, 0xc3, 0x52, 0xc9, 0xe4, 0xf1, 0x8b, 0x9d, 0xfd, 0xa2, 0x45, 0x50, 0x45, 0x14, 0xd7, +0xf3, 0xbf, 0x4c, 0x3e, 0xbf, 0x09, 0xf4, 0x14, 0xa7, 0x44, 0x8b, 0xd4, 0x86, 0x56, 0x0e, 0x58, +0x6f, 0x10, 0xb8, 0xcc, 0x62, 0xa6, 0x0b, 0x3b, 0x85, 0xcc, 0xe5, 0x96, 0xb3, 0xab, 0x15, 0xf5, +0x94, 0x88, 0x16, 0xa8, 0x0d, 0xf5, 0xec, 0x13, 0x2d, 0x82, 0x5a, 0xec, 0x2c, 0xe4, 0xa8, 0x68, +0x21, 0x54, 0x33, 0x80, 0xb9, 0xdc, 0x42, 0x18, 0x50, 0xc7, 0x2e, 0x5d, 0x0a, 0xec, 0xa6, 0x86, +0x63, 0x1d, 0x06, 0x96, 0xda, 0xa9, 0x64, 0xaf, 0xe8, 0x57, 0xa3, 0x96, 0x30, 0x8e, 0x93, 0x4d, +0xa1, 0x68, 0x31, 0x54, 0x93, 0x4c, 0x3e, 0x47, 0xd9, 0x87, 0x93, 0x34, 0x26, 0x69, 0x5e, 0x0f, +0x38, 0xcc, 0x1b, 0x3c, 0xc7, 0x9b, 0x2c, 0x25, 0x82, 0x1c, 0x03, 0xf6, 0x16, 0x15, 0xf1, 0x4f, +0xc9, 0x06, 0xa5, 0x8a, 0x18, 0xc1, 0xee, 0x80, 0xc6, 0xd5, 0x33, 0x2a, 0x6d, 0x65, 0x22, 0x70, +0x91, 0xc6, 0x98, 0xc0, 0x4e, 0x96, 0x31, 0xe9, 0xfc, 0x4e, 0x9e, 0x4c, 0x5e, 0xa2, 0x51, 0xb7, +0x44, 0x8f, 0x59, 0x60, 0x4a, 0xed, 0x81, 0x30, 0x7e, 0x4e, 0x83, 0xf0, 0xea, 0x54, 0x9f, 0x5c, +0x2c, 0x26, 0x8f, 0x78, 0x5e, 0xd7, 0x70, 0x24, 0x8c, 0x8b, 0x6f, 0x3b, 0xc4, 0xf8, 0xcd, 0xe5, +0x73, 0x9d, 0x47, 0xcb, 0x94, 0x33, 0x49, 0x74, 0x55, 0x6a, 0xa9, 0x7c, 0x70, 0x73, 0x80, 0x7e, +0x0c, 0x14, 0x2d, 0x8a, 0x6a, 0x6c, 0x64, 0x11, 0xce, 0x52, 0xe2, 0xb9, 0x52, 0xb5, 0x03, 0xcb, +0x1e, 0x1e, 0xa5, 0xa8, 0xdd, 0x5f, 0xaa, 0x69, 0x62, 0x22, 0xf1, 0x3a, 0xe4, 0xd9, 0xc0, 0x8b, +0x56, 0xb3, 0x02, 0x7c, 0xc7, 0x28, 0xb6, 0x0b, 0xff, 0xa2, 0xb5, 0xa4, 0x53, 0xdc, 0xc5, 0x30, +0x36, 0xab, 0xbc, 0xab, 0x8a, 0x07, 0x3c, 0xf4, 0xf8, 0xa9, 0x7c, 0xa4, 0xab, 0x3d, 0x7a, 0x4c, +0x82, 0x1d, 0xca, 0xaa, 0x39, 0xd7, 0x67, 0x6d, 0xe4, 0x19, 0x2a, 0x44, 0x0b, 0xa3, 0x81, 0x54, +0x1e, 0x24, 0x85, 0x35, 0xa8, 0x31, 0x67, 0xb9, 0x59, 0xc4, 0x02, 0x5a, 0x3b, 0xfd, 0xbd, 0x82, +0xc5, 0x3a, 0x86, 0x70, 0x87, 0x58, 0xe2, 0x21, 0x4f, 0xe9, 0x39, 0xa7, 0xb3, 0x4e, 0x3e, 0x60, +0x08, 0x3f, 0x91, 0xce, 0xbd, 0xc2, 0x3f, 0x43, 0x99, 0xce, 0x26, 0xaa, 0xe8, 0xa2, 0xf8, 0x8e, +0x12, 0x5e, 0xe7, 0xa4, 0xc7, 0x5f, 0xd6, 0x70, 0x4c, 0xb3, 0x97, 0xf1, 0x37, 0x6c, 0xf7, 0xfa, +0x9b, 0x8d, 0x18, 0xe2, 0x89, 0x21, 0x92, 0xe8, 0x36, 0xad, 0x84, 0x83, 0x66, 0x1c, 0x34, 0x51, +0x47, 0x93, 0x2a, 0x05, 0x36, 0x94, 0xef, 0xc4, 0x39, 0xcd, 0x3c, 0x2e, 0xe2, 0x5a, 0x51, 0x82, +0x68, 0x26, 0x8c, 0xc9, 0x54, 0x52, 0xac, 0x78, 0x2a, 0xeb, 0x62, 0x11, 0x2b, 0xbd, 0xfc, 0x76, +0x88, 0x62, 0x8d, 0x0a, 0x70, 0x82, 0x05, 0x9d, 0x8c, 0xc0, 0x36, 0xe2, 0x48, 0x21, 0x8b, 0x7c, +0xb2, 0xe9, 0x45, 0x57, 0x52, 0x49, 0x24, 0x91, 0xe8, 0xf3, 0x95, 0x7d, 0x9a, 0x7a, 0x6a, 0xa9, +0xa2, 0x9c, 0x52, 0x8e, 0x71, 0x90, 0x62, 0xaa, 0xa9, 0x09, 0xf4, 0x62, 0x52, 0xdb, 0x5e, 0xab, +0x84, 0x67, 0xe9, 0x2b, 0x99, 0x9f, 0xbd, 0x12, 0xd2, 0xb8, 0x86, 0x72, 0x5a, 0x14, 0xb6, 0x5e, +0xc7, 0x58, 0xe0, 0xd5, 0xf0, 0x53, 0xc9, 0x01, 0x4d, 0x12, 0xb8, 0xf9, 0x82, 0x55, 0x6d, 0xfe, +0x6f, 0x23, 0x89, 0x3c, 0x46, 0x51, 0xc8, 0x50, 0x32, 0x49, 0x20, 0xca, 0xe3, 0x42, 0x76, 0x4a, +0x9b, 0xfb, 0x5b, 0x68, 0xa0, 0x9a, 0x12, 0xb6, 0xb3, 0x8d, 0x1d, 0xec, 0xa3, 0x9a, 0x66, 0x11, +0xaf, 0x32, 0x82, 0x59, 0x54, 0x0b, 0x1f, 0xd8, 0xa9, 0x4f, 0x2d, 0x94, 0x50, 0xaf, 0xf0, 0xda, +0x77, 0x7c, 0x3a, 0x92, 0xcf, 0xd6, 0x64, 0x0d, 0xd8, 0xdf, 0xe6, 0x74, 0xb2, 0x48, 0xfa, 0x32, +0x93, 0x85, 0x94, 0xd0, 0xa4, 0xa9, 0x2c, 0x4d, 0x1c, 0x63, 0x19, 0xff, 0xc3, 0x14, 0xb2, 0x03, +0xb1, 0xc7, 0xa0, 0xfd, 0xb8, 0xb5, 0x85, 0xb7, 0x19, 0xc2, 0xdd, 0x96, 0x1b, 0xcd, 0x86, 0x2b, +0xde, 0x20, 0x7a, 0x9a, 0xcf, 0xa8, 0xf5, 0xf1, 0xfb, 0x11, 0x1c, 0xaa, 0xc3, 0xbc, 0x38, 0x78, +0x8b, 0x35, 0x00, 0x44, 0x33, 0x98, 0x9b, 0xb8, 0x96, 0x81, 0x3a, 0x42, 0xc5, 0x44, 0x91, 0x41, +0x06, 0xe3, 0xa8, 0x61, 0x0f, 0x2b, 0x58, 0xc1, 0x0e, 0x0e, 0x05, 0x76, 0x8c, 0x90, 0xcf, 0x7f, +0x84, 0x7f, 0xd1, 0xe6, 0xa5, 0xf5, 0x7e, 0x76, 0xf0, 0x8f, 0xe7, 0xa4, 0xea, 0x3c, 0xbf, 0xa0, +0x37, 0x10, 0x46, 0x3e, 0x7f, 0x60, 0x2f, 0xad, 0x06, 0x4a, 0xeb, 0xe2, 0x34, 0x1b, 0xf9, 0x0b, +0xb7, 0x33, 0x98, 0xc4, 0x40, 0x59, 0x19, 0xed, 0xdc, 0x18, 0xd0, 0x13, 0x77, 0x02, 0x99, 0x5c, +0xbc, 0xe4, 0xa7, 0x59, 0x1d, 0x46, 0x99, 0xca, 0x3c, 0xf7, 0x73, 0x1d, 0x36, 0xd2, 0x99, 0xc9, +0x5a, 0x9a, 0x4d, 0x92, 0xfa, 0x34, 0xdb, 0xf9, 0x3b, 0x33, 0x28, 0x20, 0x25, 0x10, 0x6a, 0x10, +0xcb, 0x1c, 0x6a, 0x84, 0x57, 0x96, 0x19, 0xa9, 0x9a, 0xdb, 0xfc, 0x94, 0x7d, 0x10, 0x25, 0xaa, +0x72, 0xac, 0x61, 0x0e, 0xf1, 0x14, 0xf0, 0x06, 0x55, 0xa6, 0x2b, 0x6f, 0x1d, 0x7b, 0x98, 0xcf, +0x2c, 0xc6, 0x90, 0x6e, 0x76, 0x50, 0xec, 0x1e, 0xbc, 0x69, 0x68, 0x53, 0x26, 0x4b, 0xda, 0xe9, +0x37, 0x94, 0x63, 0x5f, 0xb6, 0xa9, 0xc8, 0xaf, 0x99, 0x57, 0xe9, 0xc3, 0x6d, 0xac, 0x0d, 0xe0, +0xdb, 0x6a, 0xe0, 0x00, 0xef, 0xf3, 0x30, 0xe3, 0xe9, 0x6e, 0x84, 0xd5, 0xc6, 0xf3, 0x70, 0xef, +0x38, 0x2f, 0x90, 0x67, 0xf5, 0x63, 0x91, 0x3d, 0xb0, 0x8d, 0xe3, 0x7e, 0xae, 0x70, 0xab, 0xf0, +0x2e, 0x70, 0x53, 0xc4, 0x87, 0x4c, 0x67, 0x26, 0x3d, 0x03, 0x58, 0x86, 0x18, 0x72, 0xc9, 0xe5, +0x06, 0xca, 0xd8, 0x49, 0x11, 0x1b, 0xd9, 0xc6, 0x49, 0x1c, 0xda, 0xb3, 0xf3, 0x36, 0xde, 0xdf, +0xcc, 0xf3, 0xf4, 0x92, 0x2a, 0xf8, 0x8a, 0x7e, 0x9c, 0x6c, 0xf5, 0x39, 0x03, 0x38, 0xf3, 0x3e, +0x94, 0x8f, 0xdf, 0xf7, 0xb2, 0x84, 0x3b, 0xb8, 0x51, 0x88, 0x57, 0x72, 0x24, 0x59, 0x64, 0x71, +0x25, 0x27, 0xd9, 0xcd, 0x4a, 0x56, 0xb1, 0x9b, 0x63, 0x38, 0xd0, 0x30, 0x57, 0xf0, 0xa6, 0x00, +0x2e, 0xbe, 0x60, 0x30, 0xb3, 0x75, 0xad, 0x8e, 0xc9, 0x46, 0x0d, 0xbb, 0xfc, 0x5e, 0x13, 0xad, +0x78, 0xbb, 0x79, 0x15, 0xbb, 0xb9, 0x8e, 0x42, 0xa1, 0xc6, 0xf3, 0x70, 0x7a, 0xd0, 0x83, 0x71, +0x54, 0xb2, 0x8f, 0xd5, 0xac, 0x60, 0x27, 0x47, 0x68, 0x54, 0xa7, 0x06, 0xde, 0x07, 0x13, 0xcd, +0x1c, 0x20, 0x97, 0x01, 0xd6, 0x74, 0x71, 0xf0, 0x48, 0x29, 0x2f, 0x73, 0xc2, 0xcf, 0x35, 0x99, +0xdc, 0xa1, 0xe8, 0x8b, 0x76, 0x51, 0x43, 0x4f, 0x06, 0x48, 0x61, 0x31, 0xb1, 0x13, 0x47, 0x16, +0x85, 0x5c, 0xcd, 0x78, 0xf2, 0x49, 0x00, 0x1a, 0x95, 0xdb, 0x11, 0x7d, 0x8d, 0x26, 0x6b, 0x38, +0x6e, 0xdd, 0xc3, 0x90, 0x3c, 0xb0, 0x8b, 0x57, 0xfd, 0x7a, 0xff, 0x0d, 0x62, 0x9a, 0x42, 0x07, +0xb3, 0x38, 0x12, 0xa5, 0xda, 0x96, 0x66, 0x27, 0x86, 0x4c, 0x46, 0x72, 0x25, 0x57, 0x31, 0x98, +0xae, 0xb8, 0x71, 0x28, 0x19, 0x1b, 0xf8, 0x9e, 0x4e, 0x94, 0xd2, 0x18, 0x44, 0x47, 0x23, 0x2f, +0xe7, 0x43, 0xbf, 0x4e, 0xdb, 0x85, 0xdc, 0xa8, 0xc8, 0x00, 0x6b, 0x93, 0xaa, 0xf2, 0xdb, 0xca, +0x15, 0x43, 0x37, 0x86, 0x73, 0x19, 0x93, 0x18, 0x41, 0x37, 0x6c, 0x34, 0xd1, 0xec, 0xab, 0x53, +0xf0, 0xad, 0x00, 0x2e, 0x4a, 0x48, 0x61, 0xb8, 0x05, 0x17, 0x89, 0x3b, 0xe3, 0xe6, 0x33, 0x96, +0xf8, 0x1d, 0xe3, 0x4f, 0x62, 0x92, 0x14, 0xcd, 0xba, 0x3e, 0x6c, 0x44, 0x91, 0xce, 0x60, 0x26, +0x72, 0x15, 0xa3, 0xc9, 0x20, 0x8c, 0x26, 0x1c, 0x9e, 0xcb, 0xee, 0xaf, 0xb0, 0x35, 0xfc, 0x95, +0x01, 0x5c, 0x2f, 0xa9, 0xbe, 0xab, 0xc1, 0x4d, 0x99, 0x5f, 0x87, 0x0d, 0x1b, 0xbd, 0xac, 0x16, +0xe4, 0xc9, 0x27, 0x71, 0xf4, 0x25, 0x8f, 0x6b, 0x29, 0x65, 0x0f, 0x45, 0xac, 0x66, 0x07, 0x15, +0x1d, 0x43, 0x6e, 0xf8, 0xd7, 0xf6, 0x62, 0x5e, 0x20, 0x8f, 0x01, 0xa2, 0xcb, 0xa2, 0x9b, 0x66, +0x05, 0x7b, 0x88, 0x62, 0xe9, 0x19, 0x04, 0xaa, 0xde, 0x1e, 0x1b, 0x31, 0xf4, 0xa1, 0x37, 0x57, +0x50, 0xc6, 0x0e, 0x8a, 0x28, 0x62, 0x6b, 0xdb, 0x20, 0x16, 0x4a, 0x4c, 0x8a, 0xc7, 0x70, 0x31, +0x5a, 0xba, 0x60, 0x4c, 0x6a, 0x39, 0xcd, 0x3b, 0x7e, 0xb7, 0x6d, 0xf4, 0xe0, 0x4e, 0xb2, 0x44, +0x0b, 0x6a, 0x0a, 0x36, 0xc2, 0x49, 0xa6, 0x2f, 0x97, 0x72, 0x15, 0xc3, 0x08, 0xe3, 0xe4, 0x39, +0xf7, 0x55, 0x25, 0x0a, 0xe0, 0xa4, 0x98, 0x6e, 0x0c, 0x15, 0x70, 0x28, 0xa3, 0x9b, 0x56, 0x5a, +0x71, 0x1b, 0xf2, 0xe4, 0x2a, 0xfe, 0xc9, 0x61, 0x3f, 0xd7, 0x0c, 0x60, 0xa6, 0x15, 0x0f, 0x7e, +0x52, 0x41, 0x38, 0xc9, 0xe4, 0x73, 0x39, 0x83, 0xa8, 0xe1, 0x30, 0x2e, 0x25, 0x5d, 0x00, 0x40, +0x25, 0xf3, 0xe8, 0xc7, 0x15, 0x01, 0x14, 0xb4, 0x8e, 0x3d, 0x6c, 0xa7, 0x84, 0x0a, 0x9c, 0x44, +0x90, 0x4e, 0x3a, 0x59, 0x64, 0xd3, 0x8d, 0x44, 0xcd, 0x43, 0xb4, 0x66, 0x05, 0xce, 0x56, 0xb9, +0x2a, 0x3c, 0x0b, 0xad, 0x8b, 0x8d, 0x54, 0xa6, 0x32, 0x8c, 0x3f, 0x30, 0x9f, 0x66, 0xa5, 0xaf, +0x73, 0x17, 0xcf, 0x90, 0x1b, 0xa0, 0x58, 0x42, 0x2e, 0x36, 0xf2, 0x57, 0xbe, 0xa1, 0x9c, 0xfa, +0xb3, 0x23, 0x57, 0x3b, 0xd1, 0x24, 0x90, 0x48, 0x6f, 0x0a, 0x19, 0xc1, 0x60, 0x32, 0x4d, 0x39, +0xa1, 0x2f, 0x8c, 0x21, 0xd6, 0x3b, 0xf3, 0x47, 0x23, 0x36, 0x7a, 0xf3, 0x14, 0xb5, 0xbc, 0xaf, +0xfc, 0x96, 0x48, 0x1e, 0xe6, 0x74, 0x40, 0xd6, 0xbb, 0xd6, 0x31, 0xce, 0x6b, 0xa3, 0x6f, 0xa7, +0x0b, 0xc3, 0xb8, 0x9f, 0x8f, 0x38, 0xae, 0x72, 0x1f, 0xcf, 0x41, 0x46, 0xf9, 0x29, 0x61, 0x17, +0x3e, 0x15, 0xbe, 0x5e, 0x19, 0xd8, 0x54, 0x44, 0x6f, 0xe5, 0xbd, 0xab, 0x93, 0x3d, 0xf4, 0x62, +0x88, 0xe9, 0xa3, 0xe4, 0x7a, 0x9e, 0xe3, 0x23, 0xaf, 0xf1, 0x01, 0xdd, 0x34, 0x52, 0xc6, 0x06, +0xbe, 0x64, 0x39, 0xe5, 0x24, 0xd2, 0x45, 0xf1, 0xf8, 0xa0, 0x9a, 0x05, 0x94, 0xfa, 0xbc, 0x62, +0x10, 0xf7, 0x92, 0x6a, 0x72, 0xe9, 0xe4, 0x22, 0x8d, 0xa3, 0x6a, 0x86, 0x57, 0x8d, 0x14, 0x33, +0xca, 0xf4, 0xa5, 0xcf, 0x32, 0xfe, 0xec, 0x77, 0xb0, 0x06, 0x4d, 0x1c, 0x62, 0x29, 0x4b, 0x38, +0x44, 0x32, 0x69, 0x8a, 0xc6, 0x05, 0xb5, 0xbc, 0xef, 0x73, 0x23, 0xbc, 0x8d, 0xeb, 0xb9, 0x35, +0x28, 0x4c, 0x5e, 0xca, 0x89, 0xa0, 0x45, 0xdd, 0xf8, 0xba, 0x9c, 0x4a, 0xc6, 0x99, 0x6c, 0x1a, +0xae, 0xe6, 0x3d, 0x8e, 0x29, 0xba, 0xd2, 0x45, 0x05, 0x6b, 0x59, 0x4c, 0x29, 0xdd, 0x48, 0xf5, +0xdb, 0x12, 0xb4, 0xf2, 0x39, 0xc5, 0x3e, 0x7e, 0x4f, 0x60, 0x16, 0x05, 0x41, 0x67, 0x05, 0xf0, +0x47, 0x9c, 0xda, 0x09, 0xd6, 0x41, 0x62, 0x19, 0x6d, 0xea, 0x77, 0x62, 0x63, 0x39, 0x3b, 0x14, +0x5f, 0xed, 0xa6, 0x86, 0x75, 0x7c, 0xc3, 0x69, 0x7a, 0x91, 0xec, 0xa7, 0xfa, 0x16, 0xf9, 0xcc, +0x77, 0x38, 0xb3, 0xa4, 0x89, 0x97, 0x1a, 0x38, 0xe2, 0xd5, 0x2a, 0x40, 0x2b, 0xbb, 0xe9, 0x43, +0xbe, 0x89, 0x22, 0x45, 0x52, 0xce, 0x52, 0x55, 0x91, 0x76, 0xdc, 0x9c, 0xa2, 0x88, 0xf5, 0x24, +0x90, 0xed, 0x63, 0x76, 0x10, 0xc6, 0x72, 0xd6, 0xfa, 0xf8, 0xf5, 0x76, 0xa6, 0x04, 0xc1, 0x2a, +0x80, 0x5a, 0x6c, 0xea, 0x4d, 0x2c, 0x75, 0x14, 0x33, 0x96, 0x6e, 0xe6, 0x89, 0x44, 0x22, 0xeb, +0x38, 0xa2, 0xf2, 0x2e, 0x27, 0x87, 0xf8, 0x96, 0x4a, 0x72, 0xe9, 0xe2, 0xb5, 0x1d, 0xd8, 0xc5, +0x37, 0x5e, 0x17, 0x83, 0xb2, 0x78, 0xc4, 0x82, 0x7b, 0xa2, 0x0c, 0x40, 0x8b, 0x8d, 0xad, 0x8c, +0x1a, 0x2e, 0x33, 0x31, 0x4c, 0x7b, 0x17, 0xec, 0x14, 0x69, 0xd8, 0xa7, 0xdb, 0xc0, 0x3a, 0x36, +0xd1, 0x8d, 0x6c, 0x8f, 0x5f, 0xb2, 0x8d, 0x4a, 0x3e, 0xf6, 0xba, 0x42, 0x3e, 0x95, 0x1f, 0x4a, +0x16, 0x78, 0x3e, 0x30, 0x9c, 0xd0, 0x66, 0x64, 0xdd, 0x4f, 0x3c, 0x85, 0xa6, 0x99, 0x86, 0x6d, +0xe4, 0x50, 0xc6, 0x16, 0x0d, 0xc1, 0x9f, 0x5c, 0x1c, 0x66, 0x25, 0x91, 0xf4, 0xf7, 0x62, 0xd0, +0xf9, 0xcc, 0xcb, 0x82, 0x50, 0x06, 0x73, 0x18, 0x62, 0x52, 0x69, 0x64, 0xc6, 0xc5, 0xbf, 0xb5, +0x55, 0x62, 0x2b, 0x7b, 0x18, 0x44, 0x5f, 0xd3, 0x04, 0x8b, 0xa1, 0x3f, 0x7b, 0x39, 0xa0, 0xc5, +0xc9, 0x91, 0x6a, 0x56, 0xd1, 0x4c, 0xbe, 0x07, 0x6f, 0xc6, 0x30, 0x36, 0x78, 0xdc, 0xc2, 0x6d, +0xe7, 0xfb, 0xcc, 0xb8, 0x60, 0x6c, 0x80, 0x6d, 0x39, 0xca, 0x1f, 0xb5, 0x7e, 0xc5, 0xb5, 0x1c, +0x62, 0xcc, 0xf9, 0xf3, 0x3e, 0x8c, 0x27, 0x95, 0x3c, 0x76, 0x68, 0x0c, 0x60, 0xd7, 0xc4, 0x46, +0x2a, 0x29, 0xe8, 0xe4, 0xde, 0x19, 0x45, 0x05, 0x5f, 0x7b, 0xf0, 0x09, 0xc8, 0xe7, 0x77, 0xaa, +0xfb, 0x7f, 0x37, 0x4e, 0x1c, 0xb4, 0x9c, 0x4f, 0x4e, 0x6c, 0x96, 0xf3, 0x9e, 0x74, 0xf0, 0x7f, +0xcc, 0xd7, 0x3e, 0xef, 0xb5, 0x31, 0x83, 0x67, 0x4d, 0x5c, 0x3b, 0x73, 0x53, 0xc4, 0xa3, 0xac, +0xd2, 0x18, 0x05, 0x30, 0x8a, 0xe9, 0x3c, 0xd1, 0xc9, 0x68, 0xb5, 0x9f, 0x3b, 0x58, 0xdd, 0xe1, +0x6f, 0xa9, 0xfc, 0x37, 0x33, 0x15, 0x4d, 0x6c, 0x5b, 0x71, 0x50, 0xcf, 0x09, 0x4e, 0x70, 0x8a, +0x0a, 0xaa, 0x38, 0xd5, 0x66, 0x79, 0x29, 0x8e, 0x74, 0xd2, 0xe9, 0x4a, 0x26, 0xd9, 0x24, 0x58, +0xc2, 0x9c, 0xd4, 0xca, 0xbf, 0x98, 0x43, 0x99, 0x9e, 0x2c, 0x92, 0x79, 0x89, 0x16, 0x13, 0x2d, +0xd5, 0x2e, 0xd6, 0x72, 0x9d, 0xe6, 0xa9, 0x59, 0x14, 0x3f, 0xa7, 0xb2, 0x43, 0x8e, 0x2d, 0xcc, +0xeb, 0xe0, 0xf3, 0x1b, 0xcb, 0x43, 0x9d, 0xae, 0xea, 0x98, 0x5a, 0xa9, 0x65, 0x17, 0x9f, 0x31, +0x97, 0x1f, 0x32, 0x81, 0x8b, 0xe8, 0xee, 0xa5, 0xbb, 0x08, 0x23, 0x85, 0x7e, 0x5c, 0xcd, 0x6f, +0xf9, 0x9c, 0xe3, 0x38, 0x85, 0x5b, 0xfa, 0x7d, 0xa5, 0x3a, 0x5e, 0x35, 0xe2, 0xcc, 0xf5, 0x3e, +0x2c, 0x32, 0x59, 0xd0, 0xdd, 0xfc, 0x48, 0xf3, 0xde, 0x84, 0x04, 0xfe, 0xd4, 0x69, 0xbb, 0x66, +0x19, 0x77, 0xb5, 0xb1, 0x15, 0x24, 0x71, 0x1f, 0x87, 0x7c, 0x3c, 0xdd, 0x49, 0x25, 0x6b, 0x99, +0xc7, 0x6d, 0x0c, 0x27, 0x45, 0xf1, 0xa0, 0xd7, 0x4e, 0x3a, 0x37, 0x30, 0xdf, 0xaf, 0x62, 0x89, +0x4b, 0xa5, 0x3c, 0x69, 0xd4, 0x29, 0x11, 0xe3, 0x55, 0x47, 0xe8, 0x52, 0x9b, 0xca, 0x78, 0x8a, +0x5e, 0x1a, 0x4d, 0xb4, 0x79, 0x2c, 0xed, 0x94, 0x5f, 0x31, 0xf7, 0xd0, 0x83, 0x08, 0x12, 0x28, +0xe0, 0x19, 0x1f, 0x7b, 0x81, 0x9b, 0xd8, 0xc9, 0x2b, 0xdc, 0x46, 0x5f, 0xa2, 0x35, 0x3d, 0x3d, +0x8d, 0x07, 0xa5, 0xdc, 0x65, 0xed, 0x64, 0x3b, 0x77, 0x19, 0xb7, 0xe1, 0xc7, 0xce, 0x2d, 0x1c, +0x34, 0x59, 0xe4, 0x06, 0x3e, 0x60, 0x82, 0x46, 0x67, 0xcd, 0xef, 0x51, 0xd1, 0x29, 0xbf, 0x53, +0x7c, 0xca, 0x73, 0xbc, 0xc6, 0x56, 0x1c, 0x5e, 0x2b, 0x7f, 0x13, 0x4f, 0x30, 0x92, 0x78, 0x5d, +0x6b, 0x03, 0x91, 0xcc, 0x90, 0xae, 0x15, 0xa8, 0xe7, 0x23, 0xc6, 0x1b, 0x6b, 0xf1, 0x8c, 0xe2, +0xc7, 0x1c, 0x37, 0x59, 0x6c, 0x17, 0xbb, 0x79, 0x84, 0x2c, 0x0d, 0xd5, 0x91, 0xac, 0x3a, 0xf6, +0x9f, 0x93, 0x5d, 0x3c, 0xc1, 0x60, 0x43, 0x06, 0x72, 0x29, 0x2c, 0x14, 0x5e, 0xe5, 0x6d, 0xd3, +0x21, 0x9e, 0x34, 0xa2, 0xe7, 0xef, 0x48, 0x3c, 0xbf, 0x50, 0x1d, 0x56, 0x41, 0x8b, 0xee, 0x7e, +0xce, 0xcd, 0x24, 0xab, 0x96, 0x6e, 0x96, 0xaa, 0xa8, 0x3f, 0xb5, 0xbc, 0xa3, 0xb9, 0xb5, 0xe9, +0x8c, 0x9d, 0x87, 0xa4, 0xd9, 0x66, 0xef, 0x60, 0x39, 0xb7, 0x9a, 0xb5, 0x91, 0x35, 0x81, 0xd9, +0x01, 0x50, 0x01, 0x37, 0xa5, 0xbc, 0xce, 0xe5, 0x2a, 0xfb, 0xaf, 0x89, 0x9c, 0x52, 0x9c, 0x7f, +0x19, 0x8f, 0x93, 0x61, 0xe0, 0x7b, 0xb1, 0x33, 0x47, 0x92, 0xd9, 0x40, 0x25, 0xaf, 0x30, 0xcc, +0x4c, 0xb7, 0xde, 0x04, 0x7e, 0xc1, 0x89, 0x00, 0x14, 0xa4, 0x95, 0x7d, 0xfc, 0x99, 0xcb, 0x55, +0xb4, 0x04, 0x97, 0x2a, 0x8e, 0xfa, 0x53, 0xc6, 0x6c, 0x83, 0x8f, 0xa2, 0x4e, 0x66, 0x81, 0xf0, +0xaa, 0x77, 0xe3, 0x64, 0x07, 0x3f, 0x35, 0xff, 0x6c, 0xb8, 0x04, 0x1e, 0xe2, 0x58, 0x40, 0x0a, +0xd4, 0xc2, 0x21, 0xfe, 0xce, 0xcd, 0x64, 0x2a, 0xd2, 0xe8, 0x3b, 0xa8, 0x53, 0x94, 0x6b, 0x3d, +0x4f, 0x1a, 0x5c, 0xfd, 0x36, 0xa6, 0x51, 0x2e, 0xbc, 0xfa, 0x1b, 0xf8, 0x84, 0x2b, 0x02, 0xb3, +0xdf, 0x29, 0x9e, 0x19, 0xec, 0xd6, 0x19, 0x72, 0x5d, 0xb9, 0x56, 0x97, 0xb3, 0x98, 0x5f, 0x31, +0x96, 0x54, 0x9f, 0x26, 0xd8, 0x2c, 0xc5, 0x8e, 0x9e, 0x4b, 0x0c, 0xf7, 0x79, 0xbe, 0x98, 0xf5, +0xc2, 0xab, 0xff, 0x28, 0x7f, 0xa4, 0x4f, 0xe0, 0xfc, 0x9c, 0xa2, 0xb8, 0x91, 0xd5, 0x01, 0xec, +0xf5, 0x9a, 0xd8, 0xcf, 0xfb, 0x3c, 0xca, 0xd5, 0xe4, 0x92, 0xd4, 0x61, 0x72, 0x63, 0x23, 0x8e, +0x8b, 0x79, 0x5b, 0x61, 0xb8, 0x46, 0x07, 0x8f, 0x1a, 0x3a, 0x39, 0xb2, 0x33, 0x8e, 0xe5, 0x01, +0xfa, 0x18, 0xbc, 0xa5, 0x16, 0x56, 0xf3, 0x5f, 0xfe, 0x1c, 0xf8, 0x8c, 0xd6, 0x8d, 0x70, 0x46, +0xf1, 0x4b, 0xae, 0x0d, 0xe8, 0xda, 0x5a, 0x33, 0x27, 0x29, 0x63, 0x0f, 0xfb, 0x38, 0x4a, 0x15, +0x95, 0x38, 0x08, 0xa7, 0x0b, 0x19, 0x0c, 0x67, 0x3c, 0x7d, 0x14, 0x0e, 0x7c, 0x6a, 0xb8, 0x8f, +0x77, 0x31, 0x2a, 0x1c, 0x63, 0x02, 0x53, 0xf8, 0x05, 0x43, 0x85, 0x7a, 0x18, 0xd6, 0xf0, 0x11, +0x2f, 0xb2, 0x39, 0xf0, 0x11, 0xcc, 0x6d, 0xf4, 0xe1, 0xd9, 0x80, 0xcc, 0x09, 0xda, 0x27, 0x17, +0x2d, 0x34, 0x70, 0x8a, 0x12, 0xf6, 0x71, 0x80, 0x72, 0x1a, 0x55, 0xb5, 0x44, 0x8d, 0xcc, 0x36, +0x68, 0x8c, 0x1c, 0xc1, 0x70, 0x5e, 0x12, 0x50, 0xfe, 0xf6, 0xef, 0x62, 0x3f, 0x0f, 0xd1, 0x3d, +0xd0, 0x55, 0xff, 0x1d, 0x49, 0x4c, 0x67, 0xb5, 0xa9, 0x0b, 0x45, 0xc6, 0xa7, 0x2f, 0x0c, 0xd8, +0x16, 0x1a, 0xc1, 0x10, 0x9e, 0x64, 0xab, 0xe0, 0x92, 0x3b, 0xf8, 0x8a, 0x6b, 0x44, 0xfb, 0x37, +0x84, 0x33, 0x94, 0x17, 0x4c, 0xb7, 0x10, 0x1a, 0x99, 0xea, 0x99, 0xab, 0xcb, 0xbf, 0x21, 0x99, +0x09, 0xfc, 0x8f, 0x0f, 0xf3, 0x72, 0xa0, 0xd2, 0x49, 0x9e, 0xf5, 0x72, 0xcc, 0x76, 0xc0, 0x49, +0x60, 0x0a, 0x9f, 0x05, 0x68, 0x3b, 0x99, 0x11, 0xe9, 0x34, 0xff, 0x60, 0x8c, 0xea, 0x2f, 0xc7, +0x4e, 0x0a, 0x05, 0xfc, 0x84, 0x85, 0x1c, 0x15, 0xde, 0xe6, 0xb9, 0xd8, 0xcc, 0x9d, 0xea, 0xf6, +0x6d, 0x98, 0xab, 0x29, 0x36, 0x7a, 0x72, 0x03, 0xb7, 0x31, 0xca, 0x22, 0xd1, 0x05, 0x9c, 0x1c, +0xe4, 0x73, 0x3e, 0x67, 0x33, 0x55, 0x7e, 0xe2, 0x6c, 0xd9, 0x88, 0x20, 0x96, 0x6e, 0x0c, 0x64, +0x08, 0x05, 0x0c, 0xa6, 0xbb, 0x04, 0x2e, 0xa5, 0x8d, 0x7c, 0xcc, 0x73, 0x6c, 0xf4, 0xba, 0xad, +0xce, 0x4b, 0x41, 0xcc, 0x26, 0x8c, 0x4c, 0xae, 0xe5, 0x7b, 0x14, 0xea, 0x5c, 0x5b, 0x0b, 0x14, +0x2e, 0x4e, 0xb2, 0x8f, 0x0d, 0x6c, 0xe5, 0x30, 0x27, 0xa8, 0xa0, 0x16, 0x17, 0x4e, 0xdc, 0xd8, +0xb1, 0x63, 0x23, 0x89, 0x64, 0x52, 0xe8, 0x4a, 0x26, 0x79, 0xf4, 0x27, 0x9b, 0xae, 0x24, 0x09, +0x88, 0x9b, 0xe0, 0x89, 0x52, 0x5e, 0xe6, 0x55, 0xbf, 0x61, 0xf0, 0x3a, 0x11, 0x98, 0x2a, 0xb1, +0xd3, 0x95, 0x31, 0x5c, 0xcf, 0x25, 0xe4, 0x59, 0xc2, 0x5d, 0xea, 0x4c, 0x68, 0x8a, 0x2a, 0xaa, +0xa9, 0xa3, 0x01, 0x17, 0x95, 0x34, 0x10, 0x4f, 0x32, 0x76, 0x62, 0x88, 0x23, 0x9e, 0x24, 0x62, +0x25, 0xa9, 0xf6, 0x33, 0xb8, 0x58, 0xcb, 0xef, 0x59, 0xa2, 0x25, 0x64, 0x6c, 0x20, 0xbf, 0xc9, +0x68, 0x72, 0x18, 0xc9, 0x78, 0x0a, 0xc8, 0x24, 0xd5, 0xf4, 0x5d, 0x38, 0xad, 0xd4, 0xd2, 0x80, +0x9d, 0x64, 0x8b, 0x74, 0x3f, 0xda, 0x39, 0xcd, 0xbf, 0x78, 0x9e, 0x3d, 0xda, 0x6e, 0x0e, 0x74, +0xa3, 0x6c, 0x23, 0x82, 0x34, 0xf2, 0xc8, 0xa6, 0x0f, 0x39, 0x64, 0x90, 0x46, 0x04, 0x11, 0x44, +0xd1, 0x7a, 0xd6, 0x62, 0xd7, 0x82, 0x9b, 0x74, 0x52, 0x89, 0xd3, 0x21, 0x59, 0x13, 0xfb, 0x59, +0xc9, 0x5a, 0x4a, 0x38, 0x8d, 0x9d, 0x0c, 0x6e, 0x60, 0x8a, 0x86, 0x65, 0x64, 0x6b, 0xe0, 0x66, +0x0f, 0x7f, 0xe2, 0x1d, 0x4e, 0x6b, 0xcd, 0x40, 0x5c, 0xaf, 0x1c, 0x46, 0x24, 0x91, 0x84, 0x63, +0x27, 0x86, 0x44, 0x1a, 0x71, 0x90, 0xca, 0x20, 0x46, 0x33, 0x94, 0x7c, 0x92, 0x35, 0x37, 0xb0, +0x75, 0xac, 0xe6, 0x03, 0xbe, 0xe6, 0x70, 0x1b, 0x8f, 0xdd, 0x78, 0x1e, 0xe0, 0x71, 0x21, 0x21, +0x9d, 0xcd, 0xa6, 0x99, 0xc5, 0x3c, 0xcd, 0x6a, 0x75, 0xc3, 0xbe, 0xf6, 0xc8, 0x30, 0x2c, 0x8b, +0x24, 0x93, 0x51, 0x5c, 0xca, 0x18, 0x72, 0x75, 0x9d, 0x88, 0xd1, 0xc4, 0x4a, 0xde, 0x64, 0x11, +0xe5, 0x74, 0x34, 0xe9, 0xa6, 0xf1, 0x16, 0xd7, 0x78, 0xbc, 0xe3, 0x20, 0x27, 0x48, 0x25, 0xc7, +0x22, 0x03, 0xd4, 0xf6, 0x94, 0xf3, 0x2a, 0x2f, 0x2b, 0xdc, 0x4a, 0x2f, 0x29, 0x36, 0xba, 0x31, +0x85, 0x57, 0xd8, 0xa9, 0x70, 0xc9, 0xd6, 0xd7, 0x0c, 0x78, 0x0f, 0x0f, 0x7b, 0x8d, 0xf2, 0x67, +0xe3, 0x11, 0x0f, 0xf7, 0x14, 0xf3, 0x73, 0xfa, 0xd1, 0x95, 0x6c, 0xae, 0xe3, 0x55, 0x8e, 0x08, +0x5e, 0xba, 0x51, 0x97, 0x9c, 0xac, 0x63, 0x9a, 0x68, 0x6b, 0x9f, 0x3e, 0xec, 0xe4, 0xf0, 0x00, +0x5f, 0x1b, 0x72, 0x38, 0x4d, 0x2d, 0x6f, 0x33, 0xd6, 0x67, 0xb7, 0x71, 0x4b, 0xa7, 0xea, 0x3d, +0xc1, 0x9d, 0x6d, 0xee, 0x88, 0xe5, 0x2a, 0x3e, 0xb6, 0xcc, 0x19, 0xea, 0xb5, 0xbc, 0xc1, 0x60, +0x0b, 0xb6, 0x59, 0x6d, 0xe8, 0xc1, 0x7d, 0xac, 0xd0, 0x78, 0xb2, 0x5e, 0xc7, 0x6f, 0xbf, 0x84, +0x39, 0x7e, 0x8d, 0xb8, 0xff, 0xd5, 0x41, 0x01, 0x5c, 0xcc, 0xef, 0x34, 0x30, 0xec, 0xc9, 0xd3, +0x1e, 0x7c, 0x88, 0x65, 0x4b, 0x2e, 0xf6, 0x31, 0xdb, 0xda, 0xb1, 0x8c, 0x62, 0xb8, 0x8e, 0x8f, +0x15, 0x1f, 0xf4, 0xe8, 0x3b, 0x39, 0x59, 0xc3, 0x4d, 0x7e, 0xfd, 0x5d, 0xec, 0x3c, 0xda, 0xe1, +0xbe, 0x1a, 0x66, 0x7a, 0xb8, 0x2e, 0x81, 0x9f, 0x71, 0x54, 0x78, 0x15, 0xfb, 0x4a, 0x8d, 0x7c, +0xc1, 0x95, 0x16, 0xb1, 0xa5, 0x78, 0x21, 0x8b, 0xa7, 0x38, 0x62, 0xd0, 0xeb, 0x70, 0xf0, 0x29, +0xa3, 0x15, 0x0c, 0x1b, 0x63, 0x78, 0xbb, 0xc3, 0x9d, 0xfb, 0xb9, 0xd8, 0xe3, 0x95, 0x51, 0xdc, +0x2b, 0xb1, 0x0a, 0x9c, 0xe0, 0x4f, 0x81, 0xf4, 0xef, 0x31, 0x9e, 0x30, 0xc6, 0xf2, 0xb1, 0x61, +0xeb, 0x65, 0x0d, 0xfc, 0xc3, 0xef, 0x29, 0x60, 0x67, 0xe8, 0xc3, 0xf6, 0x0e, 0xf7, 0x6e, 0x26, +0xc7, 0xcb, 0xb5, 0x51, 0x3c, 0x28, 0x81, 0x27, 0x5f, 0xe7, 0xd4, 0xca, 0x66, 0xee, 0xb6, 0x76, +0x20, 0xdb, 0x68, 0xa6, 0x19, 0xb8, 0x91, 0xac, 0x81, 0xd7, 0x15, 0x6f, 0x72, 0xf8, 0x41, 0xa7, +0xe1, 0xdd, 0x06, 0x1f, 0xee, 0xdf, 0xf1, 0xfc, 0x56, 0xba, 0x35, 0xcc, 0x1a, 0xde, 0x61, 0xac, +0xb5, 0x63, 0x18, 0x25, 0xf0, 0xa0, 0xcf, 0x8d, 0x98, 0x6a, 0xab, 0xff, 0x35, 0xc5, 0x0e, 0x1c, +0x9e, 0x9c, 0xb3, 0x37, 0xf8, 0x8c, 0x77, 0x98, 0xc6, 0xdf, 0xa4, 0xd9, 0xd0, 0xe1, 0xa6, 0x85, +0xad, 0xcc, 0xb6, 0xfa, 0xd1, 0x3d, 0x49, 0xfc, 0x46, 0xc3, 0xa9, 0xbc, 0xbe, 0xbe, 0x7e, 0xe5, +0xfe, 0x3b, 0x93, 0x3c, 0xec, 0x58, 0xd8, 0xe8, 0xe7, 0x48, 0xbc, 0xfe, 0x1e, 0xb6, 0x95, 0x8a, +0x48, 0x2e, 0x8e, 0xf3, 0x1a, 0x63, 0xad, 0x3d, 0xec, 0x83, 0x44, 0x7e, 0x63, 0xe0, 0x04, 0xab, +0x85, 0x7f, 0xaa, 0xa8, 0xfe, 0x44, 0xde, 0xf4, 0x90, 0xc7, 0x4e, 0xbf, 0xa1, 0xee, 0xae, 0x97, +0x60, 0x30, 0x58, 0xc5, 0x27, 0xdc, 0x4a, 0x8a, 0xe8, 0xea, 0xd3, 0x4b, 0x2c, 0x0f, 0xa9, 0xd8, +0x9e, 0xe5, 0x2f, 0x39, 0x59, 0xc4, 0x40, 0x15, 0x4f, 0xbf, 0xd9, 0xe3, 0xb3, 0x0f, 0xfb, 0x0d, +0x7e, 0x1f, 0xc5, 0x1f, 0x85, 0xba, 0x77, 0x55, 0xf1, 0x39, 0x77, 0x93, 0x69, 0xe5, 0x31, 0xff, +0xb9, 0x17, 0x39, 0xc3, 0xb0, 0x69, 0x9f, 0x1b, 0x37, 0x1b, 0x54, 0x1d, 0x69, 0x9b, 0xc5, 0x57, +0x1e, 0x73, 0xa9, 0xe1, 0x47, 0x7e, 0xef, 0xcd, 0x61, 0x89, 0x2a, 0xc9, 0x1c, 0x06, 0xb9, 0x84, +0x39, 0x29, 0xe7, 0x53, 0x7e, 0x44, 0x4f, 0xcb, 0x45, 0x1d, 0xf2, 0x80, 0x9d, 0xc9, 0xec, 0x31, +0xb0, 0xfa, 0x8f, 0x32, 0x4d, 0xc5, 0x6b, 0x89, 0xe6, 0x09, 0x2f, 0xd6, 0xc6, 0x56, 0x9e, 0x52, +0xb0, 0xe2, 0x38, 0x45, 0xc5, 0x84, 0xd0, 0xc5, 0x71, 0x56, 0xb3, 0x89, 0x2a, 0x5d, 0x6b, 0x0a, +0x4d, 0xec, 0xe1, 0x75, 0xbe, 0x47, 0x86, 0xf5, 0xbf, 0xfc, 0x33, 0x0c, 0x37, 0x74, 0x7f, 0x4c, +0x03, 0xbf, 0x55, 0xe5, 0x7b, 0x77, 0x3d, 0x87, 0xbd, 0xe6, 0xb5, 0x40, 0xc1, 0xe9, 0x20, 0x09, +0xfc, 0x4d, 0x85, 0xf4, 0xad, 0x14, 0xf3, 0x32, 0xbf, 0xe4, 0xef, 0x6c, 0xa3, 0x4e, 0x75, 0xa9, +0x9b, 0x39, 0x76, 0x76, 0xb3, 0x5b, 0x52, 0xb0, 0x54, 0x3e, 0xf4, 0xe0, 0x2d, 0x43, 0x3d, 0x65, +0xff, 0xad, 0x6a, 0xf3, 0x76, 0x7f, 0xbe, 0xf6, 0x91, 0xd7, 0x76, 0x06, 0x29, 0xc8, 0x63, 0x2c, +0x07, 0x54, 0x49, 0x58, 0xc9, 0xdb, 0x5c, 0xcd, 0x28, 0x66, 0xf0, 0x26, 0x5b, 0x14, 0xb5, 0x06, +0x4e, 0xaa, 0xd9, 0xc9, 0x87, 0x3c, 0xca, 0xd5, 0xf4, 0x0c, 0xfc, 0x68, 0xdf, 0x4c, 0xd3, 0x42, +0x14, 0xd3, 0xb9, 0xc9, 0xc0, 0x27, 0x1c, 0xe2, 0x79, 0x3f, 0x47, 0x3e, 0xb4, 0x25, 0x8d, 0xd9, +0x8c, 0xf3, 0xf1, 0x7b, 0x06, 0x43, 0x3d, 0x86, 0x8d, 0x6c, 0xcf, 0x06, 0x3e, 0x64, 0xb6, 0x8a, +0x2f, 0x32, 0x85, 0x5b, 0xc9, 0xe7, 0x45, 0x16, 0xf0, 0x1e, 0xd9, 0x0c, 0x64, 0x10, 0xf9, 0x64, +0x91, 0x46, 0x2c, 0x31, 0x44, 0x12, 0x89, 0x0d, 0x70, 0xe2, 0xc0, 0x41, 0x23, 0xf5, 0x9c, 0xe2, +0x30, 0x3b, 0xd9, 0xc3, 0x3e, 0x0e, 0x51, 0xab, 0x2a, 0x40, 0xb6, 0x61, 0x98, 0xa9, 0x00, 0xe3, +0x98, 0x61, 0xe0, 0xd9, 0x02, 0x2d, 0xbc, 0xc9, 0x4a, 0xc5, 0x57, 0xc7, 0x72, 0x0f, 0xb7, 0xf9, +0xfc, 0x9e, 0x92, 0x18, 0xc9, 0x42, 0xbf, 0x6e, 0x94, 0xcd, 0xbc, 0xc3, 0x8d, 0xaa, 0xc2, 0x48, +0x86, 0x31, 0x98, 0xa7, 0x19, 0xc9, 0xff, 0xb2, 0x83, 0xed, 0x7c, 0x44, 0x3c, 0x09, 0x64, 0x90, +0x4e, 0xf2, 0xf9, 0xd3, 0x86, 0x1d, 0xd4, 0x52, 0x47, 0x05, 0xa7, 0x38, 0x45, 0x3d, 0x75, 0x62, +0x2a, 0xde, 0x7c, 0xb2, 0xf8, 0xc0, 0x50, 0x17, 0x8b, 0xd5, 0xf4, 0x51, 0xfc, 0xec, 0x08, 0x66, +0x52, 0xaa, 0x20, 0x47, 0x25, 0xc1, 0x6e, 0xa3, 0x78, 0x5e, 0x83, 0xb4, 0x2d, 0x7c, 0xcb, 0x35, +0x52, 0x79, 0x0e, 0x07, 0x98, 0x48, 0x1e, 0x36, 0xc4, 0xd5, 0xe3, 0x5c, 0x6a, 0x54, 0xd1, 0x10, +0x87, 0x33, 0x8d, 0x62, 0x05, 0x79, 0x56, 0x71, 0x87, 0xa2, 0xfc, 0xc6, 0x6b, 0x32, 0x09, 0xb9, +0xd8, 0xc5, 0xf4, 0xa0, 0x3a, 0x88, 0x56, 0x15, 0x85, 0x06, 0xc7, 0x0f, 0x5c, 0xa5, 0x38, 0x7c, +0x83, 0x8d, 0xeb, 0xd8, 0xa5, 0x30, 0xd7, 0x37, 0x14, 0xad, 0xae, 0x25, 0xf2, 0x8e, 0x46, 0xa9, +0x0f, 0x73, 0xb7, 0x29, 0x07, 0xdc, 0x49, 0x4f, 0x02, 0x7f, 0xee, 0x14, 0xa3, 0x53, 0x4f, 0x6a, +0xe5, 0xd7, 0x0a, 0x67, 0xff, 0x11, 0x4c, 0x66, 0x83, 0xe2, 0x7c, 0xf7, 0x71, 0xa9, 0xa2, 0x5c, +0x7f, 0xa0, 0xd9, 0x67, 0xf1, 0x08, 0x33, 0x2f, 0xc4, 0x56, 0xe0, 0x2a, 0x83, 0x83, 0x47, 0x1e, +0x66, 0xa4, 0xa2, 0xe7, 0x46, 0x30, 0x8d, 0xdd, 0x2a, 0xf2, 0x6d, 0xe6, 0x8f, 0x8a, 0x1c, 0x2b, +0x7b, 0xb3, 0x56, 0xb3, 0xec, 0x47, 0xb8, 0xcb, 0xda, 0xcb, 0xb8, 0xea, 0xe9, 0xc2, 0x3f, 0x0c, +0xf6, 0xb0, 0xfd, 0x44, 0xd1, 0x82, 0x48, 0x1c, 0xf7, 0x28, 0xea, 0xfb, 0xdb, 0xa6, 0xcd, 0x14, +0x28, 0xc8, 0x39, 0x8a, 0x67, 0x74, 0x04, 0xbe, 0xd9, 0xcb, 0x8d, 0xc1, 0x63, 0xda, 0x51, 0xc2, +0x64, 0x05, 0x23, 0x70, 0x75, 0xe9, 0xb7, 0x0a, 0x3a, 0x80, 0x54, 0x1e, 0xd3, 0x10, 0x97, 0xa3, +0x89, 0xa7, 0x14, 0xb5, 0x01, 0x93, 0x75, 0x05, 0x7d, 0x5d, 0xcd, 0x70, 0xd1, 0x95, 0x12, 0x38, +0x12, 0xf8, 0xab, 0xc1, 0xd5, 0xef, 0xf0, 0x3b, 0x5a, 0xb7, 0x31, 0x80, 0x57, 0x34, 0xce, 0x3a, +0x94, 0xb5, 0x01, 0x7d, 0x54, 0x8c, 0x2c, 0x3a, 0xa7, 0x56, 0xde, 0xbc, 0x70, 0x8e, 0xa4, 0x9b, +0x60, 0x78, 0xf0, 0xe8, 0x3a, 0x6e, 0xf5, 0xf9, 0xc4, 0x68, 0x26, 0xb3, 0x4c, 0xb3, 0xc9, 0xb9, +0x99, 0xa7, 0x15, 0xac, 0x2f, 0xc4, 0xf1, 0xaa, 0xae, 0x32, 0xd4, 0x32, 0x5b, 0x4e, 0xa7, 0x0e, +0xa3, 0x17, 0x1c, 0x63, 0xb8, 0x81, 0x5e, 0x06, 0xe7, 0x19, 0xe6, 0xc3, 0xa0, 0x62, 0xa3, 0x37, +0x8f, 0xf1, 0x17, 0xc6, 0x69, 0x1e, 0x68, 0x45, 0x30, 0x4d, 0xc1, 0x02, 0x73, 0x3d, 0xdb, 0x74, +0x59, 0xec, 0x12, 0xb8, 0x97, 0x42, 0x83, 0xdf, 0x8b, 0x94, 0x0c, 0xd2, 0xd5, 0x54, 0x7a, 0x4b, +0x8f, 0x79, 0x51, 0xd4, 0x54, 0xee, 0xa0, 0xc8, 0x80, 0xe5, 0xa6, 0xf9, 0x0a, 0xa2, 0x03, 0x5d, +0xa5, 0x33, 0xf2, 0x97, 0x93, 0xbf, 0x2b, 0x58, 0x7f, 0xb4, 0x38, 0x61, 0xdc, 0xaf, 0x7b, 0x97, +0x9f, 0xa7, 0xb4, 0x86, 0x01, 0x1d, 0x9e, 0x64, 0xa7, 0x2b, 0x37, 0xf3, 0x3e, 0x55, 0x86, 0xe4, +0x5f, 0xc9, 0x0c, 0xbf, 0x2d, 0x48, 0x7f, 0xf6, 0xea, 0x7c, 0xca, 0x09, 0x6e, 0x0f, 0xf6, 0xd9, +0x40, 0x9a, 0x49, 0xf1, 0xf1, 0x5b, 0x59, 0xc0, 0x70, 0xa2, 0x09, 0xc3, 0x4e, 0x14, 0xc9, 0x8c, +0x62, 0x36, 0x5f, 0x19, 0x7a, 0x18, 0xc3, 0x3a, 0xbf, 0x96, 0x86, 0x1e, 0x14, 0xe9, 0x7e, 0xca, +0x67, 0x7e, 0x5c, 0x51, 0x05, 0x60, 0xac, 0x89, 0x62, 0xa0, 0x42, 0x83, 0x8d, 0x5a, 0xc2, 0xb8, +0x99, 0xa1, 0xac, 0x62, 0x2f, 0x6e, 0xb2, 0xc8, 0xa3, 0x1f, 0x3d, 0x0c, 0x96, 0xbc, 0x80, 0x9f, +0xf1, 0x4b, 0x9f, 0x67, 0x68, 0x35, 0xa9, 0x58, 0x8a, 0xf6, 0xc6, 0x58, 0xae, 0xe2, 0x0d, 0x8c, +0x8a, 0x47, 0x6a, 0x08, 0x46, 0xbe, 0xc6, 0x70, 0x0a, 0x4d, 0xf3, 0x5e, 0xb7, 0xd3, 0xd7, 0xc4, +0x83, 0x2a, 0xc1, 0xce, 0x14, 0x76, 0xf3, 0x3c, 0x0d, 0x5e, 0xaf, 0x70, 0xf9, 0x89, 0x1b, 0xa6, +0x84, 0x14, 0xa6, 0xb2, 0x88, 0xe3, 0x26, 0x96, 0x43, 0x43, 0xc1, 0x8d, 0x23, 0x91, 0xb1, 0x16, +0x5e, 0xfa, 0x88, 0xe7, 0x01, 0xa6, 0xfa, 0xfc, 0x20, 0x8c, 0xe8, 0xbf, 0xc7, 0x78, 0xd9, 0x93, +0x28, 0x0c, 0x23, 0x15, 0xa0, 0x1f, 0x43, 0x45, 0x17, 0x47, 0x17, 0x19, 0x3c, 0xca, 0x44, 0x93, +0x87, 0x69, 0x69, 0x4c, 0x32, 0xd0, 0x49, 0xc6, 0x00, 0x8c, 0x53, 0x00, 0x3b, 0xa3, 0x0c, 0xb7, +0x00, 0x04, 0x9a, 0x7c, 0x9e, 0x60, 0x8c, 0x17, 0x15, 0x88, 0x30, 0xe4, 0x38, 0x09, 0x1b, 0xe3, +0xe8, 0x27, 0xba, 0x98, 0x6d, 0x31, 0x4e, 0x01, 0x92, 0x98, 0x60, 0xe1, 0x0e, 0xe0, 0x1c, 0x17, +0x33, 0x97, 0x4b, 0x3c, 0x1a, 0x9e, 0x92, 0xbd, 0xee, 0x27, 0x56, 0x47, 0x0e, 0xe3, 0x65, 0xf2, +0xf7, 0x37, 0x4e, 0x94, 0x7e, 0x0c, 0x13, 0x5d, 0x18, 0x03, 0xb0, 0x71, 0x29, 0x2f, 0x30, 0xd9, +0xc3, 0x02, 0xd1, 0x08, 0x83, 0xa6, 0x70, 0x31, 0x4c, 0x30, 0xf1, 0xd0, 0x6d, 0xd5, 0x18, 0xa5, +0x00, 0x76, 0x0a, 0xa5, 0xed, 0x00, 0x5c, 0x6d, 0x42, 0xc6, 0xf9, 0xc7, 0xc6, 0x08, 0x9e, 0x63, +0x16, 0xdd, 0xdb, 0x75, 0x05, 0x5d, 0x99, 0x6a, 0x58, 0xac, 0xc1, 0x41, 0x06, 0xb5, 0x25, 0x86, +0x60, 0x94, 0xdb, 0x62, 0x32, 0xb3, 0x18, 0x22, 0xba, 0x30, 0x5e, 0x70, 0x70, 0x58, 0x65, 0x4c, +0x9d, 0x14, 0xc6, 0xd0, 0x17, 0x07, 0x95, 0x38, 0x38, 0x13, 0xcb, 0xec, 0x3e, 0xa6, 0x1b, 0x16, +0x93, 0x2b, 0x8a, 0xed, 0x6c, 0x94, 0xc5, 0x1a, 0x60, 0x94, 0x1d, 0x20, 0x47, 0xe2, 0x15, 0xef, +0x6a, 0x76, 0xab, 0x1e, 0x78, 0x25, 0x72, 0x2b, 0xe3, 0x58, 0xc9, 0x06, 0xca, 0x88, 0x67, 0x1c, +0x93, 0x0c, 0x8c, 0xcd, 0x11, 0xc7, 0x48, 0xde, 0xa2, 0x4e, 0xf4, 0x6b, 0x39, 0x83, 0x31, 0x0a, +0x60, 0x63, 0x94, 0xa1, 0x07, 0x2e, 0x1a, 0xcb, 0x2e, 0x2a, 0x34, 0x95, 0xa9, 0x07, 0xb7, 0x30, +0x99, 0x26, 0xc2, 0x89, 0x31, 0x78, 0x72, 0x38, 0x80, 0x1e, 0xec, 0x13, 0xfd, 0x5a, 0xce, 0x60, +0xcc, 0x18, 0x20, 0x91, 0x4b, 0xa5, 0x0d, 0xc9, 0xec, 0x66, 0x93, 0x8e, 0x50, 0xaa, 0x91, 0x24, +0x12, 0x6b, 0xb8, 0x6d, 0x20, 0xd7, 0x8c, 0x13, 0x7c, 0xb5, 0x61, 0x8c, 0x02, 0x64, 0x4b, 0xbc, +0xd6, 0x5d, 0xa3, 0x2f, 0x96, 0xae, 0x29, 0x24, 0xd3, 0x4f, 0x96, 0x4d, 0x23, 0x46, 0x28, 0x80, +0x8d, 0xd1, 0xd2, 0xce, 0x00, 0xe0, 0x10, 0x5b, 0xa4, 0x5b, 0x84, 0x8d, 0xe2, 0x22, 0x59, 0x6c, +0x26, 0x46, 0x28, 0x40, 0x02, 0xe3, 0xa4, 0xed, 0x00, 0x60, 0x35, 0x27, 0xa5, 0x53, 0x00, 0x3b, +0xbd, 0x65, 0x89, 0x5e, 0x6e, 0x84, 0x02, 0xe4, 0x30, 0x5a, 0x74, 0x31, 0xbc, 0x52, 0xc7, 0xb7, +0x3e, 0x56, 0xf8, 0xc4, 0xd1, 0x4d, 0x16, 0x63, 0x90, 0x7e, 0x05, 0xb0, 0x31, 0xda, 0x80, 0x13, +0xf7, 0xcc, 0xa2, 0x84, 0x35, 0xa2, 0x45, 0xf0, 0x48, 0x5a, 0xf0, 0x28, 0x40, 0x1c, 0x97, 0x49, +0x1c, 0xb6, 0xfc, 0x5b, 0x8e, 0x61, 0x93, 0xae, 0x0b, 0x80, 0x14, 0x59, 0xdc, 0xc4, 0xf5, 0x2b, +0x40, 0x1f, 0x93, 0xbc, 0x80, 0x8c, 0xe0, 0x14, 0x9f, 0xd1, 0x8c, 0xdb, 0x00, 0x57, 0x0e, 0xa3, +0x89, 0x0e, 0x1e, 0x05, 0x18, 0x29, 0xcf, 0x9c, 0xb6, 0x13, 0x2b, 0x59, 0x03, 0xb4, 0x6a, 0x32, +0x04, 0x99, 0x8b, 0x9d, 0x74, 0x39, 0xf6, 0x09, 0xe8, 0x55, 0x80, 0x58, 0xae, 0x94, 0x65, 0x42, +0xd3, 0x89, 0x3a, 0xde, 0xa3, 0x0a, 0x70, 0xfa, 0xf4, 0xf5, 0x13, 0x83, 0x9d, 0x2e, 0x72, 0x6c, +0x19, 0xd5, 0xab, 0x00, 0xd9, 0xd2, 0x2e, 0x01, 0xc1, 0xd7, 0x2c, 0x3e, 0xfb, 0xaf, 0x52, 0x1a, +0x45, 0x0b, 0xd3, 0x01, 0x1b, 0xb1, 0x72, 0x78, 0x05, 0xe8, 0x15, 0xa2, 0x80, 0x5c, 0xd1, 0x45, +0xf0, 0xc2, 0x71, 0x5e, 0xe5, 0xe4, 0xd9, 0x7f, 0x1f, 0x3d, 0xff, 0x2f, 0x59, 0xb0, 0x05, 0x47, +0x0b, 0x10, 0xcd, 0x65, 0x12, 0x9c, 0x99, 0xeb, 0x09, 0x27, 0x0b, 0x58, 0x7a, 0xfe, 0x7f, 0x47, +0x28, 0x17, 0x2d, 0x50, 0x27, 0xec, 0x72, 0xcc, 0x4d, 0xf4, 0x29, 0x40, 0x4f, 0x55, 0x61, 0x5b, +0x03, 0xc9, 0x06, 0x5e, 0x69, 0xb3, 0xe0, 0x5a, 0xad, 0x20, 0x20, 0xdc, 0x05, 0x8a, 0x3e, 0x05, +0x18, 0x2b, 0x93, 0x6f, 0x4b, 0x1b, 0xca, 0x78, 0x96, 0x5d, 0x6d, 0xfe, 0xef, 0x60, 0xa5, 0x2c, +0x0e, 0x18, 0xb2, 0xa1, 0x47, 0x01, 0x62, 0x98, 0x20, 0xe5, 0x1a, 0x40, 0x13, 0x7f, 0xe1, 0x53, +0x5c, 0xed, 0xfe, 0xb6, 0x45, 0xae, 0xed, 0x18, 0xf2, 0xa0, 0x47, 0x01, 0x32, 0x15, 0x86, 0x58, +0x0a, 0x2c, 0x4e, 0xde, 0xe3, 0xe5, 0x4e, 0xa3, 0xfe, 0x3d, 0x6c, 0x16, 0x2d, 0x98, 0x9c, 0xe8, +0x51, 0x80, 0x31, 0x12, 0xae, 0x01, 0xb8, 0xf8, 0x92, 0xa7, 0x3d, 0x0c, 0xf9, 0x6a, 0x58, 0x4c, +0xab, 0x68, 0xe1, 0x64, 0x44, 0xbb, 0x02, 0x44, 0x4b, 0xd8, 0x01, 0xb8, 0x59, 0xc1, 0xe3, 0xed, +0x7a, 0xff, 0xef, 0xf8, 0x86, 0x12, 0xd1, 0xe2, 0xc9, 0x88, 0x76, 0x05, 0xc8, 0x96, 0x6d, 0x97, +0x1b, 0x2e, 0x56, 0xf1, 0x1b, 0x36, 0x7a, 0xf9, 0x75, 0x1f, 0x5f, 0x86, 0x06, 0x82, 0x9d, 0xd1, +0xae, 0x00, 0x23, 0x24, 0x5b, 0x03, 0x68, 0x65, 0x09, 0x0f, 0x53, 0xe4, 0xb5, 0x92, 0x1b, 0xf9, +0x20, 0x34, 0x10, 0xec, 0x8c, 0x56, 0x05, 0x08, 0x97, 0xcc, 0x0b, 0xc8, 0xc1, 0xc7, 0x3c, 0xc2, +0x2a, 0x9f, 0xdf, 0xf8, 0x7a, 0x16, 0x89, 0x16, 0x53, 0x3e, 0xb4, 0x2a, 0x40, 0x26, 0x63, 0x44, +0x8b, 0xde, 0x86, 0x6a, 0x5e, 0xe7, 0x11, 0xb6, 0xf8, 0xb9, 0xaa, 0x96, 0xb7, 0x39, 0x2a, 0x5a, +0x54, 0xd9, 0xd0, 0xaa, 0x00, 0x23, 0x4c, 0x0d, 0xd7, 0xa0, 0x06, 0x37, 0x07, 0xf8, 0x03, 0x8f, +0xb3, 0x5f, 0xc1, 0xb5, 0xab, 0xf9, 0xa0, 0x83, 0x7d, 0xe0, 0x82, 0x47, 0x9b, 0x02, 0x84, 0x73, +0x85, 0x24, 0x4e, 0x8d, 0x0e, 0xfe, 0xc3, 0xcf, 0x78, 0x49, 0xe1, 0x8a, 0x7f, 0x1d, 0xf3, 0xd9, +0x29, 0x5a, 0xe4, 0xb3, 0x48, 0xb1, 0x12, 0xa0, 0x55, 0x01, 0x7a, 0x49, 0xb1, 0x06, 0xe0, 0xe6, +0x28, 0x2f, 0x31, 0x8b, 0xcf, 0x54, 0x2c, 0xf6, 0x6e, 0xe6, 0x6f, 0xd4, 0x8b, 0x16, 0x1c, 0x80, +0x44, 0x39, 0x76, 0x06, 0x68, 0x5b, 0x92, 0x2c, 0x90, 0xa0, 0x03, 0xa8, 0x67, 0x05, 0xaf, 0xf1, +0x25, 0xb5, 0xaa, 0xee, 0x6a, 0x61, 0x01, 0xe3, 0x98, 0x2a, 0x5a, 0x78, 0x20, 0xce, 0xba, 0x0a, +0x10, 0xc1, 0x65, 0x82, 0x3b, 0x80, 0x16, 0x76, 0xf2, 0x36, 0x0b, 0x39, 0xa8, 0xa1, 0x47, 0x3f, +0xca, 0x3c, 0x06, 0x70, 0x91, 0x50, 0xf9, 0x2d, 0x4e, 0x0e, 0x1b, 0x4d, 0x89, 0x06, 0xa8, 0x34, +0x66, 0xe0, 0x5e, 0xe6, 0x32, 0x42, 0x87, 0x23, 0x5a, 0x24, 0xb3, 0x0c, 0x3c, 0xc9, 0x58, 0x6b, +0x5a, 0x41, 0x57, 0xd1, 0x15, 0x09, 0xda, 0x5a, 0x80, 0xc1, 0xc2, 0xbc, 0x80, 0x9a, 0x29, 0xe6, +0x4b, 0xfe, 0xcd, 0x3a, 0x5d, 0x9b, 0x3d, 0x9a, 0x79, 0x87, 0x7e, 0xfc, 0xf8, 0x42, 0x3c, 0xc9, +0xa3, 0x33, 0xea, 0x15, 0x20, 0x82, 0x09, 0x86, 0xc5, 0xca, 0x50, 0x43, 0x1d, 0x3b, 0x59, 0xc4, +0x27, 0xec, 0x30, 0xc0, 0xbf, 0xaf, 0x82, 0x79, 0x64, 0x71, 0xa3, 0x1c, 0x5e, 0x79, 0x62, 0x51, +0xaf, 0x00, 0xdd, 0x02, 0xbe, 0x08, 0xdc, 0xc2, 0x31, 0x36, 0xb0, 0x98, 0xe5, 0xec, 0x37, 0xcc, +0xc3, 0x7f, 0x3f, 0x73, 0xe9, 0x26, 0xc5, 0x5c, 0x46, 0x30, 0xea, 0x15, 0x60, 0x98, 0x8a, 0xf3, +0xfb, 0xf4, 0xd2, 0x42, 0x29, 0x3b, 0x59, 0x41, 0x11, 0x3b, 0xa9, 0x30, 0xd4, 0x84, 0xe3, 0x66, +0x1d, 0xbf, 0x67, 0xae, 0xa2, 0xe3, 0x63, 0x83, 0x1a, 0xb5, 0x0a, 0x60, 0xa7, 0x50, 0xd1, 0xf9, +0x3d, 0x7a, 0x69, 0xa1, 0x94, 0x2d, 0x2c, 0x63, 0x2d, 0xbb, 0xa9, 0x36, 0xe5, 0x6c, 0x4d, 0x27, +0x5f, 0x91, 0xc4, 0xef, 0x15, 0x1f, 0x47, 0x17, 0xa4, 0xa8, 0x55, 0x80, 0x14, 0x46, 0x9b, 0x3c, +0x7f, 0x6d, 0xe6, 0x38, 0x5b, 0x58, 0xc6, 0x6a, 0x76, 0x53, 0x63, 0xaa, 0x13, 0x47, 0x0b, 0x1f, +0x12, 0xc9, 0xe3, 0x17, 0xb6, 0x0a, 0xa8, 0x55, 0x80, 0x0c, 0x13, 0xe3, 0x5c, 0x36, 0x53, 0xc6, +0x46, 0x96, 0xb3, 0x8a, 0x7d, 0x54, 0x05, 0x24, 0xaa, 0x47, 0x13, 0xef, 0xe2, 0xe6, 0x77, 0x17, +0xb2, 0x0a, 0xa8, 0x55, 0x80, 0x7e, 0x06, 0x46, 0xcb, 0xfa, 0x0e, 0x07, 0xc7, 0xd8, 0xc2, 0x4a, +0x56, 0x50, 0x6c, 0x70, 0x5f, 0xef, 0x8f, 0x26, 0xde, 0xc3, 0xcd, 0xe3, 0xaa, 0x8e, 0x87, 0x0e, +0x2a, 0xd4, 0x2a, 0x40, 0x8e, 0xc1, 0x36, 0xc0, 0x16, 0x8e, 0xb2, 0x9e, 0xa5, 0xac, 0xa6, 0x84, +0x2a, 0x21, 0x2b, 0x75, 0x4d, 0xbc, 0x4b, 0x1d, 0x4f, 0x48, 0xbc, 0xc5, 0xcd, 0x54, 0xd4, 0x2a, +0x40, 0xba, 0x61, 0x5b, 0x41, 0x5b, 0x39, 0xc2, 0x46, 0xbe, 0x66, 0x2d, 0xc5, 0xd4, 0x0a, 0x75, +0xd6, 0x6a, 0xe6, 0x63, 0xaa, 0x79, 0xc2, 0x4b, 0x8c, 0xe0, 0x20, 0x47, 0xad, 0x02, 0x18, 0xf1, +0x8d, 0xb6, 0x70, 0x94, 0x4d, 0x7c, 0xc5, 0x5a, 0xf6, 0x52, 0x2f, 0x85, 0x9f, 0x9e, 0x93, 0x6f, +0xf9, 0x09, 0x8f, 0x72, 0x93, 0xa4, 0x1b, 0xdd, 0x4c, 0x44, 0xad, 0x02, 0x94, 0xd2, 0xa4, 0x23, +0x1e, 0x48, 0x33, 0xc7, 0x59, 0xc7, 0x7f, 0x58, 0x49, 0x31, 0x0d, 0x52, 0x54, 0xfd, 0x39, 0xdc, +0x6c, 0x65, 0x36, 0xc5, 0xfc, 0x58, 0x96, 0xd0, 0x2d, 0xb2, 0x32, 0x9e, 0x63, 0x9a, 0x96, 0x3e, +0x1c, 0x94, 0xf0, 0x1e, 0x0f, 0x32, 0x94, 0x44, 0x59, 0x5c, 0x21, 0x3c, 0x10, 0xc3, 0xf7, 0x59, +0x4f, 0xeb, 0x85, 0xb4, 0x18, 0xa4, 0x96, 0x24, 0xe6, 0xab, 0x3c, 0x18, 0xda, 0x41, 0x09, 0xef, +0xf3, 0x53, 0x86, 0x19, 0x72, 0xe0, 0x82, 0xd9, 0xd8, 0x19, 0xce, 0x5b, 0xa6, 0x1c, 0x7d, 0x17, +0x24, 0x0a, 0x00, 0x97, 0xb0, 0x4b, 0x61, 0x11, 0x9b, 0x39, 0xc8, 0xfb, 0xcc, 0x62, 0x38, 0xf1, +0xa2, 0x85, 0x56, 0x45, 0x17, 0x1e, 0xe6, 0x68, 0x48, 0x01, 0xbc, 0x61, 0x67, 0xba, 0xdf, 0x6e, +0xa0, 0x91, 0x5d, 0xcc, 0xe7, 0x1e, 0x86, 0x4b, 0xe2, 0x39, 0xa8, 0x96, 0x08, 0x26, 0xb1, 0x4c, +0xc7, 0x81, 0xf1, 0x41, 0xad, 0x00, 0x10, 0xc1, 0x5d, 0x1c, 0xf2, 0x52, 0xac, 0x7a, 0x76, 0xf3, +0x16, 0x77, 0x93, 0x6f, 0xf9, 0xf1, 0x74, 0x1e, 0xff, 0x67, 0xd0, 0xb9, 0xa4, 0x41, 0xa7, 0x00, +0x10, 0xc1, 0x0d, 0x2c, 0x69, 0xd7, 0x53, 0xb6, 0x52, 0xc9, 0x0e, 0xde, 0xe0, 0x1e, 0x06, 0x59, +0xf4, 0xab, 0xef, 0x4c, 0x22, 0xf7, 0xb3, 0x57, 0xe5, 0x88, 0xc7, 0x72, 0x0a, 0xa0, 0x75, 0x44, +0x6e, 0x23, 0x93, 0x6b, 0x18, 0x4b, 0x0f, 0xec, 0x38, 0xa9, 0xa0, 0x98, 0xad, 0x6c, 0xa7, 0x54, +0x12, 0x8f, 0x5b, 0xa3, 0x08, 0xe7, 0x62, 0x7e, 0xc5, 0x15, 0xa6, 0xc4, 0x41, 0x5b, 0xc9, 0x54, +0x09, 0x03, 0xd7, 0xa8, 0x24, 0x9a, 0x74, 0xba, 0x92, 0x66, 0x42, 0x44, 0x7d, 0x79, 0xc8, 0xe1, +0x19, 0xca, 0x83, 0xb7, 0x05, 0x08, 0xe1, 0x9f, 0x58, 0xee, 0x64, 0xb3, 0xe1, 0xd6, 0x01, 0x49, +0x14, 0xe0, 0x02, 0xb4, 0x7e, 0xab, 0xa6, 0x85, 0xed, 0x6c, 0x20, 0x89, 0x5c, 0x43, 0xbb, 0x82, +0xe3, 0x2c, 0xe4, 0xb4, 0xe8, 0xa2, 0x85, 0x14, 0x40, 0x19, 0x6e, 0x8e, 0xb1, 0x8a, 0x06, 0xf2, +0x0c, 0x5c, 0x0c, 0xaf, 0xe4, 0x53, 0x19, 0xa2, 0x17, 0x86, 0x14, 0x40, 0x29, 0xa7, 0x59, 0x4f, +0x09, 0x3d, 0xe9, 0x6e, 0xd0, 0x3b, 0x3b, 0xc9, 0xfb, 0x21, 0x05, 0xb0, 0x16, 0x2d, 0xec, 0x62, +0x23, 0x31, 0xe4, 0x1a, 0x12, 0x1e, 0xbf, 0x9c, 0x85, 0x21, 0x05, 0xb0, 0x1a, 0x6e, 0xca, 0x58, +0x45, 0x15, 0xb9, 0x74, 0xd1, 0x3d, 0xef, 0x91, 0x44, 0x01, 0x42, 0xa8, 0x27, 0x8a, 0xeb, 0x58, +0x4c, 0x93, 0xce, 0x59, 0xc0, 0x36, 0xf2, 0x45, 0x17, 0x04, 0x42, 0x2d, 0x80, 0x16, 0x9c, 0x14, +0xb3, 0x8e, 0x30, 0x72, 0x75, 0xd9, 0x3c, 0x25, 0x69, 0x01, 0x42, 0x0a, 0xa0, 0x8d, 0x53, 0xac, +0xe1, 0x04, 0xd9, 0xa4, 0x6b, 0xee, 0x0a, 0x42, 0x0a, 0x60, 0x71, 0x1a, 0xd9, 0xc6, 0x76, 0x92, +0xc9, 0xd6, 0x78, 0xf2, 0x87, 0x24, 0x0a, 0x20, 0x45, 0xcc, 0x7a, 0x8b, 0xd2, 0xcc, 0x52, 0x0e, +0xb1, 0x8b, 0xbb, 0xe8, 0xa1, 0xe1, 0x6e, 0x67, 0x28, 0x72, 0x69, 0x70, 0x90, 0xc0, 0x1d, 0xac, +0xd3, 0x60, 0x28, 0x5e, 0xa5, 0x49, 0x6d, 0x42, 0x48, 0x48, 0x38, 0x85, 0xfc, 0x53, 0xb5, 0x1b, +0xd9, 0x32, 0x39, 0xdc, 0x4f, 0x43, 0x63, 0x00, 0xfd, 0xb8, 0xce, 0x1a, 0x8a, 0xfb, 0xa9, 0xf2, +0x7a, 0xdc, 0xc4, 0x87, 0x34, 0x89, 0x16, 0x3d, 0x84, 0x71, 0xc4, 0x70, 0x13, 0x45, 0xb4, 0x28, +0xfc, 0xfe, 0x5d, 0xcc, 0x93, 0x2a, 0xd2, 0x6a, 0x08, 0x03, 0xb0, 0x91, 0xcf, 0x2b, 0x54, 0x2b, +0x52, 0x80, 0x7a, 0x66, 0x04, 0xb1, 0x0f, 0xc5, 0x05, 0x4c, 0x2a, 0xb3, 0x28, 0x56, 0x64, 0x07, +0x2c, 0x10, 0x2d, 0x6a, 0x08, 0x73, 0x88, 0xe0, 0x0a, 0x96, 0xf8, 0xe9, 0x0a, 0x1c, 0x3c, 0x1d, +0xea, 0x00, 0x82, 0x99, 0x5c, 0xfe, 0xe4, 0x23, 0x10, 0x9d, 0x93, 0x8f, 0x4d, 0x8c, 0xb2, 0x10, +0x42, 0x0a, 0xe2, 0xf8, 0x3e, 0xcb, 0x3d, 0xb6, 0x03, 0x8d, 0xbc, 0xcb, 0xe0, 0x50, 0xff, 0x1f, +0xfc, 0xd8, 0xe8, 0xcd, 0x93, 0xec, 0xa1, 0xb9, 0x5d, 0xe5, 0x6f, 0xe4, 0xa7, 0x74, 0x17, 0x2d, +0x5a, 0x88, 0x40, 0x11, 0xc9, 0x28, 0xe6, 0xb2, 0x09, 0x07, 0x6e, 0xea, 0x58, 0xc6, 0x1c, 0x06, +0xca, 0x66, 0x79, 0xf9, 0x7f, 0x2e, 0x03, 0x18, 0xfe, 0x43, 0xbf, 0xeb, 0x10, 0x00, 0x00, 0x00, +0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, +}; + +#endif diff --git a/src/ghostfat.c b/src/ghostfat.c index cabbc49..55cea58 100644 --- a/src/ghostfat.c +++ b/src/ghostfat.c @@ -27,17 +27,29 @@ #include #include #include +#include #include "compile_date.h" #include "board_api.h" #include "uf2.h" + + //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ +// ota0 partition size +static uint32_t _flash_size; + #define STATIC_ASSERT(_exp) _Static_assert(_exp, "static assert failed") +#define STR0(x) #x +#define STR(x) STR0(x) + +#define UF2_ARRAY_SIZE(_arr) ( sizeof(_arr) / sizeof(_arr[0]) ) +#define UF2_DIV_CEIL(_v, _d) ( ((_v) / (_d)) + ((_v) % (_d) ? 1 : 0) ) + typedef struct { uint8_t JumpInstruction[3]; uint8_t OEMInfo[8]; @@ -78,11 +90,15 @@ typedef struct { } __attribute__((packed)) DirEntry; STATIC_ASSERT(sizeof(DirEntry) == 32); -struct TextFile { +typedef struct FileContent { char const name[11]; - char const *content; -}; + void const * content; + uint32_t size; // OK to use uint32_T b/c FAT32 limits filesize to (4GiB - 2) + // computing fields based on index and size + uint16_t cluster_start; + uint16_t cluster_end; +} FileContent_t; //--------------------------------------------------------------------+ // @@ -97,13 +113,13 @@ struct TextFile { #define BPB_MEDIA_DESCRIPTOR_BYTE (0xF8) #define FAT_ENTRY_SIZE (2) #define FAT_ENTRIES_PER_SECTOR (BPB_SECTOR_SIZE / FAT_ENTRY_SIZE) +#define FAT_END_OF_CHAIN (0xFFFF) + // NOTE: MS specification explicitly allows FAT to be larger than necessary -#define TOTAL_CLUSTERS_ROUND_UP ( (BPB_TOTAL_SECTORS / BPB_SECTORS_PER_CLUSTER) + \ - ((BPB_TOTAL_SECTORS % BPB_SECTORS_PER_CLUSTER) ? 1 : 0)) -#define BPB_SECTORS_PER_FAT ( (TOTAL_CLUSTERS_ROUND_UP / FAT_ENTRIES_PER_SECTOR) + \ - ((TOTAL_CLUSTERS_ROUND_UP % FAT_ENTRIES_PER_SECTOR) ? 1 : 0)) +#define TOTAL_CLUSTERS_ROUND_UP UF2_DIV_CEIL(BPB_TOTAL_SECTORS, BPB_SECTORS_PER_CLUSTER) +#define BPB_SECTORS_PER_FAT UF2_DIV_CEIL(TOTAL_CLUSTERS_ROUND_UP, FAT_ENTRIES_PER_SECTOR) #define DIRENTRIES_PER_SECTOR (BPB_SECTOR_SIZE/sizeof(DirEntry)) -#define ROOT_DIR_SECTOR_COUNT (BPB_ROOT_DIR_ENTRIES/DIRENTRIES_PER_SECTOR) +#define ROOT_DIR_SECTOR_COUNT UF2_DIV_CEIL(BPB_ROOT_DIR_ENTRIES, DIRENTRIES_PER_SECTOR) #define BPB_BYTES_PER_CLUSTER (BPB_SECTOR_SIZE * BPB_SECTORS_PER_CLUSTER) STATIC_ASSERT((BPB_SECTORS_PER_CLUSTER & (BPB_SECTORS_PER_CLUSTER-1)) == 0); // sectors per cluster must be power of two @@ -115,12 +131,12 @@ STATIC_ASSERT(BPB_ROOT_DIR_ENTRIES % DIRENTRIES_PER_SECTOR == 0); // FAT STATIC_ASSERT(BPB_BYTES_PER_CLUSTER <= (32*1024)); // FAT requirement (64k+ has known compatibility problems) STATIC_ASSERT(FAT_ENTRIES_PER_SECTOR == 256); // FAT requirement -#define STR0(x) #x -#define STR(x) STR0(x) +#define UF2_FIRMWARE_BYTES_PER_SECTOR 256 +#define UF2_SECTOR_COUNT (_flash_size / UF2_FIRMWARE_BYTES_PER_SECTOR) +#define UF2_BYTE_COUNT (UF2_SECTOR_COUNT * BPB_SECTOR_SIZE) // always a multiple of sector size, per UF2 spec -#define UF2_ARRAY_SIZE(_arr) ( sizeof(_arr) / sizeof(_arr[0]) ) -char infoUf2File[] = +const char infoUf2File[] = "TinyUF2 Bootloader " UF2_VERSION "\r\n" "Model: " UF2_PRODUCT_NAME "\r\n" "Board-ID: " UF2_BOARD_ID "\r\n" @@ -136,23 +152,30 @@ const char indexFile[] = "" "\n"; -static struct TextFile const info[] = { - {.name = "INFO_UF2TXT", .content = infoUf2File}, - {.name = "INDEX HTM", .content = indexFile}, +#ifdef TINYUF2_FAVICON_HEADER +#include TINYUF2_FAVICON_HEADER +const char autorunFile[] = "[Autorun]\r\nIcon=FAVICON.ICO\r\n"; +#endif +// size of CURRENT.UF2: +static FileContent_t info[] = { + {.name = "INFO_UF2TXT", .content = infoUf2File , .size = sizeof(infoUf2File) - 1}, + {.name = "INDEX HTM", .content = indexFile , .size = sizeof(indexFile ) - 1}, +#ifdef TINYUF2_FAVICON_HEADER + {.name = "AUTORUN INF", .content = autorunFile , .size = sizeof(autorunFile) - 1}, + {.name = "FAVICON ICO", .content = favicon_data, .size = favicon_len }, +#endif // current.uf2 must be the last element and its content must be NULL - {.name = "CURRENT UF2", .content = NULL}, + {.name = "CURRENT UF2", .content = NULL , .size = 0 }, }; -STATIC_ASSERT(UF2_ARRAY_SIZE(infoUf2File) < BPB_BYTES_PER_CLUSTER); // GhostFAT requires files to fit in one cluster -STATIC_ASSERT(UF2_ARRAY_SIZE(indexFile) < BPB_BYTES_PER_CLUSTER); // GhostFAT requires files to fit in one cluster -#define NUM_FILES (UF2_ARRAY_SIZE(info)) -#define NUM_DIRENTRIES (NUM_FILES + 1) // Code adds volume label as first root directory entry -#define REQUIRED_ROOT_DIRECTORY_SECTORS ( ((NUM_DIRENTRIES+1) / DIRENTRIES_PER_SECTOR) + \ - (((NUM_DIRENTRIES+1) % DIRENTRIES_PER_SECTOR) ? 1 : 0)) -STATIC_ASSERT(ROOT_DIR_SECTOR_COUNT >= REQUIRED_ROOT_DIRECTORY_SECTORS); // FAT requirement -- Ensures BPB reserves sufficient entries for all files -STATIC_ASSERT(NUM_DIRENTRIES < (DIRENTRIES_PER_SECTOR * ROOT_DIR_SECTOR_COUNT)); // FAT requirement -- end directory with unused entry -STATIC_ASSERT(NUM_DIRENTRIES < BPB_ROOT_DIR_ENTRIES); // FAT requirement -- Ensures BPB reserves sufficient entries for all files +enum { + NUM_FILES = sizeof(info) / sizeof(info[0]), + FID_UF2 = NUM_FILES-1, + NUM_DIRENTRIES = NUM_FILES + 1 // including volume label as first root directory entry +}; + +STATIC_ASSERT(NUM_DIRENTRIES < BPB_ROOT_DIR_ENTRIES); // FAT requirement -- Ensures BPB reserves sufficient entries for all files STATIC_ASSERT(NUM_DIRENTRIES < DIRENTRIES_PER_SECTOR); // GhostFAT bug workaround -- else, code overflows buffer #define NUM_SECTORS_IN_DATA_REGION (BPB_TOTAL_SECTORS - BPB_RESERVED_SECTORS - (BPB_NUMBER_OF_FATS * BPB_SECTORS_PER_FAT) - ROOT_DIR_SECTOR_COUNT) @@ -165,18 +188,6 @@ STATIC_ASSERT( CLUSTER_COUNT >= 0x0FF5 && CLUSTER_COUNT < 0xFFF5 ); // So, avoid being within 32 of those limits for even greater compatibility. STATIC_ASSERT( CLUSTER_COUNT >= 0x1015 && CLUSTER_COUNT < 0xFFD5 ); -#define UF2_FIRMWARE_BYTES_PER_SECTOR 256 -#define UF2_SECTOR_COUNT (_flash_size / UF2_FIRMWARE_BYTES_PER_SECTOR) -#define UF2_CLUSTER_COUNT ( (UF2_SECTOR_COUNT / BPB_SECTORS_PER_CLUSTER) + \ - ((UF2_SECTOR_COUNT % BPB_SECTORS_PER_CLUSTER) ? 1 : 0)) -#define UF2_BYTE_COUNT (UF2_SECTOR_COUNT * BPB_SECTOR_SIZE) // always a multiple of sector size, per UF2 spec - -// NOTE: First cluster number of the UF2 file calculation is: -// Starts with NUM_FILES because each non-UF2 file is limited to a single cluster in size -// -1 because NUM_FILES includes UF2 entry is included in that array, which is zero-indexed -// +2 because FAT decided first data sector would be in cluster number 2, rather than zero -#define UF2_FIRST_CLUSTER_NUMBER ((NUM_FILES -1) + 2) -#define UF2_LAST_CLUSTER_NUMBER (UF2_FIRST_CLUSTER_NUMBER + UF2_CLUSTER_COUNT - 1) #define FS_START_FAT0_SECTOR BPB_RESERVED_SECTORS #define FS_START_FAT1_SECTOR (FS_START_FAT0_SECTOR + BPB_SECTORS_PER_FAT) @@ -208,9 +219,6 @@ static FAT_BootBlock const BootBlock = { .FilesystemIdentifier = "FAT16 ", }; -// ota0 partition size -static uint32_t _flash_size; - //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ @@ -224,10 +232,56 @@ static inline bool is_uf2_block (UF2_Block const *bl) !(bl->flags & UF2_FLAG_NOFLASH); } +// cache the cluster start offset for each file +// this allows more flexible algorithms w/o O(n) time +static void init_starting_clusters(void) +{ + // +2 because FAT decided first data sector would be in cluster number 2, rather than zero + uint16_t start_cluster = 2; + + for (uint16_t i = 0; i < NUM_FILES; i++) + { + info[i].cluster_start = start_cluster; + info[i].cluster_end = start_cluster + UF2_DIV_CEIL(info[i].size, BPB_SECTOR_SIZE*BPB_SECTORS_PER_CLUSTER) - 1; + + start_cluster = info[i].cluster_end + 1; + } + + return; +} + +// get file index for file that uses the cluster +// if cluster is past last file, returns ( NUM_FILES-1 ). +// +// Caller must still check if a particular *sector* +// contains data from the file's contents, as there +// are often padding sectors, including all the unused +// sectors past the end of the media. +static uint32_t info_index_of(uint32_t cluster) +{ + // default results for invalid requests is the index of the last file (CURRENT.UF2) + if (cluster >= 0xFFF0) return FID_UF2; + + for (uint32_t i = 0; i < NUM_FILES; i++) + { + if ( (info[i].cluster_start <= cluster) && (cluster <= info[i].cluster_end) ) + { + return i; + } + } + + return FID_UF2; +} + void uf2_init(void) { // TODO maybe limit to application size only if possible board_flash_app_size() _flash_size = board_flash_size(); + + // update CURRENT.UF2 file size + info[FID_UF2].size = UF2_BYTE_COUNT; + + init_starting_clusters(); } /*------------------------------------------------------------------*/ @@ -253,105 +307,136 @@ void uf2_read_block (uint32_t block_no, uint8_t *data) if ( block_no == 0 ) { - // Requested boot block + // Request was for the Boot block memcpy(data, &BootBlock, sizeof(BootBlock)); data[510] = 0x55; // Always at offsets 510/511, even when BPB_SECTOR_SIZE is larger data[511] = 0xaa; // Always at offsets 510/511, even when BPB_SECTOR_SIZE is larger } else if ( block_no < FS_START_ROOTDIR_SECTOR ) { - // Requested FAT table sector + // Request was for a FAT table sector sectionRelativeSector -= FS_START_FAT0_SECTOR; - // second FAT is same as the first... - if ( sectionRelativeSector >= BPB_SECTORS_PER_FAT ) sectionRelativeSector -= BPB_SECTORS_PER_FAT; - - if ( sectionRelativeSector == 0 ) + // second FAT is same as the first... use sectionRelativeSector to write data + if ( sectionRelativeSector >= BPB_SECTORS_PER_FAT ) { - // first FAT entry must match BPB MediaDescriptor - data[0] = BPB_MEDIA_DESCRIPTOR_BYTE; - // WARNING -- code presumes only one NULL .content for .UF2 file - // and all non-NULL .content fit in one sector - // and requires it be the last element of the array - uint32_t const end = (NUM_FILES * FAT_ENTRY_SIZE) + (2 * FAT_ENTRY_SIZE); - for ( uint32_t i = 1; i < end; ++i ) data[i] = 0xff; + sectionRelativeSector -= BPB_SECTORS_PER_FAT; } - // Generate the FAT chain for the firmware "file" - for ( uint32_t i = 0; i < FAT_ENTRIES_PER_SECTOR; ++i ) + uint16_t* data16 = (uint16_t*) (void*) data; + + uint32_t sectorFirstCluster = sectionRelativeSector * FAT_ENTRIES_PER_SECTOR; + uint32_t firstUnusedCluster = info[FID_UF2].cluster_end + 1; + + // OPTIMIZATION: + // Because all files are contiguous, the FAT CHAIN entries + // are all set to (cluster+1) to point to the next cluster. + // All clusters past the last used cluster of the last file + // are set to zero. + // + // EXCEPTIONS: + // 1. Clusters 0 and 1 require special handling + // 2. Final cluster of each file must be set to END_OF_CHAIN + // + + // Set default FAT values first. + for (uint16_t i = 0; i < FAT_ENTRIES_PER_SECTOR; i++) { - // `i` here is the sector-relative array index into this sector of the FAT - // `v` here is the overall array index into the FAT, which corresponds to - // where the next cluster in the chain is stored. - uint32_t v = (sectionRelativeSector * FAT_ENTRIES_PER_SECTOR) + i; - if ( UF2_FIRST_CLUSTER_NUMBER <= v && v < UF2_LAST_CLUSTER_NUMBER ) + uint32_t cluster = i + sectorFirstCluster; + if (cluster >= firstUnusedCluster) { - ((uint16_t*) (void*) data)[i] = v + 1; // contiguous file, so point to next cluster number + data16[i] = 0; } - else if ( v == UF2_LAST_CLUSTER_NUMBER) + else { - ((uint16_t*) (void*) data)[i] = 0xffff; // end of file marker in FAT16 + data16[i] = cluster + 1; + } + } + + // Exception #1: clusters 0 and 1 need special handling + if (sectionRelativeSector == 0) + { + data[0] = BPB_MEDIA_DESCRIPTOR_BYTE; + data[1] = 0xff; + data16[1] = FAT_END_OF_CHAIN; // cluster 1 is reserved + } + + // Exception #2: the final cluster of each file must be set to END_OF_CHAIN + for (uint32_t i = 0; i < NUM_FILES; i++) + { + uint32_t lastClusterOfFile = info[i].cluster_end; + if (lastClusterOfFile >= sectorFirstCluster) + { + uint32_t idx = lastClusterOfFile - sectorFirstCluster; + if (idx < FAT_ENTRIES_PER_SECTOR) + { + // that last cluster of the file is in this sector + data16[idx] = FAT_END_OF_CHAIN; + } } } } else if ( block_no < FS_START_CLUSTERS_SECTOR ) { - // Requested root directory sector - + // Request was for a (root) directory sector .. root because not supporting subdirectories (yet) sectionRelativeSector -= FS_START_ROOTDIR_SECTOR; DirEntry *d = (void*) data; // pointer to next free DirEntry this sector int remainingEntries = DIRENTRIES_PER_SECTOR; // remaining count of DirEntries this sector + uint32_t startingFileIndex; + if ( sectionRelativeSector == 0 ) { - // volume label first // volume label is first directory entry padded_memcpy(d->name, (char const*) BootBlock.VolumeLabel, 11); d->attrs = 0x28; d++; remainingEntries--; - } - uint32_t startingFileIndex = - (sectionRelativeSector == 0) ? - 0 : - (DIRENTRIES_PER_SECTOR * sectionRelativeSector) - 1; // -1 to account for volume label in first sector + startingFileIndex = 0; + }else + { + // -1 to account for volume label in first sector + startingFileIndex = DIRENTRIES_PER_SECTOR * sectionRelativeSector - 1; + } for ( uint32_t fileIndex = startingFileIndex; remainingEntries > 0 && fileIndex < NUM_FILES; // while space remains in buffer and more files to add... fileIndex++, d++ ) { // WARNING -- code presumes all files take exactly one directory entry (no long file names!) - // WARNING -- code presumes all but last file take exactly one sector - // Using the above two presumptions, can convert from file index to starting cluster number - // by simply adding two (because first data cluster has cluster number of 2 in FAT) - uint32_t startCluster = fileIndex + 2; + uint32_t const startCluster = info[fileIndex].cluster_start; - struct TextFile const *inf = &info[fileIndex]; + FileContent_t const *inf = &info[fileIndex]; padded_memcpy(d->name, inf->name, 11); - d->createTimeFine = COMPILE_SECONDS_INT % 2 * 100; - d->createTime = COMPILE_DOS_TIME; - d->createDate = COMPILE_DOS_DATE; - d->lastAccessDate = COMPILE_DOS_DATE; + d->createTimeFine = COMPILE_SECONDS_INT % 2 * 100; + d->createTime = COMPILE_DOS_TIME; + d->createDate = COMPILE_DOS_DATE; + d->lastAccessDate = COMPILE_DOS_DATE; d->highStartCluster = startCluster >> 16; - d->updateTime = COMPILE_DOS_TIME; - d->updateDate = COMPILE_DOS_DATE; - d->startCluster = startCluster & 0xFFFF; - d->size = (inf->content ? strlen(inf->content) : UF2_BYTE_COUNT); + d->updateTime = COMPILE_DOS_TIME; + d->updateDate = COMPILE_DOS_DATE; + d->startCluster = startCluster & 0xFFFF; + d->size = (inf->content ? inf->size : UF2_BYTE_COUNT); } } else if ( block_no < BPB_TOTAL_SECTORS ) { + // Request was to read from the data area (files, unused space, ...) sectionRelativeSector -= FS_START_CLUSTERS_SECTOR; - uint32_t sectionRelativeClusterIndex = sectionRelativeSector / BPB_SECTORS_PER_CLUSTER; - if ( sectionRelativeClusterIndex < (NUM_FILES - 1) ) + // plus 2 for first data cluster offset + uint32_t fid = info_index_of(2 + sectionRelativeSector / BPB_SECTORS_PER_CLUSTER); + FileContent_t const * inf = &info[fid]; + + uint32_t fileRelativeSector = sectionRelativeSector - (info[fid].cluster_start-2) * BPB_SECTORS_PER_CLUSTER; + + if ( fid != FID_UF2 ) { - // WARNING -- code presumes first data cluster == first file, second data cluster == second file, etc. - struct TextFile const * inf = &info[ sectionRelativeSector / BPB_SECTORS_PER_CLUSTER ]; - size_t fileContentStartOffset = (sectionRelativeSector % BPB_SECTORS_PER_CLUSTER) * BPB_SECTOR_SIZE; - size_t fileContentLength = strlen(inf->content); + // Handle all files other than CURRENT.UF2 + size_t fileContentStartOffset = fileRelativeSector * BPB_SECTOR_SIZE; + size_t fileContentLength = inf->size; // nothing to copy if already past the end of the file (only when >1 sector per cluster) if (fileContentLength > fileContentStartOffset) { @@ -368,16 +453,15 @@ void uf2_read_block (uint32_t block_no, uint8_t *data) } else { - // generate the UF2 file data on-the-fly - sectionRelativeSector -= (NUM_FILES - 1) * BPB_SECTORS_PER_CLUSTER; - uint32_t addr = BOARD_FLASH_APP_START + (sectionRelativeSector * UF2_FIRMWARE_BYTES_PER_SECTOR); + // CURRENT.UF2: generate data on-the-fly + uint32_t addr = BOARD_FLASH_APP_START + (fileRelativeSector * UF2_FIRMWARE_BYTES_PER_SECTOR); if ( addr < _flash_size ) // TODO abstract this out { UF2_Block *bl = (void*) data; bl->magicStart0 = UF2_MAGIC_START0; bl->magicStart1 = UF2_MAGIC_START1; bl->magicEnd = UF2_MAGIC_END; - bl->blockNo = sectionRelativeSector; + bl->blockNo = fileRelativeSector; bl->numBlocks = UF2_SECTOR_COUNT; bl->targetAddr = addr; bl->payloadSize = UF2_FIRMWARE_BYTES_PER_SECTOR; diff --git a/src/uf2.h b/src/uf2.h index 2ba2cf8..4f537ec 100644 --- a/src/uf2.h +++ b/src/uf2.h @@ -48,11 +48,13 @@ SOFTWARE. #ifndef CFG_UF2_FLASH_SIZE #define CFG_UF2_FLASH_SIZE (4*1024*1024) #endif + // Number of 512-byte blocks in the exposed filesystem, default is just under 32MB // The filesystem needs space for the current file, text files, uploaded file, and FAT #ifndef CFG_UF2_NUM_BLOCKS #define CFG_UF2_NUM_BLOCKS (0x10109) #endif + // Sectors per FAT cluster, must be increased proportionally for larger filesystems #ifndef CFG_UF2_SECTORS_PER_CLUSTER #define CFG_UF2_SECTORS_PER_CLUSTER (1)