diff --git a/.clang-format b/.clang-format index 31f6b35..44c809e 100644 --- a/.clang-format +++ b/.clang-format @@ -5,3 +5,4 @@ PointerAlignment: Right BreakBeforeBinaryOperators: NonAssignment SpaceBeforeInheritanceColon: false AlignArrayOfStructures: Left +SpaceBeforeCpp11BracedList: true diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 55b41fc..2cb2f8e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -19,9 +19,8 @@ jobs: - name: Run clang-format style check uses: jidicula/clang-format-action@v4.13.0 with: - clang-format-version: '17' + clang-format-version: '18' check-path: '.' - exclude-regex: 'lib/M5ez/examples' # This workflow contains a matrix of build platforms build: @@ -29,6 +28,7 @@ jobs: matrix: platform: - m5stick-c + - m5stick-c-plus - m5stack-core - m5stack-core2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 18af06d..33edc1a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,6 +14,7 @@ jobs: matrix: platform: - m5stick-c + - m5stick-c-plus - m5stack-core - m5stack-core2 diff --git a/README.md b/README.md index f53233b..37f7093 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,26 @@ -# furble - FUjifilm Remote Bluetooth Low Energy +# furble - ***FU***jifilm ***R***emote ***B***luetooth ***L***ow ***E***nergy ![PlatformIO CI](https://github.com/gkoh/furble/workflows/PlatformIO%20CI/badge.svg) A Bluetooth wireless remote shutter release originally targeted at Fujifilm mirrorless cameras. -![furble - M5StickC-Plus2](https://github.com/user-attachments/assets/e0eebd87-3ac0-4a8b-871a-014eb8c71395) - -The remote uses the camera's native Bluetooth Low Energy interface so additional +The remote uses the camera's native Bluetooth Low Energy interface thus additional adapters are not required. -furble is developed as a PlatformIO project for the M5StickC, M5StickC Plus and M5StickC Plus2 -(ESP32 based devices). Additionally, it can be used on the M5Stack Core2. +furble is developed as a PlatformIO project. + +## M5StickC Plus2 (Dark theme) + +![furble - Dark - M5StickC Plus2](https://github.com/user-attachments/assets/62a507cc-1c75-4d7a-a8de-b9e0bf78e0e1) + +## M5Core2 (Default theme) + +![furble - Default - M5Core2](https://github.com/user-attachments/assets/6c2d3293-201f-4330-a7c0-716c91807dca) ## Supported Cameras -The following devices have actually been tested and confirmed to work: +The following devices have been tested and confirmed to work: - Fujifilm - Fujifilm GFX100II ([@matthudsonau](https://github.com/matthudsonau)) - Fujifilm GFX100S ([@adrianuseless](https://github.com/adrianuseless)) @@ -34,6 +39,15 @@ The following devices have actually been tested and confirmed to work: - Android - iOS +## Supported Controllers + +Initially targeted at the M5StickC, the following controllers are supported: +* M5StickC (EOL) +* M5StickC Plus +* M5StickC Plus2 +* M5Core Basic +* M5Core2 + ## What Works Currently supported features in `furble`: @@ -68,8 +82,10 @@ Follow the instructions on the wiki: [Easy Web Install](https://github.com/gkoh/ PlatformIO does everything assuming things are installed and connected properly. In most cases it should be: - clone the repository -- plug in the M5StickC or M5StickC Plus/Plus2 +- plug in the M5StickC - `platformio run -e m5stick-c -t upload` +- OR plug in M5StickC Plus/Plus2 + - `platformio run -e m5stick-c-plus -t upload` - OR plug in the M5Stack Core2 - `platformio run -e m5stack-core2 -t upload` @@ -89,10 +105,100 @@ camera advertises a known, matching signature, it should appear in the list. You can then connect to the target camera, which, if successful, will save the entry and show the remote menu. -Upon subsequent use it should be enough to hit `Connect`, selecting the -previously paired device and leads to the remote menu. +`furble` will identify as `furble-xxxx` where `xxxx` is a consistent identifier enabling one to differentiate mutiple controllers. -From the remote menu you may choose to disconnect or control the shutter. +Upon subsequent use it should be enough to hit `Connect`, selecting the +previously paired device and leading to the remote menu. + +From the remote menu you may choose to disconnect, control the shutter or activate the intervalometer. + +### Navigation + +#### Hardware Buttons + +All supported controllers have three buttons designated: +- previous +- select +- next + +For M5StickC format controllers: +- previous == power button +- select == big M5 button below screen +- next == right side button + +For M5 Core controllers: +- previous == left button +- select == middle button +- next == right button + +Basic navigation: +* previous == highlight up/previous/right entry +* next == highlight down/next/left entry +* select == action + +For slider and roller elements (eg. brightness control, intervalometer numbers): +* highlight the desired element with previous/next +* press select to 'edit' the element +* press next/previous to up/down increase/decrease +* press select to 'confirm' the change + +#### Touch Screen + +At time of writing, only the M5Core2 has been tested. All user interface widget are touch responsive: +* in menus + * touch the desired entry + * drag up/down to scroll entries +* in rollers (ie intervalometer configuration) + * drag up/down to adjust entry + +For M5Core2, the 'hardware' touch buttons are active and operate identically to the M5 Core. + +During shutter control: +* press 'Shutter' button to release shutter +* press and hold 'Shutter' button for continuous shooting +* press and/or hold 'Focus' button to auto-focus +* press and hold 'Shutter Lock' to lock shutter open + * press and hold 'Shutter Lock' again to release + +### Menu Map + +* Connect (if connections are saved) + * list of connections + * Shutter + * Shutter + * Focus + * Shutter Lock + * Interval + * Start + * Stop + * Count + * Delay + * Shutter + * Disconnect +* Scan + * list of matching targets + * +* Delete (if connections are saved) + * list of connections +* Settings + * Backlight + * Backlight brightness + * Inactivity timeout + * Features + * Faux-NY + * Infinite-Reconnect + * Multi-Connect + * GPS + * GPS + * GPS Data (if GPS enabled) + * Intervalometer + * Count + * Delay + * Shutter + * Theme + * Transmit Power + * About +* Power Off ### Mobile Devices @@ -102,12 +208,11 @@ Connection to mobile devices is a little iffy: - on the mobile device: - pair with `furble` - on `furble` the mobile device should appear as a connectable target if the pairing was successful -- connect to the mobile device to save the pairing - - the devices will remain paired even if you do not connect and save - - forget `furble` on the mobile device to remove such a pair +- forget `furble` on the mobile device to remove such a pair ### GPS Location Tagging +[!WARNING] This unit is EOL, support for the replacement is pending (see #141). For Fujifilm cameras, location tagging is supported with the M5Stack GPS unit: - [Mini GPS/BDS Unit](https://shop.m5stack.com/products/mini-gps-bds-unit) @@ -135,28 +240,34 @@ simultaneously controlled. To use: * Pair with one or more cameras -* Enable `Settings->Multi-Connect` -* In `Connect` select cameras - * Selected cameras will have a `*` -* Select `Connect *` +* Enable `Settings->Features->Multi-Connect` +* In `Connect` select one or more cameras +* Select `Multi-Connect` * Selected cameras will be connected in sequence * If all cameras are connected, the standard remote control is shown -WARNING: +[!WARNING]: * mobile device connections are extremely finnicky * multi-connect involving mobile devices is not well tested and can easily crash + * multi-connect involving mobile devices is not recommended ### Infinite-ReConnect This is useful for using furble as a passive, always on GPS data source. With this, the camera will attempt to reconnect indefinitely. -You don't need to turn on this setting if you are actively using furble. +You don't need to turn on this setting if you are actively using the remote. To use: -* Enable `Settings->Infinite-ReConnect` +* Enable `Settings->Features->Infinite-ReConnect` + +[!WARNING] This will not be kind to battery life -WARNING: -* this will not be kind to battery life +### Themes + +A few basic themes are included, to change: +* `Settings->Themes->` +* `Settings->Themes->Restart` to fully set the theme + * better dynamic theme change support is improving in upstream LVGL ## Motivation @@ -171,11 +282,13 @@ but for my camera. ### Possibly Supported Cameras #### Fujifilm + Given reports from the community and access to additional cameras, it seems many (all?) Fujifilm cameras use the same Bluetooth protocol. Reports of further confirmed working Fujifilm cameras are welcome. #### Canon + With access to a Canon EOS M6, I was able to implement support for it. Other Canon cameras might work, but I suspect the shutter control protocol will be different. @@ -223,19 +336,19 @@ The M5StickC and M5StickC Plus have since been EOL and replaced with the [M5Stic The M5StickC is an ESP32 based mini-IoT development kit which covered all of the requirements (and more). At time of writing, M5Stack sell the M5StickC for US$9.95. -The M5StickC Plus sells for US$19.95. +The M5StickC Plus(2) sells for US$19.95. #### Software The project is built with [PlatformIO](https://platformio.org) and depends on the following libraries: -- [M5ez](https://github.com/M5ez/M5ez) - - severely butchered version to work on the M5StickC +- [LVGL](https://github.com/lvgl/lvgl) - [M5Unified](https://github.com/m5stack/M5Unified) - [NimBLE-Arduino](https://github.com/h2zero/NimBLE-Arduino) - [TinyGPSPlus](https://github.com/mikalhart/TinyGPSPlus) # Known Issues + - depending on your perspective, battery life is anywhere from reasonable to abysmal - with an active BLE connection, the ESP32 consumes around 50mA - an M5StickC Plus2 would last around 4 hours @@ -245,7 +358,8 @@ the following libraries: - this might last 30 hours # Things To Do -- error handling is atrocious (it'll probably crash, then restart, which is OK, + +- error handling is ~atrocious~improving (it might crash, then restart, which is OK, the M5StickC boots quickly) - improve the device matching and connection abstractions - especially if more cameras get supported diff --git a/include/FurbleControl.h b/include/FurbleControl.h new file mode 100644 index 0000000..c5d09a5 --- /dev/null +++ b/include/FurbleControl.h @@ -0,0 +1,153 @@ +#ifndef FURBLE_CONTROL_H +#define FURBLE_CONTROL_H + +#include +#include + +#include + +namespace Furble { + +class Control { + public: + typedef enum { + CMD_SHUTTER_PRESS, + CMD_SHUTTER_RELEASE, + CMD_FOCUS_PRESS, + CMD_FOCUS_RELEASE, + CMD_GPS_UPDATE, + CMD_CONNECT, + CMD_DISCONNECT, + CMD_ERROR + } cmd_t; + + typedef enum { + /** No connections, waiting. */ + STATE_IDLE, + /** Initiate connections. */ + STATE_CONNECT, + /** Connections in progress. */ + STATE_CONNECTING, + /** Initial connection attempt failed. */ + STATE_CONNECT_FAILED, + /** All connections active. */ + STATE_ACTIVE + } state_t; + + class Target { + friend class Control; + + public: + ~Target(); + + Camera *getCamera(void) const; + cmd_t getCommand(void); + void sendCommand(cmd_t cmd); + void updateGPS(Camera::gps_t &gps, Camera::timesync_t ×ync); + + void task(void); + + protected: + Target(Camera *camera); + + private: + static constexpr UBaseType_t m_QueueLength = 8; + + QueueHandle_t m_Queue = NULL; + Furble::Camera *m_Camera = NULL; + Camera::gps_t m_GPS; + Camera::timesync_t m_Timesync; + }; + + static Control &getInstance(); + + Control(Control const &) = delete; + Control(Control &&) = delete; + Control &operator=(Control const &) = delete; + Control &operator=(Control &&) = delete; + + /** + * FreeRTOS control task function. + */ + void task(void); + + /** + * Send control command to active connections. + */ + BaseType_t sendCommand(cmd_t cmd); + + /** + * Update GPS and timesync values. + */ + BaseType_t updateGPS(Camera::gps_t &gps, Camera::timesync_t ×ync); + + /** + * Are all active cameras still connected? + */ + bool allConnected(void); + + /** + * Get list of connected targets. + */ + const std::vector> &getTargets(void); + + /** + * Connect to all active cameras. + */ + void connectAll(bool infiniteReconnect); + + /** + * Disconnect all connected cameras. + */ + void disconnect(void); + + /** + * Add specified camera to active target list. + */ + void addActive(Camera *camera); + + /** + * Get current camera connection attempt. + * + * @return Camera being connected otherwise nullptr. + */ + Camera *getConnectingCamera(void); + + /** Retrieve current control state. */ + state_t getState(void); + + /** Set transmit power. */ + void setPower(esp_power_level_t power); + + private: + Control() {}; + + /** Iterate over cameras and attempt connection. */ + state_t connectAll(void); + + static constexpr UBaseType_t m_QueueLength = 32; + + QueueHandle_t m_Queue = NULL; + std::mutex m_Mutex; + std::vector> m_Targets; + + bool m_InfiniteReconnect = false; + state_t m_State = STATE_IDLE; + + // Camera connects are serialised, the following tracks the last attempt + Camera *m_ConnectCamera = nullptr; + esp_power_level_t m_Power = ESP_PWR_LVL_P3; +}; + +}; // namespace Furble + +extern "C" { +void control_task(void *param); +} + +struct FurbleCtx { + Furble::Control *control; + bool cancelled; +}; + +#endif diff --git a/include/FurbleGPS.h b/include/FurbleGPS.h new file mode 100644 index 0000000..333db69 --- /dev/null +++ b/include/FurbleGPS.h @@ -0,0 +1,53 @@ +#ifndef FURBLE_GPS_H +#define FURBLE_GPS_H + +#include + +#include + +namespace Furble { +class GPS { + public: + static GPS &getInstance(); + + GPS(GPS const &) = delete; + GPS(GPS &&) = delete; + GPS &operator=(GPS const &) = delete; + GPS &operator=(GPS &&) = delete; + + static void init(void); + + void setIcon(lv_obj_t *icon); + bool isEnabled(void); + void reloadSetting(void); + void startService(void); + TinyGPSPlus &get(void); + + void update(void); + + private: + GPS() {}; + + static constexpr const uint32_t BAUD = 9600; +#if FURBLE_GROVE_CORE + static constexpr const int8_t RX = 22; + static constexpr const int8_t TX = 21; +#else + static constexpr const int8_t RX = 33; + static constexpr const int8_t TX = 32; +#endif + static constexpr const uint16_t SERVICE_MS = 250; + static constexpr const uint32_t MAX_AGE_MS = 60 * 1000; + + void serviceSerial(void); + + lv_obj_t *m_Icon = NULL; + lv_timer_t *m_Timer = NULL; + + bool m_Enabled = false; + bool m_HasFix = false; + TinyGPSPlus m_GPS; +}; +} // namespace Furble + +#endif diff --git a/include/FurbleSettings.h b/include/FurbleSettings.h new file mode 100644 index 0000000..bc6c442 --- /dev/null +++ b/include/FurbleSettings.h @@ -0,0 +1,51 @@ +#ifndef SETTINGS_H +#define SETTINGS_H + +#include + +#include + +#include "interval.h" + +namespace Furble { +class Settings { + public: + Settings() = delete; + ~Settings() = delete; + + typedef enum { + BRIGHTNESS, + INACTIVITY, + THEME, + TX_POWER, + GPS, + INTERVAL, + MULTICONNECT, + RECONNECT, + FAUXNY, + } type_t; + + typedef struct { + type_t type; + const char *name; + const char *key; + const char *nvs_namespace; + } setting_t; + + static void init(void); + + static const setting_t &get(type_t); + + template + static T load(type_t type); + + template + static void save(const type_t type, const T &value); + + private: + static const std::unordered_map m_Setting; + static Preferences m_Prefs; +}; +} // namespace Furble + +#endif diff --git a/include/FurbleSpinValue.h b/include/FurbleSpinValue.h new file mode 100644 index 0000000..72815b3 --- /dev/null +++ b/include/FurbleSpinValue.h @@ -0,0 +1,57 @@ +#ifndef FURBLE_SPINVALUE_H +#define FURBLE_SPINVALUE_H + +#include +#include + +namespace Furble { +class SpinValue { + public: + /** + * Enumeration of unit types. + * + * Value is serialised to non-volatile storage, do not change ordering. + */ + typedef enum { + UNIT_NIL = 0, // no units + UNIT_INF = 1, // ignore value, assume infinity + UNIT_MS = 2, // milliseconds + UNIT_SEC = 3, // seconds + UNIT_MIN = 4, // minutes + } unit_t; + + typedef struct { + uint32_t hours; + uint32_t minutes; + uint32_t seconds; + } hms_t; + + typedef struct __attribute__((packed)) { + uint16_t value; + unit_t unit; + } nvs_t; + + SpinValue(nvs_t &nvs); + + /** Convert to packed format suitable for non-volatile storage. */ + nvs_t toNVS(void); + + /** Convert SpinValue to milliseconds. */ + uint32_t toMilliseconds(void); + + /** Convert unit enumeration to string. */ + const char *getUnitString(void); + + /** Convert milliseconds to hours:minutes:seconds. */ + static hms_t toHMS(uint32_t ms); + + uint16_t m_Value; + unit_t m_Unit; + + private: + // Relies on the unit_t enumeration order + static constexpr std::array m_UnitMap = {"NIL", "INF", "msec", "secs", "mins"}; +}; +} // namespace Furble + +#endif diff --git a/include/FurbleUI.h b/include/FurbleUI.h new file mode 100644 index 0000000..b8033ad --- /dev/null +++ b/include/FurbleUI.h @@ -0,0 +1,328 @@ +#ifndef FURBLE_UI_H +#define FURBLE_UI_H + +#include +#include + +#include + +#include "FurbleControl.h" +#include "FurbleGPS.h" +#include "FurbleSettings.h" +#include "interval.h" + +namespace Furble { +class UI { + public: + UI(const interval_t &interval); + + void task(void); + + /** Set inactivity timeout in multiples of 30s. */ + void setInactivityTimeout(uint8_t timeout); + + /** Check and/or handle inactivity. */ + void processInactivity(void); + + /** Configure shutter control. */ + void configShutterControl(void); + + /** Configure menu control. */ + void configMenuControl(void); + + /** Display shutter intervalometer menu .*/ + void showShutterIntervalometer(bool show); + + /** Lock shutter. */ + void shutterLock(Control &control); + + /** Unlock shutter. */ + void shutterUnlock(Control &control); + + /** Is shutter locked? */ + bool isShutterLocked(void); + + bool m_FocusPressed = false; + + private: + typedef struct { + lv_obj_t *main; + lv_group_t *group; + lv_obj_t *page; + lv_obj_t *button; + } menu_t; + + typedef struct { + GPS *gps; + lv_obj_t *gpsIcon; + lv_obj_t *batteryIcon; + lv_obj_t *reconnectIcon; + lv_obj_t *gpsData; + } status_t; + + class Intervalometer { + public: + class Spinner { + public: + Spinner(Intervalometer *intervalometer, SpinValue::nvs_t nvs, bool infinite = false) + : m_Intervalometer {intervalometer}, m_SpinValue {nvs}, m_Infinite {infinite} {}; + + static constexpr const char *m_SpinDigitRoller = "0\n1\n2\n3\n4\n5\n6\n7\n8\n9"; + static constexpr const char *m_SpinUnitsRoller = "msec\nsecs\nmins"; + + void update(void); + void updateLabels(void); + + Intervalometer *m_Intervalometer; + SpinValue m_SpinValue; + lv_obj_t *m_Button; + lv_obj_t *m_Label; + lv_obj_t *m_Value; + const bool m_Infinite; // Can support infinite? + lv_obj_t *m_RowInfinite; + lv_obj_t *m_SwitchInfinite; + + lv_obj_t *m_RowSpinners; + // array of rollers, 0 = hundred, 1 = ten, 2 = one + std::array m_Roller = {nullptr, nullptr, nullptr}; + lv_obj_t *m_RollerUnit = nullptr; + }; + + typedef enum { + STATE_IDLE, + STATE_SHUTTER_OPEN, + STATE_WAIT, + STATE_FINISHED, + } state_t; + + Intervalometer(const interval_t &interval); + + void save(void); + + state_t m_State; + Spinner m_Count; + Spinner m_Delay; + Spinner m_Shutter; + }; + + typedef enum { MODE_SCAN, MODE_DELETE, MODE_CONNECT, MODE_MULTICONNECT } CameraListMode_t; + + static std::mutex m_Mutex; + + static const uint32_t m_KeyLeft = LV_KEY_LEFT; + static const uint32_t m_KeyEnter = LV_KEY_ENTER; + static const uint32_t m_KeyRight = LV_KEY_RIGHT; + + static constexpr const char *m_Title = FURBLE_STR; + static const uint8_t m_BrightnessSteps = 16; + + // main menu + static constexpr const char *m_ConnectStr = "Connect"; + static constexpr const char *m_ScanStr = "Scan"; + static constexpr const char *m_DeleteStr = "Delete"; + static constexpr const char *m_SettingsStr = "Settings"; + + // connected + static constexpr const char *m_ConnectedStr = "Connected"; + static constexpr const char *m_RemoteShutter = "Shutter"; + static constexpr const char *m_RemoteInterval = "Interval"; + // dodgy hack, add a space so map key is unique + static constexpr const char *m_IntervalometerRunStr = "Intervalometer "; + + // settings + static constexpr const char *m_BacklightStr = "Backlight"; + static constexpr const char *m_FeaturesStr = "Features"; + static constexpr const char *m_GPSStr = "GPS"; + static constexpr const char *m_IntervalometerStr = "Intervalometer"; + static constexpr const char *m_ThemeStr = "Theme"; + static constexpr const char *m_TransmitPowerStr = "Transmit Power"; + static constexpr const char *m_AboutStr = "About"; + + // settings->gps + static constexpr const char *m_GPSDataStr = "GPS Data"; + + // settings->intervalometer + static constexpr const char *m_IntervalCountStr = "Count"; + static constexpr const char *m_IntervalDelayStr = "Delay"; + static constexpr const char *m_IntervalShutterStr = "Shutter"; + + static constexpr uint8_t BYTES_PER_PIXEL = (LV_COLOR_FORMAT_GET_SIZE(LV_COLOR_FORMAT_RGB565)); + static constexpr int32_t MAX_WIDTH = 320; + static constexpr int32_t MAX_HEIGHT = 240; + + typedef std::array + display_buffer_t; + + static LV_ATTRIBUTE_MEM_ALIGN display_buffer_t m_Buffer1; + static LV_ATTRIBUTE_MEM_ALIGN display_buffer_t m_Buffer2; + + static lv_obj_t *m_ConnectMessageBox; + static lv_obj_t *m_ConnectLabel; + static lv_obj_t *m_ConnectBar; + static lv_obj_t *m_ConnectCancel; + static lv_timer_t *m_ConnectTimer; + static lv_timer_t *m_IntervalTimer; + static lv_obj_t *m_IntervalStateLabel; + static lv_obj_t *m_IntervalCountLabel; + static lv_obj_t *m_IntervalRemainingLabel; + static lv_timer_t *m_IntervalPageRefresh; + static uint32_t m_IntervalNext; + + GPS &m_GPS; + + int32_t m_Width; + int32_t m_Height; + + uint8_t m_MinimumBrightness; + + lv_indev_t *m_ButtonL; + lv_indev_t *m_ButtonO; + lv_indev_t *m_ButtonR; + lv_indev_t *m_Touch; + lv_group_t *m_Group; + + lv_display_t *m_Display = nullptr; + lv_obj_t *m_Screen = nullptr; + lv_obj_t *m_Root = nullptr; + lv_obj_t *m_Header = nullptr; + lv_obj_t *m_Content = nullptr; + lv_obj_t *m_NavBar = nullptr; + + lv_obj_t *m_Left = nullptr; + lv_obj_t *m_OK = nullptr; + lv_obj_t *m_Right = nullptr; + + lv_obj_t *m_IntervalStart = nullptr; + Intervalometer m_Intervalometer; + + status_t m_Status; + bool m_ShutterLock = false; + uint32_t m_InactivityTimeout; + + static menu_t m_MainMenu; + + static std::unordered_map m_Menu; + + lv_obj_t *m_PowerOff = nullptr; + + static bool m_PMICHack; + static bool m_PMICClicked; + static void buttonPWRRead(lv_indev_t *drv, lv_indev_data_t *data); + static void buttonPEKRead(lv_indev_t *drv, lv_indev_data_t *data); + static void buttonARead(lv_indev_t *drv, lv_indev_data_t *data); + static void buttonBRead(lv_indev_t *drv, lv_indev_data_t *data); + static void buttonCRead(lv_indev_t *drv, lv_indev_data_t *data); + static void touchRead(lv_indev_t *drv, lv_indev_data_t *data); + + /** Flush display. */ + static void displayFlush(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map); + + /** LVGL tick function. */ + static uint32_t tick(void); + + void initInputDevices(void); + + static void setTheme(std::string name); + + void prepareShutterControl(void); + + /** Add icon to the root window header. */ + lv_obj_t *addIcon(const char *symbol); + + /** Set the icon symbol in the root window header. */ + void setIcon(lv_obj_t *icon, const char *symbol); + + /** Add a menu item. */ + static lv_obj_t *addMenuItem(const menu_t &menu, + const char *icon, + const char *text, + bool checkbox = false); + + /** Add a menu switch item. */ + void addSettingItem(lv_obj_t *page, const char *symbol, Settings::type_t setting); + + /** Add camera menu item. */ + static lv_obj_t *addCameraItem(Camera *camera, const menu_t &menu, const CameraListMode_t mode); + + /** Create a menu entry. */ + menu_t &addMenu(const char *entry, + const char *symbol, + bool button = true, + const menu_t &parent = m_MainMenu); + + /** Add the main menu to the root window content. */ + void addMainMenu(void); + + /** Add the 'Connect' menu entry. */ + void addConnectMenu(void); + + /** Add the 'Scan' menu entry. */ + void addScanMenu(void); + + /** Add the 'Delete' menu entry. */ + void addDeleteMenu(void); + + /** Add 'GPS Data' page. */ + void addGPSMenu(const menu_t &parent); + + /** Add the 'Features' menu entry. */ + void addFeaturesMenu(const menu_t &parent); + + /** Add the 'Intervalometer' menu entry. */ + void addIntervalometerMenu(const menu_t &parent); + + /** Add spinner menu item entry. */ + lv_obj_t *addSpinItem(lv_obj_t *page, const char *item, Intervalometer::Spinner &spinner); + + /** Add the spinner page menu entry. */ + void addSpinnerPage(const menu_t &parent, const char *item, Intervalometer::Spinner &spinner); + + void addBacklightMenu(const menu_t &parent); + + void addThemeMenu(const menu_t &parent); + + void addTransmitPowerMenu(const menu_t &parent); + + void addAboutMenu(const menu_t &parent); + + /** Add the 'Settings' menu entry. */ + void addSettingsMenu(void); + + /** Add 'Connected' menu. */ + menu_t &addConnectedMenu(void); + + /** Update entries in connect page. */ + static void updateItems(const menu_t &menu); + + /** Stop GPS Data timer. */ + static void gpsDataStop(lv_event_t *e); + + /** Handle connection request. */ + static void doConnect(lv_event_t *e); + + /** Handle disconnection. */ + static void doDisconnect(void); + + /** Refresh deletion items. */ + static void refreshDelete(void); + + /** Connection timer handler. */ + static void connectTimerHandler(lv_timer_t *timer); + + /** Intervalometer timer handler. */ + static void intervalometer(lv_timer_t *timer); + + /** Handle shutter event. */ + static void handleShutter(lv_event_t *e); + + /** Handle focus event. */ + static void handleFocus(lv_event_t *e); + + /** Handle shutter lock event. */ + static void handleShutterLock(lv_event_t *e); +}; +} // namespace Furble + +void vUITask(void *param); + +#endif diff --git a/include/furble_control.h b/include/furble_control.h deleted file mode 100644 index 2146a50..0000000 --- a/include/furble_control.h +++ /dev/null @@ -1,101 +0,0 @@ -#ifndef FURBLE_CONTROL_H -#define FURBLE_CONTROL_H - -#include - -#define CONTROL_CMD_QUEUE_LEN (32) -#define TARGET_CMD_QUEUE_LEN (8) - -typedef enum { - CONTROL_CMD_SHUTTER_PRESS, - CONTROL_CMD_SHUTTER_RELEASE, - CONTROL_CMD_FOCUS_PRESS, - CONTROL_CMD_FOCUS_RELEASE, - CONTROL_CMD_GPS_UPDATE, - CONTROL_CMD_CONNECT, - CONTROL_CMD_DISCONNECT, - CONTROL_CMD_ERROR -} control_cmd_t; - -namespace Furble { - -class Control { - public: - class Target { - public: - Target(Camera *camera); - ~Target(); - - Camera *getCamera(void); - control_cmd_t getCommand(void); - void sendCommand(control_cmd_t cmd); - void updateGPS(Camera::gps_t &gps, Camera::timesync_t ×ync); - - void task(void); - - private: - QueueHandle_t m_Queue = NULL; - Furble::Camera *m_Camera = NULL; - Camera::gps_t m_GPS; - Camera::timesync_t m_Timesync; - }; - - Control(void); - ~Control(); - - /** - * FreeRTOS control task function. - */ - void task(void); - - /** - * Send control command to active connections. - */ - BaseType_t sendCommand(control_cmd_t cmd); - - /** - * Update GPS and timesync values. - */ - BaseType_t updateGPS(Camera::gps_t &gps, Camera::timesync_t ×ync); - - /** - * Are all active cameras still connected? - */ - bool allConnected(void); - - /** - * Get list of connected targets. - */ - const std::vector> &getTargets(void); - - /** - * Connect to the specified camera. - */ - void connect(Camera *, esp_power_level_t power); - - /** - * Disconnect all connected cameras. - */ - void disconnect(void); - - /** - * Add specified camera to active target list. - */ - void addActive(Camera *camera); - - private: - QueueHandle_t m_Queue = NULL; - std::vector> m_Targets; - - // Camera connects are serialised, the following track the last attempt - Camera *m_ConnectCamera = nullptr; - esp_power_level_t m_Power; -}; - -}; // namespace Furble - -extern "C" { -void control_task(void *param); -} - -#endif diff --git a/include/furble_gps.h b/include/furble_gps.h deleted file mode 100644 index 9286c7d..0000000 --- a/include/furble_gps.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef FURBLE_GPS_H -#define FURBLE_GPS_H - -#include - -#include "furble_control.h" - -extern TinyGPSPlus furble_gps; - -extern bool furble_gps_enable; - -void furble_gps_init(void); -void furble_gps_update(Furble::Control *control); - -#endif diff --git a/include/furble_ui.h b/include/furble_ui.h deleted file mode 100644 index 46be1e2..0000000 --- a/include/furble_ui.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef FURBLE_UI_H -#define FURBLE_UI_H - -#include "furble_control.h" - -struct FurbleCtx { - Furble::Control *control; - bool cancelled; -}; - -void vUITask(void *param); - -#endif diff --git a/include/interval.h b/include/interval.h index b655dda..97f4e6f 100644 --- a/include/interval.h +++ b/include/interval.h @@ -1,24 +1,20 @@ #ifndef INTERVAL_H #define INTERVAL_H -#include +#include "FurbleSpinValue.h" -#include "furble_ui.h" -#include "spinner.h" +namespace Furble { -#define INTERVAL_DEFAULT_COUNT 10 -#define INTERVAL_DEFAULT_COUNT_UNIT SPIN_UNIT_NIL -#define INTERVAL_DEFAULT_SHUTTER 30 -#define INTERVAL_DEFAULT_SHUTTER_UNIT SPIN_UNIT_MS -#define INTERVAL_DEFAULT_DELAY 15 -#define INTERVAL_DEFAULT_DELAY_UNIT SPIN_UNIT_SEC +constexpr SpinValue::nvs_t INTERVAL_DEFAULT_COUNT = {10, SpinValue::UNIT_NIL}; +constexpr SpinValue::nvs_t INTERVAL_DEFAULT_DELAY = {15, SpinValue::UNIT_SEC}; +constexpr SpinValue::nvs_t INTERVAL_DEFAULT_SHUTTER = {30, SpinValue::UNIT_MS}; -struct __attribute__((packed)) interval_t { - SpinValue count; - SpinValue delay; - SpinValue shutter; -}; +typedef struct __attribute__((packed)) { + SpinValue::nvs_t count; + SpinValue::nvs_t delay; + SpinValue::nvs_t shutter; +} interval_t; -void remote_interval(FurbleCtx *ctx); +} // namespace Furble #endif diff --git a/include/lv_conf.h b/include/lv_conf.h new file mode 100644 index 0000000..6a18d79 --- /dev/null +++ b/include/lv_conf.h @@ -0,0 +1,998 @@ +/** + * @file lv_conf.h + * Configuration file for v9.0.1-dev + */ + +/* + * Copy this file as `lv_conf.h` + * 1. simply next to the `lvgl` folder + * 2. or any other places and + * - define `LV_CONF_INCLUDE_SIMPLE` + * - add the path as include path + */ + +/* clang-format off */ +#if 1 /*Set it to "1" to enable content*/ + +#ifndef LV_CONF_H +#define LV_CONF_H + +/*==================== + COLOR SETTINGS + *====================*/ + +/*Color depth: 8 (A8), 16 (RGB565), 24 (RGB888), 32 (XRGB8888)*/ +#define LV_COLOR_DEPTH 16 + +/*========================= + STDLIB WRAPPER SETTINGS + *=========================*/ + +/* Possible values + * - LV_STDLIB_BUILTIN: LVGL's built in implementation + * - LV_STDLIB_CLIB: Standard C functions, like malloc, strlen, etc + * - LV_STDLIB_MICROPYTHON: MicroPython implementation + * - LV_STDLIB_RTTHREAD: RT-Thread implementation + * - LV_STDLIB_CUSTOM: Implement the functions externally + */ +#define LV_USE_STDLIB_MALLOC LV_STDLIB_CLIB +#define LV_USE_STDLIB_STRING LV_STDLIB_CLIB +#define LV_USE_STDLIB_SPRINTF LV_STDLIB_CLIB + + +#if LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN + /*Size of the memory available for `lv_malloc()` in bytes (>= 2kB)*/ + #define LV_MEM_SIZE (64 * 1024U) /*[bytes]*/ + + /*Size of the memory expand for `lv_malloc()` in bytes*/ + #define LV_MEM_POOL_EXPAND_SIZE 0 + + /*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/ + #define LV_MEM_ADR 0 /*0: unused*/ + /*Instead of an address give a memory allocator that will be called to get a memory pool for LVGL. E.g. my_malloc*/ + #if LV_MEM_ADR == 0 + #undef LV_MEM_POOL_INCLUDE + #undef LV_MEM_POOL_ALLOC + #endif +#endif /*LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN*/ + +/*==================== + HAL SETTINGS + *====================*/ + +/*Default display refresh, input device read and animation step period.*/ +#define LV_DEF_REFR_PERIOD 33 /*[ms]*/ + +/*Default Dot Per Inch. Used to initialize default sizes such as widgets sized, style paddings. + *(Not so important, you can adjust it to modify default sizes and spaces)*/ +#if defined(FURBLE_M5STICKC) +#define LV_DPI_DEF (64) +#elif defined(FURBLE_M5STICKC_PLUS) +#define LV_DPI_DEF (128) +#elif defined(FURBLE_M5COREX) +#define LV_DPI_DEF (96) +#else +#error "furble: Unknown platform" +#endif + +/*================= + * OPERATING SYSTEM + *=================*/ +/*Select an operating system to use. Possible options: + * - LV_OS_NONE + * - LV_OS_PTHREAD + * - LV_OS_FREERTOS + * - LV_OS_CMSIS_RTOS2 + * - LV_OS_RTTHREAD + * - LV_OS_WINDOWS + * - LV_OS_CUSTOM */ +#define LV_USE_OS LV_OS_NONE + +#if LV_USE_OS == LV_OS_CUSTOM + #define LV_OS_CUSTOM_INCLUDE +#endif + +/*======================== + * RENDERING CONFIGURATION + *========================*/ + +/*Align the stride of all layers and images to this bytes*/ +#define LV_DRAW_BUF_STRIDE_ALIGN 1 + +/*Align the start address of draw_buf addresses to this bytes*/ +#define LV_DRAW_BUF_ALIGN 4 + +#define LV_USE_DRAW_SW 1 +#if LV_USE_DRAW_SW == 1 + +#define LV_DRAW_SW_SUPPORT_RGB565 1 + #define LV_DRAW_SW_SUPPORT_RGB565A8 1 + #define LV_DRAW_SW_SUPPORT_RGB888 0 + #define LV_DRAW_SW_SUPPORT_XRGB8888 0 + #define LV_DRAW_SW_SUPPORT_ARGB8888 0 + #define LV_DRAW_SW_SUPPORT_L8 0 + #define LV_DRAW_SW_SUPPORT_AL88 0 + #define LV_DRAW_SW_SUPPORT_A8 0 + #define LV_DRAW_SW_SUPPORT_I1 0 + + /* Set the number of draw unit. + * > 1 requires an operating system enabled in `LV_USE_OS` + * > 1 means multiply threads will render the screen in parallel */ + #define LV_DRAW_SW_DRAW_UNIT_CNT 1 + + /* Use Arm-2D to accelerate the sw render */ + #define LV_USE_DRAW_ARM2D_SYNC 0 + + /* If a widget has `style_opa < 255` (not `bg_opa`, `text_opa` etc) or not NORMAL blend mode + * it is buffered into a "simple" layer before rendering. The widget can be buffered in smaller chunks. + * "Transformed layers" (if `transform_angle/zoom` are set) use larger buffers + * and can't be drawn in chunks. */ + + /*The target buffer size for simple layer chunks.*/ + #define LV_DRAW_SW_LAYER_SIMPLE_BUF_SIZE (24 * 1024) /*[bytes]*/ + + /* 0: use a simple renderer capable of drawing only simple rectangles with gradient, images, texts, and straight lines only + * 1: use a complex renderer capable of drawing rounded corners, shadow, skew lines, and arcs too */ + #define LV_DRAW_SW_COMPLEX 1 + + #if LV_DRAW_SW_COMPLEX == 1 + /*Allow buffering some shadow calculation. + *LV_DRAW_SW_SHADOW_CACHE_SIZE is the max. shadow size to buffer, where shadow size is `shadow_width + radius` + *Caching has LV_DRAW_SW_SHADOW_CACHE_SIZE^2 RAM cost*/ + #define LV_DRAW_SW_SHADOW_CACHE_SIZE 0 + + /* Set number of maximally cached circle data. + * The circumference of 1/4 circle are saved for anti-aliasing + * radius * 4 bytes are used per circle (the most often used radiuses are saved) + * 0: to disable caching */ + #define LV_DRAW_SW_CIRCLE_CACHE_SIZE 4 + #endif + + #define LV_USE_DRAW_SW_ASM LV_DRAW_SW_ASM_NONE + + #if LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_CUSTOM + #define LV_DRAW_SW_ASM_CUSTOM_INCLUDE "" + #endif +#endif + +/* Use NXP's VG-Lite GPU on iMX RTxxx platforms. */ +#define LV_USE_DRAW_VGLITE 0 + +#if LV_USE_DRAW_VGLITE + /* Enable blit quality degradation workaround recommended for screen's dimension > 352 pixels. */ + #define LV_USE_VGLITE_BLIT_SPLIT 0 + + #if LV_USE_OS + /* Enable VGLite draw async. Queue multiple tasks and flash them once to the GPU. */ + #define LV_USE_VGLITE_DRAW_ASYNC 1 + #endif + + /* Enable VGLite asserts. */ + #define LV_USE_VGLITE_ASSERT 0 +#endif + +/* Use NXP's PXP on iMX RTxxx platforms. */ +#define LV_USE_DRAW_PXP 0 + +#if LV_USE_DRAW_PXP + /* Enable PXP asserts. */ + #define LV_USE_PXP_ASSERT 0 +#endif + +/* Use Renesas Dave2D on RA platforms. */ +#define LV_USE_DRAW_DAVE2D 0 + +/* Draw using cached SDL textures*/ +#define LV_USE_DRAW_SDL 0 + +/* Use VG-Lite GPU. */ +#define LV_USE_DRAW_VG_LITE 0 + +#if LV_USE_DRAW_VG_LITE +/* Enable VG-Lite custom external 'gpu_init()' function */ +#define LV_VG_LITE_USE_GPU_INIT 0 + +/* Enable VG-Lite assert. */ +#define LV_VG_LITE_USE_ASSERT 0 + +/* VG-Lite flush commit trigger threshold. GPU will try to batch these many draw tasks. */ +#define LV_VG_LITE_FLUSH_MAX_COUNT 8 + +/* Enable border to simulate shadow + * NOTE: which usually improves performance, + * but does not guarantee the same rendering quality as the software. */ +#define LV_VG_LITE_USE_BOX_SHADOW 0 + +#endif + +/*======================= + * FEATURE CONFIGURATION + *=======================*/ + +/*------------- + * Logging + *-----------*/ + +/*Enable the log module*/ +#define LV_USE_LOG 0 +#if LV_USE_LOG + + /*How important log should be added: + *LV_LOG_LEVEL_TRACE A lot of logs to give detailed information + *LV_LOG_LEVEL_INFO Log important events + *LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't cause a problem + *LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail + *LV_LOG_LEVEL_USER Only logs added by the user + *LV_LOG_LEVEL_NONE Do not log anything*/ + #define LV_LOG_LEVEL LV_LOG_LEVEL_WARN + + /*1: Print the log with 'printf'; + *0: User need to register a callback with `lv_log_register_print_cb()`*/ + #define LV_LOG_PRINTF 0 + + /*1: Enable print timestamp; + *0: Disable print timestamp*/ + #define LV_LOG_USE_TIMESTAMP 1 + + /*1: Print file and line number of the log; + *0: Do not print file and line number of the log*/ + #define LV_LOG_USE_FILE_LINE 1 + + /*Enable/disable LV_LOG_TRACE in modules that produces a huge number of logs*/ + #define LV_LOG_TRACE_MEM 1 + #define LV_LOG_TRACE_TIMER 1 + #define LV_LOG_TRACE_INDEV 1 + #define LV_LOG_TRACE_DISP_REFR 1 + #define LV_LOG_TRACE_EVENT 1 + #define LV_LOG_TRACE_OBJ_CREATE 1 + #define LV_LOG_TRACE_LAYOUT 1 + #define LV_LOG_TRACE_ANIM 1 + #define LV_LOG_TRACE_CACHE 1 + +#endif /*LV_USE_LOG*/ + +/*------------- + * Asserts + *-----------*/ + +/*Enable asserts if an operation is failed or an invalid data is found. + *If LV_USE_LOG is enabled an error message will be printed on failure*/ +#define LV_USE_ASSERT_NULL 1 /*Check if the parameter is NULL. (Very fast, recommended)*/ +#define LV_USE_ASSERT_MALLOC 1 /*Checks is the memory is successfully allocated or no. (Very fast, recommended)*/ +#define LV_USE_ASSERT_STYLE 1 /*Check if the styles are properly initialized. (Very fast, recommended)*/ +#define LV_USE_ASSERT_MEM_INTEGRITY 0 /*Check the integrity of `lv_mem` after critical operations. (Slow)*/ +#define LV_USE_ASSERT_OBJ 0 /*Check the object's type and existence (e.g. not deleted). (Slow)*/ + +/*Add a custom handler when assert happens e.g. to restart the MCU*/ +#define LV_ASSERT_HANDLER_INCLUDE +#define LV_ASSERT_HANDLER while(1); /*Halt by default*/ + +/*------------- + * Debug + *-----------*/ + +/*1: Draw random colored rectangles over the redrawn areas*/ +#define LV_USE_REFR_DEBUG 0 + +/*1: Draw a red overlay for ARGB layers and a green overlay for RGB layers*/ +#define LV_USE_LAYER_DEBUG 0 + +/*1: Draw overlays with different colors for each draw_unit's tasks. + *Also add the index number of the draw unit on white background. + *For layers add the index number of the draw unit on black background.*/ +#define LV_USE_PARALLEL_DRAW_DEBUG 0 + +/*------------- + * Others + *-----------*/ + +#define LV_ENABLE_GLOBAL_CUSTOM 0 +#if LV_ENABLE_GLOBAL_CUSTOM + /*Header to include for the custom 'lv_global' function"*/ + #define LV_GLOBAL_CUSTOM_INCLUDE +#endif + +/*Default cache size in bytes. + *Used by image decoders such as `lv_lodepng` to keep the decoded image in the memory. + *If size is not set to 0, the decoder will fail to decode when the cache is full. + *If size is 0, the cache function is not enabled and the decoded mem will be released immediately after use.*/ +#define LV_CACHE_DEF_SIZE 0 + +/*Default number of image header cache entries. The cache is used to store the headers of images + *The main logic is like `LV_CACHE_DEF_SIZE` but for image headers.*/ +#define LV_IMAGE_HEADER_CACHE_DEF_CNT 0 + +/*Number of stops allowed per gradient. Increase this to allow more stops. + *This adds (sizeof(lv_color_t) + 1) bytes per additional stop*/ +#define LV_GRADIENT_MAX_STOPS 2 + +/* Adjust color mix functions rounding. GPUs might calculate color mix (blending) differently. + * 0: round down, 64: round up from x.75, 128: round up from half, 192: round up from x.25, 254: round up */ +#define LV_COLOR_MIX_ROUND_OFS 0 + +/* Add 2 x 32 bit variables to each lv_obj_t to speed up getting style properties */ +#define LV_OBJ_STYLE_CACHE 0 + +/* Add `id` field to `lv_obj_t` */ +#define LV_USE_OBJ_ID 0 + +/* Use lvgl builtin method for obj ID */ +#define LV_USE_OBJ_ID_BUILTIN 0 + +/*Use obj property set/get API*/ +#define LV_USE_OBJ_PROPERTY 0 + +/* VG-Lite Simulator */ +/*Requires: LV_USE_THORVG_INTERNAL or LV_USE_THORVG_EXTERNAL */ +#define LV_USE_VG_LITE_THORVG 0 + +#if LV_USE_VG_LITE_THORVG + + /*Enable LVGL's blend mode support*/ + #define LV_VG_LITE_THORVG_LVGL_BLEND_SUPPORT 0 + + /*Enable YUV color format support*/ + #define LV_VG_LITE_THORVG_YUV_SUPPORT 0 + + /*Enable 16 pixels alignment*/ + #define LV_VG_LITE_THORVG_16PIXELS_ALIGN 1 + + /*Buffer address alignment*/ + #define LV_VG_LITE_THORVG_BUF_ADDR_ALIGN 64 + + /*Enable multi-thread render*/ + #define LV_VG_LITE_THORVG_THREAD_RENDER 0 + +#endif + +/*===================== + * COMPILER SETTINGS + *====================*/ + +/*For big endian systems set to 1*/ +#define LV_BIG_ENDIAN_SYSTEM 0 + +/*Define a custom attribute to `lv_tick_inc` function*/ +#define LV_ATTRIBUTE_TICK_INC + +/*Define a custom attribute to `lv_timer_handler` function*/ +#define LV_ATTRIBUTE_TIMER_HANDLER + +/*Define a custom attribute to `lv_display_flush_ready` function*/ +#define LV_ATTRIBUTE_FLUSH_READY + +/*Required alignment size for buffers*/ +#define LV_ATTRIBUTE_MEM_ALIGN_SIZE 1 + +/*Will be added where memories needs to be aligned (with -Os data might not be aligned to boundary by default). + * E.g. __attribute__((aligned(4)))*/ +#define LV_ATTRIBUTE_MEM_ALIGN + +/*Attribute to mark large constant arrays for example font's bitmaps*/ +#define LV_ATTRIBUTE_LARGE_CONST + +/*Compiler prefix for a big array declaration in RAM*/ +#define LV_ATTRIBUTE_LARGE_RAM_ARRAY + +/*Place performance critical functions into a faster memory (e.g RAM)*/ +#define LV_ATTRIBUTE_FAST_MEM + +/*Export integer constant to binding. This macro is used with constants in the form of LV_ that + *should also appear on LVGL binding API such as Micropython.*/ +#define LV_EXPORT_CONST_INT(int_value) struct _silence_gcc_warning /*The default value just prevents GCC warning*/ + +/*Prefix all global extern data with this*/ +#define LV_ATTRIBUTE_EXTERN_DATA + +/* Use `float` as `lv_value_precise_t` */ +#define LV_USE_FLOAT 0 + +/*================== + * FONT USAGE + *===================*/ + +/*Montserrat fonts with ASCII range and some symbols using bpp = 4 + *https://fonts.google.com/specimen/Montserrat*/ +#define LV_FONT_MONTSERRAT_8 0 +#define LV_FONT_MONTSERRAT_10 0 +#define LV_FONT_MONTSERRAT_12 1 +#define LV_FONT_MONTSERRAT_14 0 +#define LV_FONT_MONTSERRAT_16 1 +#define LV_FONT_MONTSERRAT_18 0 +#define LV_FONT_MONTSERRAT_20 0 +#define LV_FONT_MONTSERRAT_22 1 +#define LV_FONT_MONTSERRAT_24 0 +#define LV_FONT_MONTSERRAT_26 0 +#define LV_FONT_MONTSERRAT_28 0 +#define LV_FONT_MONTSERRAT_30 0 +#define LV_FONT_MONTSERRAT_32 0 +#define LV_FONT_MONTSERRAT_34 0 +#define LV_FONT_MONTSERRAT_36 0 +#define LV_FONT_MONTSERRAT_38 0 +#define LV_FONT_MONTSERRAT_40 0 +#define LV_FONT_MONTSERRAT_42 0 +#define LV_FONT_MONTSERRAT_44 0 +#define LV_FONT_MONTSERRAT_46 0 +#define LV_FONT_MONTSERRAT_48 0 + +/*Demonstrate special features*/ +#define LV_FONT_MONTSERRAT_28_COMPRESSED 0 /*bpp = 3*/ +#define LV_FONT_DEJAVU_16_PERSIAN_HEBREW 0 /*Hebrew, Arabic, Persian letters and all their forms*/ +#define LV_FONT_SIMSUN_16_CJK 0 /*1000 most common CJK radicals*/ + +/*Pixel perfect monospace fonts*/ +#define LV_FONT_UNSCII_8 0 +#define LV_FONT_UNSCII_16 0 + +/*Optionally declare custom fonts here. + *You can use these fonts as default font too and they will be available globally. + *E.g. #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) LV_FONT_DECLARE(my_font_2)*/ +#define LV_FONT_CUSTOM_DECLARE + +/*Always set a default font*/ +#if defined(FURBLE_M5STICKC) +#define LV_FONT_DEFAULT &lv_font_montserrat_12 +#elif defined(FURBLE_M5STICKC_PLUS) +#define LV_FONT_DEFAULT &lv_font_montserrat_16 +#elif defined(FURBLE_M5COREX) +#define LV_FONT_DEFAULT &lv_font_montserrat_22 +#endif + +/*Enable handling large font and/or fonts with a lot of characters. + *The limit depends on the font size, font face and bpp. + *Compiler error will be triggered if a font needs it.*/ +#define LV_FONT_FMT_TXT_LARGE 0 + +/*Enables/disables support for compressed fonts.*/ +#define LV_USE_FONT_COMPRESSED 0 + +/*Enable drawing placeholders when glyph dsc is not found*/ +#define LV_USE_FONT_PLACEHOLDER 1 + +/*================= + * TEXT SETTINGS + *=================*/ + +/** + * Select a character encoding for strings. + * Your IDE or editor should have the same character encoding + * - LV_TXT_ENC_UTF8 + * - LV_TXT_ENC_ASCII + */ +#define LV_TXT_ENC LV_TXT_ENC_UTF8 + +/*Can break (wrap) texts on these chars*/ +#define LV_TXT_BREAK_CHARS " ,.;:-_)]}" + +/*If a word is at least this long, will break wherever "prettiest" + *To disable, set to a value <= 0*/ +#define LV_TXT_LINE_BREAK_LONG_LEN 0 + +/*Minimum number of characters in a long word to put on a line before a break. + *Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/ +#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3 + +/*Minimum number of characters in a long word to put on a line after a break. + *Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/ +#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 3 + +/*Support bidirectional texts. Allows mixing Left-to-Right and Right-to-Left texts. + *The direction will be processed according to the Unicode Bidirectional Algorithm: + *https://www.w3.org/International/articles/inline-bidi-markup/uba-basics*/ +#define LV_USE_BIDI 0 +#if LV_USE_BIDI + /*Set the default direction. Supported values: + *`LV_BASE_DIR_LTR` Left-to-Right + *`LV_BASE_DIR_RTL` Right-to-Left + *`LV_BASE_DIR_AUTO` detect texts base direction*/ + #define LV_BIDI_BASE_DIR_DEF LV_BASE_DIR_AUTO +#endif + +/*Enable Arabic/Persian processing + *In these languages characters should be replaced with an other form based on their position in the text*/ +#define LV_USE_ARABIC_PERSIAN_CHARS 0 + +/*================== + * WIDGETS + *================*/ + +/*Documentation of the widgets: https://docs.lvgl.io/latest/en/html/widgets/index.html*/ + +#define LV_WIDGETS_HAS_DEFAULT_VALUE 1 + +#define LV_USE_ANIMIMG 0 + +#define LV_USE_ARC 0 + +#define LV_USE_BAR 1 + +#define LV_USE_BUTTON 1 + +#define LV_USE_BUTTONMATRIX 1 + +#define LV_USE_CALENDAR 0 +#if LV_USE_CALENDAR + #define LV_CALENDAR_WEEK_STARTS_MONDAY 0 + #if LV_CALENDAR_WEEK_STARTS_MONDAY + #define LV_CALENDAR_DEFAULT_DAY_NAMES {"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"} + #else + #define LV_CALENDAR_DEFAULT_DAY_NAMES {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"} + #endif + + #define LV_CALENDAR_DEFAULT_MONTH_NAMES {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"} + #define LV_USE_CALENDAR_HEADER_ARROW 1 + #define LV_USE_CALENDAR_HEADER_DROPDOWN 1 +#endif /*LV_USE_CALENDAR*/ + +#define LV_USE_CANVAS 0 + +#define LV_USE_CHART 0 + +#define LV_USE_CHECKBOX 1 + +#define LV_USE_DROPDOWN 0 /*Requires: lv_label*/ + +#define LV_USE_IMAGE 1 /*Requires: lv_label*/ + +#define LV_USE_IMAGEBUTTON 0 + +#define LV_USE_KEYBOARD 0 + +#define LV_USE_LABEL 1 +#if LV_USE_LABEL + #define LV_LABEL_TEXT_SELECTION 1 /*Enable selecting text of the label*/ + #define LV_LABEL_LONG_TXT_HINT 1 /*Store some extra info in labels to speed up drawing of very long texts*/ + #define LV_LABEL_WAIT_CHAR_COUNT 3 /*The count of wait chart*/ +#endif + +#define LV_USE_LED 0 + +#define LV_USE_LINE 0 + +#define LV_USE_LIST 0 + +#define LV_USE_MENU 1 + +#define LV_USE_MSGBOX 1 + +#define LV_USE_ROLLER 1 /*Requires: lv_label*/ + +#define LV_USE_SCALE 0 + +#define LV_USE_SLIDER 1 /*Requires: lv_bar*/ + +#define LV_USE_SPAN 0 +#if LV_USE_SPAN + /*A line text can contain maximum num of span descriptor */ + #define LV_SPAN_SNIPPET_STACK_SIZE 64 +#endif + +#define LV_USE_SPINBOX 1 + +#define LV_USE_SPINNER 0 + +#define LV_USE_SWITCH 1 + +#define LV_USE_TEXTAREA 1 /*Requires: lv_label*/ +#if LV_USE_TEXTAREA != 0 + #define LV_TEXTAREA_DEF_PWD_SHOW_TIME 1500 /*ms*/ +#endif + +#define LV_USE_TABLE 0 + +#define LV_USE_TABVIEW 0 + +#define LV_USE_TILEVIEW 0 + +#define LV_USE_WIN 1 + +/*================== + * THEMES + *==================*/ + +/*A simple, impressive and very complete theme*/ +#define LV_USE_THEME_DEFAULT 1 +#if LV_USE_THEME_DEFAULT + + /*0: Light mode; 1: Dark mode*/ + #define LV_THEME_DEFAULT_DARK 1 + + /*1: Enable grow on press*/ + #define LV_THEME_DEFAULT_GROW 1 + + /*Default transition time in [ms]*/ + #define LV_THEME_DEFAULT_TRANSITION_TIME 80 +#endif /*LV_USE_THEME_DEFAULT*/ + +/*A very simple theme that is a good starting point for a custom theme*/ +#define LV_USE_THEME_SIMPLE 0 + +/*A theme designed for monochrome displays*/ +#define LV_USE_THEME_MONO 0 + +/*================== + * LAYOUTS + *==================*/ + +/*A layout similar to Flexbox in CSS.*/ +#define LV_USE_FLEX 1 + +/*A layout similar to Grid in CSS.*/ +#define LV_USE_GRID 0 + +/*==================== + * 3RD PARTS LIBRARIES + *====================*/ + +/*File system interfaces for common APIs */ + +/*API for fopen, fread, etc*/ +#define LV_USE_FS_STDIO 0 +#if LV_USE_FS_STDIO + #define LV_FS_STDIO_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #define LV_FS_STDIO_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ + #define LV_FS_STDIO_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ +#endif + +/*API for open, read, etc*/ +#define LV_USE_FS_POSIX 0 +#if LV_USE_FS_POSIX + #define LV_FS_POSIX_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #define LV_FS_POSIX_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ + #define LV_FS_POSIX_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ +#endif + +/*API for CreateFile, ReadFile, etc*/ +#define LV_USE_FS_WIN32 0 +#if LV_USE_FS_WIN32 + #define LV_FS_WIN32_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #define LV_FS_WIN32_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ + #define LV_FS_WIN32_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ +#endif + +/*API for FATFS (needs to be added separately). Uses f_open, f_read, etc*/ +#define LV_USE_FS_FATFS 0 +#if LV_USE_FS_FATFS + #define LV_FS_FATFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #define LV_FS_FATFS_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ +#endif + +/*API for memory-mapped file access. */ +#define LV_USE_FS_MEMFS 0 +#if LV_USE_FS_MEMFS + #define LV_FS_MEMFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ +#endif + +/*LODEPNG decoder library*/ +#define LV_USE_LODEPNG 0 + +/*PNG decoder(libpng) library*/ +#define LV_USE_LIBPNG 0 + +/*BMP decoder library*/ +#define LV_USE_BMP 0 + +/* JPG + split JPG decoder library. + * Split JPG is a custom format optimized for embedded systems. */ +#define LV_USE_TJPGD 0 + +/* libjpeg-turbo decoder library. + * Supports complete JPEG specifications and high-performance JPEG decoding. */ +#define LV_USE_LIBJPEG_TURBO 0 + +/*GIF decoder library*/ +#define LV_USE_GIF 0 +#if LV_USE_GIF +/*GIF decoder accelerate*/ +#define LV_GIF_CACHE_DECODE_DATA 0 +#endif + + +/*Decode bin images to RAM*/ +#define LV_BIN_DECODER_RAM_LOAD 0 + +/*RLE decompress library*/ +#define LV_USE_RLE 0 + +/*QR code library*/ +#define LV_USE_QRCODE 0 + +/*Barcode code library*/ +#define LV_USE_BARCODE 0 + +/*FreeType library*/ +#define LV_USE_FREETYPE 0 +#if LV_USE_FREETYPE + /*Memory used by FreeType to cache characters in kilobytes*/ + #define LV_FREETYPE_CACHE_SIZE 768 + + /*Let FreeType to use LVGL memory and file porting*/ + #define LV_FREETYPE_USE_LVGL_PORT 0 + + /* Maximum number of opened FT_Face/FT_Size objects managed by this cache instance. */ + /* (0:use system defaults) */ + #define LV_FREETYPE_CACHE_FT_FACES 8 + #define LV_FREETYPE_CACHE_FT_SIZES 8 + #define LV_FREETYPE_CACHE_FT_GLYPH_CNT 256 +#endif + +/* Built-in TTF decoder */ +#define LV_USE_TINY_TTF 0 +#if LV_USE_TINY_TTF + /* Enable loading TTF data from files */ + #define LV_TINY_TTF_FILE_SUPPORT 0 +#endif + +/*Rlottie library*/ +#define LV_USE_RLOTTIE 0 + +/*Enable Vector Graphic APIs*/ +#define LV_USE_VECTOR_GRAPHIC 0 + +/* Enable ThorVG (vector graphics library) from the src/libs folder */ +#define LV_USE_THORVG_INTERNAL 0 + +/* Enable ThorVG by assuming that its installed and linked to the project */ +#define LV_USE_THORVG_EXTERNAL 0 + +/*Use lvgl built-in LZ4 lib*/ +#define LV_USE_LZ4_INTERNAL 0 + +/*Use external LZ4 library*/ +#define LV_USE_LZ4_EXTERNAL 0 + +/*FFmpeg library for image decoding and playing videos + *Supports all major image formats so do not enable other image decoder with it*/ +#define LV_USE_FFMPEG 0 +#if LV_USE_FFMPEG + /*Dump input information to stderr*/ + #define LV_FFMPEG_DUMP_FORMAT 0 +#endif + +/*================== + * OTHERS + *==================*/ + +/*1: Enable API to take snapshot for object*/ +#define LV_USE_SNAPSHOT 0 + +/*1: Enable system monitor component*/ +#define LV_USE_SYSMON 0 +#if LV_USE_SYSMON + /*Get the idle percentage. E.g. uint32_t my_get_idle(void);*/ + #define LV_SYSMON_GET_IDLE lv_timer_get_idle + + /*1: Show CPU usage and FPS count + * Requires `LV_USE_SYSMON = 1`*/ + #define LV_USE_PERF_MONITOR 0 + #if LV_USE_PERF_MONITOR + #define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT + + /*0: Displays performance data on the screen, 1: Prints performance data using log.*/ + #define LV_USE_PERF_MONITOR_LOG_MODE 0 + #endif + + /*1: Show the used memory and the memory fragmentation + * Requires `LV_USE_STDLIB_MALLOC = LV_STDLIB_BUILTIN` + * Requires `LV_USE_SYSMON = 1`*/ + #define LV_USE_MEM_MONITOR 0 + #if LV_USE_MEM_MONITOR + #define LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_LEFT + #endif + +#endif /*LV_USE_SYSMON*/ + +/*1: Enable the runtime performance profiler*/ +#define LV_USE_PROFILER 0 +#if LV_USE_PROFILER + /*1: Enable the built-in profiler*/ + #define LV_USE_PROFILER_BUILTIN 1 + #if LV_USE_PROFILER_BUILTIN + /*Default profiler trace buffer size*/ + #define LV_PROFILER_BUILTIN_BUF_SIZE (16 * 1024) /*[bytes]*/ + #endif + + /*Header to include for the profiler*/ + #define LV_PROFILER_INCLUDE "lvgl/src/misc/lv_profiler_builtin.h" + + /*Profiler start point function*/ + #define LV_PROFILER_BEGIN LV_PROFILER_BUILTIN_BEGIN + + /*Profiler end point function*/ + #define LV_PROFILER_END LV_PROFILER_BUILTIN_END + + /*Profiler start point function with custom tag*/ + #define LV_PROFILER_BEGIN_TAG LV_PROFILER_BUILTIN_BEGIN_TAG + + /*Profiler end point function with custom tag*/ + #define LV_PROFILER_END_TAG LV_PROFILER_BUILTIN_END_TAG +#endif + +/*1: Enable Monkey test*/ +#define LV_USE_MONKEY 0 + +/*1: Enable grid navigation*/ +#define LV_USE_GRIDNAV 0 + +/*1: Enable lv_obj fragment*/ +#define LV_USE_FRAGMENT 0 + +/*1: Support using images as font in label or span widgets */ +#define LV_USE_IMGFONT 0 + +/*1: Enable an observer pattern implementation*/ +#define LV_USE_OBSERVER 1 + +/*1: Enable Pinyin input method*/ +/*Requires: lv_keyboard*/ +#define LV_USE_IME_PINYIN 0 +#if LV_USE_IME_PINYIN + /*1: Use default thesaurus*/ + /*If you do not use the default thesaurus, be sure to use `lv_ime_pinyin` after setting the thesauruss*/ + #define LV_IME_PINYIN_USE_DEFAULT_DICT 1 + /*Set the maximum number of candidate panels that can be displayed*/ + /*This needs to be adjusted according to the size of the screen*/ + #define LV_IME_PINYIN_CAND_TEXT_NUM 6 + + /*Use 9 key input(k9)*/ + #define LV_IME_PINYIN_USE_K9_MODE 1 + #if LV_IME_PINYIN_USE_K9_MODE == 1 + #define LV_IME_PINYIN_K9_CAND_TEXT_NUM 3 + #endif /*LV_IME_PINYIN_USE_K9_MODE*/ +#endif + +/*1: Enable file explorer*/ +/*Requires: lv_table*/ +#define LV_USE_FILE_EXPLORER 0 +#if LV_USE_FILE_EXPLORER + /*Maximum length of path*/ + #define LV_FILE_EXPLORER_PATH_MAX_LEN (128) + /*Quick access bar, 1:use, 0:not use*/ + /*Requires: lv_list*/ + #define LV_FILE_EXPLORER_QUICK_ACCESS 1 +#endif + +/*================== + * DEVICES + *==================*/ + +/*Use SDL to open window on PC and handle mouse and keyboard*/ +#define LV_USE_SDL 0 +#if LV_USE_SDL + #define LV_SDL_INCLUDE_PATH + #define LV_SDL_RENDER_MODE LV_DISPLAY_RENDER_MODE_DIRECT /*LV_DISPLAY_RENDER_MODE_DIRECT is recommended for best performance*/ + #define LV_SDL_BUF_COUNT 1 /*1 or 2*/ + #define LV_SDL_FULLSCREEN 0 /*1: Make the window full screen by default*/ + #define LV_SDL_DIRECT_EXIT 1 /*1: Exit the application when all SDL windows are closed*/ +#endif + +/*Use X11 to open window on Linux desktop and handle mouse and keyboard*/ +#define LV_USE_X11 0 +#if LV_USE_X11 + #define LV_X11_DIRECT_EXIT 1 /*Exit the application when all X11 windows have been closed*/ + #define LV_X11_DOUBLE_BUFFER 1 /*Use double buffers for endering*/ + /*select only 1 of the following render modes (LV_X11_RENDER_MODE_PARTIAL preferred!)*/ + #define LV_X11_RENDER_MODE_PARTIAL 1 /*Partial render mode (preferred)*/ + #define LV_X11_RENDER_MODE_DIRECT 0 /*direct render mode*/ + #define LV_X11_RENDER_MODE_FULL 0 /*Full render mode*/ +#endif + +/*Driver for /dev/fb*/ +#define LV_USE_LINUX_FBDEV 0 +#if LV_USE_LINUX_FBDEV + #define LV_LINUX_FBDEV_BSD 0 + #define LV_LINUX_FBDEV_RENDER_MODE LV_DISPLAY_RENDER_MODE_PARTIAL + #define LV_LINUX_FBDEV_BUFFER_COUNT 0 + #define LV_LINUX_FBDEV_BUFFER_SIZE 60 +#endif + +/*Use Nuttx to open window and handle touchscreen*/ +#define LV_USE_NUTTX 0 + +#if LV_USE_NUTTX + #define LV_USE_NUTTX_LIBUV 0 + + /*Use Nuttx custom init API to open window and handle touchscreen*/ + #define LV_USE_NUTTX_CUSTOM_INIT 0 + + /*Driver for /dev/lcd*/ + #define LV_USE_NUTTX_LCD 0 + #if LV_USE_NUTTX_LCD + #define LV_NUTTX_LCD_BUFFER_COUNT 0 + #define LV_NUTTX_LCD_BUFFER_SIZE 60 + #endif + + /*Driver for /dev/input*/ + #define LV_USE_NUTTX_TOUCHSCREEN 0 + +#endif + +/*Driver for /dev/dri/card*/ +#define LV_USE_LINUX_DRM 0 + +/*Interface for TFT_eSPI*/ +#define LV_USE_TFT_ESPI 0 + +/*Driver for evdev input devices*/ +#define LV_USE_EVDEV 0 + +/*Driver for libinput input devices*/ +#define LV_USE_LIBINPUT 0 + +#if LV_USE_LIBINPUT + #define LV_LIBINPUT_BSD 0 + + /*Full keyboard support*/ + #define LV_LIBINPUT_XKB 0 + #if LV_LIBINPUT_XKB + /*"setxkbmap -query" can help find the right values for your keyboard*/ + #define LV_LIBINPUT_XKB_KEY_MAP { .rules = NULL, .model = "pc101", .layout = "us", .variant = NULL, .options = NULL } + #endif +#endif + +/*Drivers for LCD devices connected via SPI/parallel port*/ +#define LV_USE_ST7735 0 +#define LV_USE_ST7789 0 +#define LV_USE_ST7796 0 +#define LV_USE_ILI9341 0 + +#define LV_USE_GENERIC_MIPI (LV_USE_ST7735 | LV_USE_ST7789 | LV_USE_ST7796 | LV_USE_ILI9341) + +/* LVGL Windows backend */ +#define LV_USE_WINDOWS 0 + +/*================== +* EXAMPLES +*==================*/ + +/*Enable the examples to be built with the library*/ +#define LV_BUILD_EXAMPLES 1 + +/*=================== + * DEMO USAGE + ====================*/ + +/*Show some widget. It might be required to increase `LV_MEM_SIZE` */ +#define LV_USE_DEMO_WIDGETS 0 + +/*Demonstrate the usage of encoder and keyboard*/ +#define LV_USE_DEMO_KEYPAD_AND_ENCODER 0 + +/*Benchmark your system*/ +#define LV_USE_DEMO_BENCHMARK 0 + +/*Render test for each primitives. Requires at least 480x272 display*/ +#define LV_USE_DEMO_RENDER 0 + +/*Stress test for LVGL*/ +#define LV_USE_DEMO_STRESS 0 + +/*Music player demo*/ +#define LV_USE_DEMO_MUSIC 0 +#if LV_USE_DEMO_MUSIC + #define LV_DEMO_MUSIC_SQUARE 0 + #define LV_DEMO_MUSIC_LANDSCAPE 0 + #define LV_DEMO_MUSIC_ROUND 0 + #define LV_DEMO_MUSIC_LARGE 0 + #define LV_DEMO_MUSIC_AUTO_PLAY 0 +#endif + +/*Flex layout demo*/ +#define LV_USE_DEMO_FLEX_LAYOUT 0 + +/*Smart-phone like multi-language demo*/ +#define LV_USE_DEMO_MULTILANG 0 + +/*Widget transformation demo*/ +#define LV_USE_DEMO_TRANSFORM 0 + +/*Demonstrate scroll settings*/ +#define LV_USE_DEMO_SCROLL 0 + +/*Vector graphic demo*/ +#define LV_USE_DEMO_VECTOR_GRAPHIC 0 +/*--END OF LV_CONF_H--*/ + +#endif /*LV_CONF_H*/ + +#endif /*End of "Content enable"*/ diff --git a/include/settings.h b/include/settings.h deleted file mode 100644 index a494748..0000000 --- a/include/settings.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef SETTINGS_H -#define SETTINGS_H - -#include -#include - -#include "interval.h" - -void settings_menu_tx_power(void); -esp_power_level_t settings_load_esp_tx_power(void); - -bool settings_load_reconnect(void); -void settings_save_reconnect(bool reconnect); - -bool settings_load_gps(void); -void settings_menu_gps(void); - -void settings_load_interval(interval_t &interval); -void settings_save_interval(const interval_t &interval); - -void settings_add_interval_items(ezMenu *submenu, interval_t &interval); -void settings_menu_interval(void); - -bool settings_load_multiconnect(void); -void settings_save_multiconnect(bool multiconnect); - -#endif diff --git a/include/spinner.h b/include/spinner.h deleted file mode 100644 index 3c7c67e..0000000 --- a/include/spinner.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef SPINNER_H -#define SPINNER_H - -enum spin_unit_t { - SPIN_UNIT_NIL = 0, // no units - SPIN_UNIT_INF = 1, // ignore value, assume infinity - SPIN_UNIT_MS = 2, // milliseconds - SPIN_UNIT_SEC = 3, // seconds - SPIN_UNIT_MIN = 4 // minutes -}; - -struct __attribute__((packed)) SpinValue { - uint16_t value; - spin_unit_t unit; -}; - -void spinner_modify_value(const char *title, bool preset, SpinValue &sv); - -std::string sv2str(const SpinValue &sv); -unsigned long sv2ms(const SpinValue &sv); -void ms2hms(unsigned long ms, unsigned int *h, unsigned int *m, unsigned int *s); - -#endif diff --git a/lib/M5ez/.gitattributes b/lib/M5ez/.gitattributes deleted file mode 100644 index dfe0770..0000000 --- a/lib/M5ez/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -# Auto detect text files and perform LF normalization -* text=auto diff --git a/lib/M5ez/.gitignore b/lib/M5ez/.gitignore deleted file mode 100644 index f5548d2..0000000 --- a/lib/M5ez/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ - -.development diff --git a/lib/M5ez/LICENSE b/lib/M5ez/LICENSE deleted file mode 100644 index bde60ce..0000000 --- a/lib/M5ez/LICENSE +++ /dev/null @@ -1,165 +0,0 @@ -GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. \ No newline at end of file diff --git a/lib/M5ez/README.md b/lib/M5ez/README.md deleted file mode 100644 index a34de4a..0000000 --- a/lib/M5ez/README.md +++ /dev/null @@ -1,1120 +0,0 @@ -# `M5ez` The easy way to program on the M5Stack - ->*M5ez (pronounced "M5 easy") is a complete interface builder library for the M5Stack ESP32 system. It allows even novice programmers to create good looking interfaces. It comes with menus as text or as images, message boxes, very flexible button setup (including different length presses and multi-button functions), 3-button text input (you have to see it to believe it) and built-in Wifi support. Now you can concentrate on what your program does, and let M5ez worry about everything else.* - -*written by Rop Gonggrijp, with help and contributions from many others* - -[![](images/M5ez.png)](https://youtu.be/132gvdlwKZw) - -## Introduction - -The M5Stack is a small computer that is a tinkerer's dream. It is based on Espressif's ESP32 chip (with WiFi and Bluetooth), it has a 320x240 pixel color screen, three buttons, a speaker, an SD slot and it allows you to stack boards below it, each in their own plastic enclosure. The makers sell boards for GSM, GPS and LoRa (LOng RAnge radio) as well as a motor controller board and an empty experimenter board. The Chinese operation that makes them appears to sell a lot of them and I could get mine off of Amazon in a week. If you like to build things that you can hold in your hand and not just write code for yet another naked-looking board plugged into your USB port, this thing is your friend. - -On the software side, the easiest way to program these is using the Arduino IDE. M5Stack provides a library which, when you include it, creates an m5 object instance that allows access to the various hardware components in the device. For instance: the display driver is accessible through commands all starting with `m5.lcd.`. - -Making something that looks good and allows users to interact with it is not simple though: you have to program everything yourself. If you try to make something a little more complex, you quickly get bogged down in figuring out where things go on the display, what state your interface is in, etc etc. - -In the budding M5Stack community, there have been some initiatives to make it easier to create user interfaces. Most notably a M5Stack forum user named Calin make something called "M5Stack MultiApp" which allows more easy integration of multiple existing programs into one app. His work serves as an inspiration for my work. But as much as you could have multiple programs have a common user interface, creating the programs still was nowhere near simple enough. - -Enter M5ez, our contribution to making programming on the M5Stack *a lot* easier. We hope you'll enjoy programming with this. - -### Other products by M5 - -M5 is not only making the M5stack anymore. There's now an M5Stick that is an even smaller ESP32 system with a smaller screen, there's the M5 Atom which has no screen and just LEDs, and soon there will be an M5stack Core2 which has a capacitive touch screen. We plan to support some of these devices, the stick support will be rolled out soon. - -### Alternatives - -#### UiFlow - -Since writing M5ez, there has been another interface toolkit for the M5Stack, called UiFlow. It uses a web-and-cloud-based IDE that can be seen as "blocky" symbols or as python and creates micropython code. UiFlow supports a lot of the hardware sold in conjunction with the M5Stack and is very much geared towards learning and education. - -That said, building the functionality that comes "packaged" with M5ez would take quite a bit of work with UiFlow. If you have existing things that run on Arduino, like programming in C and/or with the Arduino IDE, then M5ez might be the better choice. Also it is generally easier to create consistent and visually pleasing interfaces in M5ez, and complex things can take surprisingly little work. - -  - -## Getting started - -M5ez is an Arduino library. To start using it with the Arduino IDE: - -* Choose Sketch -> Include Library -> Manage Libraries... -* Type **`M5ez`** into the search box. -* Click the row to select the library. -* Click the Install button to install the library. -* Repeat this process for the **`ezTime`** library - -in File -> Examples you will now see an M5ez heading down under "Examples from custom libraries" - -You'll have your first application on the screen in no time. You can also start with one of the sketches below in the "Menus" section of the documentation. In fact we strongly recommend that you play around with the M5ez demo application, since it is a comprehensive showcase of M5ez's functionality that will give you a good feel for what you can and cannot do with M5ez. - -  - -### If you get `fatal error: ezTime.h: No such file or directory` - -Note that it says above that you need to install two libraries: *M5ez* and *ezTime*. - -> Alternatively, you can install M5ez without the time library (and without the on-screen clock), by commenting out `#define M5EZ_CLOCK` in file `m5ez.h` in the M5ez library directory. - -  - -## Structure of documentation - -You've now seen the [Introduction](https://github.com/ropg/M5ez#introduction) and [Getting Started](https://github.com/ropg/M5ez#getting-started) sections of the manual. Below is the [User Manual](https://github.com/ropg/M5ez#m5ez-user-manual) which lists all the functions of M5ez in detail. At the end there's a [Table of Contents](https://github.com/ropg/M5ez#table-of-contents) which may be of help finding something. - -### Tech Notes - -Separate from this document, there is a directory [tech_notes](https://github.com/ropg/M5ez/tree/master/tech_notes) where we keep useful technical notes that may be a bit too obscure or in-depth for a general user manual. - -  - -# M5ez User Manual - -M5ez is a complete system for building appliance-grade interfaces for the M5Stack. It comes with a lot of the common functionality built-in that a programmer would otherwise have to create. While it is very easy to create functional programs — just have a look at the examples that come with the library — the entire library can be a bit overwhelming because of how much it offers. The following is a reference for programmers that want to get the most out of M5ez. It is certainly not necessary to know or fully understand every function, but at some point you probably want to go through this document very quickly to see what M5ez can do for you. - -If you feel anything is still unclear after reading this document, please file an issue in the repository. I take documentation seriously — if something is not clear in here, it is as much a bug as something that's wrong with the software itself. Pull requests for typos etc. gladly accepted. - -  - -## How it all works - -As you can see from the examples, the commands that activate M5ez's functionality start with `ez.`. That `ez` refers to an object `ez` that is within the "root namespace" of the sketch. Within it are commands like `ez.msgBox`, but also other objects. For instance: commands that deal with the entire screen are prefixed with `ez.screen.`, WiFi commands start with `ez.wifi.` and so forth. - -After you include the library with `#include `, those objects all exist. To get things going you then — in the `void setup()` part of your sketch — do `ez.begin()`. This replaces `m5.begin`, which is called from within `ez.begin()`. You still need to `#include ` before `#include ` if you want to use any commands from the m5 library in your sketch. - -  - -## Screen - -The screen object deals with the entire screen, which consists of an optional header at the top, optional buttons at the bottom and an area called "the canvas" in between. - -`void ez.screen.clear()` - -`void ez.screen.clear(uint16_t color)` - -`uint16_t ez.screen.background()` - -If you clear the screen, the header and buttons are hidden, the canvas is cleared, and the current background color is set to the color you specify, or to the background color from the current theme (`ez.theme->background`, see the chapter on themes) if you do not specify a color. `ez.screen.background()` will return the currently set background color. - -If you clear the screen, the header and buttons are hidden, so the canvas takes up the entire screen. - -  - -## Header - -The header is the bar at the top of the screen. In the default theme it is 30 pixels high and blue, and it has text in font `&FreeSansBold9pt7b`, but all of that can be changed, either by modifying the values in the current theme on the fly or by creating your own theme. - -### Showing, hiding, title - -`void ez.header.show(String title = "")` - -`void ez.header.title(String title = "")` - -`bool ez.header.shown()` - -`void ez.header.clear(bool wipe = true)` - -`ez.header.show` will show the header, either with the present title or with a new one provided as an argument. `ez.header.title` will merely change the title, but will not change whether the header is displayed or not. `ez.header.shown()` returns true or false depending on whether the header is currently shown, and `ez.header.clear` removes the header, returning the top of the screen to the canvas. If you set `wipe` to false, it will not draw background pixels there. (This may help prevent flicker or add a bit of performance if you're about to clear the canvas anyway.) - -### Your own header widgets - -The clock and WiFi signal strength displayed in the M5ez header are "widgets". You can register your own code to display widgets too. - -`void ez.header.insert(uint8_t position, String name, uint16_t width, void (*function)(uint16_t x, uint16_t w), bool leftover = false)` - -`void ez.header.remove(String name)` - -`uint8_t ez.header.position(String name)` - -`void ez.header.draw(String name = "")` - -`ez.header.insert` lets you add your own header widget. The header widget positions start at 0 (the leftmost widget), and `position` specifies t the left of which widget to insert the new one. `name` is just a name you use to refer to your widget. The names `title`, `clock` and `wifi` are reserved for use by M5ez. - -Exactly one widget can be specified as `leftover`, meaning it gets all the pixels not claimed by any other widgets. By default, this is the widget called "title" that holds the header title. All other widgets must have a width in pixels specified. - -`function` refers to the name of the function that draws your widget. It must be a function that returns nothing (`void`) and that takes two `uint16_t` arguments: the x position and width of your widget. This function will be called by the header code whenever the header is redrawn. But you can also force a redraw with `ez.header.draw`. For instance: the clock code calls for the `clock` widget to be redrawn every minute, for instance. Note that you would never call your widget draw function directly, because your code doesn't know where all the widgets are within the header. You would only ever call it indirectly using `ez.header.draw("your_widget_name")`, which will then see if the header is currently displayed, and if so lookup x position and width and call your draw function. - -Your widget draw function has to make sure to stay inside the lines: not lower than `ez.theme->header_height`, not further left than x, not further right than x + w. If you want to be able to use your widget with multiple themes, your code should probably also respect `ez.theme->header_bgcolor` and `ez.theme->header_fgcolor` to make sure things look pretty. - -`ez.header.position` will return the relative position of an existing header widget, starting with 0 for the leftmost widget, adding one for each further widget. This can be quite useful. For instance, if you want to make sure a widget is insert directly to the left of the title, you can specify `ez.header.insert(ez.header.position("title"), ...`. If you specify `(ez.header.position("title") + 1` your new widget gets inserted directly to the right of the title. - -As the name implies, `ez.header.remove` removes the widget with the given name. - -  - -## Canvas - -The "canvas" is M5ez's name for the area between the header and buttons. So if there are no header and no buttons, the canvas is the whole screen. Once they are drawn, the canvas becomes smaller. - -`void ez.canvas.clear()` - -Clears the canvas area to the current background color — either the default from the theme or the one specified in the most recent `ez.screen.clear` command. Sets x and y positions for the the next print command to the top left (respecting the currently set left margin). - -`void ez.canvas.reset()` - -Reset does everything `ez.canvas.clear()` does, but also turns on text wrap, turns off scrolling, sets the print font and color back to the defaults from the theme and sets the left margin to 0. - -### Canvas dimensions - -Because the canvas size differs when header or buttons are shown, your code can ask for various locations and dimensions of the canvas as follows: - -`uint8_t ez.canvas.top()` - -`uint8_t ez.canvas.bottom()` - -`uint16_t ez.canvas.left()` - -`uint16_t ez.canvas.right()` - -`uint8_t ez.canvas.height()` - -`uint16_t ez.canvas.width()` - -### Printing to the canvas - -The M5ez canvas inherits from the Arduino `Print` object. What that means is that you can use the `print` and `println` functions in just the way you are used to using them. Here's all the different arguments taken by `printf`, `print` and `println`. - -``` -size_t ez.canvas.printf(const char * format, ...) __attribute__ ((format (printf, 2, 3))); -size_t ez.canvas.print(const __FlashStringHelper *); -size_t ez.canvas.print(const String &); -size_t ez.canvas.print(const char[]); -size_t ez.canvas.print(char); -size_t ez.canvas.print(unsigned char, int = DEC); -size_t ez.canvas.print(int, int = DEC); -size_t ez.canvas.print(unsigned int, int = DEC); -size_t ez.canvas.print(long, int = DEC); -size_t ez.canvas.print(unsigned long, int = DEC); -size_t ez.canvas.print(double, int = 2); -size_t ez.canvas.print(const Printable&); -size_t ez.canvas.print(struct tm * timeinfo, const char * format = NULL); - -size_t ez.canvas.println(const __FlashStringHelper *); -size_t ez.canvas.println(const String &s); -size_t ez.canvas.println(const char[]); -size_t ez.canvas.println(char); -size_t ez.canvas.println(unsigned char, int = DEC); -size_t ez.canvas.println(int, int = DEC); -size_t ez.canvas.println(unsigned int, int = DEC); -size_t ez.canvas.println(long, int = DEC); -size_t ez.canvas.println(unsigned long, int = DEC); -size_t ez.canvas.println(double, int = 2); -size_t ez.canvas.println(const Printable&); -size_t ez.canvas.println(struct tm * timeinfo, const char * format = NULL); -size_t ez.canvas.println(void); -``` - -`bool ez.canvas.scroll()` - -`void ez.canvas.scroll(bool s)` - -If you turn on scrolling with `ez.canvas.scroll(true)`, M5ez will store what has been printed to the screen, so the contents of the screen can scroll. Note that when the canvas starts scrolling, only the contents placed there with the print functions from above will scroll, everything else will be wiped. So if, for example, you have drawn something with `m5.lcs.fillRect`, it will be gone once you print beyond the last line. - -You can turn scrolling off with `ez.canvas.scroll(false)`, and you can ask what the present scroll status is with `ez.canvas.scroll()`. - ->Note on scrolling: for scrolling to work, everything that is printed to the screen is kept in memory. It is only forgotten if it scrolls off the screen or if you clear the canvas or screen. What that means is that if you turn scrolling on and then print and overwrite something lots of times, eventually the memory will fill up and your application will crash. - -`bool ez.canvas.wrap()` - -`void ez.canvas.wrap(bool w)` - -`wrap` determines whether the excess from the print functions that doesn't fit the current line is wrapped to the next line. - -`uint16_t ez.canvas.lmargin()` - -`void ez.canvas.lmargin(uint16_t newmargin)` - -Ask for and set the left margin for printing. - -`void ez.canvas.font(const GFXfont* font)` - -`const GFXfont* ez.canvas.font()` - -`void ez.canvas.color(uint16_t color)` - -`uint16_t ez.canvas.color()` - -Ask for and set the font and color for printing. - -`uint16_t ez.canvas.x()` - -`void ez.canvas.x(uint16_t newx)` - -`uint8_t ez.canvas.y()` - -`void ez.canvas.y(uint8_t newy)` - -`void pos(uint16_t x, uint8_t y)` - -Ask for and set the x and y position for the next print command. - -  - -## Buttons - -There are only three buttons on the M5Stack. To make sure we can make good use of our rare buttons, we can assign two functions to each button: one for a short press and one for a slightly longer press. We can also assign functions to all three combinations of two keys being pressed simultaneously (AB, BC and AC). So there is a total of nine possible key functions we can assign. The functions for a single key are displayed in the bottom row of the display, the functions for key combinations in a row above. Each key has a 'name' (which is also what is returned to the program if it is pressed and a 'caption', which is what is displayed on the key. - -![](images/buttons1.png) - -Because there are multiple functions that get told what buttons are going to be displayed, it would get a little hairy to give each of these function 18 possible arguments for the key names and captions. So for your convenience, all the keys you want displayed and detected are specified as one string. The different key names and captions are separated with hashes. You can specify one key name like `OK`, which means you will only use the middle button, it will say OK on it and it will also return the string "OK" to your code. - -If you specify three keys, like `yes # no # maybe` you will get three buttons with only one function per button. If you specify six keys, they will be the short and long presses for each key. If you specify nine, the last three will specify the AB, BC and AC button-combo functions respectively. Specifying any other number of keys does not work, so the string should always contain one, three, six or nine parts, separated by hashes. - -![](images/buttons2.png) - -The captions on the key can differ from the name. To specify this, specify a name, followed by a pipe sign (`|`) followed by the caption. So `one | un # two | deux # three | trois` specifies three keys that each show a number in french but return the number in english. Note that any spaces around the hashes or pipe signs in the string are ignored, to allow you to make things more readable in your code. - -If your button is captioned `up`, `down`, `left` or `right`, the caption is replaced by a triangle pointing in that direction. - -If a button has only one function, (no long press defined), the caption will be printed in the middle. If there is a short and a long press defined, they will be printed on the left and right, the right in cyan (in the default theme) to signify that it needs a longer press. - -*In some cases it may be necessary to define only a long or only a short press action explicitly. In that case, defining a the key with the placeholder "~" will make sure its function is not interpreted. Take the key definition string `~ # up # select # # right # down`: this defines the leftmost key to have the 'up' function, but only with a long press, a short press is ignored. The caption prints on the right side of the button and not in the middle. (One might use this to signify that the user got to the left edge of something she's navigating, not causing the 'up' function to trigger when she keeps pressing short before realising she's at the left edge.)* - -`void ez.buttons.show(String buttons)` - -Shows all the buttons you specified, only redrawing the buttons that have changed from the buttons displayed already to prevent flicker. If needed it updates the canvas size to account for a changed number of keyboard rows displayed on the screen. - -`void ez.buttons.clear(bool wipe = true)` - -This hides the buttons, growing the canvas to cover the area where the buttons were printed. If you set `wipe` to false, it will not draw background pixels there. (This may help prevent flicker or add a bit of performance if you're about to clear the canvas anyway.) - -`String ez.buttons.poll()` - -If you call `ez.buttons.poll`, it will return the name of the key pressed since you last called it, or the empty string is nothing was pressed. This function is where everyone's M5ez programs will spends most of their time: waiting for the user to make things continue. If your code is waiting in a loop that includes `ez.buttons.poll`, clock and WiFi signal updating, as well as execution of user-registered functions (see below) will continue. - -`String ez.buttons.wait()` - - `ez.buttons.wait` does the same thing as `ez.buttons.poll()` except it does not return if nothing is pressed. If you call it with no arguments, it assumes the buttons have already been drawn with `ez.drawButtons`. (And if not your program is stuck.) - -`String ez.buttons.wait(String buttons)` - -You can specify the keys to be drawn straight into `ez.buttons.wait` for simple usages. For instance `ez.buttons.wait("OK")` will display a single "OK" on the center button and return "OK" when it is pressed. (But given that you are not interested in the return value in this case, you can just specify that as a statement.) - -  - -## Scheduling tasks within M5ez - -Now that we're dealing with waiting for key presses, this is a good moment to talk about scheduling, yielding and such. As discussed above, a typical program written with M5ez will spend most of its time waiting for keys to be pressed. But some things need to continue to happen while that happening: the clock and the wifi signal indicator need to update, for instance. - -### Yield - -`void ez.yield()` - -It could be that your code needs to do things that take a little while. If something takes many seconds, consider putting in an "abort" key. But suppose your code is busy and is not using `ez.buttons.poll` or `ez.buttons.wait` to check for keys. In that case use `ez.yield()` in your loop to make sure the clock and WiFi updating (as well as user defined tasks) get executed. `ez.yield` calls the Arduino `yield` function, so you do not need to call that separately. - -### Your own events - -`void ez.addEvent(uint16_t (*function)(), uint32_t when = 1)` - -`void ez.removeEvent(uint16_t (*function)())` - -With `addevent` you can register a function of your own to be executed periodically as part of M5ez's own loop when it is waiting for buttons. This function has to be a function that takes no arguments and returns a 16-bit unsigned integer. Make sure you just specify the name of this function without any brackets. You can optionally specify when to run this function by adding a time in `millis()`. By default, the function you specify will run immediately. - -The value returned by your function is the number of milliseconds to wait before calling the function again. So a function that only needs to run once every second would simply return 1000. If your function returns 0, the event is deleted and not executed any further. - -> Note: These events are meant for things that need to happen frequently. The next event cannot be more than 65 seconds out as the period between them is a 16-bit unsigned integer. If you use M5ez with ezTime, you can use [ezTime's events](https://github.com/ropg/ezTime#events) for things that need to happen with more time between them. - -As the name implies, `ez.removeEvent` also removes your function from the loop. - -#### Redrawing after an event - -`void ez.redraw()` - -Sometimes code executed in an event will have changed the contents of the screen. The running menu knows nothing about this, and so when your event ends, it will not refresh the screen. To fix this, you can execute `ez.redraw()` whenever your event routine has done something on the screen. The menu code will then redraw the screen accordingly. - -  - -## Showing messages with msgBox - -![](images/msgBox1.png) - -``` -String ez.msgBox(String header, - String msg, String buttons = "OK", - const bool blocking = true, - const GFXfont* font = MSG_FONT, - uint16_t color = MSG_COLOR) -``` - -`ez.msgBox` produces a screen that just has your text message centered in the middle. If your message consists of multiple lines, msgBox will word-wrap and justify to the best fit on the screen. You can indicate where to break the lines yourself with a pipe sign (`|`). The only two arguments you have to provide are the header text and the message to be printed. If you do not specify buttons, `ez.msgBox` will put a single "OK" button in the middle. You can specify buttons in the same way it's done with the button commands seen earlier. - -![](images/msgBox2.png) - -By default, msgBox then waits for the user to press any of the keys specified and returns the name of that key. If you want to scan for the keys yourself with `ez.buttons.poll()` (for instance because there are other things your code need to wait for or check) then you can specify `false` with blocking, msgBox will then show the message, header and buttons and exit. - -The font and color options allow you to use something other than the default (theme determined) defaults for the message printed by msgBox. They act as you would expect, see the section on fonts and colors for details. - -  - -## ezProgressBar - -**`class ezProgressBar(String header = "", String msg = "", String buttons = "", const GFXfont* font = MSG_FONT, uint16_t color = MSG_COLOR, uint16_t bar_color = PROGRESSBAR_COLOR)`** - -![](images/ezProgressBar.png) - -If you want to show progress on a download or something else, use the `ezProgressBar` class. It behaves very similarly to msgBox, except you need to create a class instance. To create an ezProgressBar instance called pb, one could use: - -``` - ezProgressBar pb ("This is a header", "Message, just like with msgBox", "Abort"); -``` - -This will draw header, message, an empty (0 %) progress bar and the specified single "Abort" button. Unlike msgBox this will not block by default. In code that executes after this, one would presumably check for the "Abort" button with `ez.buttons.poll()`. To advance the bar, the code would simply call: - -``` - pb.value(float val) -``` - -where `val` is a floating point value between 0 and 100. Check out the [Over-The-Air https update example](https://github.com/ropg/M5ez/tree/master/examples/OTA_https) to see how the ezProgressBar object is used there. (You'll see that the `ez.wifi.update()` software update function accepts a pointer to an ezProgressBar instance to show its progress.) - -  - -## 3-button text input - -**`String ez.textInput(String header = "", String defaultText = "")`** - -This function will provide a text-entry field, pre-filled with `defaultText` if specified. The user can then select a group of letter and then press the letter using the short and long keypresses and multi-key presses as discussed above in the "Buttons" chapter. By using this method, the lower case letters can be reached in two presses, upper case letters in three. If you are in shifted or numeric mode and press lock, the keyboard will return there instead of to lower case after each successful key. Once the user presses "done" (buttons A and C together), the function returns the entered text. - -![](images/textInput.png) - ->Advanced users can make their own keyboard definitions by looking at the `_keydefs` keyboard definition that is part of the default theme. As you can see there are multiple lines, each holding a key definition string like we have gotten to know them by now. Any key whose name is "KB" followed by a number causes nothing to be added to the input but the buttons in that line of the definitions to be printed instead. A key whose name is LCK: optionally followed by a string means that if it is pressed the current keyboard is what the user comes back to after a line is entered. Pressing a key whose name is "Back" returns to that keyboard as well. The string is used to denote the name of that keyboard in the on-screen capslock/numlock message. - -  - -## FACES keyboard support - -![](images/FACES.png) - -`String ez.faces.poll()` - -`bool ez.faces.on()` - -M5ez supports the M5 FACES keyboard: simply set the keyboard to "attached" in the M5ez settings menu. When you do, you will be able to use the `ez.faces.poll` function to get the last key pressed on the keyboard. The `textInput` and `textBox` functions will then also look for keys from that keyboard, and start with different key definitions for the M5Stack's own buttons. `ez.faces.on()` can be used to see if the keyboard is set to "attached" in the menu. - -  - -## Composing or viewing longer texts: textBox - -``` -String ez.textBox(String header = "", - String text = "", bool readonly = false, - String buttons = "up#Done#down", - const GFXfont* font = TB_FONT, uint16_t color = TB_COLOR) -``` - -This will word-wrap and display the string in `text` (up to 32 kB), allowing the user to page through it. Ideal for LoRa or SMS messages, short mails or whatever else. If a FACES keyboard is attached and `readonly` is false, the user can edit the text: a cursor appears, which can be moved with the arrow keys on the FACES keyboard. `TB_FONT` and `TB_COLOR` are the defaults from the theme, but they can be overridden by supplying a font and/or a color directly. - -![](images/textBox.png) - -  - -## Fonts - -**`void ez.setFont(const GFXfont* font)`** - -**`int16_t ez.fontHeight()`** - -Just like when using the `m5.lcd.setFreeFont` function directly, you can specify the FreeFont fonts to `ez.setFont` with a pointer to where they are in flash. M5ez makes it possible to do the same but also use the built-in "pre-FreeFont" fonts that are available. Normally, you would have to pass these as a numeric parameter to `m5.lcd.setTextFont`. M5ez provides a set of "fake pointers" that are treated specially to make this happen, but they can only be used by M5ez functions, not with any of the `m5.lcd` functions. - -The fontHeight without arguments returns the height of the current font - FreeFont or otherwise - in pixels, without needing to specify which text font like in the `m5.lcd` version. - -What that all means is that without adding any fonts of your own, you can specify the following fonts: - -### FreeFonts from the Adafruit library - -``` - &TomThumb - &FreeMono9pt7b - &FreeMono12pt7b - &FreeMono18pt7b - &FreeMono24pt7b - &FreeMonoBold9pt7b - &FreeMonoBold12pt7b - &FreeMonoBold18pt7b - &FreeMonoBold24pt7b - &FreeMonoOblique9pt7b - &FreeMonoOblique12pt7b - &FreeMonoOblique18pt7b - &FreeMonoOblique24pt7b - &FreeMonoBoldOblique9pt7b - &FreeMonoBoldOblique12pt7b - &FreeMonoBoldOblique18pt7b - &FreeMonoBoldOblique24pt7b - &FreeSans9pt7b - &FreeSans12pt7b - &FreeSans18pt7b - &FreeSans24pt7b - &FreeSansBold9pt7b - &FreeSansBold12pt7b - &FreeSansBold18pt7b - &FreeSansBold24pt7b - &FreeSansOblique9pt7b - &FreeSansOblique12pt7b - &FreeSansOblique18pt7b - &FreeSansOblique24pt7b - &FreeSansBoldOblique9pt7b - &FreeSansBoldOblique12pt7b - &FreeSansBoldOblique18pt7b - &FreeSansBoldOblique24pt7b - &FreeSerif9pt7b - &FreeSerif12pt7b - &FreeSerif18pt7b - &FreeSerif24pt7b - &FreeSerifItalic9pt7b - &FreeSerifItalic12pt7b - &FreeSerifItalic18pt7b - &FreeSerifItalic24pt7b - &FreeSerifBold9pt7b - &FreeSerifBold12pt7b - &FreeSerifBold18pt7b - &FreeSerifBold24pt7b - &FreeSerifBoldItalic9pt7b - &FreeSerifBoldItalic12pt7b - &FreeSerifBoldItalic18pt7b - &FreeSerifBoldItalic24pt7b -``` - -### FreeFonts included by the M5Stack driver - -``` - &Orbitron_Light_24 - &Orbitron_Light_32 - &Roboto_Thin_24 - &Satisfy_24 - &Yellowtail_32 -``` - -### Older fonts available only through M5ez - -``` - mono6x8 - sans16 - sans26 - numonly48 - numonly7seg48 - numonly75 - - mono12x16 - sans32 - sans52 - numonly96 - numonly7seg96 - numonly150 -``` - -Note that these fonts need to be specified without the `&` in front, and that the second batch consists of scaled up versions of the first batch, but they're nice and big and they still might be quite useful. - -### Using your own fonts - -You can convert your own TrueType fonts to font files that can be included in your project and used on the M5Stack. As a separate project, I wrote a graphical converter where you can test the fonts on a virtual M5Stack display before converting. It's an online tool, so all of the action takes place online and in your web browser. Click [here](https://rop.nl/truetype2gfx/) (or on the image below) to use it. Everything you need to know to use it is on that page also. - -[![](images/truetype2gfx-screenshot.png)](https://rop.nl/truetype2gfx/) - -  - -## Menus - -Menus are a way of letting users choose between different options, usually to influence program flow. With ezMenu, you can create a menu, give it some options and run it for the user to choose. An ezMenu can display as a list of options, allowing the user to go up and down the list (possibly scrolling off the screen). Or it can be a set of full-screen (or really full-canvas) images, with the user scrolling left and right before choosing one. They are both instances of the ezMenu object - -![](images/ezMenu1.png) - -![](images/ezMenu2.png) - - -### Let's start with text menus - -To create a menu we create an instance of the `ezMenu` object. By having each menu be it's own instance of that object, we can store information that will disappear from RAM if a submenu closes and we can return to higher menus which have kept their state. let's start with a complete sketch for a very simple text menu: - -``` -#include -#include - -void setup() { - ez.begin(); -} - -void loop() { - ezMenu myMenu; - myMenu.addItem("Option 1", mainmenu_one); - myMenu.addItem("Option 2", mainmenu_two); - myMenu.addItem("Option 3"); - myMenu.run(); -} - -void mainmenu_one() { - ez.msgBox("", "You pressed one"); -} - -void mainmenu_two() { - Serial.println("Number two was pressed"); -} -``` - -As you can see we define our menu named `myMenu` and we added three items with names and optional functions to jump to. We have inserted this in the Arduino `loop` function, but we might as well have stuck it in setup, because this menu will keep running forever: the `myMenu.run()` statement will never complete. It will display the menu, call and wait for the appropriate function if the user selects a menu option, rinse and repeat. - -Which is all fine and well if that's our main menu, but it doesn't work if if the menu is a submenu. This is why the `yourMenu.run()` function exits if the user selects a menu item named "Exit", "Back" or "Done". This may be with a capitalized first letter or all lower case. (Menu items are just like buttons in that they have a name and a caption. If you add an item with `yourMenu.addItem("Exit | Go back to main menu"), it will display the longer text, but still cause the menu to exit. - -Let's see our sketch again, this time with a submenu added: - -``` -#include -#include - -void setup() { - ez.begin(); -} - -void loop() { - ezMenu myMenu("Main menu"); - myMenu.addItem("Item 1", mainmenu_one); - myMenu.addItem("Item 2", mainmenu_two); - myMenu.addItem("Item 3", mainmenu_three); - myMenu.run(); -} - -void mainmenu_one() { - ez.msgBox("", "You pressed one"); -} - -void mainmenu_two() { - Serial.println("Number two was pressed"); -} - -void mainmenu_three() { - ezMenu subMenu("Submenu"); - subMenu.addItem("Item A"); - subMenu.addItem("Item B"); - subMenu.addItem("Item C"); - subMenu.addItem("Back | Back to main menu"); - subMenu.run(); -} -``` - -As you can see, the submenu called `subMenu` has three items that do nothing because we did not supply functions, as well as a fourth item called "Back" (which displays "Back to main menu"), which, when selected, causes `subMenu.run()` to exit, which in turn causes `mainmenu_three` to exit which brings us back to the main menu. The main menu will sit there as if we never left, still showing "Item 3" highlighted. The subMenu object instance meanwhile has gone out of scope when the function exited, neatly cleaning up the RAM it used. - -> Did you notice we're now providing brackets and an argument when we create the menu. That is the header above our menu. - -### `.runOnce()` - -Until now we have considered menus that run all by themselves, unless they exit when a user selects an option named "Back", "Exit" or "Done". But you can also call `yourMenu.runOnce()` which will exit every time a user selects an option (but still after executing the supplied function or functions). Take a look at this: - -``` -#include -#include - -void setup() { - ez.begin(); -} - -void loop() { - ezMenu myMenu("Main menu"); - myMenu.addItem("Item 1"); - myMenu.addItem("Item 2"); - myMenu.addItem("Item 3"); - myMenu.runOnce(); - if (myMenu.pickName() == "Item 1") { - ez.msgBox("", "You pressed one"); - } - if (myMenu.pickName() == "Item 2") { - Serial.println("Number two was pressed"); - } -} -``` - -This does exactly the same as the first example we started with. Note that `.runOnce()` returns an integer that holds the position of the item selected. So the following would be yet another way to get the same functionality: - -``` -#include -#include - -void setup() { - ez.begin(); -} - -void loop() { - ezMenu myMenu("Main menu"); - myMenu.addItem("Item 1"); - myMenu.addItem("Item 2"); - myMenu.addItem("Item 3"); - while (true) { - switch (myMenu.runOnce()) { - case 1: - ez.msgBox("", "You pressed one"); - break; - case 2: - Serial.println("Number two was pressed"); - break; - } - } -} -``` - -### Image menus - -![](images/ezMenu3.png) - -You can include jpg files in the flash by creating a special .h file that holds byte arrays encoding the bytes in the JPG file. If you `#include` this file in your program and then add at least one menu items with `addItem(picture1, "Item 1")` where `picture1` is the name of the JPG array in the .h file, the menu's `.run()` and `.runOnce()` functions will change behavior: they will show an image menu instead of a text menu. - -You can also include the images to be shown as files, either on an SD card or on the special SPIFFS partition in the flash memory of the M5Stack. - -The code in the demo application to show the picture menu looks as follows: - -``` -void mainmenu_image() { - ezMenu images; - - ... - - images.addItem(sysinfo_jpg, "System Information", sysInfo); - images.addItem(wifi_jpg, "WiFi Settings", mainmenu_wifi); - images.addItem(about_jpg, "About M5ez", aboutM5ez); - images.addItem(sleep_jpg, "Power Off", powerOff); - images.addItem(return_jpg, "Back"); - images.run(); -} -``` - -As you can see we simply call `addItem()` with the name of the byte array that holds the encoded JPG file, a name to be used as a caption on the image and a function to be executed. - -### Menus: all the functions documented - -As you will see in the function documentation below, there is much more you can do with menus, like adding and deleting items on the fly. And if you draw different buttons, it's important to know that any button not named "up" or "down" selects the currently highlighted item. You might, for instance, create a button or button combination named "info" that provides more information about the current selection. There is also an advanced function you can pass that has access to more data and that can cause the menu to exit. - -**`ezMenu yourMenu(String header)`** - -**`ezMenu yourMenu`** - -If you issue on of these statements, you are creating an instance of the ezMenu object called "yourMenu". This will allow you to reference it when you start adding items to it, change various properties of it. Eventually your code will run the menu, which means it will display on the screen. "yourMenu" should of course be replaced by any name of your choice, but we'll use it in this text to show all the functions that apply to menus. These functions should be called with the name of the menu followed by a dot in front of them, as they are "member functions" of the ezMenu object. - -``` -bool addItem(String nameAndCaption, - void (*simpleFunction)() = NULL, - bool (*advancedFunction)(ezMenu* callingMenu) = NULL, - void (*drawFunction)(ezMenu* callingMenu, int16_t x, int16_t y, int16_t w, int16_t h) = NULL) -``` - -``` -bool addItem(const char *image, String nameAndCaption, - void (*simpleFunction)() = NULL, - bool (*advancedFunction)(ezMenu* callingMenu) = NULL, - void (*drawFunction)(ezMenu* callingMenu, int16_t x, int16_t y, int16_t w, int16_t h) = NULL) -``` - -``` -bool addItem(fs::FS &fs, String path, String nameAndCaption, - void (*simpleFunction)() = NULL, - bool (*advancedFunction)(ezMenu* callingMenu) = NULL, - void (*drawFunction)(ezMenu* callingMenu, int16_t x, int16_t y, int16_t w, int16_t h) = NULL) -``` - -Adds items to a menu. The first form adds a text only item, the second and third forms make image menus. You can either supply a pointer to the image which is encoded as a byte array in a special .h file that you should have included, or a file system reference and a path for a file stored on SD of SPIFFS. - -In the latter case your sketch must do either `#include ` and `SPIFFS.begin()` or `#include ' and 'SD.begin()`, and provide both the SD or SPIFFS object reference as well as the path to the file. The "ImagesSPIFFSorSD" example shows how to use this feature. Images from program flash, SPIFFS and SD-card can be freely mixed in the same menu. - -The `simpleFunction` should be the name of a `void` function without parameters that will simply be executed as the menu item is picked. You supply the name of the function **without the brackets** here, as you are passing a pointer to the function and not the output of it. - -The string named `nameAndCaption` can (as the name implies) hold both a name and a caption. If you don't do anything special they are set to the same string you supply. But if you pass `somename | Some caption`, the name is the part before the first pipe sign (`|`), the caption is everything after it. (Spaced around the pipe sign are removed.) - -The name is what is returned when you call `yourMenu.pickName()` after the menu has ran, and you can also supply it as an argument to `deleteItem()` (see below). The caption is what is printed, either in the item's line in the menu or as a caption with the image in an image menu. The caption is also returned by `yourMenu.pickCaption()`. - -If you put a tab character (Represented by `\t`) in the caption of a menu item, the text to the left of the tab will be left-aligned to the left of the menu item, and the text to the right of it will be right-aligned to the right of the item. You can see this feature in use in M5ez's settings menu where the part on the right generally represents the actual state of a setting. - -If an item is named "Back", "Exit" or "Done" (either with first letter capitalized all all lower case), the menu will exit is this item is picked, unless an advancedFunction is supplied (see below). Note that while it has to be *named* one of these three things, it can be *captioned* something completely different. - -If you want your code to have more access to what just happened in the menu, you can supply an advancedFunction pointer. This way you can have a function that has access to all the member functions of the menu that called it, and it can determine whether the menu exits or not by returning `true` (keep running) or `false` (exit). - -``` -bool myAdvancedFunction(ezMenu* callingMenu) { - Serial.print( callingMenu->pickCaption() ) - If (msgBox("Wow...", "Are you sure?", "yes##no") == "yes") return false; - return true; -} -``` - -This function will print the caption printed on the menu item that was picked, and it will ask the user whether she really wants to exit the menu, causing the calling menu to exit by returning `false` if she presses yes. Note that items called "Back", "Exit" or "Done" do not cause the menu to exit immediately if they have an advancedFunction set: in that case the decision is left to the advancedFunction. - -If you want your code to have access to the way the menus are drawn, you can supply an drawFunction pointer. The coordinates x, y, w (width) and h (height) define the rectangle where the menu is located. - -``` -void myDrawFunction(ezMenu* callingMenu, int16_t x, int16_t y, int16_t w, int16_t h){ - m5.lcd.setTextDatum(CL_DATUM); - m5.lcd.setTextColor(ez.theme->menu_item_color); - // New callingMenu aspect - m5.lcd.fillRoundRect(x, y, w, h, 1, TFT_RED); - m5.lcd.drawString("New text",x + ez.theme->menu_item_hmargin,y + ez.fontHeight() / 2 - 2); -} -``` - -Note that to address member functions of the calling menu in this function we need to use '->' instead of '.'. That's because we were passed a pointer to the instance, not an instance itself, but don't worry too much if you don't understand that. - -**`bool deleteItem(int16_t index)`** - -**`bool deleteItem(String name)`** - -The first form deletes the menu item at the position indicated, starting with 1 for the first menu item. The second form allows you to delete a menu item by name. Note that this will only delete the first menu item with that name. `deleteItem` will return true if it works, false if the name or index do not point to a valid menu item. - -**`bool setCaption(int16_t index, String caption)`** - -**`bool setCaption(String name, String caption)`** - -As the name implies, changes the caption (but not the name) of the item specified either by position or by name. - -**`int16_t getItemNum(String name)`** - -Returns the index in the menu (starting at 1) of the first menu item with the specified name, or 0 if no item with that name exists. - -**`void buttons(String bttns)`** - -This allows you to pass your own buttons to the function, using the format discussed in the chapter on buttons earlier in the documentation. By default, a text menu has the buttons `up # select # down` defined while an image menu has `left # select # right`. You can provide your own button layout, moving the buttons or adding functions for long presses or key combinations. Note that there is nothing special about the word "Select". Any button not named "up", "down", "left" or "right" is used to pick the current item, and you can use either an advancedFunction or `.runOnce()` and then a call to `.pickButton()` to figure out which key was pressed. - -Buttons *captioned* "up", "down", "left" or "right" are displayed with a triangular arrow in the corresponding direction instead of that word. Buttons *named* "Back", "Exit" or "Done" cause the menu to exit. Note that menus without buttons named "up and "down" (text menus) or "left" and "right" (image menus) become impossible to navigate. - -Buttons named "first" and "last" ump to the first or last item of the menu respectively. - -**`void upOnFirst(String nameAndCaption)`** - -**`void leftOnFirst(String nameAndCaption)`** - -**`void downOnLast(String nameAndCaption)`** - -**`void rightOnLast(String nameAndCaption)`** - -These functions allow you to replace the navigation button that would navigate off the menu with something else. Normally this button would just disappear. You can make menu rotate (like the main menu in the demo app) like this: - -``` - main.upOnFirst("last|up"); - main.downOnLast("first|down"); -``` - -Note that the "first" and "last" are button *names* with special functions, and "up" and "down" are *captions* with special functions (they are replaced by triangle arrows). The first two and last two functions are actually synonyms: they do they same thing, it's just clearer to use the second form when creating an image menu (which is horizontal). - -**`void txtFont(const GFXfont* font)`** - -**`void txtBig()`** - -**`void txtSmall()`** - -These apply to text menus only. You can set the font. The theme can supply a default big and small menu font, which can be set by simply calling `.txtBig()` or `.txtSmall()` on a menu. In the default theme, the big menu can display 6 items at a time when a header and a single row of button labels is displayed, a small menu displays eight items at a time. You can set your own font with `txtFont` or create a new theme with different defaults. - -**`void setSortFunction(bool (*sortFunction)(const char* s1, const char* s2))`** - -Ordinarily, menu items are displayed in the order in which they are added. However, if you are building a menu from data which you have no control over, such as a list of file names from an SD card, a sort function will ensure that the names are displayed in a specific order. You may define a sort function that takes two `const char*`'s and returns true when the second is greater than the first, or use one of the eight built-in sorting functions. - -``` -bool ascendingCaseSensitive(const char* s1, const char* s2) { - return 0 > strcmp(s1, s2); -} -... -menu.setSortFunction(ascendingCaseSensitive); -``` - -Once set, the menu is automatically kept sorted. All calls to addItem result in a sorted menu without further interaction. If setSortFunction is called after the menu has been populated, it's immediately resorted and maintained in its new order as items are added and deleted. - -Note that if your menus use Captions as well as Names, sorting is a bit more complicated. A set of functions is provided for sorting text in most typical manners: - -| Function | Purpose | -| :----------------------------- | :----------------------------------------------------------------------- | -| `ezMenu::sort_asc_name_cs` | Sort ascending by menu name, case sensitive | -| `ezMenu::sort_asc_name_ci` | Sort ascending by menu name, case insensitive | -| `ezMenu::sort_dsc_name_cs` | Sort descending by menu name, case sensitive | -| `ezMenu::sort_dsc_name_ci` | Sort descending by menu name, case insensitive | -| `ezMenu::sort_asc_caption_cs` | Sort ascending by menu caption or name if no caption, case sensitive | -| `ezMenu::sort_asc_caption_ci` | Sort ascending by menu caption or name if no caption, incase sensitive | -| `ezMenu::sort_dsc_caption_cs` | Sort descending by menu caption or name if no caption, case sensitive | -| `ezMenu::sort_dsc_caption_ci` | Sort descending by menu caption or name if no caption, case insensitive | - -See the example program SortedMenus for typical usage. - ---- - -Then there are some functions that only apply to image menus - -**`void imgBackground(uint16_t color)`** - -This sets a temporary background color different from the background from the theme or set manually with `ez.background`. This is so that background between the buttons can match the background of the images displayed. The background will return to its previous setting when the menu exits or runs any of the functions supplied with `addItem`. - -**`void imgFromTop(int16_t offset)`** - -The offset in pixels from `canvasTop()` where the top of the image is rendered. - -**`void imgCaptionFont(const GFXfont* font)`** - -**`void imgCaptionLocation(uint8_t datum)`** - -**`void imgCaptionColor(uint16_t color)`** - -**`void imgCaptionMargins(int16_t hmargin, int16_t vmargin)`** - -These settings allow for the menu item caption to be printed somewhere on the canvas. The font and color options are obvious, the location is one of - -| | left | middle | right | -| ----|:-----|:-----|:------ | -| **top** | `TL_DATUM` | `TC_DATUM` | `TR_DATUM` | -| **middle** | `ML_DATUM` | `MC_DATUM` | `MR_DATUM` | -| **bottom** | `BL_DATUM` | `BC_DATUM` | `BR_DATUM` | - -and the margins specify how far away from the edges of the canvas the caption is printed. The default is for no caption to be printed, you must specify all these options if you want captions. - -**`int16_t runOnce()`** - -If you execute `yourMenu.runOnce()`, the menu will show and the user can interact with it until an item is picked. Then the function returns the index of the picked item (starting at one). The menu can also 'exit' (which is not the same as the `.runOnce()` function exiting, which happens when an item is picked. - -* If the user picks an item named "Back", "Exit" or "Done" that does not have an advancedFunction passed. -* If the user picks any item using a button named "Back", "Exit" or "Done". -* if the advancedFunction that was ran for a picked item returned `false`. - -In all of the above cases, `.runOnce` will return 0. The index of the item picked will still be returned with `.pick()`, see below. - -**`void run()`** - -The internal code for `.run()` is very simple: - -``` -void ezMenu::run() { - while (runOnce()) {} -} -``` - -In other words: all it does is just call `.runOnce()` on your menu until it exits for any of the reasons listed above. If your menu is the main menu of the program, you would simply not supply any items or buttons called "Exit", "Back" or "Done" and have no advancedFunctions ever return `false`. This will cause the menu to run forever. - -**`int16_t pick()`** - -**`String pickName()`** - -**`String pickCaption()`** - -**`String pickButton()`** - -These functions will show the position, name and caption of the picked item. They are useful after your menu has been ran once with `.runOnce` or in an advancedFunction (see under `addItem` above). - -  - -## Settings - -M5ez comes with a number of built-in settings menus. The settings are saved to flash in the `M5ez` section of the ESP32 NVS facility (using the Arduino `Preferences` library). All you need to do is create one menu item that points to `ez.settings.menu` and all settings will be available. By commenting out certain compiler defines, menu items (and all code associated with them) can be disabled. The various menus can also be accessed directly. Here's a quick table of the menus, their `#define` keyword, and their direct access function. - -| Menu | `#define` | function | -|:-----|:----------|:---------| -| Wifi menu | `M5EZ_WIFI` | `ez.wifi.menu` | -| Battery menu | `M5EZ_BATTERY` | `ez.battery.menu` | -| Clock menu | `M5EZ_CLOCK` | `ez.clock.menu` | -| Backlight brightness | `M5EZ_BACKLIGHT` | `ez.backlight.menu` | -| FACES keyboard | `M5EZ_FACES` | `ez.faces.menu` | -| Theme chooser | n/a | `ez.theme.menu` | - -### Wifi - -The wifi menu allows the user to connect to an Access Point. The user can also turn on and off the "autoconnect" feature. With this feature on, M5EZ will behave like most smartphones and automatically connect to any Access Point which has been added to the autoconnect list when it was joined. - -Note that this doesn't use the `WiFi.setAutoConnect` and `WiFi.setAutoReconnect` functions of the ESP32 WiFi library: they can only connect to one access point. Instead M5ez has it's own logic for connecting, saving the ssid and password of networks you want to automatically connect to in flash. - -Below you can see how to access the stored networks as well as the stored "on/off" toggle for the autoconnect feature from code. You probably won't need this as `ez.wifi.menu` lets the user manage all of this. Note that if you do make any changes, you have to call `ez.wifi.writeFlash()` when you are done to save them to flash. - -`std::vector networks` - -`ez.wifi.networks` is a vector (think of it as an array), that holds all the stored networks after M5ez boots (or `ez.wifi.readFlash()` is executed). Here's a code example to print all the SSIDs and their passwords to the M5ez canvas. - -``` -for (int8_t n = 0; n < ez.wifi.networks.size(); n++) { - ez.canvas.print(ez.wifi.networks[n].SSID); - ez.canvas.print(": "); - ez.canvas.println(ez.wifi.networks[n].key); -} -``` - -`void ez.wifi.add(String ssid, String key)` - -`bool ez.wifi.remove(int8_t index)` - -`bool ez.wifi.remove(String ssid)` - -Adds or removes a network. the index is the index in te networks array above. - -`int8_t indexForSSID(String ssid)` - -Can be used to find the index for a named SSID. -1 is returned if the name is not found. - -#### The weird Wifi ghost button problem - -when you connect to Wifi, on some M5Stack devices, you may notice a strange quirk of the M5Stack hardware, or possibly of the ESP32 chip. When you are connected, the left button is getting ghost clicks. If this happens to you when you are on Wifi, you will need to do the following to fix it. - -Navigate to the Arduino libraries directory, and then from there to `M5Stack/src/utility/Button.cpp`. In that file (around line 60) find - -``` - pinVal = digitalRead(_pin); -``` - -and replace that line with: - -``` -// The digitalRead of the button pin is commented out below. -// The two lines below fix an issue where BtnA gets spurious presses if the -// Wifi is active. (The second line fixes it, the first remediates resulting -// speaker noise.) For details: https://github.com/m5stack/M5Stack/issues/52 - -// pinVal = digitalRead(_pin); - dacWrite(25, 0); - pinVal = analogRead(_pin); -``` - -Now recompile and the problem is gone. It does mean that you cannot use the speaker while `ez.buttons.poll()` is checking for button presses. - -#### Over-The-Air (OTA) updates via https - -You might deploy hardware that needs updates but that you don't want to hook up via USB every time. But you will want this upload mechanism to offer some security against attackers that could otherwise compromise large numbers of internet-connected IoT devices. M5ez allows you to boot from a compiled binary file that is downloaded from the internet over https. - -`bool ez.wifi.update(String url, const char* root_cert, ezProgressBar* pb = NULL)` - -`String ez.wifi.updateError()` - -Takes a URL and a root certificate. A shell script called `get_cert` is provided in the `/tools` directory of this repository to get the right (non-forwarded) URL and create an include file to provide the correct certificate. The optional third argument is a pointer to the `ezProgressBar` instance that will show the progress of the firmware download. It must be provided with a leading ampersand. - -`ez.wifi.update` returns `true` if the file is downloaded and everything is set up. The next reboot - which can be forced with `ESP.restart()` - will start the new binary. If `ez.wifi.update` returns `false`, you can use `ez.wifi.updateError()` to return a String with a human-readable error message. (The way the https stream data is handled by the underlying ESP32 `Update` library does not seem terribly robust: stream timeouts happen, even on otherwise good internet connections.) - -The [README.rd file of the OTA_https sample sketch](https://github.com/ropg/M5ez/tree/master/examples/OTA_https) provides a step-by-step recipe that describes how to determine the URL and get the certificate using `get_cert`. - -  - -### BLE - -BLE, short for [Bluetooth Low Energy](https://en.wikipedia.org/wiki/Bluetooth_Low_Energy). Is intended to provide considerably reduced power consumption and cost while maintaining a similar communication range. We implemented a device manager, so you can just focus on communicate with the target device. You can access it's interface from `ez.ble` , and can get access to connected devices from `ez.ble.getClient` function. - -  - -### Battery - -The battery menu allows you to selectively show a battery level icon in the header bar. But due to [hardware limitations](https://github.com/m5stack/M5Stack/issues/74) it can only show four different battery levels. You can access its menu from `ez.battery.menu`. - -  - -### Clock - -If M5ez is compiled with M5EZ_CLOCK (which it is by default), it will be set up to display a clock in the header bar at the top of the screen. By default this clock displays time in a 24 hour format, but this can be changed in the clock menu, which is also accessible directly via `ez.clock.menu`. - -The first time M5ez goes online, it will try to look up the timezone using the GeoIP country of the IP address you are connecting to the timezone server from. If this lookup fails or if you are in a country that spans multiple timezones this lookup will fail and an official timezone name will have to be set manually in the menu. (Timezone names are of the from `Continent/Some_City`, see [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) for a list of timezone names. Note that the name of the timezone has to be entered in the correct case. - -If you use M5ez with on-screen clock, your code can also use all the other features of [ezTime](https:://github.com.ropg/ezTime), my full-featured general purpose Arduino time library. - -  - -### Backlight - -You can set the brightness of the LCD backlight LEDs here. The menu is also directly accessible via `ez.backlight.menu`. - -  - -### FACES keyboard - -Here you can set whether or not you have a FACES keyboard attached. I would have preferred to detect the keyboard automatically, but that's not easy, so it's solved with a settings menu. - -  - -### Factory defaults - -This will erase M5ez's NVS flash section and boot, setting all M5ez settings back to their defaults. - -  - -### Adding your own settings - -You can add your own settings menu. The settings menu object `ez.settings.menuObj` is a regular M5ez menu, and so your own code can, at startup, add its own submenus. To show how, let's see a snippet of M5ez's own code - -``` -ez.settings.menuObj.addItem("Backlight brightness", ez.backlight.menu); -``` - -That's how the backlight menu item is added to the menu. - -  - -## Themes - -### Including themes - -In the `setup()` part of your code, you can include themes from the themes directory. If you include multiple themes, the settings menu will show a theme chooser where the user can choose their theme. For instance, the M5ez demo program offers both the 'default' and the 'dark' theme as follows: - -``` -void setup() { - #include - #include - ez.begin(); -} -``` - -And thus the settings menu offers a "Theme Chooser" option for these two themes: - -![](images/theme-default.jpg)     ![](images/theme-dark.jpg) - -If you include no themes, the default theme is loaded as the only theme. - -### Making your own - -To make your own theme, copy the default theme from the src/themes directory to your sketch directory, rename it and change the values you want to change. The dark theme should give you an idea how to uncomment and change some of the values. Make sure you at least change the 'name' property as that will be displayed in the theme chooser. Now include this file in the `setup()` section of your sketch. Since you're including from the sketch directory and not from a library use quotes and not pointy brackets, like `#include "myTheme.h"`. If you spend some time to create a cool looking theme, please send it along so it can be included for everyone else to use. - -  - -## z-sketches - -With a simple trick, you can make code that does something pretty on its own, but that can also be included to provide a submenu in a bigger program. If you look at the M5ez-demo program, you will see that its directory includes another sketch named z-sysinfo.ino. This is the same z-sysinfo.ino that can be compiled on its own. (It is in the examples directory.) - -Normally putting two .ino files in the same directory is a bad idea as the compiler then finds two programs that each have a `setup()` and a `loop()`. But if one of the programs (the "master" program) specifies `#define MAIN_DECLARED` and the slave program has `#ifndef MAIN_DECLARED` and `#endif` around its declaration of `setup()` and `loop()`, it no longer conflicts. This means the master program can call functions from it. As you can see z-sysinfo.ino also uses whether or not `MAIN_DECLARED` is defined to determine whether to provide an "Exit" button. After all: if it is running stand-alone there is nothing to exit to. - -Also note that you do not need to `#include` any sketches placed in the same directory as your master program: the compiler combines them automatically. That also means you must ensure that no names are declared twice, or otherwise the compiler will complain. - -> *It's called a z-sketch and starts with a z because that way the Arduino program that turns the .ino files into .cpp files for the compiler looks at it last, which is important. Silly hack, but it is nice to able to write smaller bits of functionality that can be shown and tested individually.* - -  - -## Table of Contents - -* [`M5ez` The easy way to program on the M5Stack](#m5ez-the-easy-way-to-program-on-the-m5stack) - * [Introduction](#introduction) - * [Other products by M5](#other-products-by-m5) - * [Alternatives](#alternatives) - * [UiFlow](#uiflow) - * [Getting started](#getting-started) - * [If you get `fatal error: ezTime.h: No such file or directory`](#if-you-get-fatal-error-eztimeh-no-such-file-or-directory) - * [Structure of documentation](#structure-of-documentation) - * [Tech Notes](#tech-notes) -* [M5ez User Manual](#m5ez-user-manual) - * [How it all works](#how-it-all-works) - * [Screen](#screen) - * [Header](#header) - * [Showing, hiding, title](#showing-hiding-title) - * [Your own header widgets](#your-own-header-widgets) - * [Canvas](#canvas) - * [Canvas dimensions](#canvas-dimensions) - * [Printing to the canvas](#printing-to-the-canvas) - * [Buttons](#buttons) - * [Scheduling tasks within M5ez](#scheduling-tasks-within-m5ez) - * [Yield](#yield) - * [Your own events](#your-own-events) - * [Redrawing after an event](#redrawing-after-an-event) - * [Showing messages with msgBox](#showing-messages-with-msgbox) - * [ezProgressBar](#ezprogressbar) - * [3-button text input](#3-button-text-input) - * [FACES keyboard support](#faces-keyboard-support) - * [Composing or viewing longer texts: textBox](#composing-or-viewing-longer-texts-textbox) - * [Fonts](#fonts) - * [FreeFonts from the Adafruit library](#freefonts-from-the-adafruit-library) - * [FreeFonts included by the M5Stack driver](#freefonts-included-by-the-m5stack-driver) - * [Older fonts available only through M5ez](#older-fonts-available-only-through-m5ez) - * [Using your own fonts](#using-your-own-fonts) - * [Menus](#menus) - * [Let's start with text menus](#lets-start-with-text-menus) - * [`.runOnce()`](#runonce) - * [Image menus](#image-menus) - * [Menus: all the functions documented](#menus-all-the-functions-documented) - * [Settings](#settings) - * [Wifi](#wifi) - * [The weird Wifi ghost button problem](#the-weird-wifi-ghost-button-problem) - * [Over-The-Air (OTA) updates via https](#over-the-air-ota-updates-via-https) - * [BLE](#ble) - * [Battery](#battery) - * [Clock](#clock) - * [Backlight](#backlight) - * [FACES keyboard](#faces-keyboard) - * [Factory defaults](#factory-defaults) - * [Adding your own settings](#adding-your-own-settings) - * [Themes](#themes) - * [Including themes](#including-themes) - * [Making your own](#making-your-own) - * [z-sketches](#z-sketches) -* [Table of Contents](#table-of-contents) diff --git a/lib/M5ez/compiled_binaries/M5ez-demo.bin b/lib/M5ez/compiled_binaries/M5ez-demo.bin deleted file mode 100644 index e56be73..0000000 Binary files a/lib/M5ez/compiled_binaries/M5ez-demo.bin and /dev/null differ diff --git a/lib/M5ez/compiled_binaries/OTA_https.bin b/lib/M5ez/compiled_binaries/OTA_https.bin deleted file mode 100644 index 17119a8..0000000 Binary files a/lib/M5ez/compiled_binaries/OTA_https.bin and /dev/null differ diff --git a/lib/M5ez/component.mk b/lib/M5ez/component.mk deleted file mode 100644 index 3bd5ac1..0000000 --- a/lib/M5ez/component.mk +++ /dev/null @@ -1,7 +0,0 @@ -# -# Main Makefile. This is basically the same as a component makefile. -# -# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) - -COMPONENT_SRCDIRS := src -COMPONENT_ADD_INCLUDEDIRS := src diff --git a/lib/M5ez/examples/Hello_World/Hello_World.ino b/lib/M5ez/examples/Hello_World/Hello_World.ino deleted file mode 100644 index 3820740..0000000 --- a/lib/M5ez/examples/Hello_World/Hello_World.ino +++ /dev/null @@ -1,10 +0,0 @@ -#include - -void setup() { - ez.begin(); -} - -void loop() { - ez.msgBox("M5ez minimal program", "Hello World !", "Settings"); - ez.settings.menu(); -} diff --git a/lib/M5ez/examples/ImagesSPIFFSorSD/ImagesSPIFFSorSD.ino b/lib/M5ez/examples/ImagesSPIFFSorSD/ImagesSPIFFSorSD.ino deleted file mode 100644 index 2e86ce8..0000000 --- a/lib/M5ez/examples/ImagesSPIFFSorSD/ImagesSPIFFSorSD.ino +++ /dev/null @@ -1,34 +0,0 @@ -/* - * To use this sketch, you need to use the Arduino ESP32 filesystem uploader to upload the - * images from the data directory to the SPIFFS partition on your ESP32. See - * - * https://github.com/me-no-dev/arduino-esp32fs-plugin for the uploader - * - */ - -#include -#include -#include - -#include "images.h" - -void setup() { - #include - #include - ez.begin(); - SPIFFS.begin(); -} - -void loop() { - ezMenu images; - images.imgBackground(TFT_BLACK); - images.imgFromTop(40); - images.imgCaptionFont(&FreeSansBold12pt7b); - images.imgCaptionLocation(TC_DATUM); - images.imgCaptionColor(TFT_WHITE); - images.imgCaptionMargins(10,10); - images.addItem(system_jpg, "Settings", ez.settings.menu); // This image comes from images.h file - images.addItem(SPIFFS, "/rose.jpg", "Rose"); // This and image below come from SPIFFS - images.addItem(SPIFFS, "/galaxy.jpg", "Galaxy"); - images.run(); -} diff --git a/lib/M5ez/examples/ImagesSPIFFSorSD/data/galaxy.jpg b/lib/M5ez/examples/ImagesSPIFFSorSD/data/galaxy.jpg deleted file mode 100644 index 9b4f0f6..0000000 Binary files a/lib/M5ez/examples/ImagesSPIFFSorSD/data/galaxy.jpg and /dev/null differ diff --git a/lib/M5ez/examples/ImagesSPIFFSorSD/data/rose.jpg b/lib/M5ez/examples/ImagesSPIFFSorSD/data/rose.jpg deleted file mode 100644 index 8ef68c6..0000000 Binary files a/lib/M5ez/examples/ImagesSPIFFSorSD/data/rose.jpg and /dev/null differ diff --git a/lib/M5ez/examples/ImagesSPIFFSorSD/images.h b/lib/M5ez/examples/ImagesSPIFFSorSD/images.h deleted file mode 100644 index 177eb1b..0000000 --- a/lib/M5ez/examples/ImagesSPIFFSorSD/images.h +++ /dev/null @@ -1,501 +0,0 @@ -#pragma once - -const char system_jpg[] PROGMEM = -{ - 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x00, 0x60, - 0x00, 0x60, 0x00, 0x00, 0xFF, 0xE1, 0x00, 0x22, 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x4D, 0x4D, - 0x00, 0x2A, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x02, 0x01, 0x01, - 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x05, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x06, 0x04, 0x04, 0x03, 0x05, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x08, - 0x09, 0x0B, 0x09, 0x08, 0x08, 0x0A, 0x08, 0x07, 0x07, 0x0A, 0x0D, 0x0A, 0x0A, 0x0B, 0x0C, 0x0C, - 0x0C, 0x0C, 0x07, 0x09, 0x0E, 0x0F, 0x0D, 0x0C, 0x0E, 0x0B, 0x0C, 0x0C, 0x0C, 0xFF, 0xDB, 0x00, - 0x43, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x06, 0x03, 0x03, 0x06, 0x0C, 0x08, 0x07, 0x08, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0xB5, 0x01, 0x40, 0x03, 0x01, 0x22, 0x00, 0x02, - 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, - 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, - 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, - 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, - 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, - 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, - 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, - 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, - 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, - 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, - 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, - 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xC4, 0x00, - 0x1F, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, - 0x00, 0xB5, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, - 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, - 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, - 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, - 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, - 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, - 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, - 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, - 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, - 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, - 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, - 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, - 0x00, 0x3F, 0x00, 0xFE, 0x7F, 0xE8, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xAB, 0x7A, 0x3E, 0x81, 0x7D, 0xE2, 0x1B, 0x9F, 0x27, 0x4F, - 0xB2, 0xBB, 0xBE, 0x9B, 0x19, 0xD9, 0x6F, 0x0B, 0x4A, 0xDF, 0x92, 0x82, 0x6A, 0x1B, 0xDB, 0x19, - 0xB4, 0xDB, 0xA6, 0x86, 0xE2, 0x19, 0x6D, 0xE6, 0x8C, 0xE1, 0xE3, 0x91, 0x0A, 0xB2, 0x9F, 0x70, - 0x79, 0x14, 0xEC, 0xED, 0x70, 0xF3, 0x22, 0xA2, 0x8A, 0x29, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, - 0x52, 0x5B, 0x5B, 0x49, 0x79, 0x3A, 0xC5, 0x0C, 0x72, 0x4B, 0x24, 0x87, 0x6A, 0xA2, 0x29, 0x66, - 0x63, 0xE8, 0x00, 0xAE, 0xCB, 0x40, 0xFD, 0x9E, 0x3C, 0x5F, 0xE2, 0x06, 0x1B, 0x74, 0x79, 0xEC, - 0xD3, 0x38, 0xDD, 0x77, 0xFB, 0x9C, 0x7F, 0xC0, 0x5B, 0xE6, 0xFC, 0x85, 0x69, 0x4E, 0x8D, 0x4A, - 0x8E, 0xD4, 0xD3, 0x7E, 0x85, 0xC6, 0x9C, 0xA5, 0xA4, 0x55, 0xCE, 0x26, 0x8A, 0xF6, 0xE8, 0xBF, - 0xE0, 0x9F, 0x5F, 0x13, 0x2F, 0xF4, 0x76, 0xBC, 0xB2, 0xD0, 0x6F, 0x2E, 0xD5, 0x53, 0xCC, 0x03, - 0xC8, 0x78, 0x96, 0x61, 0xFF, 0x00, 0x4C, 0xD9, 0xC0, 0x0D, 0xFA, 0x57, 0x8D, 0xEB, 0x9A, 0x1D, - 0xEF, 0x86, 0x75, 0x7B, 0x8D, 0x3F, 0x51, 0xB3, 0xB9, 0xB0, 0xBE, 0xB3, 0x90, 0xC5, 0x3D, 0xBD, - 0xC4, 0x46, 0x39, 0x61, 0x71, 0xD5, 0x59, 0x5B, 0x90, 0x47, 0xA1, 0xAD, 0xB1, 0x18, 0x1C, 0x45, - 0x04, 0x9D, 0x68, 0x38, 0xA7, 0xB5, 0xD6, 0xE5, 0xD5, 0xC3, 0xD5, 0xA6, 0x93, 0xA9, 0x16, 0xAE, - 0x55, 0xA2, 0x8A, 0x2B, 0x94, 0xC4, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, - 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, - 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, - 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x92, 0xCE, 0xCE, 0x6D, 0x42, 0xEE, 0x2B, 0x7B, 0x78, 0xA4, - 0x9E, 0xE2, 0x77, 0x11, 0xC7, 0x1C, 0x6A, 0x59, 0xE4, 0x62, 0x70, 0x14, 0x01, 0xC9, 0x24, 0xF0, - 0x00, 0xAF, 0x45, 0xF0, 0x7F, 0xEC, 0x93, 0xE3, 0xCF, 0x18, 0x4B, 0x16, 0xDD, 0x0E, 0x5D, 0x36, - 0x29, 0x24, 0xF2, 0xCC, 0x9A, 0x8B, 0x0B, 0x63, 0x19, 0xF5, 0x31, 0x9F, 0xDE, 0x63, 0xE8, 0x87, - 0x3D, 0xAB, 0xD8, 0x7F, 0xE0, 0x92, 0x5A, 0x95, 0xAF, 0x85, 0x3F, 0x68, 0x69, 0xB5, 0xE9, 0x34, - 0xDB, 0x5D, 0x4A, 0xFA, 0xCE, 0x18, 0xEC, 0xAD, 0x16, 0xE1, 0x03, 0x2C, 0x0D, 0x70, 0xE5, 0x1A, - 0x45, 0xC8, 0xE1, 0xB6, 0x82, 0xB9, 0x1F, 0xC2, 0xEE, 0x3B, 0xD7, 0xE9, 0xC6, 0x95, 0xA6, 0xDA, - 0x8D, 0x52, 0x4B, 0xB8, 0xEC, 0xED, 0x20, 0x9E, 0xE1, 0x8B, 0xBB, 0x45, 0x10, 0x53, 0xEB, 0x8C, - 0xF5, 0xC0, 0xAF, 0xBB, 0xE1, 0x7E, 0x0F, 0x59, 0x95, 0x2F, 0xAC, 0x56, 0x93, 0x51, 0xE9, 0x6F, - 0x5B, 0x1F, 0x49, 0x93, 0xE4, 0x4B, 0x17, 0x0F, 0x6B, 0x51, 0xD9, 0x79, 0x1F, 0x9C, 0xFF, 0x00, - 0x09, 0xBF, 0xE0, 0x90, 0x5E, 0x24, 0xF1, 0x59, 0x59, 0x35, 0x9B, 0xDB, 0xA8, 0xD4, 0x72, 0xD1, - 0xDA, 0xDB, 0xF9, 0x2A, 0x47, 0xAA, 0xCB, 0x36, 0x33, 0xF8, 0x21, 0xAF, 0xA9, 0xFE, 0x0D, 0x7F, - 0xC1, 0x1D, 0xFC, 0x0B, 0xE1, 0x37, 0x86, 0x6D, 0x4A, 0xCE, 0xCE, 0xFA, 0x64, 0x4C, 0x17, 0xBB, - 0x2F, 0x7C, 0x64, 0xCF, 0x5D, 0xC8, 0xDB, 0x62, 0x07, 0xDC, 0x21, 0xF6, 0xAF, 0xA8, 0xB4, 0x61, - 0x8A, 0xEA, 0x74, 0x96, 0xE1, 0x6B, 0xF4, 0x7C, 0x17, 0x04, 0xE5, 0x54, 0x35, 0xE4, 0xE6, 0x7E, - 0x7A, 0x9F, 0x55, 0x87, 0xE1, 0xFC, 0x1D, 0x3D, 0x79, 0x6F, 0xEA, 0x72, 0x7F, 0x0F, 0x7F, 0x63, - 0xAF, 0x03, 0x78, 0x4F, 0x4A, 0xB7, 0xB1, 0x8B, 0x49, 0x59, 0xED, 0x6D, 0x70, 0x22, 0x81, 0xB1, - 0x1C, 0x31, 0xFB, 0x08, 0xE3, 0x0A, 0x9B, 0x7D, 0x88, 0x35, 0x0F, 0xED, 0x0D, 0xFF, 0x00, 0x04, - 0xDA, 0xF8, 0x4F, 0xFB, 0x4F, 0x78, 0x1A, 0x4D, 0x27, 0x56, 0xF0, 0xBE, 0x9F, 0xA4, 0xDE, 0xC6, - 0xAC, 0x6C, 0xB5, 0x5D, 0x2A, 0x05, 0xB4, 0xBC, 0xB1, 0x72, 0x31, 0xB8, 0x15, 0x00, 0x3A, 0xFA, - 0xA3, 0x86, 0x53, 0xD7, 0x19, 0x00, 0x8F, 0x58, 0xD2, 0xEE, 0x15, 0x40, 0xE6, 0xB7, 0x2C, 0xF5, - 0x38, 0xE3, 0xEE, 0x2B, 0xE8, 0x25, 0x94, 0xE0, 0x65, 0x49, 0xD1, 0x95, 0x28, 0xB8, 0xB5, 0x6B, - 0x59, 0x7F, 0x5F, 0xE4, 0x7A, 0x8F, 0x05, 0x86, 0x70, 0xE4, 0x70, 0x56, 0xF4, 0x3F, 0x9F, 0x6F, - 0xDA, 0xF7, 0xFE, 0x09, 0xE5, 0xF1, 0x23, 0xF6, 0x3E, 0xF8, 0x99, 0x36, 0x87, 0xAB, 0x68, 0xB7, - 0xDA, 0xC6, 0x97, 0x31, 0x32, 0x69, 0x9A, 0xD6, 0x9F, 0x69, 0x24, 0x96, 0x7A, 0x94, 0x59, 0xE0, - 0xE4, 0x03, 0xE5, 0xC8, 0x3A, 0x3C, 0x4C, 0x77, 0x29, 0xE4, 0x6E, 0x52, 0xAE, 0xDC, 0x67, 0x83, - 0xBF, 0x65, 0x4F, 0x88, 0x9E, 0x3B, 0x69, 0x3E, 0xC1, 0xE1, 0x3D, 0x59, 0x16, 0x35, 0x0C, 0x64, - 0xBC, 0x8C, 0x59, 0xA1, 0x07, 0xD1, 0xA6, 0x2A, 0x1B, 0xE8, 0xB9, 0x35, 0xFD, 0x1F, 0x6B, 0x53, - 0x69, 0x3E, 0x22, 0xD2, 0xA4, 0xB1, 0xD5, 0x2C, 0xEC, 0x75, 0x2B, 0x19, 0x86, 0x24, 0xB7, 0xBB, - 0x85, 0x26, 0x86, 0x4F, 0xAA, 0x30, 0x20, 0xFE, 0x22, 0xB9, 0x6B, 0x1F, 0x0A, 0x78, 0x27, 0xC0, - 0xD2, 0x79, 0xDA, 0x5F, 0x86, 0xFC, 0x3F, 0xA7, 0xCC, 0x3A, 0x49, 0x05, 0x8C, 0x4A, 0xEB, 0xF4, - 0x60, 0x32, 0x3F, 0x0A, 0xFC, 0xDF, 0x15, 0xE1, 0xCD, 0x1F, 0x6E, 0xE5, 0x4A, 0xB5, 0xA9, 0xBE, - 0x8F, 0x75, 0xE5, 0x7E, 0xBF, 0x87, 0xEA, 0x7C, 0x9D, 0x6E, 0x15, 0xA7, 0xED, 0x2F, 0x0A, 0x96, - 0x8F, 0x6E, 0xAB, 0xFC, 0xCF, 0xC6, 0x2F, 0x84, 0xBF, 0xF0, 0x47, 0x7F, 0x88, 0x7F, 0x10, 0xCD, - 0xBC, 0x97, 0x52, 0xAC, 0x36, 0xF7, 0x00, 0x15, 0x7B, 0x2B, 0x59, 0x27, 0x4C, 0xF7, 0x56, 0x95, - 0xFC, 0xB8, 0xD7, 0x1E, 0xBB, 0x88, 0xCF, 0xAD, 0x7D, 0x23, 0xF0, 0xD7, 0xFE, 0x08, 0x6F, 0xA2, - 0x68, 0xB0, 0xC7, 0x37, 0x88, 0x26, 0x59, 0x1F, 0x78, 0x2D, 0xF6, 0xCB, 0xB6, 0x9A, 0x44, 0x23, - 0xB8, 0x8E, 0x1D, 0x91, 0x95, 0x3E, 0x8C, 0xE7, 0xDE, 0xBE, 0xFA, 0xF1, 0x37, 0xC5, 0x68, 0x60, - 0x0D, 0xFB, 0xC5, 0xFC, 0xFA, 0x57, 0x9C, 0x78, 0xAF, 0xE3, 0x24, 0x6B, 0xBB, 0xF7, 0x9F, 0xAD, - 0x7A, 0x18, 0x7E, 0x0F, 0xCA, 0x70, 0xDA, 0xD4, 0xBC, 0xDF, 0x9E, 0xDF, 0x77, 0xFC, 0x13, 0xAA, - 0x96, 0x45, 0x81, 0xA5, 0xF1, 0xFB, 0xCC, 0xF3, 0x9F, 0x0A, 0xFE, 0xC1, 0xDF, 0x0D, 0xFE, 0x19, - 0xDA, 0x49, 0x1A, 0x5A, 0x79, 0xCB, 0x21, 0xCB, 0xC5, 0x02, 0x2D, 0x94, 0x2E, 0x47, 0x7D, 0xB1, - 0x00, 0xD9, 0xFA, 0xB9, 0xAE, 0x82, 0x1D, 0x23, 0xC3, 0x5E, 0x02, 0x19, 0xD1, 0xF4, 0x9D, 0x33, - 0x4F, 0x91, 0x53, 0x67, 0x9B, 0x0C, 0x0A, 0x25, 0x65, 0xF4, 0x2F, 0xF7, 0x9B, 0xF1, 0x26, 0xB9, - 0x9F, 0x15, 0xFC, 0x65, 0x04, 0x37, 0xEF, 0x47, 0xE7, 0x5E, 0x67, 0xE2, 0x9F, 0x8C, 0x41, 0x8B, - 0x6D, 0x91, 0x9F, 0x9C, 0x00, 0xBC, 0xE6, 0xBD, 0x48, 0xE2, 0xB0, 0x58, 0x55, 0x6A, 0x30, 0x51, - 0xFC, 0xFE, 0xFD, 0xCE, 0xCF, 0x6D, 0x87, 0xA2, 0xAD, 0x4E, 0x29, 0x1E, 0x99, 0xE2, 0x4F, 0x88, - 0x31, 0xA1, 0x6F, 0x9F, 0xF5, 0xAF, 0xCF, 0x7F, 0xF8, 0x2A, 0x96, 0x91, 0xA7, 0xDC, 0x7C, 0x43, - 0xF0, 0xDF, 0x88, 0x2D, 0x95, 0x56, 0xF3, 0x56, 0xB5, 0x9A, 0xDA, 0xEB, 0x6A, 0x80, 0x24, 0xF2, - 0x0A, 0x6C, 0x73, 0xEA, 0xD8, 0x97, 0x6E, 0x4F, 0x64, 0x51, 0xDA, 0xBE, 0xB0, 0xB0, 0xF0, 0x0F, - 0xC4, 0x4F, 0x88, 0x8A, 0x64, 0xD1, 0xFC, 0x29, 0xAB, 0x49, 0x0B, 0x7D, 0xC9, 0xEE, 0x57, 0xEC, - 0xD0, 0xBF, 0xD1, 0xE4, 0x2A, 0xAD, 0xF8, 0x35, 0x4F, 0xAE, 0x7F, 0xC1, 0x32, 0xF5, 0x6F, 0x8A, - 0xD7, 0xB6, 0x97, 0x3E, 0x36, 0xD4, 0xB4, 0x28, 0x57, 0x4F, 0x46, 0x36, 0xB1, 0xC5, 0x0B, 0xDE, - 0x34, 0x6C, 0xFB, 0x77, 0x86, 0x53, 0xB1, 0x7F, 0x85, 0x79, 0xCB, 0x74, 0xE3, 0xDF, 0xCB, 0xCE, - 0x30, 0xF8, 0x9C, 0xDB, 0x0A, 0xF0, 0xF4, 0x60, 0xDD, 0xDA, 0x69, 0xB5, 0x64, 0xAC, 0xFB, 0xBF, - 0x2B, 0x9C, 0x78, 0xFA, 0x75, 0xB1, 0xB4, 0x5D, 0x28, 0x47, 0x7B, 0x59, 0xF4, 0xFB, 0xFD, 0x0F, - 0xC9, 0xEA, 0x2B, 0xF6, 0x23, 0x46, 0xFF, 0x00, 0x82, 0x6F, 0xFC, 0x34, 0xF0, 0xF5, 0xA8, 0x8A, - 0xFB, 0x4F, 0xBA, 0xD6, 0x97, 0xCB, 0xD8, 0xF1, 0x4F, 0x39, 0x82, 0x02, 0x7B, 0x90, 0x90, 0xEC, - 0x20, 0x7B, 0x16, 0x3F, 0x8D, 0x7C, 0x51, 0xFF, 0x00, 0x05, 0x27, 0xFD, 0x86, 0x34, 0x6F, 0xD9, - 0xB2, 0x3D, 0x2F, 0xC5, 0x1E, 0x13, 0x69, 0xA0, 0xF0, 0xFE, 0xAD, 0x74, 0x6C, 0x6E, 0x2C, 0x67, - 0x94, 0xCA, 0x6C, 0xEE, 0x0A, 0xB3, 0xA7, 0x96, 0xC7, 0xE6, 0x31, 0xB2, 0xA3, 0xF0, 0xC4, 0x95, - 0x29, 0xD4, 0x86, 0x01, 0x7E, 0x1B, 0x35, 0xE0, 0x9C, 0x76, 0x07, 0x0C, 0xF1, 0x53, 0x6A, 0x51, - 0x5B, 0xA5, 0xBA, 0xF3, 0xFF, 0x00, 0x86, 0x67, 0xCE, 0xE3, 0x38, 0x7F, 0x13, 0x87, 0xA3, 0xED, - 0x9D, 0x9A, 0x5B, 0xF7, 0x47, 0xC9, 0xD4, 0x51, 0x45, 0x7C, 0x71, 0xE0, 0x85, 0x14, 0x51, 0x40, - 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, - 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, - 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x1F, 0x4E, 0x7F, 0xC1, 0x30, 0x8E, 0x3E, 0x2A, - 0xDD, 0x7F, 0xD7, 0xD6, 0x9F, 0xFF, 0x00, 0xA3, 0x9A, 0xBF, 0x52, 0x74, 0xDB, 0x81, 0x18, 0x5A, - 0xFC, 0xB5, 0xFF, 0x00, 0x82, 0x62, 0xB6, 0xDF, 0x8A, 0x57, 0x87, 0xD2, 0xE6, 0xC3, 0xFF, 0x00, - 0x47, 0x35, 0x7E, 0x95, 0xDC, 0x6B, 0x02, 0xD6, 0x2F, 0xBD, 0x8C, 0x57, 0xEE, 0x5C, 0x09, 0x2E, - 0x5C, 0xAA, 0x2D, 0xF7, 0x7F, 0x9B, 0x3F, 0x44, 0xE1, 0xB7, 0x6C, 0x12, 0xF5, 0x7F, 0x99, 0xDD, - 0x5A, 0x6B, 0xC9, 0x07, 0xF1, 0x7E, 0xB5, 0xA3, 0x07, 0x8E, 0x23, 0xB6, 0x1F, 0x7D, 0x7F, 0x3A, - 0xF1, 0x0D, 0x73, 0xE2, 0x38, 0xB2, 0xDD, 0x87, 0xAE, 0x53, 0x55, 0xF8, 0xDF, 0x15, 0xA3, 0x36, - 0xEB, 0x85, 0x04, 0x1C, 0x60, 0x1C, 0x9F, 0xC8, 0x57, 0xD0, 0x57, 0xCD, 0x23, 0x07, 0x63, 0xD4, - 0xA9, 0x8C, 0x51, 0x76, 0x47, 0xD4, 0x27, 0xE2, 0x9C, 0x36, 0xEB, 0xFE, 0xB3, 0xF5, 0xAA, 0xB7, - 0x7F, 0x1B, 0x23, 0x88, 0x71, 0x2F, 0xEB, 0x5F, 0x30, 0xE8, 0xDE, 0x3E, 0xD7, 0xBC, 0x73, 0x2E, - 0xCD, 0x0B, 0x47, 0xD5, 0xB5, 0x6F, 0x9C, 0x46, 0x5E, 0x08, 0x19, 0xA3, 0x52, 0x7D, 0x58, 0x02, - 0x17, 0xF1, 0xC5, 0x77, 0x7E, 0x1E, 0xFD, 0x9B, 0xFE, 0x24, 0x78, 0xB2, 0x37, 0x92, 0xFA, 0x5D, - 0x2B, 0xC3, 0xF0, 0xAB, 0x81, 0xB6, 0x79, 0xFC, 0xE9, 0x58, 0x77, 0x20, 0x47, 0xBD, 0x78, 0xF7, - 0x2A, 0x7F, 0x9D, 0x71, 0xFD, 0x7F, 0x13, 0x5B, 0xF8, 0x30, 0x6F, 0xD1, 0x7E, 0xA6, 0x1F, 0x59, - 0xAB, 0x3F, 0x82, 0x2D, 0x9E, 0x93, 0xAA, 0x7C, 0x7B, 0xD8, 0x1B, 0x12, 0xFE, 0xB5, 0xC7, 0xEB, - 0xFF, 0x00, 0xB4, 0x27, 0x9C, 0x4A, 0xAD, 0xC6, 0xF3, 0xE8, 0xA7, 0x35, 0xA3, 0xA3, 0xFE, 0xCA, - 0xFE, 0x1D, 0xD2, 0x2E, 0x55, 0xB5, 0xED, 0x77, 0x55, 0xD6, 0x99, 0x0F, 0x0A, 0xB8, 0xB6, 0x88, - 0x9E, 0xFC, 0x7C, 0xCD, 0xF9, 0x30, 0xAF, 0x5A, 0xF0, 0x47, 0x85, 0xBC, 0x17, 0xE0, 0xAD, 0x3A, - 0x39, 0x34, 0x9F, 0x0E, 0xE9, 0x96, 0xF7, 0x16, 0xC7, 0xF7, 0x77, 0x4D, 0x17, 0x9D, 0x3A, 0x31, - 0x07, 0x91, 0x2B, 0xEE, 0x70, 0x4E, 0x0F, 0xF1, 0x7A, 0xD7, 0x7D, 0x1C, 0x87, 0x36, 0xC4, 0x59, - 0xCA, 0xD0, 0x4F, 0xBB, 0xBB, 0xFB, 0x96, 0x9F, 0x89, 0xBC, 0x32, 0xFC, 0x65, 0x5D, 0x5D, 0xA2, - 0xBE, 0xFF, 0x00, 0xCB, 0xFC, 0xCF, 0x9F, 0x2D, 0xEF, 0xFC, 0x4D, 0xE3, 0xA4, 0x57, 0xD3, 0xF4, - 0xCB, 0xE9, 0xA1, 0x99, 0x8A, 0x24, 0xC6, 0x32, 0xB1, 0x13, 0xDF, 0xE7, 0x38, 0x5F, 0xD6, 0xBA, - 0x0D, 0x0F, 0xF6, 0x5B, 0xD7, 0xBC, 0x4D, 0x74, 0x91, 0xEB, 0x1E, 0x20, 0xD2, 0x74, 0x56, 0x98, - 0x7C, 0x96, 0xFE, 0x61, 0xB8, 0xB9, 0x6F, 0x70, 0x8B, 0x80, 0x47, 0xB8, 0x7A, 0xF5, 0xEB, 0x9B, - 0x5B, 0x8F, 0x88, 0xBE, 0x23, 0x8F, 0x48, 0xB1, 0x79, 0x05, 0xC6, 0xA1, 0xBA, 0x14, 0x68, 0xCF, - 0xCC, 0x9F, 0x29, 0x25, 0x87, 0xB8, 0x00, 0x9F, 0xC2, 0xBD, 0x6B, 0xFE, 0x09, 0xD7, 0xFB, 0x19, - 0x2D, 0xB7, 0xC4, 0x8D, 0x56, 0xD7, 0x59, 0xB0, 0x30, 0xD8, 0xE9, 0xA8, 0x2E, 0x56, 0xE4, 0x03, - 0xBA, 0x76, 0x2F, 0x80, 0x8C, 0x0F, 0x73, 0xC9, 0xCE, 0x7A, 0x0E, 0x95, 0xCB, 0xC5, 0x39, 0x7E, - 0x03, 0x20, 0xA5, 0x4E, 0xA6, 0x3E, 0x73, 0xAB, 0x29, 0xAF, 0x85, 0x35, 0x15, 0xF7, 0x6A, 0xED, - 0xE7, 0x7F, 0xBC, 0xE7, 0xCD, 0xA8, 0x61, 0xB2, 0xE8, 0x46, 0x78, 0x99, 0x4A, 0x6E, 0x5D, 0x15, - 0x97, 0xFC, 0x1F, 0xC4, 0xF9, 0x81, 0xBF, 0x65, 0x0F, 0x07, 0xF8, 0x52, 0xFD, 0x53, 0x55, 0x9B, - 0x54, 0xD6, 0xAE, 0x23, 0x05, 0x24, 0x49, 0xA4, 0xFB, 0x3C, 0x45, 0xBD, 0x76, 0xA7, 0xCE, 0xA4, - 0x7A, 0x17, 0x3E, 0xF9, 0xAF, 0x5B, 0xF8, 0x41, 0xF0, 0xBB, 0x40, 0xD2, 0x74, 0x49, 0xAF, 0xBC, - 0x39, 0xE1, 0xED, 0x36, 0x38, 0xF4, 0x86, 0x11, 0x4F, 0x77, 0x6B, 0x6E, 0xB2, 0x4D, 0x6A, 0x5B, - 0x20, 0x09, 0xA4, 0x19, 0x75, 0x2D, 0xB5, 0xB1, 0xBC, 0xFC, 0xC0, 0x71, 0x91, 0x5F, 0x73, 0x7E, - 0xD7, 0xFF, 0x00, 0xB3, 0x07, 0x87, 0x3E, 0x27, 0x69, 0x3A, 0x0D, 0xD5, 0xAE, 0x8F, 0x63, 0x65, - 0xA9, 0x68, 0xF7, 0x01, 0x64, 0xB8, 0x82, 0x32, 0xB3, 0x5E, 0xDB, 0x88, 0x9F, 0x31, 0x39, 0x07, - 0xE6, 0xF9, 0x84, 0x64, 0x16, 0x04, 0xA8, 0x56, 0x0A, 0x40, 0x63, 0x5F, 0x32, 0xFE, 0xCF, 0xDF, - 0xB1, 0x8E, 0xB1, 0xA8, 0x7E, 0xD5, 0x8B, 0xE2, 0xCF, 0xB6, 0xEA, 0x1A, 0x2E, 0x83, 0xA0, 0xE4, - 0xDE, 0x0B, 0x69, 0x1A, 0x1F, 0xED, 0x62, 0x4E, 0x56, 0xCD, 0xB1, 0xC3, 0xC6, 0xCC, 0xA1, 0xA4, - 0x52, 0x08, 0xDA, 0x98, 0xF9, 0x59, 0x91, 0x87, 0x8D, 0x2E, 0x38, 0xC8, 0x68, 0x60, 0x29, 0xD6, - 0xC1, 0xE1, 0xED, 0x56, 0xF6, 0x94, 0x5E, 0xAD, 0x79, 0xF3, 0x3D, 0x5E, 0xDA, 0x1C, 0x3F, 0xEB, - 0x0E, 0x5D, 0x4F, 0x0D, 0x1A, 0x94, 0x69, 0xDA, 0x7D, 0x57, 0x5F, 0xBC, 0xE2, 0xB5, 0x42, 0xAC, - 0x1A, 0xB9, 0xAD, 0x5A, 0x3E, 0xB5, 0xEF, 0xDF, 0xB6, 0xCF, 0xC2, 0x1F, 0x0A, 0xFC, 0x18, 0xF0, - 0xAA, 0xF8, 0xA2, 0xD2, 0xEA, 0xFE, 0xC6, 0x5B, 0xCB, 0xB5, 0xB7, 0xFB, 0x18, 0x22, 0x4B, 0x72, - 0x4A, 0xB3, 0xB3, 0x01, 0x8D, 0xEB, 0x80, 0xA7, 0x80, 0x5B, 0x24, 0x81, 0x81, 0xD6, 0xBE, 0x75, - 0xB4, 0xF1, 0x0D, 0x9F, 0x89, 0xF4, 0xA4, 0xBC, 0xB1, 0xB8, 0x8E, 0xEA, 0xDE, 0x4F, 0xE3, 0x43, - 0x9C, 0x1E, 0xE0, 0x8E, 0xA0, 0x8E, 0xE0, 0xE0, 0x8E, 0xF5, 0xF5, 0x79, 0x4E, 0x71, 0x86, 0xCC, - 0xB0, 0xEB, 0x11, 0x87, 0x7B, 0xF4, 0x7B, 0xAB, 0x7F, 0x5B, 0x9E, 0xE6, 0x0B, 0x1F, 0x4B, 0x17, - 0x49, 0x55, 0xA5, 0xF7, 0x3D, 0xCC, 0x3D, 0x61, 0x7E, 0x66, 0xAF, 0x8A, 0x3F, 0xE0, 0xB3, 0x63, - 0x1F, 0xB3, 0x2E, 0x8B, 0xFF, 0x00, 0x63, 0x3D, 0xBF, 0xFE, 0x92, 0xDD, 0xD7, 0xDB, 0x1A, 0xC1, - 0xF9, 0x9A, 0xBE, 0x28, 0xFF, 0x00, 0x82, 0xCE, 0x9C, 0xFE, 0xCC, 0xBA, 0x27, 0xFD, 0x8C, 0xF6, - 0xFF, 0x00, 0xFA, 0x4B, 0x77, 0x5E, 0x77, 0x16, 0x7F, 0xC8, 0xA6, 0xBF, 0xF8, 0x4E, 0x5C, 0xEB, - 0xFD, 0xC6, 0xA7, 0xA1, 0xF9, 0x97, 0x45, 0x14, 0x57, 0xF3, 0x99, 0xF9, 0x58, 0x51, 0x45, 0x14, - 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, - 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, - 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x01, 0xF4, 0xC7, 0xFC, 0x13, 0x31, 0xB6, 0xFC, - 0x4C, 0xBE, 0x3E, 0x97, 0x16, 0x3F, 0xFA, 0x35, 0xAB, 0xF4, 0x13, 0xC4, 0xBA, 0x99, 0x8A, 0xD9, - 0xB9, 0xAF, 0xCF, 0x9F, 0xF8, 0x26, 0x93, 0x6D, 0xF8, 0x8D, 0xA8, 0x9F, 0x49, 0xAC, 0x7F, 0xF4, - 0x6B, 0x57, 0xDD, 0xDE, 0x2F, 0xBA, 0xFF, 0x00, 0x46, 0x6E, 0x7B, 0x57, 0xED, 0x1C, 0x1D, 0x2B, - 0x65, 0x0B, 0xD5, 0xFE, 0x6C, 0xFB, 0xEC, 0x85, 0xDB, 0x02, 0xBD, 0x5F, 0xE6, 0x71, 0x37, 0xDE, - 0x36, 0x8F, 0x44, 0xF1, 0x3D, 0xA5, 0xD5, 0xD5, 0xB2, 0x5F, 0x5B, 0xDB, 0x4C, 0xB2, 0x3D, 0xBB, - 0x9F, 0x96, 0x60, 0x0E, 0x76, 0x9F, 0x51, 0xED, 0xDF, 0xA5, 0x7D, 0x0D, 0xE1, 0x4F, 0x87, 0x3E, - 0x13, 0xD6, 0x67, 0x87, 0x5E, 0x8B, 0xC3, 0xBA, 0x72, 0xCD, 0xA9, 0x46, 0x97, 0x00, 0x4D, 0x10, - 0x97, 0xCA, 0xDC, 0xA0, 0xFC, 0xA1, 0xB2, 0x01, 0x19, 0xEA, 0x06, 0x7D, 0xEB, 0xE4, 0xBF, 0x16, - 0xCF, 0xBE, 0xFC, 0x2F, 0xB9, 0xAF, 0xAF, 0xBE, 0x19, 0xDC, 0x63, 0xC1, 0x7A, 0x38, 0xCF, 0xFC, - 0xB9, 0x43, 0xFF, 0x00, 0xA2, 0xD6, 0xBD, 0xBC, 0x96, 0x94, 0x2A, 0x56, 0x9D, 0x49, 0xAB, 0xB4, - 0x7A, 0x39, 0x7C, 0x23, 0x29, 0xCA, 0x52, 0xDC, 0xF4, 0x8D, 0x1A, 0x45, 0x82, 0x28, 0xD1, 0x42, - 0xAC, 0x68, 0x30, 0xAA, 0xA3, 0x01, 0x47, 0xA0, 0x15, 0xB5, 0x3D, 0xD7, 0xFA, 0x03, 0x61, 0x87, - 0x4F, 0x5A, 0xF3, 0xFD, 0x4B, 0xC5, 0x2B, 0xA4, 0x5B, 0x6E, 0xDD, 0xFA, 0xD7, 0x2F, 0x7D, 0xF1, - 0xB1, 0x62, 0x76, 0x8C, 0x4D, 0xED, 0xD2, 0xBE, 0xDF, 0x0D, 0x42, 0x53, 0x7E, 0xE9, 0xEE, 0xD3, - 0x8B, 0x7B, 0x1A, 0xBE, 0x29, 0xBE, 0x8A, 0x6D, 0x7E, 0xDD, 0x6E, 0xEF, 0x3E, 0xC5, 0x6A, 0xF2, - 0x05, 0x96, 0x72, 0x86, 0x41, 0x0A, 0xE7, 0x05, 0xB6, 0x8E, 0x4E, 0x2B, 0xED, 0x5F, 0x80, 0xFF, - 0x00, 0x0F, 0xFC, 0x1F, 0xE1, 0xAF, 0x86, 0x66, 0xE3, 0x54, 0xFB, 0x0C, 0xDE, 0x17, 0x92, 0x20, - 0xD3, 0x4D, 0x29, 0x13, 0x0B, 0xEC, 0x8E, 0x08, 0x23, 0xEF, 0x31, 0xED, 0xB7, 0xA1, 0xC6, 0x31, - 0x8E, 0x3F, 0x3C, 0xDF, 0x5E, 0xB8, 0xF1, 0xBE, 0xBD, 0x6B, 0x63, 0x63, 0x1B, 0x5C, 0x5D, 0x5D, - 0xCA, 0xB1, 0xC5, 0x18, 0xC6, 0x5D, 0x89, 0xC0, 0xEB, 0xC6, 0x3D, 0xCF, 0x00, 0x7B, 0x57, 0xDC, - 0xDA, 0x17, 0xC3, 0xAD, 0x37, 0x55, 0xFD, 0x9C, 0xEE, 0x61, 0xD2, 0x75, 0x08, 0x5F, 0x52, 0xF0, - 0xF5, 0xC2, 0xCD, 0x7D, 0xA6, 0xAC, 0xA7, 0x74, 0x28, 0xF8, 0x43, 0x2A, 0xA7, 0xF1, 0x26, 0xF2, - 0x8B, 0xBC, 0x0F, 0xE3, 0x00, 0xE0, 0x9C, 0x57, 0xCC, 0x78, 0xA5, 0x98, 0x63, 0xB0, 0x79, 0x75, - 0x15, 0x86, 0x9F, 0x2C, 0x5B, 0xD6, 0xDA, 0x3B, 0xAD, 0x53, 0xB9, 0xE2, 0xF1, 0x6E, 0x2B, 0x11, - 0x47, 0x0B, 0x0F, 0x62, 0xEC, 0x9B, 0xD6, 0xDB, 0x9C, 0x8E, 0xA7, 0xA6, 0x5A, 0xFC, 0x0D, 0xF1, - 0xD4, 0xBE, 0x3E, 0xF0, 0x15, 0x95, 0xC5, 0xFD, 0x9D, 0x84, 0xB2, 0x49, 0x6D, 0xA7, 0xDE, 0xFF, - 0x00, 0xA5, 0x35, 0x9C, 0x6E, 0x85, 0x1C, 0x38, 0x18, 0x67, 0x4D, 0xAC, 0xC3, 0x70, 0x3B, 0x95, - 0x58, 0x64, 0xE4, 0x6F, 0x3F, 0x74, 0x7E, 0xCE, 0xFE, 0x3F, 0x83, 0xE2, 0x2F, 0x80, 0x6C, 0x75, - 0xA3, 0xE1, 0x0D, 0x4B, 0xC1, 0x7A, 0x8E, 0xA1, 0xFF, 0x00, 0x1F, 0x9A, 0x7D, 0xF3, 0x23, 0xC8, - 0xA5, 0x73, 0xB5, 0x95, 0x97, 0xE6, 0xD8, 0x77, 0x1E, 0x24, 0x54, 0x7C, 0x83, 0xF2, 0xE3, 0x6B, - 0x1F, 0xCF, 0xFF, 0x00, 0x87, 0x1F, 0x13, 0xE4, 0xF0, 0xA7, 0x8A, 0x7E, 0xC7, 0x70, 0xCC, 0x9F, - 0x3E, 0x39, 0x3D, 0x79, 0xFF, 0x00, 0x3E, 0xDF, 0x5A, 0xFB, 0x9B, 0xF6, 0x77, 0xF1, 0x19, 0xF1, - 0x25, 0x84, 0x3E, 0x58, 0xDD, 0xF2, 0x8A, 0xFC, 0x2B, 0x3C, 0xE2, 0x4C, 0x76, 0x6F, 0xEC, 0xE5, - 0x8F, 0x97, 0x34, 0xA0, 0xB9, 0x53, 0xEB, 0x6F, 0x3E, 0xE7, 0xE7, 0xF9, 0x86, 0x6B, 0x88, 0xC6, - 0xF2, 0xBC, 0x43, 0xBB, 0x8A, 0xB2, 0x7D, 0x7E, 0x7D, 0xCE, 0xBB, 0xE3, 0x97, 0xC3, 0x7D, 0x27, - 0xE3, 0x0F, 0x81, 0xEE, 0xBC, 0x3B, 0xAE, 0x47, 0x75, 0x2E, 0x9B, 0x75, 0x2C, 0x13, 0xB8, 0xB6, - 0xBD, 0x96, 0xCE, 0x74, 0x78, 0x66, 0x8E, 0x78, 0xD9, 0x26, 0x85, 0x96, 0x48, 0xD9, 0x64, 0x8D, - 0x0E, 0xE4, 0x60, 0x78, 0xEB, 0xCD, 0x53, 0xB5, 0xB3, 0x87, 0x42, 0xD3, 0x23, 0xB5, 0xB7, 0x56, - 0x58, 0x61, 0x5C, 0x6E, 0x77, 0x32, 0x3B, 0x9C, 0x63, 0x73, 0xBB, 0x12, 0xCC, 0xC7, 0x8C, 0xB3, - 0x12, 0x4E, 0x06, 0x49, 0xAE, 0xCB, 0x58, 0xF0, 0xCD, 0xE5, 0xDC, 0x9B, 0x96, 0x36, 0x6C, 0x7B, - 0x1A, 0xE2, 0xFC, 0x5C, 0x64, 0xF0, 0xD5, 0xBC, 0x92, 0xDC, 0xAF, 0x96, 0xAB, 0x92, 0x58, 0xF1, - 0x5E, 0x15, 0xF4, 0xB1, 0xE6, 0x9E, 0x1B, 0xFB, 0x5C, 0x7C, 0x2E, 0xBE, 0xF8, 0xCD, 0x25, 0xBE, - 0x9A, 0xF6, 0xD0, 0xC7, 0xA7, 0xDA, 0xDA, 0x19, 0x6D, 0xEF, 0x9E, 0xE5, 0x76, 0x2C, 0xCC, 0xE4, - 0x48, 0x8E, 0x9D, 0x46, 0x11, 0x62, 0x65, 0x65, 0xDD, 0xBB, 0xF7, 0x80, 0x85, 0x21, 0x3C, 0xCF, - 0x9A, 0x7C, 0x2F, 0xFB, 0x28, 0x78, 0x3F, 0xE0, 0x9D, 0x96, 0xB1, 0x1F, 0xDB, 0x2F, 0xB5, 0x2F, - 0x10, 0x6B, 0x28, 0xA1, 0xEF, 0x5A, 0x67, 0x8E, 0xD6, 0xCD, 0x94, 0xEE, 0x02, 0x38, 0x01, 0xDA, - 0xD9, 0x3F, 0x29, 0x79, 0x03, 0x36, 0x07, 0xCA, 0x23, 0xDC, 0xC0, 0xFB, 0x4F, 0xC7, 0x7F, 0xDA, - 0x22, 0xDD, 0x6E, 0x25, 0xB5, 0xB7, 0x91, 0x4C, 0xA3, 0x23, 0xE4, 0x3D, 0x3B, 0x7F, 0x9F, 0xF1, - 0xAF, 0x16, 0xF1, 0x7A, 0xEB, 0xBA, 0x97, 0x82, 0xA4, 0xD6, 0x96, 0xD5, 0xCD, 0x8B, 0x4C, 0xD0, - 0x19, 0xF7, 0x0F, 0x94, 0x80, 0x09, 0xC0, 0xCE, 0x7A, 0x30, 0xE7, 0xA7, 0x26, 0xBD, 0x5C, 0x9E, - 0xB6, 0x25, 0x62, 0xE9, 0xC3, 0x0D, 0x53, 0x91, 0xDD, 0x59, 0xB7, 0x64, 0xBC, 0xDF, 0xF9, 0x75, - 0xD8, 0xED, 0xC0, 0xD4, 0xAA, 0xAB, 0x46, 0x34, 0xA5, 0xCA, 0xEF, 0xBD, 0xEC, 0xBF, 0xAF, 0xCC, - 0xF3, 0x3F, 0x10, 0x2B, 0x5A, 0xDD, 0xCD, 0x13, 0xED, 0xDF, 0x13, 0x14, 0x6C, 0x1C, 0x8C, 0x83, - 0x8E, 0x0D, 0x7C, 0x51, 0xFF, 0x00, 0x05, 0x99, 0x7D, 0xDF, 0xB3, 0x36, 0x89, 0xFF, 0x00, 0x63, - 0x3D, 0xBF, 0xFE, 0x92, 0xDD, 0xD7, 0xD8, 0x77, 0xBA, 0x8C, 0x77, 0x51, 0x79, 0x91, 0xC8, 0xB2, - 0x23, 0x64, 0x06, 0x53, 0x91, 0xC7, 0x07, 0xF2, 0x35, 0xF1, 0xB7, 0xFC, 0x16, 0x46, 0x5D, 0xFF, - 0x00, 0xB3, 0x56, 0x8A, 0x3F, 0xEA, 0x66, 0xB7, 0xFF, 0x00, 0xD2, 0x5B, 0xBA, 0xFD, 0xD3, 0x8A, - 0x2F, 0xFD, 0x8F, 0x5F, 0x5B, 0xFB, 0xBF, 0xE4, 0x7E, 0x8F, 0x9C, 0x7F, 0xB8, 0xD4, 0xF4, 0x3F, - 0x35, 0x28, 0xA2, 0x8A, 0xFE, 0x75, 0x3F, 0x2B, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, - 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, - 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, - 0x0A, 0x28, 0xA2, 0x80, 0x3E, 0x92, 0xFF, 0x00, 0x82, 0x6D, 0xBE, 0xCF, 0x88, 0x1A, 0x91, 0xFF, - 0x00, 0xA6, 0xB6, 0x5F, 0xFA, 0x35, 0xAB, 0xEE, 0x0F, 0x16, 0xDD, 0x7E, 0xE1, 0xBE, 0x95, 0xF0, - 0xDF, 0xFC, 0x13, 0x91, 0xF6, 0x78, 0xF7, 0x53, 0xFF, 0x00, 0xAE, 0x96, 0x7F, 0xFA, 0x31, 0xAB, - 0xED, 0x3F, 0x17, 0x5C, 0xFE, 0xE1, 0xF9, 0xF5, 0xAF, 0xD9, 0x38, 0x43, 0xFE, 0x45, 0x0B, 0xD5, - 0xFE, 0x6C, 0xFB, 0xDC, 0x8F, 0xFD, 0xC5, 0x7C, 0xFF, 0x00, 0x33, 0xCD, 0x3C, 0x41, 0x3E, 0xFD, - 0x59, 0x47, 0xD7, 0xF9, 0x1A, 0xFA, 0xE3, 0xE1, 0xD5, 0xD6, 0x3C, 0x1D, 0xA4, 0x7B, 0x59, 0xC3, - 0xFF, 0x00, 0xA0, 0x0A, 0xF8, 0xEF, 0x57, 0x9F, 0x76, 0xB6, 0x9F, 0x56, 0xFE, 0x46, 0xBE, 0xB0, - 0xF0, 0x2D, 0xFF, 0x00, 0x91, 0xE0, 0xFD, 0x2D, 0xBD, 0x2C, 0xE2, 0xFF, 0x00, 0xD0, 0x05, 0x7D, - 0x0E, 0x43, 0xF1, 0xD4, 0x3D, 0x3C, 0xB7, 0xE2, 0x91, 0xB9, 0xE3, 0x18, 0xE4, 0xBA, 0xB1, 0x62, - 0xB9, 0x3C, 0x76, 0x3D, 0x2B, 0x37, 0xF6, 0x75, 0xF8, 0x6C, 0xDE, 0x26, 0xF1, 0xEC, 0xD3, 0x4D, - 0x12, 0xDC, 0x44, 0x9F, 0xB9, 0x54, 0x75, 0xDC, 0xAC, 0x5B, 0xA8, 0x20, 0xFB, 0x7F, 0x33, 0x59, - 0xDE, 0x29, 0xF8, 0x93, 0x15, 0x85, 0xBB, 0x29, 0x65, 0xFA, 0x9A, 0xE4, 0xBE, 0x1D, 0xFE, 0xD1, - 0x1A, 0xEF, 0xC3, 0x0F, 0x89, 0x36, 0x3A, 0x97, 0x86, 0x42, 0x5D, 0x5E, 0x79, 0xCA, 0x3E, 0xC5, - 0x24, 0x46, 0x68, 0x6F, 0x8E, 0xE1, 0x88, 0xD9, 0x01, 0x04, 0xE7, 0xA0, 0x2A, 0x43, 0x0C, 0xF0, - 0x46, 0x6B, 0xEB, 0xEA, 0x7D, 0x66, 0x58, 0x4A, 0x90, 0xC2, 0xB4, 0xA6, 0xD3, 0xE5, 0x6F, 0x6B, - 0xF4, 0x3D, 0x6A, 0x8E, 0xA7, 0xB2, 0x92, 0xA5, 0xF1, 0x5B, 0x4F, 0x53, 0xF4, 0xDF, 0xE0, 0xC7, - 0xEC, 0xAF, 0xE1, 0xFF, 0x00, 0x84, 0x86, 0xD6, 0xE9, 0x24, 0x7F, 0x2E, 0xF6, 0x2F, 0x32, 0x7D, - 0x32, 0x45, 0x59, 0x22, 0x4C, 0xF6, 0x04, 0xF2, 0x01, 0x1F, 0xC2, 0x73, 0x8F, 0x5C, 0x10, 0x07, - 0x47, 0x67, 0xF0, 0x23, 0xC2, 0x7A, 0x07, 0x8C, 0x75, 0x2D, 0x63, 0x47, 0xB7, 0xBA, 0xB7, 0xFB, - 0x74, 0x22, 0x28, 0x6D, 0xE7, 0xB8, 0xFB, 0x42, 0xD9, 0x13, 0x9F, 0x30, 0xC6, 0xC5, 0x43, 0x7C, - 0xE3, 0x6A, 0x8C, 0x92, 0xC0, 0x07, 0x1B, 0x88, 0x7C, 0x0F, 0x38, 0xD3, 0xFF, 0x00, 0x69, 0xC5, - 0xF1, 0x6E, 0xBA, 0xD6, 0xB7, 0x91, 0xFD, 0x97, 0x58, 0x65, 0x2D, 0x25, 0xBA, 0x39, 0x92, 0x30, - 0x15, 0x72, 0x7C, 0xB6, 0xC7, 0x2A, 0xAA, 0x3B, 0x80, 0x40, 0x1D, 0x30, 0x33, 0x5B, 0x36, 0x9F, - 0x1A, 0x6D, 0xA3, 0x9D, 0x56, 0x49, 0x82, 0xFB, 0x67, 0xF0, 0x3F, 0x95, 0x7F, 0x2F, 0xE6, 0x78, - 0x9C, 0x73, 0xAA, 0xE8, 0x63, 0x67, 0x26, 0xE2, 0xED, 0x66, 0xEF, 0x66, 0x8F, 0xC9, 0x71, 0x95, - 0xB1, 0x2E, 0x6E, 0x9E, 0x22, 0x4D, 0xB8, 0xF4, 0x6C, 0xE5, 0xFE, 0x2C, 0x7E, 0xC7, 0xDA, 0xC7, - 0x8C, 0x35, 0x4F, 0xB6, 0x68, 0x2A, 0xCB, 0x34, 0x67, 0x7F, 0xCA, 0x3A, 0x81, 0xD7, 0xF4, 0xAE, - 0xF3, 0xC1, 0xDF, 0xB5, 0xDC, 0x9F, 0xB3, 0xF7, 0xC3, 0xB8, 0xF4, 0x9D, 0x0F, 0x4F, 0xB3, 0xBA, - 0xD7, 0xA3, 0x4D, 0xB3, 0xDE, 0xDD, 0x65, 0xE1, 0x81, 0x87, 0x1C, 0x28, 0x20, 0xBB, 0x75, 0xE4, - 0x90, 0x07, 0x1F, 0x7B, 0x90, 0x36, 0xBE, 0x30, 0x7E, 0xD2, 0xD1, 0x78, 0x13, 0xE0, 0x58, 0xB3, - 0xD1, 0xE5, 0x68, 0xF5, 0x7F, 0x12, 0x66, 0xDC, 0x5C, 0x2F, 0x0D, 0x6D, 0x00, 0x1F, 0xBD, 0x65, - 0x3D, 0x98, 0xE5, 0x54, 0x1E, 0xA0, 0x39, 0x20, 0x82, 0x01, 0xAF, 0x8E, 0x3C, 0x69, 0xE3, 0xA8, - 0xF4, 0xDB, 0x07, 0x1B, 0xFE, 0x62, 0x3A, 0x66, 0xBE, 0xEF, 0x81, 0xB8, 0x46, 0x8E, 0x36, 0x1F, - 0x5E, 0xC6, 0xAE, 0x68, 0xDE, 0xD1, 0x8F, 0x47, 0x6D, 0xDB, 0xF2, 0xBE, 0x89, 0x79, 0x3B, 0xE8, - 0x7D, 0x1F, 0x0E, 0xE4, 0x74, 0xEB, 0xC7, 0xEB, 0x38, 0x85, 0x78, 0xF4, 0x5D, 0xED, 0xD5, 0xFF, - 0x00, 0x97, 0xDE, 0x74, 0xBF, 0x17, 0x3F, 0xE0, 0xA7, 0x3F, 0x1A, 0xF4, 0x7F, 0x11, 0xFF, 0x00, - 0x68, 0x5A, 0xF8, 0xFE, 0xFE, 0xDE, 0x58, 0xA4, 0xDC, 0xB1, 0x47, 0x69, 0x6A, 0xB6, 0xF8, 0x18, - 0xF9, 0x4C, 0x5E, 0x56, 0xC2, 0x3B, 0x72, 0xA7, 0x3D, 0xF3, 0xCD, 0x7D, 0x29, 0xE0, 0x6F, 0xDB, - 0x73, 0x58, 0xFD, 0xA9, 0xFE, 0x1D, 0x5F, 0x78, 0x57, 0x56, 0xB0, 0x81, 0x7C, 0x71, 0x6B, 0x68, - 0x2E, 0xED, 0xEE, 0xAD, 0x63, 0xF2, 0xA1, 0xD5, 0x6D, 0x9B, 0x00, 0x3E, 0xCE, 0x76, 0x48, 0xA5, - 0x94, 0x30, 0x1C, 0x1D, 0xDB, 0x94, 0x00, 0x18, 0x0F, 0xCC, 0xCB, 0xE8, 0xB5, 0x1F, 0x8C, 0x5F, - 0x12, 0x34, 0xAF, 0x0D, 0xE8, 0xB1, 0xBD, 0xD6, 0xA7, 0xAE, 0x5E, 0xC5, 0x63, 0x6B, 0x1A, 0x02, - 0xDB, 0xA4, 0x91, 0xC2, 0x83, 0xEC, 0x01, 0x39, 0x27, 0xA0, 0x00, 0x9E, 0x82, 0xBF, 0x49, 0x3F, - 0x67, 0x8F, 0x84, 0x51, 0x7C, 0x2D, 0xFD, 0xB4, 0x3C, 0x3B, 0x37, 0x96, 0xD1, 0xE9, 0x7A, 0x5D, - 0x85, 0xE4, 0x12, 0x99, 0x3A, 0x18, 0xC5, 0x9C, 0xA8, 0x9B, 0xBF, 0xE0, 0x7E, 0x5F, 0xE3, 0x5F, - 0x43, 0xE2, 0x76, 0x57, 0x96, 0x61, 0xB0, 0x14, 0x5D, 0x0A, 0x71, 0x85, 0x4B, 0xFD, 0x94, 0x95, - 0xD7, 0x9D, 0xAD, 0x7F, 0x2F, 0x99, 0xE8, 0x71, 0x5E, 0x13, 0x09, 0x4F, 0x0D, 0x07, 0x4E, 0x2A, - 0x32, 0xBF, 0x44, 0x96, 0x9F, 0x23, 0x8A, 0xF0, 0x17, 0xEC, 0x99, 0xAC, 0x6A, 0xBE, 0x21, 0x7B, - 0xAD, 0x64, 0x49, 0xBB, 0xCC, 0xDC, 0x15, 0x87, 0x41, 0xFE, 0x7F, 0xA5, 0x7B, 0xF6, 0x85, 0xF0, - 0x37, 0x4B, 0xB3, 0xF0, 0xE4, 0x9A, 0x6D, 0xE5, 0xAD, 0xBD, 0xC5, 0x9C, 0xC4, 0x19, 0x21, 0x90, - 0x65, 0x58, 0x8E, 0x3F, 0xCF, 0xFF, 0x00, 0x5A, 0xB7, 0x3C, 0x7D, 0xF1, 0x97, 0x4A, 0xB0, 0xD5, - 0xA7, 0x5B, 0x76, 0x8D, 0x42, 0xE7, 0xEE, 0xD7, 0x86, 0xFE, 0xD0, 0x5F, 0xB6, 0xB3, 0x7C, 0x34, - 0x8A, 0x1B, 0x6D, 0x36, 0xCE, 0x2B, 0xED, 0x4A, 0xEE, 0x36, 0x91, 0x4C, 0xD2, 0x11, 0x14, 0x03, - 0x38, 0x05, 0x94, 0x72, 0xD9, 0x21, 0xB8, 0x04, 0x74, 0xEB, 0x5F, 0x8E, 0x65, 0xF8, 0x1A, 0xF8, - 0xCA, 0xF1, 0xC3, 0xE1, 0x95, 0xE4, 0xFE, 0x5F, 0x89, 0xF1, 0x18, 0x5C, 0x35, 0x4A, 0xF5, 0x15, - 0x2A, 0x4A, 0xED, 0x9C, 0x0F, 0xFC, 0x14, 0x07, 0xE1, 0x3F, 0x85, 0x3E, 0x14, 0x68, 0x1E, 0x19, - 0x6F, 0x0D, 0xE8, 0x3A, 0x4E, 0x8D, 0x26, 0xA1, 0x71, 0x72, 0xD7, 0x4F, 0x67, 0x6C, 0xB1, 0x35, - 0xC1, 0x0B, 0x16, 0x37, 0x95, 0x19, 0x6C, 0x64, 0xF5, 0xF5, 0xAF, 0xCA, 0x9F, 0xF8, 0x2C, 0x1C, - 0xDE, 0x67, 0xEC, 0xDD, 0xA3, 0x7F, 0xD8, 0xCB, 0x01, 0xFF, 0x00, 0xC9, 0x5B, 0xAA, 0xFB, 0x53, - 0xE2, 0xCF, 0xC6, 0x7F, 0x10, 0xFC, 0x5B, 0xD5, 0x85, 0xD6, 0xBD, 0xA9, 0x4F, 0x7A, 0x63, 0xCF, - 0x93, 0x11, 0x3B, 0x61, 0xB7, 0x07, 0xA8, 0x44, 0x18, 0x55, 0xEC, 0x33, 0x8C, 0x9C, 0x0C, 0x93, - 0x8A, 0xF8, 0x83, 0xFE, 0x0A, 0xE9, 0x3F, 0x99, 0xFB, 0x39, 0x68, 0xE3, 0xFE, 0xA6, 0x38, 0x3F, - 0xF4, 0x9A, 0xEA, 0xBF, 0x63, 0xC5, 0x65, 0xF5, 0xB0, 0x3C, 0x35, 0x53, 0x0B, 0x5E, 0x5C, 0xD2, - 0x8C, 0x5D, 0xDA, 0xBF, 0x57, 0x7B, 0x6B, 0xD8, 0xFB, 0xBA, 0xD8, 0x69, 0xE1, 0xF2, 0x99, 0x51, - 0xA8, 0xEE, 0xD2, 0x7F, 0x8B, 0x3F, 0x3A, 0x28, 0xA2, 0x8A, 0xFC, 0x3C, 0xFC, 0xF0, 0x28, 0xA2, - 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, - 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, - 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0xFA, 0x2B, 0xFE, 0x09, 0xDA, 0xDB, - 0x7C, 0x75, 0xA9, 0xFF, 0x00, 0xD7, 0x4B, 0x3F, 0xFD, 0x18, 0xD5, 0xF6, 0x3F, 0x8B, 0xAE, 0x3F, - 0x70, 0xDF, 0x4A, 0xF8, 0xD3, 0xFE, 0x09, 0xE8, 0xD8, 0xF1, 0xDE, 0xA5, 0xFF, 0x00, 0x5D, 0x2C, - 0xFF, 0x00, 0xF4, 0x63, 0x57, 0xD8, 0x5E, 0x2F, 0xDD, 0xE5, 0x35, 0x7E, 0xC5, 0xC2, 0x1F, 0xF2, - 0x28, 0x5E, 0xAF, 0xF3, 0x67, 0xDE, 0x64, 0x7F, 0xEE, 0x2B, 0xE7, 0xF9, 0x9E, 0x6B, 0xA8, 0xCF, - 0x9D, 0x7E, 0x3F, 0x72, 0xDF, 0xC8, 0xD7, 0xD4, 0x5E, 0x19, 0xBA, 0x63, 0xE0, 0xCD, 0x3D, 0x57, - 0xFE, 0x7D, 0x22, 0xFF, 0x00, 0xD0, 0x05, 0x7C, 0xAB, 0x7D, 0xB8, 0x6B, 0xD1, 0xE7, 0xD5, 0xBF, - 0xF4, 0x13, 0x5F, 0x4E, 0x78, 0x3E, 0xEB, 0x3E, 0x19, 0xD3, 0x87, 0xFD, 0x3A, 0xC5, 0xFF, 0x00, - 0xA0, 0x8A, 0xFA, 0x0C, 0x87, 0x49, 0xD4, 0x3D, 0x2C, 0xB7, 0xE2, 0x91, 0xC5, 0x7C, 0x42, 0xB2, - 0xB8, 0x94, 0x34, 0x9B, 0x88, 0x8D, 0x14, 0xB1, 0x19, 0xEC, 0x2B, 0xE9, 0x0F, 0xF8, 0x27, 0x4A, - 0xBE, 0x85, 0xA9, 0x58, 0xEA, 0x1E, 0x1F, 0xF0, 0x9D, 0xC5, 0xE5, 0xF5, 0xCE, 0x03, 0x6A, 0xDE, - 0x47, 0x9A, 0xD1, 0x03, 0x85, 0x60, 0x65, 0xFB, 0xB0, 0xAF, 0x38, 0x3C, 0xA8, 0xE8, 0x4E, 0x7A, - 0xD7, 0x8D, 0x78, 0xD5, 0xED, 0x8E, 0x93, 0x3C, 0x6F, 0xD6, 0x68, 0xD9, 0x32, 0x3A, 0x8C, 0x8C, - 0x57, 0xB1, 0x7E, 0xC1, 0x5F, 0xB5, 0x57, 0x87, 0xBE, 0x03, 0xFC, 0x09, 0xD7, 0x74, 0x79, 0xB5, - 0x6B, 0x77, 0xD7, 0xA4, 0xD4, 0x2D, 0xED, 0xED, 0xAC, 0x4F, 0xCB, 0x3D, 0xD0, 0x6D, 0xF8, 0x68, - 0xD7, 0xF8, 0xF9, 0x01, 0x48, 0x5C, 0x90, 0x58, 0x64, 0x72, 0x33, 0x3C, 0x61, 0x8F, 0xCC, 0xA8, - 0x60, 0xFF, 0x00, 0xD8, 0x13, 0xB3, 0xD2, 0x56, 0x57, 0x69, 0x7E, 0x7F, 0x33, 0x2C, 0xF3, 0x11, - 0x8A, 0xA5, 0x47, 0xFD, 0x9F, 0xAE, 0x8E, 0xCA, 0xED, 0x1F, 0x62, 0xFC, 0x62, 0xF8, 0x5F, 0x6F, - 0xF1, 0x57, 0x53, 0xB1, 0xD5, 0x2C, 0xF4, 0x7B, 0x5D, 0x07, 0x5E, 0x48, 0xD9, 0x2F, 0x75, 0x65, - 0x9B, 0x61, 0xBB, 0x0C, 0x39, 0x0D, 0x10, 0x1B, 0x59, 0x86, 0x48, 0xDE, 0x7E, 0x62, 0x38, 0x39, - 0x00, 0x63, 0x92, 0xB1, 0xF8, 0x2B, 0xE0, 0xEF, 0x0B, 0x5E, 0xC1, 0x37, 0x8A, 0x3C, 0x62, 0xF8, - 0x52, 0xBB, 0x92, 0x26, 0x54, 0xDD, 0xEA, 0x32, 0x7D, 0x6B, 0xE7, 0xDF, 0xDA, 0x27, 0xF6, 0x9A, - 0xF8, 0x91, 0xE1, 0xFB, 0x98, 0x61, 0xD4, 0x34, 0xAD, 0x57, 0xC3, 0xB0, 0x5F, 0xC6, 0x64, 0xB7, - 0x6B, 0x88, 0x5A, 0x3F, 0x39, 0x3A, 0x65, 0x49, 0xE0, 0xE0, 0xF0, 0x71, 0xC8, 0xCF, 0x38, 0xAF, - 0x1F, 0xF0, 0xDE, 0xBB, 0xAC, 0x78, 0xDF, 0xC4, 0x09, 0x35, 0xE5, 0xC5, 0xCD, 0xC1, 0x66, 0xC1, - 0x2E, 0xF9, 0xE0, 0xE3, 0xFC, 0xFD, 0x4D, 0x7E, 0x05, 0x52, 0x52, 0x94, 0xAF, 0x2D, 0xCF, 0xCE, - 0x25, 0x26, 0xDD, 0xD9, 0xF4, 0xE7, 0xED, 0xFD, 0xF1, 0x23, 0xC3, 0x9E, 0x0A, 0xF1, 0xAF, 0x87, - 0x2D, 0xFC, 0x2E, 0xD7, 0x1F, 0xD9, 0x2F, 0xA1, 0x46, 0xEB, 0x24, 0xCE, 0xC4, 0x49, 0x2F, 0x9D, - 0x30, 0x62, 0x09, 0xFF, 0x00, 0x64, 0x27, 0x4A, 0xF9, 0x17, 0xC7, 0x3F, 0x18, 0x64, 0xD5, 0xE4, - 0x68, 0xE2, 0x66, 0x91, 0xE4, 0x3B, 0x40, 0x1D, 0x49, 0x3D, 0x80, 0xAF, 0xBB, 0xAE, 0x7F, 0x66, - 0x2D, 0x03, 0xF6, 0xBD, 0xF8, 0x15, 0xA5, 0xE8, 0xBA, 0x85, 0xC7, 0xF6, 0x7F, 0x88, 0x7C, 0x3E, - 0x5E, 0x6D, 0x3A, 0xE8, 0xCA, 0xC8, 0x8D, 0xBC, 0x00, 0xF1, 0x48, 0x46, 0x4F, 0x96, 0xFB, 0x53, - 0xE6, 0x00, 0x95, 0x2A, 0x08, 0x07, 0x95, 0x6C, 0x3F, 0x83, 0x5F, 0xB1, 0xA6, 0xB1, 0xFB, 0x3F, - 0x78, 0x9D, 0x2F, 0x34, 0xDF, 0x86, 0xF6, 0x7F, 0xDA, 0x76, 0xAE, 0x44, 0x5A, 0xAC, 0xDA, 0x95, - 0xBD, 0xD3, 0x7A, 0x79, 0x91, 0xBC, 0x92, 0x9F, 0x2F, 0x23, 0xFB, 0xA1, 0x0E, 0x0E, 0x08, 0x1C, - 0x8A, 0xFD, 0xB7, 0x85, 0x38, 0xCF, 0x2B, 0xC2, 0x65, 0x30, 0xA7, 0x5E, 0x5C, 0xB5, 0x29, 0xA6, - 0xAD, 0x6D, 0xF5, 0x76, 0x6B, 0xD7, 0xAF, 0x99, 0xF7, 0xD9, 0x3E, 0x79, 0x84, 0xA5, 0x82, 0x8C, - 0x2A, 0x3B, 0x4A, 0x3A, 0x5B, 0xBF, 0xA1, 0xEE, 0x9F, 0xF0, 0x44, 0x0F, 0xF8, 0x26, 0x05, 0xE7, - 0xC3, 0x89, 0x87, 0xC6, 0x3F, 0x89, 0x16, 0x5E, 0x4F, 0x89, 0x2E, 0xAD, 0xDA, 0x1F, 0x0F, 0xE8, - 0xB7, 0x0A, 0x3C, 0xCD, 0x26, 0x29, 0x57, 0x0F, 0x73, 0x3A, 0x91, 0x94, 0x9D, 0xD0, 0x94, 0x54, - 0xE0, 0xC6, 0x8C, 0xFB, 0x86, 0xF6, 0x02, 0x3F, 0xAE, 0xBE, 0x28, 0xF8, 0x1B, 0xC3, 0x9E, 0x0C, - 0xF1, 0x26, 0xA5, 0x7F, 0x1B, 0xDB, 0xFD, 0xB2, 0xEA, 0xDF, 0xC9, 0xE4, 0xF3, 0x08, 0x2C, 0x18, - 0x8F, 0xA9, 0x2A, 0xBF, 0x4C, 0x7B, 0x9A, 0xF1, 0xBF, 0x0C, 0xFC, 0x61, 0xF1, 0x37, 0xC3, 0x9F, - 0x84, 0x9A, 0xC4, 0xC3, 0x5A, 0xB5, 0x8F, 0x5E, 0x92, 0xC9, 0xD6, 0xDE, 0x3B, 0x76, 0xF3, 0x85, - 0xB3, 0x95, 0xEA, 0x58, 0x8C, 0x16, 0x53, 0x9E, 0x00, 0x23, 0x20, 0x1C, 0x9E, 0x95, 0xF9, 0xCB, - 0xA4, 0xFF, 0x00, 0xC1, 0x44, 0xBC, 0x63, 0x17, 0x8E, 0x2F, 0xB4, 0xBF, 0x14, 0x5F, 0x5C, 0x4D, - 0x34, 0x37, 0x0D, 0xB9, 0xE4, 0x6C, 0xB7, 0x5E, 0x49, 0x27, 0xAE, 0x6B, 0xF2, 0xAE, 0x20, 0xCE, - 0xEB, 0xE6, 0x98, 0xD9, 0xE2, 0xAB, 0x3D, 0xF4, 0x4B, 0xA2, 0x4B, 0x64, 0x97, 0xF5, 0xAE, 0xA7, - 0xC7, 0x66, 0x58, 0xE9, 0xE2, 0xEB, 0xBA, 0xB3, 0x7E, 0x9E, 0x48, 0xFB, 0xA7, 0xC6, 0x3E, 0x11, - 0xB7, 0xBD, 0xBE, 0x9A, 0x44, 0x5D, 0xDE, 0x63, 0x13, 0xC7, 0xA5, 0x79, 0x77, 0xC5, 0x1F, 0xD9, - 0xDF, 0x47, 0xF8, 0x93, 0x16, 0x2E, 0x9A, 0x4B, 0x5B, 0xB8, 0x93, 0x6C, 0x57, 0x91, 0x7D, 0xE8, - 0xC7, 0x27, 0xE6, 0x07, 0x86, 0x5C, 0x9C, 0xE0, 0xE3, 0xA9, 0xC1, 0x19, 0xAE, 0x1D, 0x3F, 0x6D, - 0xF5, 0xD1, 0xB4, 0x4B, 0x79, 0xE2, 0xB3, 0x6D, 0x56, 0x69, 0x8F, 0xFA, 0xA1, 0x37, 0x94, 0x11, - 0x71, 0xF7, 0x8B, 0x6D, 0x6E, 0x73, 0x8E, 0x31, 0xF8, 0xF1, 0x5C, 0xA7, 0xC6, 0x1F, 0xF8, 0x28, - 0x0D, 0x8E, 0xA7, 0xF0, 0xE7, 0x54, 0xD3, 0x8D, 0x9A, 0xE8, 0x77, 0xD7, 0xF1, 0x79, 0x1F, 0x68, - 0x6B, 0xD5, 0x93, 0xCA, 0x43, 0xC3, 0xE0, 0x05, 0x07, 0x71, 0x5D, 0xC0, 0x7A, 0x67, 0x3D, 0xAB, - 0xD1, 0xC8, 0xB2, 0x3C, 0xE1, 0xCE, 0x9E, 0x3B, 0x09, 0x17, 0x18, 0xDF, 0x49, 0x5D, 0x24, 0x95, - 0xEC, 0xDB, 0x4D, 0xDD, 0xAE, 0xFA, 0x6A, 0x75, 0xE5, 0xB9, 0x7E, 0x3B, 0x9A, 0x38, 0x8A, 0x0B, - 0x95, 0x5F, 0xE2, 0xD3, 0xE7, 0xF2, 0x3C, 0x27, 0xC4, 0x8D, 0x15, 0x86, 0xAD, 0x77, 0x6F, 0x6F, - 0x74, 0x97, 0x90, 0xC3, 0x33, 0xC7, 0x1D, 0xC2, 0x02, 0x16, 0x75, 0x04, 0x80, 0xE0, 0x1E, 0x40, - 0x23, 0x9F, 0xC6, 0xBE, 0x47, 0xFF, 0x00, 0x82, 0xB1, 0xCA, 0x64, 0xFD, 0x9D, 0xF4, 0x7F, 0xFB, - 0x18, 0xA0, 0xFF, 0x00, 0xD2, 0x6B, 0x9A, 0xF4, 0xDF, 0x8A, 0x1F, 0xB6, 0xD7, 0x80, 0x7E, 0x1A, - 0x09, 0x16, 0x6D, 0x4A, 0x19, 0xAE, 0x17, 0xA2, 0x07, 0xF9, 0x8F, 0xFC, 0x04, 0x65, 0x8F, 0xE5, - 0x5F, 0x17, 0xFE, 0xDA, 0xDF, 0xB6, 0xC9, 0xFD, 0xA4, 0x34, 0xBB, 0x2D, 0x03, 0x4F, 0xB5, 0x58, - 0x34, 0x6B, 0x1B, 0xBF, 0xB6, 0xB4, 0xAC, 0x85, 0x5E, 0x69, 0x55, 0x5D, 0x17, 0x68, 0xEA, 0x14, - 0x2B, 0xB7, 0x51, 0x92, 0x48, 0xE0, 0x63, 0x9F, 0xD1, 0xB8, 0xB3, 0x3B, 0xC0, 0xC7, 0x2F, 0xAB, - 0x86, 0xF6, 0xAA, 0x53, 0x92, 0xB2, 0x4B, 0x5D, 0x7C, 0xEC, 0x7D, 0x56, 0x75, 0x98, 0x61, 0xD6, - 0x16, 0x74, 0xB9, 0xD3, 0x93, 0x56, 0xB2, 0x3E, 0x77, 0xA2, 0x8A, 0x2B, 0xF0, 0xD3, 0xF3, 0xB0, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x03, 0xE8, 0x8F, 0xF8, 0x27, - 0x6A, 0x79, 0xBF, 0x12, 0x6E, 0xA3, 0xFF, 0x00, 0x9E, 0x97, 0x16, 0x4B, 0xF9, 0xCA, 0x45, 0x7D, - 0xD5, 0xE2, 0xDF, 0x07, 0x30, 0xB7, 0x6F, 0x96, 0xBF, 0x3D, 0x7F, 0x61, 0x7F, 0x12, 0x7F, 0xC2, - 0x3D, 0xF1, 0x8F, 0x71, 0x3F, 0x2A, 0xC0, 0xB3, 0x81, 0x9F, 0xBC, 0xD1, 0xCD, 0x1B, 0x01, 0xF9, - 0x6E, 0xAF, 0xD4, 0x5F, 0x88, 0xFA, 0x8D, 0x95, 0x9D, 0xBC, 0x8A, 0xBB, 0x49, 0xC6, 0x2B, 0xF7, - 0x0F, 0x0F, 0xA9, 0xD3, 0xAB, 0x94, 0xCD, 0x4B, 0xEC, 0xBF, 0xCE, 0xE7, 0xE8, 0x9C, 0x31, 0x18, - 0xD4, 0xC1, 0x49, 0x3E, 0x8F, 0xF3, 0x3E, 0x64, 0xF1, 0x76, 0x87, 0xFD, 0x9F, 0x78, 0x64, 0xC7, - 0xDD, 0x3F, 0xFD, 0x6A, 0xF6, 0xEF, 0x09, 0x5D, 0x63, 0xC3, 0x9A, 0x7F, 0xFD, 0x7B, 0x47, 0xFF, - 0x00, 0xA0, 0x8A, 0xF2, 0x1F, 0x89, 0x5A, 0xBC, 0x77, 0x4F, 0x22, 0xAE, 0x39, 0x23, 0xF9, 0xD7, - 0xA7, 0x78, 0x5E, 0x7D, 0x9E, 0x1E, 0xB0, 0xFF, 0x00, 0xAF, 0x78, 0xFF, 0x00, 0xF4, 0x11, 0x5E, - 0xD6, 0x5D, 0x18, 0xC6, 0xBC, 0xD4, 0x7C, 0x8F, 0x43, 0x0A, 0x92, 0xAB, 0x24, 0x89, 0x7C, 0x65, - 0x6B, 0x25, 0xF5, 0xBF, 0xCB, 0xDC, 0x60, 0xD6, 0x57, 0xC1, 0x2F, 0x14, 0xE9, 0x3F, 0x07, 0x7E, - 0x2B, 0xDB, 0xDF, 0x6A, 0x36, 0xEB, 0x36, 0xA0, 0x10, 0xBD, 0x99, 0x95, 0x3E, 0x55, 0x39, 0xC3, - 0x32, 0x13, 0xC1, 0x61, 0x9C, 0x71, 0xCA, 0x86, 0xCF, 0x70, 0x6B, 0xA4, 0x49, 0x96, 0x41, 0x86, - 0xE6, 0xAB, 0xEB, 0x1E, 0x17, 0xB3, 0xF1, 0x04, 0x30, 0x0B, 0x8B, 0x35, 0xB8, 0x16, 0xF2, 0x89, - 0x90, 0x1E, 0x0A, 0xB0, 0xF4, 0x3D, 0xB2, 0x32, 0x3F, 0x1A, 0xF4, 0x71, 0xAB, 0x11, 0x2C, 0x3C, - 0xA3, 0x85, 0x69, 0x4E, 0xDA, 0x5D, 0x5D, 0x1D, 0x18, 0x88, 0xD4, 0x74, 0xDF, 0xB1, 0xB7, 0x35, - 0xB4, 0xB9, 0xF5, 0x07, 0xC6, 0x1F, 0xDA, 0x43, 0x50, 0xF8, 0xBF, 0xF0, 0x52, 0xCF, 0xC1, 0xB0, - 0xD8, 0xDB, 0xCD, 0x0D, 0xFC, 0x91, 0xDE, 0x4D, 0x73, 0x3C, 0x2B, 0x23, 0xC0, 0x17, 0x95, 0x11, - 0x12, 0x32, 0x8C, 0x7B, 0xB2, 0xE0, 0x94, 0x25, 0x73, 0xB5, 0x88, 0x3C, 0xF7, 0xC2, 0xEF, 0x85, - 0xC9, 0x63, 0x2C, 0x47, 0xCB, 0xF9, 0xFF, 0x00, 0x8B, 0x8E, 0x95, 0x15, 0xBF, 0xC5, 0x6F, 0x07, - 0xD9, 0x68, 0xFA, 0x5D, 0xC2, 0x4B, 0x71, 0xF6, 0xEB, 0xED, 0xB1, 0xCF, 0x66, 0x60, 0xDB, 0x25, - 0xAC, 0x99, 0xDA, 0xC5, 0x9B, 0x01, 0x0A, 0x74, 0x60, 0x54, 0xF2, 0x18, 0x70, 0x18, 0x32, 0xAF, - 0xD0, 0x3F, 0x0D, 0x35, 0x7F, 0x87, 0xBE, 0x32, 0xF0, 0xBC, 0xD6, 0xFA, 0x3D, 0x9D, 0xEB, 0xEB, - 0xD6, 0x36, 0xFE, 0x73, 0xDC, 0x25, 0xE1, 0x21, 0xC6, 0xF5, 0x19, 0x78, 0xDB, 0x28, 0x14, 0x33, - 0x81, 0xF2, 0x05, 0x6E, 0x46, 0x49, 0xC5, 0x7F, 0x3B, 0x66, 0x19, 0x6E, 0x33, 0x0B, 0x2F, 0xF6, - 0xA8, 0x38, 0xDD, 0xBD, 0xD6, 0xFE, 0x9E, 0x5E, 0x7B, 0x76, 0x3F, 0x2D, 0xC5, 0x61, 0x6B, 0xD1, - 0x97, 0xEF, 0xA2, 0xD7, 0xA9, 0x6F, 0xC0, 0xF7, 0x72, 0x78, 0x4E, 0xCD, 0x3C, 0xA6, 0x65, 0x38, - 0x18, 0x20, 0xF4, 0xAA, 0x7F, 0x15, 0xBE, 0x3B, 0x6A, 0x9A, 0x7E, 0x98, 0xD1, 0xC7, 0x75, 0x22, - 0xAE, 0x30, 0x49, 0x23, 0xFC, 0x2A, 0x0F, 0x17, 0xEB, 0xF6, 0x5E, 0x0E, 0xD0, 0xAE, 0x75, 0x0D, - 0x4E, 0xE1, 0x2C, 0x6C, 0xED, 0x57, 0x74, 0x92, 0xCB, 0xF2, 0xAA, 0xF6, 0x1C, 0xF7, 0x24, 0x90, - 0x00, 0xE4, 0x92, 0x40, 0x19, 0x24, 0x57, 0x9A, 0xFC, 0x53, 0xB7, 0x9E, 0xEF, 0x44, 0xB1, 0xD4, - 0xC1, 0x76, 0xB1, 0xD5, 0x03, 0x35, 0xBB, 0xA4, 0x82, 0x4F, 0x30, 0x29, 0x19, 0xC8, 0x53, 0x91, - 0xD4, 0x75, 0xC5, 0x79, 0xA7, 0x29, 0xE8, 0x5F, 0x07, 0x7E, 0x27, 0x5D, 0x6B, 0x91, 0x18, 0xEE, - 0xAE, 0x1E, 0x4F, 0x34, 0x15, 0xF9, 0x8F, 0x50, 0x7F, 0xCF, 0xD2, 0xBE, 0x6C, 0xFD, 0xB4, 0xFE, - 0x09, 0x35, 0x97, 0x8A, 0x24, 0xD6, 0xED, 0x23, 0xF2, 0x55, 0xDB, 0x2E, 0x14, 0x72, 0x5B, 0xD7, - 0xFC, 0xF7, 0xAE, 0x17, 0xF6, 0xFF, 0x00, 0xFD, 0xA1, 0x75, 0xAF, 0xD9, 0x8B, 0xF6, 0x2D, 0xD6, - 0xB5, 0x4F, 0x0D, 0xDF, 0x5F, 0x68, 0x9A, 0xFE, 0xB5, 0x79, 0x6F, 0xA4, 0x69, 0xFA, 0x84, 0x18, - 0x59, 0xAD, 0x9E, 0x46, 0x32, 0x48, 0x54, 0x9C, 0xED, 0x63, 0x0C, 0x53, 0x28, 0x61, 0x82, 0xA5, - 0x81, 0x52, 0x08, 0x06, 0xBE, 0x2B, 0xFD, 0x95, 0x3F, 0xE0, 0xB0, 0x5E, 0x34, 0xF8, 0x63, 0x0F, - 0xF6, 0x0F, 0xC4, 0xC9, 0xB5, 0x4F, 0x89, 0x1E, 0x13, 0x78, 0xCA, 0x47, 0x2D, 0xE5, 0xC9, 0x9B, - 0x57, 0xD3, 0x88, 0x1F, 0x2F, 0x97, 0x3C, 0x87, 0x32, 0x46, 0x38, 0x1E, 0x5C, 0x84, 0xE0, 0x00, - 0x15, 0x90, 0x02, 0x0B, 0x5A, 0x82, 0x3D, 0x63, 0xF6, 0xC3, 0xFD, 0xA0, 0x3C, 0x5D, 0xF0, 0x2F, - 0xE0, 0xD6, 0x9B, 0xA9, 0x68, 0xB2, 0x43, 0x1A, 0x4F, 0x7C, 0x2C, 0x65, 0x49, 0x8B, 0x65, 0x77, - 0x46, 0xEE, 0xAD, 0xC1, 0x04, 0xFD, 0xC6, 0x07, 0x9E, 0xEB, 0x5F, 0x0B, 0xF8, 0xE3, 0xF6, 0x8B, - 0xF1, 0xA7, 0xC4, 0x39, 0x9D, 0xB5, 0x2D, 0x7A, 0xF8, 0xA3, 0x31, 0x6F, 0x2E, 0x17, 0xF2, 0x54, - 0x67, 0xB7, 0xCB, 0x82, 0x47, 0xFB, 0xC4, 0xD5, 0xAF, 0xDA, 0x3B, 0xF6, 0x8D, 0xD7, 0x3F, 0x69, - 0x4F, 0x1F, 0x4D, 0xAC, 0x6A, 0xDB, 0x6D, 0x6C, 0xE3, 0x66, 0x5B, 0x0D, 0x3A, 0x27, 0x26, 0x0B, - 0x08, 0xC9, 0xFB, 0xA3, 0xFB, 0xCC, 0x70, 0x37, 0x39, 0x19, 0x62, 0x07, 0x40, 0x15, 0x47, 0x9F, - 0xD7, 0x6C, 0xB3, 0x2C, 0x54, 0xA9, 0x46, 0x83, 0xA8, 0xF9, 0x23, 0xA2, 0x57, 0xD0, 0xE8, 0x96, - 0x2E, 0xB3, 0x82, 0xA6, 0xE4, 0xEC, 0xBA, 0x5C, 0x56, 0x62, 0xEC, 0x59, 0x89, 0x66, 0x63, 0x92, - 0x4F, 0x7A, 0x4A, 0x28, 0xAE, 0x13, 0x9C, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, - 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, - 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, - 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0xE9, 0x7E, 0x10, 0xF8, 0xBD, 0x7C, 0x0D, 0xF1, 0x1B, - 0x4B, 0xD4, 0xA4, 0x6D, 0xB6, 0xF1, 0xCB, 0xE5, 0xCE, 0x79, 0xC0, 0x8D, 0xC1, 0x56, 0x3C, 0x7A, - 0x03, 0x9F, 0xC2, 0xBF, 0x41, 0x6E, 0xFE, 0x30, 0x0B, 0xBF, 0x0C, 0x5A, 0x8B, 0x89, 0x98, 0xCD, - 0x1C, 0x42, 0x37, 0x2C, 0xD9, 0x2C, 0x40, 0xC6, 0x7F, 0x1E, 0xBF, 0x8D, 0x7E, 0x6A, 0xD7, 0xAF, - 0x7C, 0x2F, 0xFD, 0xA4, 0xE1, 0xD1, 0xFC, 0x39, 0x0E, 0x8F, 0xE2, 0x2B, 0x5B, 0x8B, 0xAB, 0x7B, - 0x55, 0x11, 0xC1, 0x71, 0x07, 0xFA, 0xC5, 0x41, 0xC0, 0x56, 0x19, 0x1D, 0x07, 0x19, 0x07, 0x38, - 0x18, 0xC1, 0xEB, 0x5F, 0x5D, 0xC2, 0xF9, 0xF2, 0xC1, 0x39, 0xD1, 0xA8, 0xED, 0x19, 0x7E, 0x67, - 0xB9, 0x93, 0x66, 0x4B, 0x0F, 0xCD, 0x4E, 0x6E, 0xC9, 0x9F, 0x48, 0x49, 0xE2, 0x43, 0xE2, 0x8D, - 0x48, 0xC5, 0x6E, 0xA6, 0x46, 0x2C, 0x14, 0x01, 0xC9, 0x66, 0x27, 0x00, 0x0F, 0xAD, 0x7D, 0x1B, - 0x63, 0xA4, 0x43, 0xA0, 0xE9, 0x56, 0xF1, 0x5D, 0x5C, 0xC6, 0xAB, 0x6F, 0x12, 0xA1, 0x62, 0x70, - 0x0E, 0x00, 0x1F, 0xD2, 0xBE, 0x02, 0x6F, 0xDB, 0x26, 0xD3, 0xC1, 0xD3, 0x2B, 0x78, 0x6B, 0x46, - 0x66, 0x9A, 0x03, 0x98, 0x66, 0xBC, 0x60, 0xAA, 0x8D, 0xFD, 0xED, 0xAA, 0x49, 0x63, 0xE8, 0x4B, - 0x03, 0xD0, 0xF5, 0xAE, 0x13, 0xC7, 0x9F, 0xB5, 0x47, 0x8E, 0xBE, 0x22, 0x49, 0x27, 0xDB, 0x75, - 0xEB, 0xA8, 0x21, 0x76, 0xDC, 0x22, 0xB4, 0x3E, 0x4A, 0xAF, 0xB0, 0x61, 0xF3, 0x63, 0xD8, 0xB1, - 0xAF, 0xAA, 0xA3, 0xC6, 0x78, 0x1C, 0x1F, 0x3C, 0xA2, 0x9D, 0x49, 0x3E, 0xDA, 0x25, 0xEA, 0xDF, - 0xE9, 0x73, 0xDA, 0x86, 0x7F, 0x86, 0xA1, 0xCC, 0xD5, 0xE4, 0xDF, 0xDC, 0x7E, 0x86, 0xF8, 0xEB, - 0xF6, 0x90, 0xF0, 0x4F, 0xC2, 0xF8, 0x59, 0xB5, 0x0D, 0x5E, 0xD1, 0x24, 0x51, 0x90, 0x86, 0x41, - 0xB9, 0xBE, 0x83, 0xA9, 0xFC, 0x01, 0xAF, 0x0E, 0xF8, 0x8B, 0xFF, 0x00, 0x05, 0x4E, 0xD3, 0x6C, - 0x16, 0x48, 0x7C, 0x39, 0xA5, 0xDC, 0x5E, 0xBA, 0x9C, 0x2C, 0x8E, 0x3C, 0x98, 0xD8, 0x7A, 0xE4, - 0xFC, 0xDF, 0xF8, 0xED, 0x7C, 0x45, 0x2C, 0xCD, 0x3C, 0xAD, 0x23, 0xB3, 0x3B, 0xB9, 0x2C, 0xCC, - 0xC7, 0x25, 0x8F, 0xA9, 0x34, 0xDA, 0xF0, 0xF1, 0xFE, 0x20, 0xE6, 0x35, 0xBD, 0xDA, 0x09, 0x53, - 0x5E, 0x5A, 0xBF, 0xBD, 0xFF, 0x00, 0x91, 0xE6, 0xE2, 0x78, 0x9F, 0x15, 0x53, 0x4A, 0x76, 0x8A, - 0xFB, 0xD9, 0xEC, 0x9E, 0x3F, 0xFD, 0xBA, 0x7C, 0x7F, 0xE3, 0x8B, 0xA2, 0xD1, 0x6A, 0x2B, 0xA4, - 0xC6, 0x1F, 0x72, 0x0B, 0x65, 0xF9, 0xC0, 0xEC, 0x0B, 0x36, 0x7A, 0x7A, 0x80, 0x2B, 0xEA, 0x1F, - 0xF8, 0x26, 0xF7, 0xFC, 0x16, 0x3B, 0x43, 0xFD, 0x9C, 0xE5, 0xD7, 0x21, 0xF8, 0xA1, 0xA1, 0xEB, - 0xDA, 0xCD, 0xC6, 0xA8, 0x90, 0x45, 0x6D, 0xAC, 0x69, 0x1E, 0x54, 0x92, 0xDB, 0xA2, 0x6F, 0x2E, - 0x8F, 0x03, 0xB2, 0x29, 0x12, 0x31, 0x8D, 0x8B, 0x2B, 0x70, 0x62, 0x50, 0x13, 0x93, 0x5F, 0x9F, - 0x54, 0x57, 0xC7, 0xE2, 0xB3, 0x0C, 0x4E, 0x26, 0x5C, 0xD8, 0x89, 0xB9, 0x7A, 0xBB, 0x9E, 0x15, - 0x6C, 0x55, 0x5A, 0xAE, 0xF5, 0x64, 0xDF, 0xA9, 0xFA, 0x01, 0xFF, 0x00, 0x05, 0x72, 0xFF, 0x00, - 0x82, 0xB1, 0xE9, 0xFF, 0x00, 0xB5, 0x2D, 0xA7, 0x87, 0xFC, 0x23, 0xF0, 0xC2, 0xFB, 0x52, 0x87, - 0xC2, 0x76, 0x66, 0x2D, 0x57, 0x54, 0xBC, 0x96, 0x16, 0xB6, 0x96, 0xFA, 0xF0, 0x10, 0xD1, 0x41, - 0xB1, 0x86, 0x42, 0x40, 0x46, 0xE2, 0x72, 0x55, 0xE4, 0x20, 0x81, 0x88, 0xD5, 0x9B, 0x7B, 0xF6, - 0x5B, 0xFF, 0x00, 0x82, 0xD8, 0xE8, 0xDE, 0x1C, 0xF8, 0x2E, 0xDA, 0x07, 0xC4, 0xBD, 0x27, 0x58, - 0xB9, 0xD4, 0xF4, 0xA7, 0x57, 0xB2, 0xB8, 0xD2, 0x60, 0x8E, 0x64, 0xBD, 0x1C, 0x82, 0x19, 0x5E, - 0x44, 0xF2, 0xDB, 0x1B, 0x49, 0xE4, 0xA9, 0xC1, 0x23, 0x6F, 0xDD, 0xAF, 0xCE, 0x1A, 0x2B, 0x95, - 0x49, 0xA5, 0x63, 0x1E, 0x6E, 0x87, 0xD4, 0x5F, 0xB7, 0x9F, 0xFC, 0x14, 0xB6, 0xFB, 0xF6, 0xCE, - 0xF0, 0x7E, 0x93, 0xE1, 0x9B, 0x5F, 0x0C, 0xC5, 0xE1, 0xAF, 0x0F, 0xE9, 0x77, 0xE7, 0x51, 0x65, - 0x7B, 0xCF, 0xB5, 0x5C, 0x5E, 0x4E, 0x11, 0xE3, 0x8D, 0x89, 0x08, 0x81, 0x15, 0x52, 0x47, 0xF9, - 0x40, 0x6E, 0x5B, 0xEF, 0x70, 0x2B, 0xE5, 0xDA, 0x28, 0xA9, 0x10, 0x51, 0x45, 0x14, 0x00, 0x51, - 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, - 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, - 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, - 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, - 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, - 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, - 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, - 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, - 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, - 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, - 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, - 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, - 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, - 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, - 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, - 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, - 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x01, 0xFF, 0xD9, -} ; diff --git a/lib/M5ez/examples/M5ez-demo/M5ez-demo.ino b/lib/M5ez/examples/M5ez-demo/M5ez-demo.ino deleted file mode 100644 index 0c276fc..0000000 --- a/lib/M5ez/examples/M5ez-demo/M5ez-demo.ino +++ /dev/null @@ -1,180 +0,0 @@ -#include - -#include - -#include "images.h" - -#define MAIN_DECLARED - -void setup() { - #include - #include - ezt::setDebug(INFO); - ez.begin(); -} - -void loop() { - ezMenu mainmenu("Welcome to M5ez"); - mainmenu.txtSmall(); - mainmenu.addItem("Flexible text menus", mainmenu_menus); - mainmenu.addItem("Image menus", mainmenu_image); - mainmenu.addItem("Neat messages", mainmenu_msgs); - mainmenu.addItem("Multi-function buttons", mainmenu_buttons); - mainmenu.addItem("3-button text entry", mainmenu_entry); - mainmenu.addItem("Built-in wifi & other settings", ez.settings.menu); - mainmenu.addItem("Updates via https", mainmenu_ota); - mainmenu.upOnFirst("last|up"); - mainmenu.downOnLast("first|down"); - mainmenu.run(); -} - -void mainmenu_menus() { - ezMenu submenu("This is a sub menu"); - submenu.txtSmall(); - submenu.buttons("up#Back#select##down#"); - submenu.addItem("You can make small menus"); - submenu.addItem("Or big ones"); - submenu.addItem("(Like the Main menu)"); - submenu.addItem("In this menu most options"); - submenu.addItem("Do absolutely nothing"); - submenu.addItem("They are only here"); - submenu.addItem("To demonstrate that menus"); - submenu.addItem("Can run off the screen"); - submenu.addItem("And will just scroll"); - submenu.addItem("And scroll"); - submenu.addItem("And Scroll"); - submenu.addItem("And Scroll even more"); - submenu.addItem("more | Learn more about menus", submenu_more); - submenu.addItem("Exit | Go back to main menu"); - submenu.run(); -} - -void submenu_more() { - ez.header.show("A simple menu in code..."); - ez.canvas.lmargin(10); - ez.canvas.println(""); - ez.canvas.println("ezMenu menu(\"Main menu\");"); - ez.canvas.println("menu.addItem(\"Option 1\");"); - ez.canvas.println("menu.addItem(\"Option 2\");"); - ez.canvas.println("menu.addItem(\"Option 3\");"); - ez.canvas.println("while ( menu.run() ) {"); - ez.canvas.println(" if (menu.pick == 1) {"); - ez.canvas.println(" ez.msgBox (\"One!\");"); - ez.canvas.println(" }"); - ez.canvas.println("}"); - ez.buttons.wait("OK"); - - ezMenu fontmenu("Menus can change looks"); - fontmenu.txtFont(&Satisfy_24); - fontmenu.addItem("Menus can use"); - fontmenu.addItem("Various Fonts"); - fontmenu.runOnce(); - - ezMenu delmenu("Menus are dynamic"); - delmenu.txtSmall(); - delmenu.addItem("You can delete items"); - delmenu.addItem("While the menu runs"); - delmenu.addItem("Delete me!"); - delmenu.addItem("Delete me!"); - delmenu.addItem("Delete me!"); - delmenu.addItem("Exit | Go back" ); - while (delmenu.runOnce()) { - if (delmenu.pickName() == "Delete me!") { - delmenu.deleteItem(delmenu.pick()); - } - } -} - -void mainmenu_image() { - ezMenu images; - images.imgBackground(TFT_BLACK); - images.imgFromTop(40); - images.imgCaptionColor(TFT_WHITE); - images.addItem(sysinfo_jpg, "System Information", sysInfo); - images.addItem(wifi_jpg, "WiFi Settings", ez.wifi.menu); - images.addItem(about_jpg, "About M5ez", aboutM5ez); - images.addItem(sleep_jpg, "Power Off", powerOff); - images.addItem(return_jpg, "Back"); - images.run(); -} - -void mainmenu_msgs() { - String cr = (String)char(13); - ez.msgBox("You can show messages", "ez.msgBox shows text"); - ez.msgBox("Looking the way you want", "In any font !", "OK", true, &FreeSerifBold24pt7b, TFT_RED); - ez.msgBox("More ez.msgBox", "Even multi-line messages where everything lines up and is kept in the middle of the screen"); - ez.msgBox("Questions, questions...", "But can it also show any buttons you want?", "No # # Yes"); - ez.textBox("And there's ez.textBox", "To present or compose longer word-wrapped texts, you can use the ez.textBox function." + cr + cr + "M5ez (pronounced \"M5 easy\") is a complete interface builder library for the M5Stack ESP32 system. It allows even novice programmers to create good looking interfaces. It comes with menus as text or as images, message boxes, very flexible button setup (including different length presses and multi-button functions), 3-button text input (you have to see it to believe it) and built-in Wifi support. Now you can concentrate on what your program does, and let M5ez worry about everything else.", true); -} - -void mainmenu_buttons() { - ez.header.show("Simple buttons..."); - ez.canvas.font(&FreeSans12pt7b); - ez.canvas.lmargin(20); - ez.canvas.println(""); - ez.canvas.println("You can have three buttons"); - ez.canvas.println("with defined funtions."); - ez.buttons.show("One # Two # Done"); - printButton(); - ez.canvas.clear(); - ez.header.show("More functions..."); - ez.canvas.println(""); - ez.canvas.println("But why stop there?"); - ez.canvas.println("If you press a little longer"); - ez.canvas.println("You access the functions"); - ez.canvas.println("printed in cyan."); - ez.buttons.show("One # Two # Three # Four # Done #"); - printButton(); - ez.canvas.clear(); - ez.header.show("Two keys ..."); - ez.canvas.y(ez.canvas.top() + 10); - ez.canvas.println("It gets even better..."); - ez.canvas.println("The purple bar shows the"); - ez.canvas.println("functions for key combis."); - ez.canvas.println("See if you can work it out..."); - ez.buttons.show("One # Two # Three # Four # Five # Six # Seven # Eight # Done"); - printButton(); -} - -void printButton(){ - while (true) { - String btnpressed = ez.buttons.poll(); - if (btnpressed == "Done") break; - if (btnpressed != "") { - m5.lcd.fillRect (0, ez.canvas.bottom() - 45, TFT_W, 40, ez.theme->background); - ez.canvas.pos(20, ez.canvas.bottom() - 45); - ez.canvas.color(TFT_RED); - ez.canvas.font(&FreeSansBold18pt7b); - ez.canvas.print(btnpressed); - ez.canvas.font(&FreeSans12pt7b); - ez.canvas.color(TFT_BLACK); - } - } -} - -void mainmenu_entry() { - if (ez.msgBox("We're gonna enter text ... !", "Have you learned to use the buttons? Go there first if you haven't been there. Or hit 'Go' to see if you can enter your name.", "Back # # Go") == "Go") { - String your_name = ez.textInput(); - ez.msgBox("Pfew...", "Hi " + your_name + "! | | Now that was a pain! But it is good enough for entering, say, a WPA key, or don't you think?"); - ez.msgBox("Don't worry", "(You do get better with practice...)"); - } -} - -void mainmenu_ota() { - if (ez.msgBox("Get OTA_https demo", "This will replace the demo with a program that can then load the demo program again.", "Cancel#OK#") == "OK") { - ezProgressBar progress_bar("OTA update in progress", "Downloading ...", "Abort"); - #include "raw_githubusercontent_com.h" // the root certificate is now in const char * root_cert - if (ez.wifi.update("https://raw.githubusercontent.com/ropg/M5ez/master/compiled_binaries/OTA_https.bin", root_cert, &progress_bar)) { - ez.msgBox("Over The Air updater", "OTA download successful. Reboot to new firmware", "Reboot"); - ESP.restart(); - } else { - ez.msgBox("OTA error", ez.wifi.updateError(), "OK"); - } - } -} - -void powerOff() { m5.powerOFF(); } - -void aboutM5ez() { - ez.msgBox("About M5ez", "M5ez was written by | Rop Gonggrijp | | https://github.com/ropg/M5ez"); -} diff --git a/lib/M5ez/examples/M5ez-demo/images.h b/lib/M5ez/examples/M5ez-demo/images.h deleted file mode 100644 index 9a812dd..0000000 --- a/lib/M5ez/examples/M5ez-demo/images.h +++ /dev/null @@ -1,2613 +0,0 @@ -#pragma once - -const char wifi_jpg[] PROGMEM = -{ - 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x00, 0x60, - 0x00, 0x60, 0x00, 0x00, 0xFF, 0xE1, 0x00, 0x22, 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x4D, 0x4D, - 0x00, 0x2A, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x02, 0x01, 0x01, - 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x05, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x06, 0x04, 0x04, 0x03, 0x05, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x08, - 0x09, 0x0B, 0x09, 0x08, 0x08, 0x0A, 0x08, 0x07, 0x07, 0x0A, 0x0D, 0x0A, 0x0A, 0x0B, 0x0C, 0x0C, - 0x0C, 0x0C, 0x07, 0x09, 0x0E, 0x0F, 0x0D, 0x0C, 0x0E, 0x0B, 0x0C, 0x0C, 0x0C, 0xFF, 0xDB, 0x00, - 0x43, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x06, 0x03, 0x03, 0x06, 0x0C, 0x08, 0x07, 0x08, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0xB5, 0x01, 0x40, 0x03, 0x01, 0x22, 0x00, 0x02, - 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, - 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, - 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, - 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, - 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, - 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, - 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, - 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, - 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, - 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, - 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, - 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xC4, 0x00, - 0x1F, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, - 0x00, 0xB5, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, - 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, - 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, - 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, - 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, - 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, - 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, - 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, - 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, - 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, - 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, - 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, - 0x00, 0x3F, 0x00, 0xFE, 0x7F, 0xE8, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0x55, 0x2C, - 0x70, 0x28, 0x03, 0x26, 0xBD, 0x23, 0xE0, 0x47, 0xC1, 0x0D, 0x43, 0xE2, 0x8F, 0x88, 0xED, 0xAD, - 0x6D, 0xED, 0xE4, 0x99, 0xA6, 0x90, 0x22, 0xAA, 0xAE, 0x4B, 0x13, 0xDA, 0xBA, 0xB0, 0x78, 0x4A, - 0xB8, 0x9A, 0xAA, 0x8D, 0x15, 0x79, 0x3D, 0x8E, 0x7C, 0x56, 0x26, 0x9E, 0x1E, 0x9B, 0xAB, 0x55, - 0xD9, 0x2D, 0xD9, 0xC1, 0x26, 0x89, 0x73, 0x2A, 0x6E, 0x58, 0x9D, 0x86, 0x33, 0xC0, 0x27, 0x8A, - 0x8A, 0xEB, 0x4F, 0xB8, 0xB2, 0x58, 0xDA, 0x68, 0x66, 0x89, 0x66, 0x1B, 0x90, 0xBA, 0x15, 0x0E, - 0x3D, 0x47, 0xA8, 0xAF, 0xDB, 0x6F, 0xD9, 0x5B, 0xFE, 0x09, 0x35, 0xE0, 0xFF, 0x00, 0x82, 0xFF, - 0x00, 0x00, 0x1B, 0xE2, 0x4F, 0xC4, 0xC8, 0x16, 0x4D, 0x3E, 0xE0, 0x1B, 0x7D, 0x2E, 0xC7, 0x03, - 0xCE, 0xD7, 0xEE, 0x07, 0x54, 0x8C, 0x1F, 0xF9, 0x62, 0x87, 0x86, 0x7E, 0x84, 0xE7, 0xDB, 0x3F, - 0x02, 0xFF, 0x00, 0xC1, 0x4C, 0xBE, 0x09, 0x5F, 0x5B, 0x78, 0xA9, 0x3C, 0x58, 0xDF, 0x62, 0xB3, - 0xD3, 0x6E, 0x8F, 0xD9, 0xAD, 0x74, 0xE8, 0x02, 0xC6, 0xB6, 0x08, 0xA3, 0x21, 0x23, 0x5F, 0xE2, - 0x5C, 0x75, 0x6E, 0xB9, 0xFA, 0x8A, 0xFA, 0x8C, 0xCB, 0x82, 0xB1, 0xD8, 0x4A, 0x33, 0xAE, 0x97, - 0x32, 0xA7, 0xA4, 0xDA, 0xD9, 0x3E, 0xB1, 0x4F, 0xAB, 0x5F, 0x6A, 0xDA, 0x27, 0xA6, 0xF7, 0xB7, - 0xCF, 0x65, 0xFC, 0x59, 0x82, 0xC5, 0x55, 0x8D, 0x24, 0xEC, 0xE7, 0xAC, 0x57, 0x56, 0xBA, 0x3B, - 0x74, 0x4F, 0xEC, 0xDF, 0x75, 0xAE, 0xD6, 0x3E, 0x3D, 0xA2, 0x95, 0x97, 0x6B, 0x63, 0xD2, 0x92, - 0xBE, 0x34, 0xFA, 0x90, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x01, - 0xF0, 0x5B, 0xC9, 0x72, 0xFB, 0x63, 0x46, 0x91, 0xB1, 0x9C, 0x28, 0xC9, 0xAB, 0x70, 0xF8, 0x7E, - 0xEA, 0x68, 0x3C, 0xC1, 0x0C, 0x9B, 0x48, 0xC8, 0x3B, 0x78, 0x35, 0xEC, 0xFF, 0x00, 0xB1, 0x0F, - 0xC0, 0x5D, 0x47, 0xE2, 0x7F, 0xC4, 0x68, 0x75, 0x0B, 0x59, 0xA1, 0x8E, 0x3D, 0x1D, 0xD2, 0x69, - 0x15, 0xC0, 0x63, 0x28, 0xCF, 0xDD, 0xDB, 0xDD, 0x4F, 0x42, 0x7B, 0x57, 0xEB, 0x5F, 0x89, 0x7F, - 0xE0, 0x97, 0x7E, 0x04, 0xFD, 0xA8, 0x7F, 0x65, 0xFB, 0xAF, 0x14, 0x78, 0x17, 0x4F, 0x87, 0x4F, - 0xF1, 0x07, 0x86, 0xA0, 0xF3, 0x3C, 0x41, 0xA1, 0xC6, 0xA0, 0xCD, 0x62, 0xB8, 0xFF, 0x00, 0x8F, - 0xC8, 0x07, 0x59, 0x20, 0xCF, 0xDF, 0x03, 0x95, 0x1C, 0xF1, 0x8E, 0x7E, 0xBB, 0x25, 0xE0, 0xFC, - 0x5E, 0x3E, 0x9A, 0xAE, 0xFD, 0xD8, 0x49, 0xD9, 0x49, 0xED, 0xCD, 0xB2, 0x4D, 0xF4, 0x4D, 0xE8, - 0x9B, 0xD2, 0xF6, 0x4E, 0xD7, 0x47, 0xCC, 0xE6, 0xFC, 0x51, 0x85, 0xC0, 0xCD, 0xD2, 0xBF, 0x34, - 0xE2, 0xAE, 0xE2, 0xB7, 0xB2, 0xD5, 0xB4, 0xBA, 0xD9, 0x6A, 0xD2, 0xD6, 0xD7, 0x6A, 0xF6, 0x3F, - 0x08, 0x65, 0x89, 0xA1, 0x7D, 0xAC, 0x30, 0x69, 0xB5, 0xED, 0x9F, 0xB5, 0x1F, 0xEC, 0xD7, 0xA8, - 0xFC, 0x1E, 0xF1, 0x4D, 0xD5, 0xBD, 0xC5, 0xBB, 0x47, 0xE5, 0x39, 0xE4, 0x0F, 0x95, 0x87, 0x62, - 0x0F, 0xA1, 0xAF, 0x14, 0x65, 0xD8, 0xD8, 0x35, 0xF3, 0xF9, 0x96, 0x5D, 0x5F, 0x03, 0x88, 0x96, - 0x1B, 0x11, 0x1B, 0x4A, 0x2E, 0xCD, 0x33, 0xDA, 0xC0, 0x63, 0xA8, 0xE3, 0x28, 0x47, 0x11, 0x42, - 0x4A, 0x51, 0x92, 0xBA, 0x68, 0x4A, 0x28, 0xA2, 0xB8, 0x4E, 0xC0, 0xA2, 0x8A, 0x28, 0x00, 0xA2, - 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, - 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, - 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x00, 0xC9, 0xA0, 0x0D, 0x8F, 0x05, 0xF8, 0x6E, 0x4F, 0x12, 0xEB, - 0x50, 0xC1, 0x1A, 0xEE, 0xDC, 0xC0, 0x74, 0xAF, 0xDA, 0x2F, 0xF8, 0x22, 0xAF, 0xFC, 0x13, 0xD7, - 0x4D, 0xD7, 0x64, 0x6F, 0x13, 0x78, 0xA7, 0xFE, 0x25, 0xFE, 0x19, 0xD0, 0xAC, 0xCE, 0xAB, 0xAC, - 0xDE, 0xB2, 0xE3, 0xEC, 0xD6, 0x6B, 0xC9, 0x55, 0x3F, 0xF3, 0xD2, 0x4F, 0xBA, 0x31, 0xCF, 0x35, - 0xF9, 0xC7, 0xFF, 0x00, 0x04, 0xFA, 0xF8, 0x19, 0xFF, 0x00, 0x0B, 0x23, 0xE2, 0x1D, 0x92, 0xCD, - 0x1B, 0x35, 0xBE, 0xF1, 0x24, 0xC7, 0x1D, 0x11, 0x79, 0x6F, 0xCC, 0x71, 0xF5, 0x35, 0xFB, 0xB9, - 0xF1, 0xDA, 0x08, 0xBF, 0x66, 0x5F, 0xD9, 0x87, 0xC2, 0x7F, 0x09, 0xEC, 0x55, 0x6D, 0x75, 0xAF, - 0x11, 0x43, 0x17, 0x88, 0xBC, 0x55, 0xB4, 0x6D, 0x65, 0x0C, 0x33, 0x6D, 0x6A, 0xDE, 0xCA, 0xB8, - 0x6E, 0xDD, 0x2B, 0xF7, 0x6F, 0x0C, 0x78, 0x6E, 0x6E, 0x9C, 0x71, 0x51, 0xD2, 0xAD, 0x57, 0xCB, - 0x07, 0xFC, 0xA9, 0x2B, 0xCE, 0xA7, 0xAC, 0x63, 0x65, 0x1E, 0x9C, 0xF2, 0x89, 0xF8, 0x87, 0x89, - 0xFC, 0x49, 0x0A, 0x6E, 0x58, 0x59, 0xEB, 0x4A, 0x94, 0x54, 0xEA, 0x2F, 0xE6, 0x6D, 0xDA, 0x9D, - 0x3F, 0x49, 0xCA, 0xEE, 0x5F, 0xDC, 0x8C, 0x8F, 0x1F, 0xFD, 0xB6, 0xFF, 0x00, 0x6B, 0x08, 0x7E, - 0x21, 0xEB, 0x57, 0x9E, 0x28, 0xD4, 0xA3, 0x5D, 0x27, 0xC2, 0xFA, 0x1C, 0x02, 0xCB, 0x42, 0xD2, - 0x53, 0xE5, 0x8E, 0xC2, 0xD5, 0x06, 0x22, 0x89, 0x57, 0xA6, 0xE2, 0x06, 0x5B, 0xDF, 0x3D, 0xAB, - 0xF1, 0xAF, 0xF6, 0xCB, 0xFD, 0xA3, 0x2E, 0xBE, 0x32, 0xF8, 0xDA, 0x76, 0x76, 0xCA, 0x2B, 0x18, - 0xE1, 0x89, 0x4F, 0xCB, 0x0A, 0x76, 0x51, 0xFD, 0x6B, 0xDE, 0xBF, 0xE0, 0xA5, 0xBF, 0xB5, 0x03, - 0x5E, 0xEB, 0x12, 0x68, 0x1A, 0x7D, 0xC7, 0xFA, 0x2E, 0x9E, 0x4C, 0x5F, 0x2B, 0x71, 0x24, 0x9D, - 0x19, 0xBF, 0xA0, 0xFA, 0x57, 0xCA, 0xFF, 0x00, 0x00, 0xBE, 0x10, 0xDF, 0xFC, 0x64, 0xF1, 0xC5, - 0xBC, 0x31, 0xC2, 0xF3, 0xBD, 0xC4, 0xCA, 0xAA, 0x31, 0xD4, 0x93, 0x5A, 0x78, 0x89, 0x9E, 0xAC, - 0x4E, 0x22, 0x1C, 0x35, 0x93, 0xC7, 0xF7, 0x70, 0xB4, 0x6C, 0xBA, 0xBF, 0xD7, 0x5F, 0xBD, 0xEB, - 0xBB, 0x27, 0xC3, 0x8C, 0x8A, 0x78, 0x6C, 0x2C, 0xF8, 0x8F, 0x39, 0x95, 0xEA, 0xD4, 0xF7, 0xB5, - 0xFB, 0x2B, 0x7F, 0x45, 0xA7, 0xDC, 0xAC, 0xB6, 0x47, 0x11, 0x67, 0xF0, 0xDF, 0x56, 0xD5, 0x6D, - 0x1E, 0x6B, 0x7B, 0x49, 0xE6, 0xDA, 0xA5, 0xC8, 0x44, 0x27, 0x81, 0xC9, 0x35, 0x85, 0x35, 0xBC, - 0x96, 0xD2, 0x32, 0xC8, 0x8C, 0x8C, 0xA7, 0x04, 0x30, 0xC6, 0x0D, 0x7E, 0xF5, 0x7F, 0xC1, 0x3E, - 0xBF, 0xE0, 0x9B, 0x1A, 0x6E, 0xB5, 0x67, 0x75, 0x69, 0x24, 0x96, 0x3A, 0x4E, 0x87, 0xA4, 0xDA, - 0x7D, 0xAB, 0xC5, 0x3E, 0x22, 0xB9, 0x8C, 0x18, 0xAC, 0xAD, 0xC0, 0xCB, 0x46, 0xB9, 0xEA, 0x5B, - 0xA2, 0xAF, 0x73, 0xEB, 0x5F, 0x34, 0x7F, 0xC1, 0x6D, 0x3C, 0x03, 0xE0, 0x97, 0xF8, 0x6D, 0xA1, - 0xE9, 0x3F, 0x09, 0xFC, 0x0F, 0xE1, 0x9F, 0x0A, 0xFC, 0x39, 0xF0, 0xC5, 0xCB, 0x88, 0xEE, 0xDA, - 0xCA, 0x3F, 0xED, 0xED, 0x52, 0xE1, 0xCF, 0xCD, 0x3C, 0xD7, 0x04, 0x19, 0x0A, 0x3F, 0x5F, 0x2D, - 0x48, 0x0B, 0xC0, 0xE8, 0x06, 0x3E, 0x4F, 0x3F, 0xF0, 0xD7, 0x15, 0x80, 0xA1, 0xCD, 0x4A, 0x5C, - 0xF3, 0x84, 0x79, 0xAA, 0xA4, 0xB4, 0x82, 0x7B, 0x26, 0xEF, 0xF1, 0x3D, 0xEC, 0xB5, 0x4B, 0x57, - 0xA5, 0x9B, 0xFA, 0xAC, 0x87, 0xC4, 0x4C, 0x26, 0x61, 0x59, 0x46, 0x6B, 0x92, 0x13, 0x93, 0x8D, - 0x26, 0xDE, 0xB5, 0x2D, 0xBB, 0x4B, 0xF9, 0x53, 0xD2, 0xEF, 0x46, 0xF4, 0x5A, 0xDD, 0x2F, 0xCA, - 0x8A, 0x2A, 0x4B, 0x98, 0x7C, 0x89, 0x99, 0x7F, 0xBA, 0x6A, 0x3A, 0xFC, 0xC4, 0xFD, 0x1C, 0x28, - 0xA2, 0x95, 0x46, 0xE6, 0xC5, 0x00, 0x3E, 0x1B, 0x69, 0x2E, 0x1B, 0x08, 0xAC, 0xD9, 0x38, 0xE0, - 0x56, 0xE3, 0x7C, 0x37, 0xD5, 0x22, 0xB1, 0x5B, 0x86, 0xB5, 0x99, 0x63, 0x71, 0xB9, 0x58, 0xA9, - 0xC3, 0x0F, 0x6A, 0xFB, 0x1F, 0xFE, 0x08, 0xFB, 0xE0, 0xAB, 0x3B, 0x8F, 0x18, 0x6A, 0x8D, 0xE2, - 0xAF, 0x0D, 0xE8, 0xDE, 0x2E, 0xF8, 0x7F, 0x7C, 0x82, 0xDF, 0x5A, 0xD3, 0x6E, 0xE2, 0x53, 0x70, - 0xC0, 0xF4, 0x7B, 0x77, 0xFB, 0xF1, 0xC8, 0xBD, 0x43, 0x03, 0xDB, 0xD7, 0x06, 0xBF, 0x46, 0xFF, - 0x00, 0x6B, 0x9F, 0xF8, 0x26, 0xEF, 0x84, 0x34, 0x9F, 0x85, 0x3A, 0x3E, 0xA1, 0xE1, 0x73, 0x1E, - 0xB5, 0xF0, 0xCF, 0xC4, 0x10, 0xE3, 0x41, 0xD4, 0x96, 0x20, 0x27, 0xD2, 0x25, 0xC7, 0x36, 0xB3, - 0xE3, 0xEE, 0xC8, 0xBD, 0x39, 0xE1, 0xBA, 0xD7, 0xE8, 0x7C, 0x33, 0xC0, 0x35, 0xF3, 0x48, 0x29, - 0x55, 0x97, 0xB3, 0xF6, 0x89, 0xFB, 0x36, 0xD6, 0x93, 0x92, 0xDE, 0x37, 0xE8, 0xEC, 0x9B, 0x57, - 0xDE, 0xCE, 0xD7, 0xB3, 0xB7, 0xC2, 0x71, 0x27, 0x1C, 0x61, 0xF2, 0xB6, 0xF9, 0x57, 0x3F, 0xB3, - 0x6B, 0xDA, 0x24, 0xF5, 0x84, 0x5F, 0xDA, 0xB7, 0x55, 0x7B, 0x5E, 0xDB, 0x69, 0x7D, 0xD1, 0xF8, - 0xBB, 0xFB, 0x31, 0xFC, 0x65, 0xBA, 0xF8, 0x3F, 0xE3, 0xCB, 0x4B, 0xA8, 0x64, 0x68, 0x25, 0x85, - 0xC7, 0x3D, 0x98, 0x77, 0x07, 0xD8, 0xD7, 0xEC, 0x8F, 0xEC, 0x55, 0xFB, 0x5B, 0x5C, 0x68, 0xBA, - 0x96, 0x8F, 0xE3, 0xCF, 0x0A, 0x4E, 0xB1, 0xDF, 0x58, 0x90, 0xB7, 0x76, 0x64, 0xFE, 0xEE, 0x65, - 0x3F, 0xEB, 0x21, 0x90, 0x77, 0x47, 0x5C, 0x8F, 0xC7, 0xDA, 0xBF, 0x21, 0xBF, 0x6A, 0xFF, 0x00, - 0xD9, 0xD2, 0xFF, 0x00, 0xE0, 0xA7, 0x8D, 0xAE, 0xA1, 0x78, 0x4C, 0x66, 0x17, 0xC8, 0x2A, 0x38, - 0x61, 0xD9, 0x87, 0xB1, 0x15, 0xEA, 0x9F, 0xF0, 0x4F, 0x0F, 0xDA, 0x82, 0x4F, 0x03, 0x78, 0xC2, - 0xDE, 0xCE, 0xF6, 0x76, 0xFB, 0x0D, 0xC6, 0x21, 0xB8, 0x52, 0x78, 0xDA, 0x7F, 0x8B, 0xFE, 0x03, - 0xD6, 0xBE, 0xC7, 0x80, 0x73, 0xC9, 0x64, 0xD8, 0xF9, 0xE4, 0x19, 0xBC, 0x6F, 0x4A, 0xA5, 0xE3, - 0x25, 0x2D, 0x95, 0xF4, 0xFF, 0x00, 0x80, 0xFC, 0x8F, 0x90, 0xF1, 0x03, 0x21, 0x59, 0xCE, 0x5D, - 0x0C, 0xF3, 0x27, 0x9D, 0xAB, 0x53, 0x4A, 0x71, 0x71, 0x7B, 0xDB, 0x5F, 0xF8, 0x6F, 0x3F, 0x26, - 0xCF, 0xD3, 0x2F, 0xF8, 0x2B, 0xFF, 0x00, 0xEC, 0x45, 0xE1, 0xDF, 0x8B, 0x3F, 0x0E, 0x34, 0xFF, - 0x00, 0x89, 0x9E, 0x04, 0xB3, 0x1F, 0xF0, 0x8D, 0x78, 0xB2, 0x17, 0xB9, 0xB4, 0x89, 0x47, 0xCD, - 0xA6, 0x5C, 0x8C, 0x99, 0xEC, 0x5B, 0x1D, 0x0A, 0xB6, 0x4A, 0x8F, 0x4A, 0xFC, 0x27, 0xF8, 0xA1, - 0xE0, 0xD9, 0x3C, 0x1F, 0xE2, 0x39, 0xE1, 0x65, 0x2A, 0xAA, 0xC4, 0x0A, 0xFE, 0x93, 0x7F, 0x62, - 0x9D, 0x6E, 0xD3, 0xE2, 0xC6, 0x89, 0xE2, 0x4F, 0x83, 0x7A, 0xBC, 0xD1, 0xFF, 0x00, 0x66, 0xF8, - 0xEA, 0xDC, 0xDD, 0x68, 0x93, 0x49, 0xCA, 0xD9, 0x6A, 0x91, 0xAE, 0x62, 0x65, 0xFF, 0x00, 0x7F, - 0x01, 0x48, 0x1D, 0x6B, 0xF1, 0x97, 0xFE, 0x0A, 0x91, 0xFB, 0x36, 0xCD, 0xF0, 0xD3, 0xE2, 0x4E, - 0xA4, 0xE6, 0xCD, 0xAD, 0x4B, 0xCA, 0xEC, 0xF1, 0x15, 0xC1, 0x86, 0x40, 0xC5, 0x64, 0x8C, 0xFB, - 0xAB, 0x86, 0xE3, 0xE9, 0x5D, 0xFE, 0x26, 0xF0, 0xCB, 0x74, 0x67, 0x53, 0x7A, 0x94, 0x2C, 0x9C, - 0xBA, 0xCE, 0x9C, 0xAF, 0xEC, 0xE4, 0xDF, 0x56, 0xAC, 0xE1, 0x27, 0xD5, 0xC5, 0x3F, 0xB4, 0x79, - 0xBE, 0x18, 0x71, 0x44, 0x67, 0x52, 0x34, 0xBE, 0x1A, 0x75, 0xD3, 0x6A, 0x3D, 0x21, 0x56, 0x36, - 0xF6, 0x90, 0x4B, 0xA4, 0x5D, 0xD5, 0x48, 0x2E, 0x8A, 0x4D, 0x7D, 0x93, 0xE2, 0x6A, 0x29, 0xD2, - 0x27, 0x97, 0x23, 0x29, 0xFE, 0x13, 0x8A, 0x6D, 0x7F, 0x3E, 0x1F, 0xBE, 0x05, 0x14, 0x51, 0x40, - 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, - 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, - 0x05, 0x14, 0x51, 0x40, 0x05, 0x58, 0xD2, 0xED, 0xBE, 0xD7, 0x7F, 0x1C, 0x63, 0xF8, 0x8D, 0x57, - 0xAD, 0xCF, 0x87, 0xB6, 0x5F, 0x6E, 0xF1, 0x35, 0xB2, 0x7A, 0xB8, 0xAD, 0x28, 0xC7, 0x9A, 0x6A, - 0x3D, 0xD9, 0x15, 0x25, 0xCB, 0x16, 0xCF, 0xD7, 0xCF, 0xF8, 0x37, 0xEB, 0xF6, 0x65, 0xB5, 0xF1, - 0xEF, 0xC5, 0x9D, 0x03, 0xFB, 0x42, 0x38, 0xD6, 0xC2, 0x4B, 0xA5, 0x9A, 0xED, 0xE4, 0x1F, 0x20, - 0xB5, 0xB7, 0x53, 0x71, 0x3E, 0x4F, 0x60, 0x56, 0x30, 0x3F, 0x1A, 0xF6, 0x6F, 0xDB, 0xD3, 0xF6, - 0x8B, 0x9B, 0xC4, 0x7A, 0xF7, 0x8F, 0xBC, 0x79, 0x3B, 0x04, 0x92, 0xF6, 0xE2, 0x5F, 0xB2, 0x2E, - 0x72, 0xB1, 0x2E, 0x4A, 0x44, 0x83, 0xD8, 0x0C, 0x57, 0x47, 0xFF, 0x00, 0x04, 0x63, 0xD1, 0x63, - 0xF8, 0x69, 0xFB, 0x2B, 0x7C, 0x46, 0xF1, 0x47, 0x92, 0x0C, 0x9A, 0x77, 0x82, 0xEE, 0x62, 0x85, - 0xFF, 0x00, 0xE7, 0x9C, 0xD7, 0x92, 0xC7, 0x6E, 0x18, 0x7B, 0xED, 0x2D, 0x5F, 0x23, 0xFF, 0x00, - 0xC1, 0x49, 0x3C, 0x60, 0xDE, 0x1F, 0xF8, 0x35, 0x6F, 0x68, 0xAD, 0xB5, 0xAE, 0xE4, 0x2E, 0xFC, - 0xF5, 0x00, 0x7F, 0x8D, 0x7F, 0x5D, 0x64, 0xF6, 0xCA, 0xF2, 0xAA, 0xD8, 0xA8, 0xE9, 0xEC, 0x28, - 0xC2, 0x11, 0xF2, 0x94, 0xD7, 0xB4, 0x93, 0xF9, 0xF3, 0x53, 0xFF, 0x00, 0xC0, 0x4F, 0xE4, 0xAC, - 0xEA, 0x2F, 0x36, 0xCE, 0x70, 0xF8, 0x49, 0x6A, 0xAB, 0xD7, 0x9C, 0xE5, 0xE7, 0x0A, 0x5F, 0xBB, - 0x8A, 0xF9, 0x72, 0xD4, 0x7F, 0xF6, 0xF1, 0xF9, 0xB9, 0xF1, 0x73, 0xC4, 0x53, 0xF8, 0xF3, 0xE2, - 0x14, 0xCA, 0x5D, 0xA4, 0xDD, 0x2E, 0x3D, 0x73, 0xCD, 0x7E, 0x86, 0x7F, 0xC1, 0x2D, 0x7F, 0x66, - 0x99, 0xA0, 0xD2, 0xAD, 0xB5, 0x28, 0x6C, 0x4D, 0xCE, 0xAB, 0xAA, 0xCD, 0x1E, 0x9F, 0xA6, 0x44, - 0x07, 0xCD, 0x24, 0xAE, 0x42, 0xF1, 0xEE, 0x49, 0x55, 0x07, 0xDC, 0xD7, 0xE7, 0x8F, 0xC2, 0x6D, - 0x2D, 0xBC, 0x51, 0xF1, 0x26, 0x3C, 0xFC, 0xC5, 0xA5, 0xE3, 0xEB, 0x9A, 0xFE, 0x81, 0x3F, 0xE0, - 0x93, 0xDE, 0x00, 0xB3, 0xF0, 0x2E, 0xB3, 0x06, 0xBD, 0x73, 0x0C, 0x26, 0xCF, 0xE1, 0x9F, 0x85, - 0xEE, 0x7C, 0x44, 0x52, 0x55, 0xF9, 0x24, 0xB8, 0x58, 0xC2, 0xC2, 0xA7, 0xDF, 0xCD, 0x95, 0x48, - 0xFF, 0x00, 0x76, 0xBF, 0x33, 0xF0, 0xC7, 0x0F, 0x17, 0x5F, 0x15, 0x9F, 0x56, 0x8F, 0x33, 0xA2, - 0x9B, 0x8A, 0x7D, 0x66, 0xF4, 0x8A, 0xF9, 0xBD, 0x3D, 0x4F, 0xD3, 0xBC, 0x50, 0xC4, 0xCB, 0xEA, - 0xF8, 0x5C, 0x86, 0x8C, 0xB9, 0x15, 0x79, 0x28, 0xC9, 0xAE, 0x90, 0x5A, 0xCD, 0xFC, 0x97, 0xE0, - 0x74, 0xDF, 0xB6, 0xEF, 0xC4, 0x4D, 0x2F, 0xF6, 0x70, 0xF8, 0x6B, 0x6F, 0xF0, 0x7F, 0x47, 0xBC, - 0x58, 0xF4, 0x8F, 0x0A, 0xC6, 0x2F, 0xBC, 0x5D, 0x7B, 0x1E, 0x41, 0xD5, 0x75, 0x2D, 0xA0, 0xB2, - 0x37, 0xAA, 0x46, 0x78, 0x0B, 0xCF, 0x38, 0xAF, 0xC3, 0xDF, 0xDB, 0xB3, 0xF6, 0xB0, 0xB8, 0xF8, - 0xAB, 0xE2, 0x69, 0xED, 0xD6, 0x42, 0xB6, 0x90, 0x31, 0x48, 0x20, 0x0D, 0xF2, 0xC4, 0xBF, 0xE2, - 0x7B, 0x9A, 0xFB, 0x0B, 0xFE, 0x0A, 0x7D, 0xF1, 0xD6, 0xEA, 0xC3, 0xC1, 0xF3, 0xAC, 0xB7, 0x12, - 0x35, 0xF6, 0xBD, 0x3C, 0x97, 0xD7, 0x4C, 0xC7, 0xE6, 0x7C, 0xB6, 0x79, 0x3F, 0x52, 0x3F, 0x2A, - 0xFC, 0xA1, 0xF1, 0x26, 0xAF, 0x26, 0xB1, 0xAB, 0x4D, 0x34, 0x8C, 0x58, 0xB3, 0x13, 0x5D, 0x9E, - 0x29, 0x67, 0xD5, 0x32, 0xFA, 0x31, 0xC8, 0x68, 0x4A, 0xF2, 0xF8, 0xAA, 0xCB, 0xAC, 0xE7, 0x2D, - 0x64, 0xDF, 0xE8, 0xBA, 0x2B, 0x2D, 0x92, 0x38, 0x3C, 0x2D, 0xC8, 0xE9, 0xE3, 0xEA, 0x4B, 0x88, - 0x2B, 0x46, 0xC9, 0xFB, 0xB4, 0xA3, 0xD2, 0x14, 0xE3, 0xA4, 0x52, 0xF3, 0xEE, 0xFA, 0xBB, 0xBD, - 0xDB, 0x28, 0xCD, 0x2F, 0x9D, 0x2B, 0x31, 0xFE, 0x23, 0x5E, 0xBF, 0xF0, 0x6B, 0xF6, 0x06, 0xF8, - 0xB9, 0xF1, 0xEB, 0x49, 0x5D, 0x43, 0xC3, 0x7E, 0x0B, 0xD4, 0xAE, 0x34, 0xD7, 0x19, 0x5B, 0xBB, - 0x82, 0x96, 0xB0, 0xB8, 0xF5, 0x53, 0x21, 0x52, 0xC3, 0xDD, 0x41, 0x15, 0xE9, 0x3F, 0xF0, 0x4B, - 0x2F, 0xD9, 0x73, 0x4D, 0xF8, 0xDD, 0xF1, 0x5D, 0xF5, 0xEF, 0x11, 0x5B, 0xA5, 0xD6, 0x87, 0xE1, - 0xF9, 0x13, 0x65, 0xBC, 0x83, 0x29, 0x73, 0x70, 0x79, 0x1B, 0x87, 0x75, 0x50, 0x33, 0x8F, 0x52, - 0x2B, 0xF7, 0xB3, 0xE0, 0x77, 0x86, 0x7C, 0x3A, 0xBE, 0x0F, 0x58, 0xE4, 0x58, 0x61, 0x58, 0xE2, - 0xC4, 0x68, 0xAA, 0x15, 0x46, 0x3A, 0x0C, 0x57, 0xF1, 0xAF, 0x13, 0xF1, 0xF4, 0xB0, 0x78, 0xE7, - 0x96, 0x60, 0x23, 0x17, 0x52, 0x2A, 0xF2, 0x94, 0xAF, 0x65, 0x7D, 0x52, 0xB2, 0x6A, 0xEE, 0xDA, - 0xBD, 0x74, 0xF3, 0x3F, 0xB7, 0x78, 0x47, 0xC2, 0xD5, 0x8C, 0xCA, 0xE1, 0x9D, 0x66, 0x92, 0x94, - 0x69, 0x4D, 0xB5, 0x08, 0xC2, 0xDC, 0xD2, 0x49, 0xD9, 0xC9, 0xB6, 0x9A, 0x4A, 0xF7, 0x49, 0x59, - 0xB7, 0xBE, 0x8A, 0xD7, 0xFE, 0x71, 0x7E, 0x2D, 0xFE, 0xC2, 0xFF, 0x00, 0x15, 0x3E, 0x08, 0xD9, - 0xC9, 0x73, 0xAF, 0xF8, 0x46, 0xFE, 0x3B, 0x38, 0x86, 0x5E, 0x7B, 0x66, 0x4B, 0x94, 0x41, 0xDC, - 0xB7, 0x96, 0x49, 0x00, 0x7A, 0x91, 0x8A, 0xF2, 0x45, 0x6D, 0xAD, 0x9A, 0xFE, 0x88, 0xFF, 0x00, - 0x69, 0x7D, 0x13, 0x4F, 0x1A, 0x84, 0x91, 0xC2, 0xB1, 0xBA, 0x31, 0xDA, 0x54, 0x8C, 0x82, 0x3D, - 0x31, 0x5F, 0x8D, 0xBF, 0xF0, 0x51, 0xAF, 0x80, 0x5A, 0x6F, 0xC3, 0x0F, 0x89, 0x4D, 0xAC, 0x68, - 0xB0, 0xC7, 0x6B, 0x63, 0xAA, 0x48, 0xC2, 0x7B, 0x74, 0x18, 0x48, 0xA5, 0xE4, 0xE5, 0x47, 0x60, - 0xDC, 0xF1, 0xEA, 0x3D, 0xEB, 0x8F, 0x83, 0x3C, 0x47, 0x9E, 0x67, 0x8E, 0x96, 0x59, 0x8E, 0x82, - 0x8C, 0xFE, 0xCC, 0xA3, 0x7B, 0x3F, 0x26, 0x9B, 0x76, 0x7E, 0x77, 0xD4, 0xF4, 0xBC, 0x40, 0xF0, - 0x82, 0x9E, 0x53, 0x95, 0x47, 0x3A, 0xCA, 0xEA, 0x4A, 0x54, 0xFE, 0xD4, 0x67, 0x6E, 0x65, 0x7E, - 0xA9, 0xA4, 0x93, 0x5D, 0xD5, 0xAE, 0xB7, 0xBB, 0x31, 0xFF, 0x00, 0x63, 0xCF, 0xDA, 0x22, 0xF3, - 0xE1, 0x0F, 0x8D, 0x6D, 0x67, 0xB7, 0x97, 0xCB, 0xF9, 0xB6, 0xB2, 0x93, 0xF2, 0x4A, 0xBD, 0xD5, - 0x87, 0xA1, 0xAF, 0xDC, 0xDF, 0xF8, 0x27, 0x67, 0xED, 0x23, 0xA0, 0xF8, 0xE3, 0x45, 0x6F, 0x08, - 0x6B, 0xF2, 0x09, 0xBE, 0x1E, 0xFC, 0x4A, 0x8D, 0x6D, 0x6E, 0xD1, 0xDB, 0xFE, 0x41, 0x17, 0xA7, - 0x88, 0xEE, 0x50, 0xFF, 0x00, 0x0B, 0x2B, 0x60, 0x12, 0x3A, 0x8A, 0xFE, 0x70, 0x34, 0xCB, 0xD6, - 0xB0, 0xBC, 0x49, 0x15, 0xB6, 0x95, 0x35, 0xFA, 0x51, 0xFF, 0x00, 0x04, 0xB7, 0xF8, 0xC9, 0x36, - 0xB1, 0xA7, 0xDC, 0x68, 0x92, 0xCE, 0xDF, 0x34, 0x62, 0x78, 0x79, 0xFB, 0xB2, 0x2E, 0x39, 0xFE, - 0x55, 0xFD, 0x91, 0xE1, 0x5E, 0x79, 0xF5, 0x97, 0x2C, 0x83, 0x16, 0xFD, 0xCA, 0x9F, 0x0B, 0xEB, - 0x09, 0xAD, 0x63, 0x28, 0xBE, 0x8D, 0x34, 0x9A, 0xF3, 0xB1, 0xFC, 0x3F, 0xE2, 0xA6, 0x4A, 0xF0, - 0xF4, 0xA3, 0xC4, 0x18, 0x45, 0xEF, 0xD2, 0xF8, 0x97, 0x49, 0xC1, 0xE9, 0x28, 0xC9, 0x75, 0x4D, - 0x37, 0x7F, 0x2B, 0x9E, 0xD9, 0xFF, 0x00, 0x05, 0x58, 0xFD, 0x90, 0x6E, 0xF4, 0x2B, 0x6F, 0x10, - 0x69, 0x3A, 0x8D, 0xBF, 0xFC, 0x4F, 0x3C, 0x1B, 0x72, 0xF6, 0xB3, 0x38, 0x4C, 0x7D, 0xA2, 0x1C, - 0xFC, 0xB2, 0x0E, 0xF8, 0x20, 0xAB, 0x8F, 0x67, 0xAF, 0xC9, 0xDD, 0x0A, 0xEE, 0x6F, 0x87, 0xDF, - 0x10, 0x94, 0x64, 0xA6, 0xC9, 0x70, 0x7F, 0x3A, 0xFE, 0x8B, 0xFF, 0x00, 0x6E, 0xCD, 0x32, 0x2F, - 0x8C, 0x7F, 0x08, 0xBE, 0x19, 0xF8, 0xF6, 0x48, 0xD1, 0xA4, 0xF1, 0x97, 0x87, 0x24, 0xD1, 0x75, - 0x46, 0x07, 0x73, 0x4B, 0x77, 0x64, 0x4C, 0x26, 0x47, 0xFF, 0x00, 0x69, 0xA3, 0x31, 0x9E, 0x7D, - 0x05, 0x7F, 0x3E, 0xBF, 0xB5, 0x27, 0x84, 0xFF, 0x00, 0xE1, 0x0F, 0xF8, 0x9F, 0x79, 0x12, 0xAE, - 0xDF, 0x26, 0xE1, 0x93, 0xA7, 0xA1, 0x22, 0xBA, 0xBC, 0x50, 0xC2, 0x4A, 0xA5, 0x2C, 0x36, 0x79, - 0x6B, 0x54, 0x77, 0x85, 0x4B, 0x6D, 0xED, 0x29, 0xBE, 0x59, 0x7C, 0xAE, 0x9D, 0xBC, 0x8E, 0x5F, - 0x0B, 0x31, 0x91, 0xA7, 0x53, 0x15, 0x91, 0xA6, 0xDD, 0x38, 0x5A, 0x74, 0xEF, 0xBF, 0xB3, 0xA9, - 0x15, 0x38, 0xFD, 0xC9, 0xAB, 0xF9, 0x9F, 0xAB, 0x3F, 0xB0, 0xEF, 0xC6, 0xEB, 0xAD, 0x53, 0xE1, - 0xFF, 0x00, 0x87, 0x35, 0xEB, 0x3B, 0x96, 0x8F, 0x55, 0xF0, 0xEC, 0xF1, 0xB2, 0xC8, 0xAD, 0xF3, - 0x2B, 0x46, 0x41, 0x53, 0x9F, 0x52, 0x2B, 0xB4, 0xFF, 0x00, 0x83, 0x82, 0x7E, 0x0C, 0x59, 0xF8, - 0xB2, 0xE9, 0x7C, 0x6B, 0xA6, 0xDA, 0xA4, 0x76, 0x3E, 0x32, 0xD3, 0xAD, 0x7C, 0x4F, 0x6E, 0xA8, - 0x77, 0x2A, 0x7D, 0xA6, 0x30, 0x27, 0x19, 0xF5, 0x13, 0xA3, 0xF1, 0xDB, 0x35, 0xF2, 0x47, 0xFC, - 0x12, 0xAB, 0xC6, 0x72, 0x6A, 0x7A, 0x06, 0xA5, 0xA7, 0x3B, 0x6E, 0x53, 0x0A, 0x4A, 0x06, 0x7B, - 0x8E, 0x3F, 0x95, 0x7E, 0x88, 0xFE, 0xDB, 0x3A, 0x3C, 0x3F, 0x14, 0x7F, 0xE0, 0x99, 0x3F, 0x0E, - 0xF5, 0x06, 0x4D, 0xF7, 0x5A, 0x40, 0xD5, 0xB4, 0x19, 0xE4, 0x3C, 0xEE, 0x58, 0xE4, 0x5B, 0x88, - 0x87, 0xE0, 0x24, 0x22, 0xBF, 0x44, 0xF6, 0x8B, 0x36, 0xC9, 0xB0, 0x78, 0x99, 0xEA, 0xEA, 0x42, - 0x74, 0x65, 0xFF, 0x00, 0x80, 0xB9, 0xC5, 0xBF, 0x49, 0x52, 0x56, 0xFF, 0x00, 0x13, 0xEE, 0x7E, - 0x77, 0xEC, 0xDE, 0x4F, 0x9F, 0x63, 0x70, 0xB0, 0xD1, 0x53, 0x9D, 0x3A, 0xF1, 0xF2, 0xF7, 0xD4, - 0x26, 0x97, 0xAC, 0x6A, 0xBB, 0xFF, 0x00, 0x85, 0x76, 0x3F, 0x9C, 0xAF, 0x14, 0xD8, 0x1D, 0x37, - 0x5B, 0x9E, 0x32, 0x31, 0xB5, 0xC8, 0xAC, 0xEA, 0xEC, 0x3E, 0x34, 0xE9, 0xBF, 0xD9, 0xFE, 0x33, - 0xBA, 0x5C, 0x63, 0xE7, 0x26, 0xB8, 0xFA, 0xFE, 0x49, 0xC5, 0x53, 0xF6, 0x75, 0xA5, 0x0E, 0xCC, - 0xFE, 0xB2, 0xC3, 0xD4, 0xE7, 0xA7, 0x19, 0x77, 0x41, 0x45, 0x14, 0x57, 0x39, 0xB0, 0x51, 0x45, - 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, - 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, - 0x14, 0x00, 0x57, 0x55, 0xF0, 0x85, 0x37, 0x78, 0xC6, 0xD7, 0xFD, 0xF1, 0x5C, 0xAD, 0x74, 0x9F, - 0x0B, 0x6E, 0x7E, 0xCB, 0xE2, 0xDB, 0x56, 0x3F, 0xDF, 0x15, 0xD5, 0x83, 0x76, 0xAF, 0x0B, 0xF7, - 0x46, 0x18, 0x9D, 0x69, 0x4B, 0xD0, 0xFE, 0x87, 0xBF, 0x60, 0x08, 0x57, 0x4E, 0xFF, 0x00, 0x82, - 0x58, 0xFC, 0x59, 0x99, 0x7E, 0x69, 0x1A, 0xDB, 0x42, 0xB5, 0xC8, 0xEC, 0xAD, 0x76, 0x5C, 0xFE, - 0x1F, 0x20, 0x15, 0xF9, 0xE3, 0xFF, 0x00, 0x05, 0x60, 0xB9, 0x92, 0x2F, 0x0F, 0x69, 0x51, 0xA9, - 0x3B, 0x4C, 0x0E, 0x71, 0xF8, 0xD7, 0xE8, 0x07, 0xFC, 0x13, 0x2B, 0x58, 0x5F, 0x14, 0xFF, 0x00, - 0xC1, 0x38, 0x3E, 0x2C, 0x69, 0x31, 0x0D, 0xF2, 0x49, 0xA0, 0x69, 0xF7, 0xE0, 0x0E, 0xC2, 0xDE, - 0xF9, 0x37, 0x1F, 0xC9, 0xAB, 0xE1, 0x5F, 0xF8, 0x2A, 0x57, 0x87, 0x9B, 0x51, 0xF8, 0x7F, 0xA6, - 0xDD, 0xA8, 0x27, 0x6A, 0xBC, 0x47, 0xDB, 0xBD, 0x7F, 0x57, 0x66, 0xD0, 0x73, 0xE1, 0xCC, 0xC5, - 0x43, 0xBD, 0x37, 0xF2, 0xF6, 0x34, 0x3F, 0xC9, 0x9F, 0xCA, 0x99, 0x4C, 0x95, 0x3E, 0x28, 0xCB, - 0x5C, 0xFB, 0x56, 0x5F, 0x3F, 0x6F, 0x5F, 0xFC, 0xD1, 0xF0, 0xC7, 0xEC, 0x99, 0x6E, 0xB7, 0x3F, - 0x15, 0xEC, 0x55, 0xBB, 0xDC, 0x27, 0xFE, 0x86, 0x2B, 0xFA, 0x09, 0xFD, 0x97, 0xAD, 0x57, 0x4E, - 0xFD, 0x90, 0xBE, 0x3D, 0x6A, 0x11, 0xF1, 0x34, 0x3A, 0x56, 0x95, 0x64, 0x84, 0x75, 0xD9, 0x25, - 0xE2, 0x6E, 0xFF, 0x00, 0xD0, 0x45, 0x7F, 0x3C, 0xFF, 0x00, 0xB3, 0x8E, 0xAC, 0x34, 0x3F, 0x89, - 0xD6, 0xCE, 0xDF, 0x2F, 0x97, 0x32, 0xB7, 0xE4, 0xC0, 0xD7, 0xF4, 0x21, 0xFB, 0x07, 0xDC, 0xFF, - 0x00, 0xC2, 0xC8, 0xF8, 0x29, 0xF1, 0x6F, 0xC3, 0xB6, 0xEE, 0xBE, 0x66, 0xBD, 0xE0, 0xB4, 0xD5, - 0x60, 0x5D, 0xDF, 0xEB, 0x1A, 0xD2, 0x68, 0xA6, 0x20, 0x7A, 0x9D, 0xBB, 0x8E, 0x3D, 0x8D, 0x7C, - 0x67, 0x86, 0x35, 0x62, 0xB2, 0x3C, 0x4A, 0x7F, 0x66, 0xAD, 0x09, 0x3F, 0xF0, 0xC6, 0xAC, 0x5C, - 0xBF, 0x04, 0xCF, 0xB3, 0xF1, 0x3A, 0x94, 0xDE, 0x77, 0x84, 0x6B, 0xED, 0x53, 0xC4, 0x45, 0x7F, - 0x8A, 0x54, 0x64, 0xA3, 0xF3, 0xBB, 0x47, 0xE4, 0xCF, 0xFC, 0x15, 0x97, 0x52, 0x98, 0x78, 0x96, - 0x28, 0x77, 0x1F, 0x2D, 0x2D, 0x53, 0x03, 0xD3, 0x35, 0xF0, 0x1C, 0xA7, 0x32, 0x37, 0xD6, 0xBF, - 0x49, 0xBF, 0xE0, 0xAA, 0xBE, 0x00, 0x93, 0x52, 0xD3, 0xAC, 0x75, 0x48, 0xE3, 0x3B, 0x66, 0x80, - 0xC6, 0xC4, 0x0F, 0xE2, 0x5F, 0xFE, 0xB5, 0x7E, 0x6E, 0x5E, 0x42, 0xD6, 0xF7, 0x2E, 0xAD, 0xFC, - 0x27, 0x15, 0xF1, 0x1E, 0x2D, 0xE1, 0xEA, 0x53, 0xE2, 0x2A, 0xCE, 0xA7, 0x57, 0x75, 0xE8, 0xCF, - 0xB4, 0xF0, 0x97, 0x11, 0x4E, 0xAF, 0x0D, 0xE1, 0xFD, 0x9F, 0x45, 0x67, 0xEA, 0xB4, 0x67, 0xDE, - 0xBF, 0xF0, 0x49, 0xBF, 0x1C, 0x59, 0xE8, 0x7F, 0x0E, 0xF5, 0x1B, 0x56, 0x65, 0x59, 0x3E, 0xDE, - 0xC6, 0x4E, 0x79, 0xE5, 0x53, 0x07, 0xF4, 0xFD, 0x2B, 0xF4, 0x03, 0xC3, 0x3F, 0x1B, 0xAE, 0xB4, - 0xED, 0x3F, 0x64, 0x37, 0x28, 0xCB, 0xB7, 0xAE, 0xEC, 0x57, 0xE2, 0x67, 0xEC, 0xDB, 0xF1, 0xCE, - 0x5F, 0x82, 0xDE, 0x2F, 0x69, 0x24, 0x69, 0x3F, 0xB3, 0x2F, 0xB0, 0x97, 0x01, 0x39, 0x31, 0x91, - 0xF7, 0x5C, 0x0E, 0xF8, 0xC9, 0xC8, 0xF4, 0x3E, 0xD5, 0xF6, 0xCF, 0x86, 0x7E, 0x39, 0x5C, 0x6B, - 0x3A, 0x3C, 0x37, 0x56, 0x37, 0x31, 0xDD, 0x5A, 0xCC, 0xA1, 0x92, 0x48, 0xDF, 0x86, 0x15, 0xFC, - 0x3F, 0xE2, 0x17, 0x07, 0xE2, 0x96, 0x6D, 0x3C, 0x75, 0x19, 0x34, 0xAA, 0x6B, 0x7E, 0x9E, 0x6B, - 0xD5, 0x7E, 0x47, 0xFA, 0x21, 0xE1, 0x57, 0x88, 0x18, 0x2A, 0xB9, 0x15, 0x2C, 0xB7, 0x11, 0x15, - 0x29, 0x51, 0x56, 0x6B, 0xAD, 0x96, 0xCF, 0xD1, 0xAF, 0xC4, 0xFA, 0xA3, 0xE2, 0x5F, 0xC5, 0xB4, - 0x78, 0xA5, 0x9E, 0xE2, 0xE1, 0x5E, 0x4C, 0x1C, 0x00, 0x7E, 0xED, 0x7E, 0x73, 0xFF, 0x00, 0xC1, - 0x40, 0x7E, 0x21, 0xC3, 0xE2, 0x98, 0x1E, 0x10, 0xC1, 0xB1, 0x28, 0xDB, 0xF5, 0xCF, 0xFF, 0x00, - 0xAE, 0xBD, 0x6B, 0xE2, 0x07, 0xC4, 0xDB, 0xA5, 0xD1, 0xAE, 0x2E, 0x2F, 0x2E, 0x96, 0xDE, 0x08, - 0xD4, 0xB3, 0x33, 0x36, 0x30, 0x2B, 0xE2, 0x3F, 0x8B, 0xDF, 0x10, 0xFF, 0x00, 0xE1, 0x3C, 0xF1, - 0x14, 0x8F, 0x13, 0x33, 0x5A, 0xC6, 0xC7, 0x61, 0x3F, 0xC7, 0xEF, 0x5E, 0x87, 0x86, 0xBC, 0x25, - 0x2A, 0x78, 0xCF, 0xAD, 0xCD, 0xDD, 0xC7, 0x76, 0x71, 0x78, 0xB5, 0xC7, 0x54, 0xA5, 0x95, 0xCB, - 0x07, 0x1B, 0x2E, 0x75, 0x65, 0x1F, 0x5D, 0xDD, 0xBB, 0x2E, 0xE7, 0x23, 0x5F, 0x62, 0x7F, 0xC1, - 0x2E, 0x35, 0x89, 0xAD, 0x7E, 0x2A, 0xE9, 0xA8, 0xB9, 0xDA, 0xF2, 0x6C, 0x39, 0xF4, 0x23, 0x9A, - 0xF8, 0xF6, 0x34, 0xDE, 0xE1, 0x7D, 0x4D, 0x7D, 0xDB, 0xFF, 0x00, 0x04, 0xA6, 0xF8, 0x7F, 0x34, - 0xDE, 0x34, 0x8F, 0x50, 0x68, 0xFF, 0x00, 0x77, 0x67, 0x13, 0x4A, 0xC4, 0x8E, 0x9C, 0x60, 0x57, - 0xF5, 0xFF, 0x00, 0x86, 0xB8, 0x7A, 0x95, 0x78, 0x83, 0x0E, 0xA9, 0xF4, 0x92, 0x7F, 0x73, 0x3F, - 0x83, 0xFC, 0x46, 0xC4, 0x53, 0xA5, 0xC3, 0xF8, 0x99, 0x55, 0xDB, 0x92, 0x4B, 0xEF, 0x5A, 0x1F, - 0xAF, 0xFA, 0xFC, 0x5F, 0xDA, 0x3F, 0xF0, 0x4D, 0xEF, 0x0D, 0xDC, 0x49, 0xCB, 0x69, 0x7E, 0x39, - 0xBD, 0xB6, 0x8B, 0x3D, 0x76, 0xC9, 0x6D, 0x03, 0x9C, 0x7B, 0x66, 0xBF, 0x09, 0x7F, 0xE0, 0xA2, - 0x76, 0xAB, 0x6D, 0xF1, 0xBF, 0x5A, 0x55, 0xE8, 0xB7, 0x8D, 0x8F, 0xD0, 0xD7, 0xEE, 0xD7, 0xC5, - 0xF8, 0xBF, 0xE1, 0x07, 0xFD, 0x82, 0xFE, 0x15, 0x69, 0x92, 0xC8, 0xAB, 0x71, 0xE2, 0x2D, 0x57, - 0x54, 0xF1, 0x04, 0x91, 0xFF, 0x00, 0x12, 0x44, 0xA6, 0x38, 0x10, 0x9F, 0xAF, 0x96, 0xDF, 0x95, - 0x7E, 0x07, 0xFE, 0xDC, 0xFE, 0x24, 0x4F, 0x12, 0x7C, 0x5F, 0xD5, 0x26, 0x56, 0x0D, 0xE6, 0xDD, - 0xB9, 0xE3, 0xD8, 0xE3, 0xFA, 0x57, 0xEB, 0x1E, 0x27, 0x54, 0x87, 0xF6, 0x13, 0xFE, 0xF6, 0x22, - 0xB3, 0x5E, 0x8A, 0x6D, 0x3F, 0xFC, 0x9A, 0xEC, 0xFC, 0x9F, 0xC2, 0xEA, 0x53, 0x59, 0xE6, 0xBF, - 0x67, 0x0D, 0x41, 0x4B, 0xD5, 0xC1, 0x49, 0x7F, 0xE4, 0xB6, 0x3D, 0xF7, 0xFE, 0x09, 0x2D, 0x7F, - 0x22, 0xF8, 0xB2, 0x48, 0xBF, 0x86, 0x4B, 0x67, 0x07, 0xF0, 0x15, 0xFA, 0xCD, 0xE3, 0x0B, 0x75, - 0xBE, 0xFF, 0x00, 0x82, 0x51, 0x5F, 0x48, 0xE0, 0x16, 0xB5, 0xF1, 0xC5, 0xCC, 0x69, 0xBB, 0xD1, - 0xAC, 0x22, 0x27, 0x1F, 0x95, 0x7E, 0x58, 0x7F, 0xC1, 0x25, 0xFC, 0x36, 0xC9, 0x7D, 0x7D, 0x78, - 0xCB, 0xF2, 0xC3, 0x6B, 0xD7, 0x1D, 0x0B, 0x1C, 0x57, 0xEA, 0x27, 0xC6, 0x9D, 0x41, 0xBC, 0x21, - 0xFF, 0x00, 0x04, 0xA8, 0xD3, 0x56, 0x4F, 0x91, 0x75, 0xBF, 0x14, 0x6A, 0x77, 0xC9, 0x9F, 0xE2, - 0x58, 0xAD, 0xA1, 0x8B, 0x23, 0xF1, 0xE2, 0xBD, 0xEE, 0x10, 0x84, 0xA1, 0xC3, 0x58, 0x15, 0x3E, - 0xB5, 0xAE, 0xBD, 0x14, 0x64, 0xDF, 0xE0, 0x99, 0xE0, 0xF1, 0x8C, 0x94, 0xF8, 0xAB, 0x1D, 0xC9, - 0xD2, 0x85, 0x9F, 0xAB, 0x94, 0x12, 0xFC, 0x5A, 0x3F, 0x9E, 0xFF, 0x00, 0xDA, 0x46, 0x31, 0x1F, - 0x8F, 0x2F, 0x00, 0xFE, 0xF9, 0xFE, 0x75, 0xE7, 0x35, 0xDE, 0x7C, 0x7F, 0xBF, 0x17, 0xBE, 0x38, - 0xBA, 0x6F, 0xF6, 0xCD, 0x70, 0x75, 0xFC, 0xB9, 0x9B, 0x49, 0x3C, 0x65, 0x46, 0xBB, 0xB3, 0xFA, - 0x8B, 0x2F, 0x4D, 0x61, 0xA0, 0x9F, 0x64, 0x14, 0x51, 0x45, 0x79, 0xE7, 0x60, 0x51, 0x45, 0x14, - 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, - 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, - 0x00, 0x56, 0x87, 0x86, 0x6F, 0x7E, 0xC3, 0xAC, 0xC1, 0x27, 0xF7, 0x58, 0x1A, 0xCF, 0xA7, 0x44, - 0xFE, 0x5C, 0xAA, 0xDE, 0x86, 0xAA, 0x12, 0xE5, 0x92, 0x64, 0xCA, 0x37, 0x56, 0x67, 0xEF, 0x97, - 0xFC, 0x1B, 0xCF, 0xF1, 0x1E, 0x2F, 0x1B, 0xA6, 0xA1, 0xE0, 0x89, 0xA6, 0x88, 0x2F, 0x8C, 0xB4, - 0x3B, 0xFF, 0x00, 0x0F, 0x7E, 0xF4, 0xFC, 0xA8, 0xD3, 0x40, 0x64, 0x84, 0xFD, 0x7C, 0xD8, 0x80, - 0x1E, 0xE6, 0xBC, 0xBB, 0xF6, 0xE0, 0xF8, 0x69, 0x2F, 0x89, 0x3E, 0x0D, 0x6B, 0x56, 0x4F, 0x0F, - 0xFA, 0x5E, 0x8D, 0x2B, 0x33, 0x29, 0x1F, 0x32, 0xEC, 0x25, 0x5F, 0xF4, 0xCD, 0x7C, 0xCB, 0xFF, - 0x00, 0x04, 0x74, 0xFD, 0xA7, 0x2E, 0x3E, 0x12, 0xFC, 0x60, 0xD1, 0xAE, 0x2D, 0xEE, 0x04, 0x77, - 0x16, 0xD7, 0x31, 0x5C, 0x41, 0x93, 0xC7, 0x9D, 0x13, 0x89, 0x13, 0x3E, 0xC4, 0xAE, 0xDF, 0xF8, - 0x15, 0x7E, 0xAA, 0x7F, 0xC1, 0x41, 0x3E, 0x1B, 0x69, 0xE3, 0xE2, 0xA3, 0x78, 0x93, 0x4D, 0x8D, - 0x64, 0xF0, 0x9F, 0xC5, 0x0B, 0x05, 0xD7, 0x2C, 0x08, 0x50, 0x13, 0xF7, 0xAB, 0xFB, 0xE8, 0xB1, - 0xD0, 0x6D, 0x72, 0x46, 0x2B, 0xFA, 0xDB, 0x82, 0xF1, 0x54, 0xB3, 0x3C, 0x0A, 0xC3, 0xCD, 0xE9, - 0x88, 0xA5, 0xC9, 0xFF, 0x00, 0x6F, 0xD2, 0xBF, 0xE2, 0xE1, 0x28, 0xB5, 0xE5, 0x07, 0xD8, 0xFE, - 0x4F, 0xE3, 0x8C, 0x2D, 0x5C, 0xAF, 0x30, 0xFA, 0xE5, 0x35, 0xAE, 0x1E, 0xB2, 0xA9, 0xEB, 0x4E, - 0xAD, 0xBF, 0x05, 0x52, 0x12, 0x4F, 0xFE, 0xBE, 0x2E, 0xE7, 0xF3, 0xB7, 0x2F, 0x99, 0xE0, 0x8F, - 0x88, 0xED, 0xB8, 0x6C, 0xD9, 0x2F, 0xF5, 0xAF, 0xDA, 0x3F, 0xF8, 0x24, 0x0F, 0xED, 0x65, 0x6B, - 0xE1, 0x1D, 0x43, 0xC1, 0xFE, 0x20, 0xBA, 0x64, 0xB8, 0xB5, 0xD1, 0x66, 0xFE, 0xCE, 0xD5, 0xA2, - 0x6F, 0x98, 0x49, 0x65, 0x3A, 0x98, 0xE4, 0xC8, 0xEE, 0x02, 0x3B, 0x1F, 0xAA, 0x0A, 0xFC, 0xB9, - 0xFD, 0xBA, 0xFE, 0x03, 0x5C, 0x7C, 0x2F, 0xF8, 0x8B, 0x7C, 0xAB, 0x1B, 0x79, 0x71, 0xC9, 0x94, - 0x7C, 0x7D, 0xF4, 0x3C, 0xA9, 0xFC, 0x46, 0x2B, 0xA3, 0xFF, 0x00, 0x82, 0x7F, 0x7E, 0xD4, 0xCD, - 0xF0, 0xB3, 0xC5, 0x91, 0x5B, 0xDE, 0xC8, 0xCD, 0x63, 0x71, 0x88, 0x2E, 0x63, 0xCF, 0x54, 0x3D, - 0xFE, 0xA3, 0xAF, 0xE7, 0x5F, 0x96, 0xF0, 0x86, 0x2E, 0x19, 0x26, 0x75, 0x5F, 0x29, 0xCC, 0xF4, - 0xA5, 0x55, 0x4A, 0x9C, 0xBC, 0x93, 0xD2, 0xFF, 0x00, 0x23, 0xF5, 0x6E, 0x30, 0xC1, 0x4F, 0x3B, - 0xC9, 0x68, 0xE6, 0xB9, 0x5B, 0xBD, 0x5A, 0x4E, 0x35, 0x21, 0xE6, 0xD6, 0xB6, 0xF9, 0xAE, 0x87, - 0xEA, 0x37, 0xFC, 0x14, 0xC3, 0xF6, 0x41, 0xFE, 0xC6, 0xF1, 0x17, 0x89, 0xBC, 0x1F, 0xF2, 0xDC, - 0x40, 0xE7, 0xFB, 0x4F, 0x42, 0xBA, 0xE3, 0x6D, 0xD5, 0xBC, 0x9F, 0x3C, 0x6C, 0xA7, 0xA7, 0x2A, - 0x71, 0xED, 0x5F, 0x88, 0x7F, 0x1D, 0xFE, 0x16, 0xDD, 0x78, 0x13, 0xC5, 0x57, 0x51, 0xC9, 0x0B, - 0xC7, 0xB6, 0x42, 0x0A, 0xB0, 0xC1, 0x07, 0xB8, 0xAF, 0xE8, 0x9F, 0xE1, 0x97, 0xC4, 0x4D, 0x0F, - 0xF6, 0xCE, 0xFD, 0x9F, 0x34, 0xEF, 0x02, 0xEA, 0x7A, 0xA5, 0xAD, 0xBF, 0x8D, 0xFC, 0x2F, 0x6E, - 0xD3, 0x78, 0x33, 0x55, 0x99, 0xC0, 0x8F, 0x54, 0xB6, 0xC6, 0xE3, 0x61, 0x23, 0x7A, 0xE3, 0xEE, - 0x9F, 0x50, 0x2B, 0xF2, 0x53, 0xFE, 0x0A, 0x9F, 0xA5, 0xF8, 0x77, 0x44, 0xD4, 0x24, 0x4B, 0xC4, - 0x6D, 0x37, 0xC5, 0xD1, 0xCA, 0x61, 0xBB, 0xB0, 0x64, 0xC3, 0x36, 0x38, 0x2C, 0xC3, 0xF8, 0x48, - 0xF5, 0xEF, 0x5F, 0x4D, 0xE2, 0x36, 0x0E, 0x86, 0x3B, 0x03, 0xCF, 0x8A, 0x9A, 0x58, 0xAC, 0x3A, - 0x4A, 0x5D, 0x3D, 0xA4, 0x7E, 0xCC, 0xE3, 0xDF, 0x99, 0x6B, 0xE4, 0xEF, 0x1E, 0x8A, 0xFF, 0x00, - 0x37, 0xE1, 0xBE, 0x22, 0xBE, 0x07, 0x16, 0xE9, 0xE1, 0xA0, 0xDE, 0x16, 0xBB, 0x72, 0x8A, 0xEB, - 0x4E, 0x5F, 0x6A, 0x9C, 0xBB, 0x72, 0xBD, 0x3C, 0xD5, 0xA5, 0xD5, 0xDB, 0xE0, 0x0A, 0xFA, 0x8B, - 0xE1, 0xAD, 0x84, 0xBA, 0x37, 0x84, 0x34, 0xD8, 0x95, 0xA4, 0x8D, 0xA3, 0xB7, 0x5E, 0x01, 0xEF, - 0x8C, 0xD7, 0xCD, 0x16, 0x96, 0x9F, 0x6F, 0xD6, 0x60, 0x81, 0x57, 0x77, 0x9D, 0x32, 0xA0, 0x03, - 0xBE, 0x48, 0x15, 0xF5, 0xEE, 0x91, 0xA4, 0x79, 0x56, 0xB0, 0xC7, 0xB7, 0xEE, 0x20, 0x5C, 0x7D, - 0x05, 0x7F, 0x1F, 0xF1, 0x8D, 0x75, 0x18, 0x53, 0xA6, 0xFA, 0xDD, 0xFD, 0xD6, 0xFF, 0x00, 0x33, - 0xFB, 0x23, 0xC3, 0xBC, 0x2B, 0x9D, 0x5A, 0xB5, 0x57, 0x44, 0x97, 0xDF, 0x77, 0xFA, 0x1C, 0xAF, - 0xC4, 0x8B, 0x59, 0xB5, 0x6F, 0x09, 0x6A, 0x50, 0xB4, 0x92, 0x48, 0x64, 0x81, 0x80, 0xDC, 0xD9, - 0xC1, 0xC5, 0x7C, 0xC3, 0x5F, 0x5F, 0xEB, 0xDA, 0x57, 0x99, 0x63, 0x34, 0x78, 0xC8, 0x65, 0x23, - 0xF3, 0x15, 0xF2, 0x45, 0xF5, 0xA7, 0xF6, 0x7E, 0xA7, 0x35, 0xBB, 0x7F, 0xCB, 0x19, 0x1A, 0x33, - 0xF8, 0x1C, 0x55, 0x70, 0x7D, 0x65, 0x2A, 0x75, 0x20, 0xBB, 0xA6, 0x4F, 0x88, 0x58, 0x77, 0x0A, - 0xB4, 0xAA, 0x3E, 0xA9, 0xAF, 0xBA, 0xDF, 0xE6, 0x74, 0x9F, 0x0B, 0x7C, 0x05, 0x71, 0xE3, 0x1D, - 0x7A, 0x18, 0xE3, 0x8D, 0x99, 0x59, 0x86, 0x78, 0xAF, 0xD8, 0xEF, 0xF8, 0x27, 0x07, 0xEC, 0x89, - 0xA8, 0x2D, 0xB7, 0x87, 0xFC, 0x2F, 0x63, 0x6B, 0x9D, 0x7B, 0xC5, 0x57, 0x11, 0xC6, 0xCA, 0x46, - 0x3C, 0x88, 0xF8, 0xFB, 0xC7, 0xB0, 0x0B, 0x92, 0x73, 0xD2, 0xBE, 0x09, 0xFF, 0x00, 0x82, 0x67, - 0x43, 0xE1, 0x7D, 0x4F, 0xC6, 0xAA, 0xBA, 0x83, 0x0B, 0x8D, 0x62, 0x26, 0x5F, 0xB0, 0x58, 0xEC, - 0xCF, 0xDA, 0x18, 0xF7, 0x1F, 0xDE, 0x23, 0xB2, 0xD7, 0xED, 0xC6, 0x83, 0xAA, 0xDA, 0xFE, 0xC2, - 0x1F, 0x04, 0x6E, 0x2E, 0xE6, 0xB8, 0x87, 0xFE, 0x17, 0x0F, 0x8D, 0xAC, 0x4C, 0x69, 0x1A, 0xB0, - 0x27, 0xC2, 0x7A, 0x7B, 0x8F, 0x99, 0x98, 0xFF, 0x00, 0x0C, 0xEE, 0xBD, 0xBB, 0x7E, 0x15, 0xFD, - 0x69, 0xE1, 0x7E, 0x0E, 0x86, 0x0B, 0x0F, 0xF5, 0xDA, 0x0D, 0x4F, 0x15, 0x56, 0xF1, 0xA7, 0x1F, - 0xE5, 0x7D, 0x67, 0x2E, 0xD1, 0x8A, 0xD5, 0xF9, 0x69, 0xBC, 0x92, 0x3F, 0x90, 0x7C, 0x50, 0xC4, - 0x57, 0xC6, 0xD5, 0x58, 0x2A, 0xC9, 0xC3, 0x0B, 0x4E, 0xD2, 0xAB, 0x2E, 0xB2, 0x4B, 0x68, 0x47, - 0xBC, 0xA4, 0xF4, 0x5E, 0x7A, 0xEC, 0x99, 0xE4, 0xFF, 0x00, 0xF0, 0x56, 0xDF, 0xDA, 0x13, 0x48, - 0xD0, 0x35, 0x2D, 0x5A, 0xD7, 0x45, 0xB8, 0x56, 0xD0, 0x3C, 0x0B, 0xA5, 0xC5, 0xE1, 0x3D, 0x19, - 0x86, 0x14, 0x4E, 0x61, 0x52, 0xB2, 0x48, 0x31, 0xC6, 0x5A, 0x43, 0x2B, 0x64, 0x75, 0x00, 0x57, - 0xE0, 0xCF, 0xC4, 0x0D, 0x5E, 0x4F, 0x1C, 0x7C, 0x45, 0x93, 0x69, 0x69, 0x37, 0xCB, 0xD7, 0xD7, - 0x9A, 0xFA, 0xBF, 0xFE, 0x0A, 0x37, 0xFB, 0x58, 0x43, 0xE2, 0xDD, 0x45, 0xB4, 0x5D, 0x2E, 0xE3, - 0x7E, 0x9F, 0x62, 0x0A, 0x21, 0x07, 0xFD, 0x73, 0x9F, 0xBC, 0xFF, 0x00, 0x8E, 0x30, 0x3E, 0x9E, - 0xF5, 0xF3, 0xE7, 0xEC, 0xB1, 0xF0, 0x92, 0xF7, 0xE2, 0x87, 0xC4, 0x4B, 0x44, 0x8E, 0x16, 0x91, - 0xE7, 0x94, 0x05, 0xE3, 0xB9, 0x3F, 0xCB, 0xBD, 0x78, 0xFC, 0x79, 0x8E, 0x86, 0x61, 0x8F, 0xC3, - 0xE4, 0x59, 0x7B, 0x73, 0x85, 0x14, 0xA3, 0x7F, 0xE6, 0x97, 0xDA, 0x97, 0xAB, 0x7B, 0xF7, 0x3D, - 0x7E, 0x01, 0xCB, 0xEA, 0x65, 0xF8, 0x0C, 0x46, 0x79, 0x98, 0xA5, 0x0A, 0x95, 0x9B, 0x9B, 0x5F, - 0xCB, 0x1B, 0x7B, 0xB1, 0xF4, 0x8C, 0x74, 0xF2, 0xD8, 0xFD, 0x08, 0xFF, 0x00, 0x82, 0x75, 0x7C, - 0x39, 0x6F, 0x07, 0xFC, 0x20, 0x92, 0xF9, 0xA3, 0x2B, 0x26, 0xA0, 0xE1, 0x63, 0xE3, 0x92, 0xAB, - 0xC7, 0xFE, 0x85, 0x5F, 0x5A, 0x7F, 0xC1, 0x61, 0x7C, 0x5E, 0xBF, 0x06, 0x3F, 0x63, 0xAF, 0x86, - 0xFE, 0x05, 0x69, 0x91, 0x66, 0xD2, 0xFC, 0x30, 0x2F, 0xAE, 0x10, 0x70, 0x62, 0xB9, 0xD4, 0x25, - 0x79, 0xD9, 0x5B, 0xFD, 0xA1, 0x18, 0x4A, 0x93, 0xF6, 0x14, 0xFD, 0x9D, 0xAD, 0xFC, 0x6F, 0xF1, - 0x3F, 0xC2, 0x7E, 0x11, 0x55, 0xF2, 0xB4, 0x4D, 0x25, 0x45, 0xE6, 0xA9, 0x37, 0xDD, 0x58, 0x6D, - 0x60, 0x1B, 0xE5, 0x72, 0x7A, 0x64, 0x81, 0x5F, 0x1D, 0xFF, 0x00, 0xC1, 0x78, 0x7F, 0x6C, 0x25, - 0xF8, 0xCD, 0xF1, 0x93, 0x5E, 0xBA, 0xB7, 0x93, 0x6D, 0xBD, 0xD5, 0xCB, 0xC9, 0x0C, 0x43, 0x8F, - 0x2E, 0x10, 0x04, 0x76, 0xE9, 0x8F, 0xF6, 0x61, 0x44, 0xFC, 0xCD, 0x7E, 0xA9, 0xC4, 0x13, 0xA7, - 0x93, 0xE5, 0xB4, 0xF0, 0x69, 0xFF, 0x00, 0xBB, 0x52, 0x72, 0x7F, 0xF5, 0xF2, 0xA2, 0x74, 0xE2, - 0xBE, 0xE7, 0x55, 0xFA, 0x25, 0xDC, 0xFC, 0xA3, 0x86, 0xE3, 0x53, 0x3A, 0xCD, 0x6A, 0xE3, 0x9A, - 0xFF, 0x00, 0x79, 0xAB, 0x18, 0xAF, 0xFA, 0xF7, 0x49, 0xAA, 0x92, 0x7F, 0xF8, 0x12, 0xA5, 0x1F, - 0x56, 0xFB, 0x1F, 0x96, 0xFF, 0x00, 0x11, 0xB5, 0x2F, 0xED, 0x3F, 0x14, 0x5D, 0x49, 0x9C, 0xE5, - 0xCD, 0x60, 0xD4, 0xD7, 0xF7, 0x06, 0xEA, 0xEE, 0x49, 0x09, 0xCE, 0xE3, 0x9A, 0x86, 0xBF, 0x91, - 0x6B, 0x54, 0xE7, 0x9B, 0x9F, 0x76, 0x7F, 0x5A, 0xD3, 0x8F, 0x2C, 0x54, 0x42, 0x8A, 0x28, 0xAC, - 0xCB, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, - 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, - 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x3D, 0x03, 0xE0, 0x17, 0xC4, 0x69, 0xBC, - 0x07, 0xE3, 0x0B, 0x59, 0xE3, 0x95, 0xA2, 0x31, 0xC8, 0xAC, 0x18, 0x1F, 0xBA, 0x41, 0xC8, 0x35, - 0xFD, 0x07, 0x7E, 0xC0, 0xDF, 0x15, 0xEC, 0x7F, 0xE0, 0xA0, 0x1F, 0xB1, 0xC4, 0xDF, 0x0E, 0x5A, - 0x68, 0x4F, 0x8C, 0x7C, 0x3F, 0xBF, 0x57, 0xF0, 0xB1, 0x67, 0x00, 0xC9, 0x30, 0x19, 0xB8, 0xB2, - 0xCF, 0xA3, 0x8C, 0x95, 0xC9, 0xC7, 0x4A, 0xFE, 0x6D, 0x6D, 0xE7, 0x6B, 0x79, 0x95, 0x94, 0xF2, - 0xA6, 0xBE, 0xCE, 0xFF, 0x00, 0x82, 0x6D, 0x7E, 0xDC, 0xBA, 0x9F, 0xEC, 0xFB, 0xF1, 0x17, 0x4C, - 0xBC, 0xB7, 0xBE, 0x9A, 0xD5, 0xAD, 0x67, 0x49, 0x15, 0xE3, 0x6C, 0x34, 0x6C, 0xA4, 0x15, 0x75, - 0xF7, 0x07, 0xF3, 0xE9, 0x5F, 0xA7, 0xF8, 0x7B, 0xC4, 0x9F, 0x56, 0xAB, 0xF5, 0x1A, 0xB3, 0xE5, - 0x52, 0x6A, 0x50, 0x93, 0xDA, 0x33, 0x8F, 0xC2, 0xDF, 0x93, 0xBB, 0x8C, 0x97, 0x58, 0xC9, 0xA3, - 0xF3, 0x7E, 0x3F, 0xE1, 0xBF, 0xAE, 0x51, 0xFA, 0xDD, 0x38, 0x73, 0xB8, 0xA7, 0x19, 0x45, 0x6F, - 0x3A, 0x72, 0xF8, 0xA2, 0x9F, 0xF3, 0x68, 0xA5, 0x07, 0xD2, 0x71, 0x8B, 0xEE, 0x7D, 0x85, 0xFB, - 0x69, 0x7E, 0xCD, 0xAB, 0xF1, 0x93, 0xC1, 0x57, 0x1B, 0x6D, 0x9A, 0x3D, 0x73, 0x4B, 0x0C, 0x86, - 0x27, 0x4D, 0xAE, 0xC0, 0x67, 0x74, 0x64, 0x1E, 0x43, 0x03, 0x9E, 0x3E, 0xB5, 0xF9, 0x51, 0xE3, - 0xAF, 0x0A, 0x5F, 0x7C, 0x2D, 0xF1, 0x64, 0x98, 0x59, 0x22, 0x31, 0xC8, 0x7A, 0x8C, 0x11, 0x83, - 0xDE, 0xBF, 0xA3, 0x7F, 0x8B, 0xDA, 0x56, 0x8B, 0xFB, 0x69, 0x7C, 0x27, 0x6F, 0x8D, 0x1F, 0x0F, - 0x63, 0xB7, 0x6D, 0x66, 0x38, 0x56, 0x4F, 0x18, 0xE8, 0x76, 0x83, 0x2C, 0x92, 0x11, 0xFF, 0x00, - 0x1F, 0xF0, 0xA0, 0xE4, 0xC6, 0xFD, 0x58, 0x76, 0x39, 0xEF, 0x9C, 0x7E, 0x3D, 0xFF, 0x00, 0xC1, - 0x52, 0x7C, 0x2D, 0xE1, 0x6D, 0x35, 0xED, 0xF5, 0x3B, 0x3B, 0x88, 0x61, 0xD6, 0x2F, 0x98, 0x99, - 0xAC, 0xD0, 0x67, 0x78, 0xC7, 0xFA, 0xDF, 0xF6, 0x7D, 0x39, 0xEB, 0xF8, 0x57, 0xE8, 0x3E, 0x25, - 0x65, 0xB8, 0x4C, 0xCF, 0x07, 0xFD, 0xB5, 0x46, 0xD4, 0xEB, 0xD3, 0xB2, 0xA9, 0x16, 0xD5, 0xD3, - 0xD3, 0xEF, 0xEF, 0x16, 0xB4, 0x94, 0x5A, 0x68, 0xF8, 0x1F, 0x0C, 0x71, 0xD8, 0xDC, 0xB3, 0x13, - 0xFD, 0x8B, 0x5A, 0xF5, 0x28, 0x4F, 0x5A, 0x73, 0x4B, 0x47, 0x1F, 0x35, 0xD3, 0xB3, 0x4F, 0x58, - 0xCA, 0xF1, 0x67, 0x91, 0xFC, 0x2F, 0xFF, 0x00, 0x82, 0x82, 0x78, 0x93, 0xE1, 0x87, 0x85, 0xE4, - 0xB1, 0xB7, 0x91, 0xA6, 0x45, 0x1B, 0xED, 0x8B, 0x48, 0xCA, 0xD6, 0x92, 0x8E, 0x8E, 0x8C, 0x39, - 0x18, 0x3C, 0xE3, 0xF9, 0x57, 0x09, 0xFB, 0x40, 0x7E, 0xD6, 0x3A, 0xDF, 0xED, 0x41, 0x24, 0x37, - 0xBE, 0x33, 0x11, 0x6A, 0x1E, 0x20, 0xB5, 0x41, 0x12, 0xEA, 0xF1, 0xAF, 0x97, 0x3D, 0xCA, 0x0E, - 0x82, 0x50, 0x38, 0x6C, 0x74, 0x07, 0xAD, 0x79, 0x44, 0x87, 0xE7, 0x6A, 0x6D, 0x7E, 0x07, 0x8E, - 0xCE, 0x31, 0x78, 0xB8, 0x42, 0x9D, 0x79, 0xB9, 0x28, 0x2B, 0x2B, 0xF4, 0x5D, 0x8F, 0xDD, 0xF0, - 0x99, 0x6E, 0x1B, 0x0D, 0x29, 0x4E, 0x8C, 0x14, 0x5C, 0x9D, 0xDD, 0xBA, 0xB3, 0xA4, 0xF8, 0x49, - 0xA2, 0xC9, 0xAF, 0xFC, 0x4B, 0xD1, 0xED, 0xE3, 0x8D, 0xA6, 0xFF, 0x00, 0x49, 0x59, 0x18, 0x0E, - 0xCA, 0xBF, 0x31, 0x3F, 0x86, 0x2B, 0xEC, 0xAD, 0x2F, 0x49, 0xDC, 0x3A, 0x7E, 0x9D, 0x6B, 0xE5, - 0x9F, 0xD9, 0x47, 0x4B, 0x7D, 0x4F, 0xE3, 0x15, 0xA1, 0x50, 0xDB, 0x6D, 0xE1, 0x92, 0x56, 0xC1, - 0xC7, 0x18, 0xDB, 0xFC, 0xD8, 0x57, 0xD9, 0xFA, 0x26, 0x8F, 0x91, 0xF7, 0x7B, 0x57, 0xE2, 0x7C, - 0x7D, 0x8C, 0xE5, 0xC5, 0x42, 0x9F, 0x68, 0xFE, 0x6D, 0xFF, 0x00, 0xC0, 0x3F, 0xA1, 0xFC, 0x29, - 0xCB, 0x9C, 0xF0, 0x35, 0x2A, 0xB5, 0xF1, 0x4E, 0xDF, 0x24, 0x97, 0xEA, 0xD9, 0xC5, 0xEB, 0x7A, - 0x59, 0x08, 0xD9, 0x1D, 0xAB, 0xE3, 0xCF, 0x89, 0xBA, 0x33, 0xE8, 0xFE, 0x3F, 0xD5, 0x61, 0x65, - 0xDB, 0xFE, 0x90, 0xD2, 0x0C, 0xF1, 0x90, 0xDF, 0x30, 0xFE, 0x75, 0xF7, 0x4F, 0x88, 0xF4, 0x9D, - 0xA1, 0xBE, 0x5A, 0xF8, 0xDF, 0xF6, 0x9A, 0xD3, 0x9F, 0x4E, 0xF8, 0xB1, 0x75, 0xBB, 0x76, 0xD9, - 0xA2, 0x8E, 0x44, 0x07, 0xB0, 0xC1, 0x1F, 0xCC, 0x1A, 0xDB, 0x80, 0xF1, 0x9C, 0xD8, 0x89, 0xD3, - 0xEF, 0x1F, 0xC9, 0xA3, 0x0F, 0x14, 0xB2, 0xF7, 0x0C, 0x25, 0x3A, 0xA9, 0x7C, 0x32, 0xFC, 0x1A, - 0x7F, 0xAA, 0x46, 0xA7, 0xEC, 0xCD, 0xFB, 0x49, 0xDC, 0x7E, 0xCB, 0x3E, 0x28, 0xFF, 0x00, 0x84, - 0x93, 0x41, 0xB1, 0xB4, 0xB9, 0xF1, 0x54, 0x20, 0x8B, 0x0B, 0xDB, 0xA5, 0xF3, 0x23, 0xD3, 0x58, - 0xF1, 0xE6, 0x22, 0x74, 0x2E, 0x3B, 0x13, 0xC0, 0x3F, 0x95, 0x7B, 0x4F, 0xC4, 0xCF, 0xF8, 0x2A, - 0x0F, 0x8A, 0xBE, 0x26, 0xF8, 0x2D, 0xAD, 0x75, 0x0B, 0x87, 0x6D, 0x52, 0xF4, 0x16, 0xD4, 0xAF, - 0xCC, 0xA5, 0xA6, 0xBF, 0x6F, 0x73, 0xFC, 0x23, 0xD8, 0x7E, 0x83, 0x8A, 0xF9, 0x06, 0x9D, 0xBC, - 0xE6, 0xBF, 0x69, 0xCB, 0x33, 0xCC, 0x66, 0x5E, 0xA6, 0xB0, 0xB3, 0xE5, 0xE7, 0x56, 0x76, 0xED, - 0xDA, 0xE7, 0xF3, 0xDE, 0x61, 0x94, 0x61, 0x71, 0xAE, 0x0F, 0x13, 0x1E, 0x6E, 0x57, 0x75, 0x7E, - 0xFD, 0xEC, 0x76, 0x76, 0x51, 0x5F, 0xFC, 0x51, 0xF1, 0x42, 0xEE, 0xDC, 0xFE, 0x63, 0x7D, 0x78, - 0xAF, 0xD3, 0x1F, 0xD8, 0x1F, 0xF6, 0x65, 0x87, 0xE1, 0x57, 0x84, 0xE2, 0xD7, 0xAF, 0xE0, 0xD9, - 0x7D, 0x75, 0x17, 0xFA, 0x3A, 0x30, 0xE6, 0x34, 0x23, 0x97, 0x3E, 0xE7, 0xA7, 0xD3, 0x35, 0xF1, - 0x1F, 0xEC, 0x06, 0xFA, 0x0D, 0xDF, 0xC4, 0xC8, 0x57, 0x57, 0x74, 0x69, 0x20, 0xC3, 0xC1, 0x6E, - 0xC3, 0x89, 0xD8, 0x7A, 0x9F, 0x6E, 0xB8, 0xEF, 0x5F, 0xB9, 0x3F, 0xB2, 0x37, 0xC3, 0xCD, 0x03, - 0xC1, 0x9F, 0x0F, 0x47, 0xC6, 0x4F, 0x89, 0x11, 0xC7, 0x1F, 0x83, 0xF4, 0x53, 0xBB, 0x45, 0xD2, - 0xA4, 0x21, 0x1F, 0xC4, 0x57, 0x88, 0x32, 0x88, 0x01, 0xE9, 0x02, 0x11, 0xB9, 0x9B, 0x18, 0xC2, - 0xFA, 0x66, 0xBF, 0x63, 0xF0, 0xB7, 0x0F, 0x81, 0xC3, 0xC6, 0x59, 0xCE, 0x2F, 0xDF, 0xAB, 0x7B, - 0x42, 0x2B, 0x56, 0xE4, 0xF6, 0x4B, 0xCD, 0xF4, 0xE8, 0xB5, 0x93, 0xB2, 0x57, 0x3F, 0x22, 0xF1, - 0x4A, 0xA6, 0x3F, 0x15, 0x08, 0xE4, 0xF8, 0x4F, 0xDD, 0xD3, 0x6A, 0xF3, 0x9B, 0xD1, 0x28, 0x2D, - 0xDB, 0xF2, 0x5D, 0x7A, 0xBF, 0x85, 0x5D, 0xB3, 0x5B, 0xE2, 0xBF, 0x8B, 0x6D, 0x7F, 0xE0, 0x9F, - 0xBF, 0xB1, 0x06, 0xA5, 0x71, 0xAA, 0x38, 0xB5, 0xF1, 0xE7, 0xC5, 0x0B, 0x31, 0x2D, 0xC4, 0x4D, - 0xF2, 0xCD, 0xA5, 0xE9, 0x1D, 0x52, 0x33, 0xDD, 0x5E, 0x63, 0xDB, 0x82, 0x01, 0xAF, 0xE7, 0xEB, - 0xF6, 0xB4, 0xF8, 0xCB, 0x71, 0xF1, 0x3B, 0xC7, 0x97, 0xD7, 0x12, 0x4B, 0xB9, 0xA6, 0x94, 0xBB, - 0x60, 0xF0, 0x3D, 0x87, 0xB0, 0xE8, 0x3E, 0x95, 0xF5, 0x97, 0xFC, 0x15, 0xAB, 0xFE, 0x0A, 0x39, - 0xAA, 0x7E, 0xD2, 0xFF, 0x00, 0x13, 0x35, 0x6B, 0xC9, 0xAF, 0xBC, 0xEF, 0xB5, 0x4C, 0xCD, 0xF2, - 0x70, 0x9E, 0x8A, 0x14, 0x76, 0x45, 0x1C, 0x28, 0xF4, 0x15, 0xF9, 0xE1, 0xA8, 0xDF, 0x3E, 0xA3, - 0x76, 0xF2, 0xB9, 0xCB, 0x31, 0xCF, 0x35, 0xE0, 0xF8, 0x8D, 0xC4, 0xD2, 0xAF, 0x27, 0x81, 0x8C, - 0x94, 0xA4, 0xE4, 0xE5, 0x51, 0xAD, 0x9C, 0xDA, 0x4A, 0xCB, 0xFB, 0xB0, 0x8A, 0x51, 0x8F, 0x74, - 0x9C, 0xB7, 0x93, 0x3D, 0xBF, 0x0E, 0xF8, 0x66, 0x38, 0x5A, 0x6B, 0x16, 0xE0, 0xE3, 0x15, 0x15, - 0x1A, 0x71, 0x7B, 0xA8, 0x27, 0x7B, 0xCB, 0xFB, 0xF3, 0x93, 0x73, 0x97, 0x66, 0xD4, 0x76, 0x8A, - 0x20, 0xA2, 0x8A, 0x2B, 0xF2, 0x33, 0xF5, 0x50, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xAB, 0x9A, 0x2E, 0xB3, 0x36, 0x89, 0x7A, 0x93, 0x44, 0xCC, 0xAC, 0xA7, - 0x23, 0x06, 0xA9, 0xD1, 0x55, 0x19, 0x38, 0xBB, 0xA1, 0x4A, 0x29, 0xAB, 0x33, 0xED, 0x0F, 0xD8, - 0xA3, 0xFE, 0x0A, 0xDF, 0xE3, 0x7F, 0xD8, 0xCB, 0x54, 0x8F, 0x51, 0xF0, 0xFD, 0xD0, 0x92, 0x48, - 0x51, 0x97, 0xEC, 0xF3, 0x31, 0x30, 0xC8, 0x0F, 0x55, 0x71, 0xFC, 0x48, 0xDD, 0xD7, 0xA1, 0xEB, - 0xD6, 0xB9, 0xBF, 0xDB, 0xEF, 0xE3, 0x67, 0x81, 0x7F, 0x6D, 0x0D, 0x6D, 0xFE, 0x24, 0x78, 0x3A, - 0xDA, 0x4F, 0x09, 0x78, 0x93, 0x50, 0x01, 0xBC, 0x45, 0xE1, 0x89, 0x24, 0xDD, 0x6C, 0xB3, 0xF7, - 0x9E, 0xD0, 0x9E, 0x91, 0xB7, 0x74, 0xED, 0xC7, 0xBD, 0x7C, 0xA6, 0x0E, 0x29, 0x55, 0x8A, 0xB6, - 0x41, 0xC1, 0xF5, 0xAF, 0x53, 0x1B, 0x9E, 0x62, 0xF1, 0x54, 0xE3, 0x4A, 0xBC, 0xAE, 0xA2, 0xAC, - 0xBD, 0x3A, 0x27, 0xDD, 0x2E, 0x97, 0xDA, 0xEE, 0xDB, 0x9E, 0x76, 0x13, 0x29, 0xC3, 0x61, 0xA7, - 0x2A, 0x94, 0x63, 0x67, 0x27, 0x77, 0xEB, 0xD5, 0xF9, 0x37, 0xD6, 0xDB, 0xD9, 0x5F, 0x61, 0x08, - 0xDA, 0x68, 0xA1, 0x9B, 0x71, 0xA2, 0xBC, 0x93, 0xD2, 0x3D, 0xDF, 0xF6, 0x10, 0xF0, 0xF9, 0xD4, - 0x7C, 0x6D, 0xAB, 0x5E, 0x6D, 0xCA, 0xDB, 0x5B, 0x2C, 0x5B, 0xBB, 0x82, 0xED, 0x9F, 0xFD, 0x92, - 0xBE, 0xCF, 0xF0, 0xD6, 0x85, 0xB9, 0x47, 0x1D, 0xBF, 0x3A, 0xF9, 0xC7, 0xFE, 0x09, 0xE7, 0xE0, - 0xC9, 0xA2, 0xF0, 0x8E, 0xA9, 0xAA, 0x4B, 0x01, 0x8E, 0x3D, 0x42, 0xE9, 0x52, 0x17, 0x23, 0xFD, - 0x62, 0xC6, 0x0E, 0x7F, 0x00, 0x58, 0xFE, 0x39, 0xAF, 0xB1, 0x3C, 0x21, 0xE1, 0xEF, 0x31, 0x07, - 0xCB, 0xDA, 0xBF, 0x9C, 0xBC, 0x46, 0xCC, 0x97, 0xF6, 0xA5, 0x54, 0x9F, 0xC3, 0x65, 0xF7, 0x25, - 0xFA, 0xDC, 0xFE, 0xBE, 0xF0, 0x93, 0x29, 0xFF, 0x00, 0x84, 0x3A, 0x12, 0x6B, 0x59, 0x5D, 0xFD, - 0xF2, 0x76, 0xFC, 0x2C, 0x79, 0xAF, 0x8B, 0x74, 0x6D, 0x9B, 0xBE, 0x5A, 0xF8, 0xD3, 0xF6, 0xD3, - 0xD1, 0x3E, 0xC1, 0xE3, 0x9D, 0x36, 0xEB, 0x1F, 0x2D, 0xC5, 0xB1, 0x8B, 0xFE, 0xF8, 0x62, 0x7F, - 0xF6, 0x7A, 0xFB, 0xEF, 0xC7, 0x7A, 0x0F, 0x96, 0x24, 0xF9, 0x7D, 0x6B, 0xE3, 0x5F, 0xDB, 0xCF, - 0x40, 0x6F, 0xEC, 0xBD, 0x1E, 0xF4, 0x03, 0xB6, 0xDE, 0x79, 0x22, 0x27, 0xD3, 0x78, 0x07, 0xFF, - 0x00, 0x64, 0xAE, 0xCF, 0x0E, 0xF3, 0x0B, 0xE6, 0x14, 0xD5, 0xF7, 0xBA, 0xFC, 0x1F, 0xEA, 0x79, - 0xFE, 0x2D, 0x65, 0x76, 0xC9, 0xEB, 0x4A, 0xDF, 0x0F, 0x2B, 0xFF, 0x00, 0xC9, 0x97, 0xE9, 0x73, - 0xE6, 0x7A, 0x28, 0x14, 0xE6, 0xDA, 0x07, 0x1F, 0x9D, 0x7F, 0x40, 0x9F, 0xCA, 0x27, 0xA5, 0x7E, - 0xCD, 0x7A, 0xDF, 0x86, 0x7E, 0x1E, 0x78, 0xDE, 0xCF, 0xC4, 0xFE, 0x2D, 0x17, 0x17, 0x9A, 0x7E, - 0x93, 0x20, 0x9E, 0x1D, 0x2E, 0xDD, 0xF6, 0xC9, 0xA8, 0xC8, 0xBC, 0xAA, 0x33, 0x7F, 0x0A, 0x67, - 0xAF, 0xA8, 0xE2, 0xBE, 0x92, 0xFD, 0xA8, 0xBF, 0xE0, 0xB0, 0xBE, 0x34, 0xFD, 0xA8, 0x74, 0x0B, - 0x5B, 0x4D, 0x41, 0x60, 0xD2, 0x6C, 0x6C, 0x60, 0xFB, 0x35, 0xB5, 0x85, 0x9B, 0x15, 0x82, 0xDE, - 0x11, 0xF7, 0x63, 0x51, 0xD8, 0x60, 0x0C, 0xFF, 0x00, 0x78, 0x8E, 0x6B, 0xE2, 0x12, 0x73, 0x4B, - 0xB8, 0xE2, 0xBD, 0x3C, 0xBF, 0x36, 0xC4, 0xE0, 0xB9, 0x9E, 0x1E, 0x56, 0x6D, 0x35, 0xF2, 0x7B, - 0xDB, 0xB5, 0xF6, 0x76, 0xDD, 0x69, 0xB1, 0xC1, 0x8E, 0xCB, 0x70, 0xF8, 0xB4, 0x95, 0x78, 0xDD, - 0x26, 0x9F, 0xCD, 0x6D, 0x7E, 0xF6, 0xDD, 0x5F, 0x67, 0xAE, 0xE6, 0xA7, 0x8A, 0x7C, 0x51, 0x71, - 0xE2, 0x6D, 0x45, 0xE6, 0x9A, 0x46, 0x6D, 0xC4, 0xF5, 0x35, 0x95, 0x45, 0x15, 0xE7, 0x4E, 0x72, - 0x9C, 0xB9, 0xA5, 0xB9, 0xDB, 0x18, 0xA8, 0xAE, 0x54, 0x14, 0x51, 0x45, 0x49, 0x41, 0x45, 0x14, - 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, - 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, - 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, - 0x50, 0x01, 0x45, 0x14, 0x01, 0x93, 0x40, 0x1F, 0xA0, 0x1F, 0xB0, 0x2E, 0x87, 0x38, 0xF8, 0x13, - 0xA3, 0xB4, 0xCC, 0x5C, 0x4D, 0x24, 0xAF, 0x10, 0xFE, 0xEA, 0x6F, 0x23, 0x1F, 0x98, 0x27, 0xF1, - 0xAF, 0xAD, 0xBC, 0x17, 0xA1, 0x2C, 0x16, 0xCA, 0xCE, 0x3E, 0x50, 0x2B, 0xC5, 0xFF, 0x00, 0x63, - 0xFF, 0x00, 0x06, 0x36, 0x89, 0xF0, 0x7B, 0xC2, 0xF6, 0xAC, 0x9B, 0x58, 0x58, 0xC7, 0x23, 0x0C, - 0x77, 0x6F, 0x9F, 0xFF, 0x00, 0x66, 0xAF, 0xA3, 0xB4, 0x2D, 0x1F, 0xED, 0x2C, 0xB6, 0xEB, 0xF7, - 0x54, 0x65, 0x88, 0xAF, 0xE2, 0xFE, 0x36, 0xCC, 0x9E, 0x27, 0x33, 0xAC, 0xE2, 0xF7, 0x9C, 0xBE, - 0xEB, 0xE9, 0xF8, 0x1F, 0xE8, 0x97, 0x87, 0x39, 0x3C, 0x70, 0xB9, 0x3E, 0x1E, 0x32, 0x56, 0xE5, - 0xA7, 0x1B, 0xFA, 0xD9, 0x5F, 0xF1, 0x3C, 0xEB, 0xE2, 0x4E, 0x81, 0xE5, 0x09, 0x08, 0x1C, 0x76, - 0x22, 0xBE, 0x3C, 0xFD, 0xB7, 0xBC, 0x22, 0xDA, 0xBF, 0xC3, 0x3B, 0xC6, 0x56, 0xDA, 0xD6, 0x32, - 0xAD, 0xC8, 0x18, 0xFB, 0xD8, 0xC8, 0x23, 0xFF, 0x00, 0x1E, 0xCF, 0xE1, 0x5F, 0x77, 0xF8, 0xE3, - 0x49, 0xF3, 0xF4, 0xE9, 0x15, 0x95, 0xB7, 0x45, 0xC1, 0xFA, 0x57, 0xCA, 0x3F, 0xB5, 0x1E, 0x82, - 0x2E, 0xFC, 0x15, 0xAC, 0x42, 0xCB, 0xFE, 0xB2, 0xD6, 0x5C, 0x7D, 0x76, 0x9C, 0x57, 0xA9, 0xC0, - 0x39, 0x84, 0xA9, 0xE3, 0x69, 0x3E, 0xAA, 0x4B, 0xF3, 0x3C, 0x9F, 0x13, 0x32, 0x98, 0xD6, 0xCB, - 0xEB, 0xD3, 0x6B, 0x49, 0x45, 0xFE, 0x5A, 0x1F, 0x9D, 0x54, 0x53, 0x9D, 0x4C, 0x6D, 0x83, 0xF4, - 0xA6, 0xD7, 0xF5, 0xE1, 0xFC, 0x06, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, - 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, - 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, - 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, - 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x15, 0x35, 0x9C, 0x82, 0x29, 0x95, 0x8E, 0x3E, 0x52, 0x0F, - 0x35, 0x0D, 0x14, 0xD3, 0xB3, 0x03, 0xEF, 0x2F, 0xD9, 0x77, 0xFE, 0x0A, 0x57, 0xE1, 0x5D, 0x17, - 0x49, 0xB2, 0xD3, 0x7C, 0x61, 0xA7, 0xDD, 0x69, 0x53, 0xDA, 0xC4, 0x90, 0xAD, 0xDD, 0x92, 0x79, - 0xD0, 0x48, 0x14, 0x60, 0x12, 0x84, 0x86, 0x4E, 0x00, 0xCE, 0x0B, 0x7D, 0x05, 0x7D, 0x7B, 0xF0, - 0x9F, 0xF6, 0xDD, 0xF8, 0x2F, 0x7F, 0x10, 0xB8, 0x9B, 0xE2, 0x16, 0x87, 0x6F, 0xBB, 0x24, 0x89, - 0xFC, 0xC4, 0x61, 0xF8, 0x14, 0xAF, 0xC5, 0x25, 0x95, 0x97, 0xBD, 0x06, 0x62, 0x7F, 0xBB, 0xF9, - 0x57, 0xE6, 0xB9, 0x97, 0x84, 0xDC, 0x3F, 0x8C, 0xC5, 0x7D, 0x69, 0x73, 0xD3, 0x6F, 0x56, 0xA3, - 0x25, 0x6B, 0xFF, 0x00, 0xDB, 0xD1, 0x95, 0xBD, 0x13, 0xB7, 0x6B, 0x2D, 0x0F, 0xD9, 0x32, 0x9F, - 0x1D, 0xB8, 0x9B, 0x03, 0x82, 0xFA, 0x93, 0xF6, 0x75, 0x12, 0xB2, 0x52, 0x9C, 0x5F, 0x35, 0x97, - 0xF8, 0x25, 0x14, 0xFD, 0x5A, 0xBE, 0x9A, 0xDD, 0xDD, 0x9F, 0xB3, 0x1E, 0x3E, 0xFD, 0xB1, 0x7E, - 0x11, 0xA5, 0xCD, 0xC3, 0xC7, 0xE3, 0xDD, 0x06, 0x58, 0x5D, 0x4F, 0x31, 0xB4, 0x8D, 0xFF, 0x00, - 0xB2, 0xD7, 0xC7, 0x7F, 0xB5, 0x1F, 0xED, 0x9D, 0xE1, 0x0D, 0x42, 0xD2, 0xE6, 0xD7, 0xC3, 0x73, - 0xC9, 0xAD, 0x5C, 0x4A, 0x0A, 0xAC, 0x82, 0x32, 0x90, 0x2E, 0x7B, 0x92, 0xDC, 0x9F, 0xA6, 0x2B, - 0xE2, 0xB1, 0x31, 0x07, 0xF8, 0x7F, 0x2A, 0x1A, 0x56, 0x61, 0xD6, 0x96, 0x53, 0xE1, 0x4E, 0x49, - 0x97, 0xE2, 0x56, 0x26, 0x9C, 0xEA, 0x4B, 0xAD, 0xA5, 0x28, 0xDB, 0xFF, 0x00, 0x25, 0x8C, 0x5F, - 0xE2, 0x56, 0x75, 0xE3, 0xA7, 0x10, 0xE6, 0x58, 0x47, 0x85, 0xA9, 0x4E, 0x94, 0x34, 0xB7, 0x34, - 0x63, 0x2B, 0xFF, 0x00, 0xE4, 0xD3, 0x92, 0xFC, 0x09, 0xB5, 0x09, 0x84, 0xF3, 0xB3, 0x77, 0x62, - 0x49, 0xC5, 0x57, 0xA2, 0x8A, 0xFD, 0x2E, 0x4E, 0xEE, 0xE7, 0xE3, 0x21, 0x45, 0x14, 0x52, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x03, 0xFF, 0xD9, -} ; - -const char sysinfo_jpg[] PROGMEM = -{ - 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x00, 0x60, - 0x00, 0x60, 0x00, 0x00, 0xFF, 0xE1, 0x00, 0x22, 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x4D, 0x4D, - 0x00, 0x2A, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x02, 0x01, 0x01, - 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x05, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x06, 0x04, 0x04, 0x03, 0x05, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x08, - 0x09, 0x0B, 0x09, 0x08, 0x08, 0x0A, 0x08, 0x07, 0x07, 0x0A, 0x0D, 0x0A, 0x0A, 0x0B, 0x0C, 0x0C, - 0x0C, 0x0C, 0x07, 0x09, 0x0E, 0x0F, 0x0D, 0x0C, 0x0E, 0x0B, 0x0C, 0x0C, 0x0C, 0xFF, 0xDB, 0x00, - 0x43, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x06, 0x03, 0x03, 0x06, 0x0C, 0x08, 0x07, 0x08, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0xB5, 0x01, 0x40, 0x03, 0x01, 0x22, 0x00, 0x02, - 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, - 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, - 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, - 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, - 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, - 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, - 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, - 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, - 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, - 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, - 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, - 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xC4, 0x00, - 0x1F, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, - 0x00, 0xB5, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, - 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, - 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, - 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, - 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, - 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, - 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, - 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, - 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, - 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, - 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, - 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, - 0x00, 0x3F, 0x00, 0xFE, 0x7F, 0xE8, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0xA6, - 0xB6, 0xB3, 0x96, 0xFA, 0x5D, 0x90, 0xC6, 0xF2, 0xB1, 0xEC, 0x8A, 0x49, 0xA0, 0x0F, 0x6E, 0xFD, - 0x9B, 0xBF, 0x67, 0x6B, 0x7F, 0x8B, 0x1F, 0xB3, 0x57, 0xC6, 0xEF, 0x13, 0xC9, 0xE0, 0xFF, 0x00, - 0x1B, 0x6B, 0xF7, 0xFE, 0x0B, 0xD2, 0x2C, 0xAE, 0x74, 0xAD, 0x4B, 0x47, 0xB7, 0x69, 0x34, 0xED, - 0x1A, 0x53, 0x76, 0x8D, 0x71, 0x25, 0xFB, 0x01, 0xF2, 0xC6, 0x6C, 0xE3, 0xB9, 0x2A, 0x4F, 0x1B, - 0x94, 0x57, 0xB6, 0x7C, 0x3F, 0xFF, 0x00, 0x82, 0x76, 0x68, 0xFA, 0xC7, 0xC7, 0xBF, 0xD9, 0x97, - 0xC3, 0xFA, 0x97, 0x80, 0x7E, 0x2A, 0x8D, 0x33, 0xE2, 0xB7, 0x85, 0x9B, 0x52, 0xD6, 0x6D, 0xE2, - 0xB1, 0x7F, 0xB6, 0x6A, 0x37, 0x3B, 0xAE, 0x71, 0x35, 0x90, 0xC7, 0x36, 0xE1, 0x0D, 0x9B, 0x16, - 0xE9, 0xB5, 0x98, 0xD7, 0x8E, 0xFC, 0x00, 0xFD, 0xAF, 0x3C, 0x6D, 0xFB, 0x39, 0x7E, 0xCC, 0x7F, - 0x13, 0xBC, 0x03, 0xE1, 0xCB, 0x89, 0x6D, 0xAC, 0x7E, 0x2A, 0x08, 0x6C, 0x35, 0x9B, 0x70, 0xAD, - 0xFE, 0x91, 0x6A, 0xB0, 0xCE, 0x87, 0x91, 0xC7, 0x59, 0x71, 0x83, 0xEB, 0x5F, 0x4B, 0x78, 0x2F, - 0xFE, 0x0A, 0xCB, 0xE2, 0x4D, 0x2B, 0xF6, 0x8D, 0xFD, 0x98, 0xFC, 0x55, 0x71, 0xE2, 0x07, 0x8A, - 0x3F, 0x84, 0x5E, 0x11, 0xFF, 0x00, 0x84, 0x6E, 0x79, 0xFE, 0xCD, 0x39, 0x16, 0x11, 0x98, 0xCC, - 0x46, 0x32, 0x02, 0xE5, 0xBE, 0x48, 0xE2, 0xFB, 0x80, 0x8A, 0x00, 0xF0, 0xFD, 0x23, 0xF6, 0x38, - 0xD1, 0xA1, 0xFF, 0x00, 0x82, 0x7C, 0x7C, 0x4B, 0xF8, 0x81, 0xA9, 0x78, 0x77, 0xC7, 0xB1, 0x78, - 0xBF, 0xC2, 0xFE, 0x3E, 0xB2, 0xF0, 0xE6, 0x9B, 0x7A, 0x96, 0xA4, 0x68, 0xBF, 0x67, 0x71, 0x22, - 0x4F, 0x0C, 0x8F, 0x8F, 0x9A, 0xE0, 0x48, 0x14, 0x6D, 0x04, 0x63, 0x2B, 0xEB, 0x8A, 0xF6, 0x5D, - 0x4B, 0xFE, 0x09, 0x7B, 0xE1, 0xB1, 0xFB, 0x69, 0xEA, 0xBE, 0x05, 0xB2, 0xF0, 0x0F, 0xC6, 0x09, - 0xB4, 0x58, 0x7E, 0x12, 0x4B, 0xE2, 0xDB, 0x6B, 0x19, 0x2C, 0x9C, 0x6A, 0x43, 0x54, 0xFE, 0xCD, - 0x2F, 0x1C, 0x8C, 0x02, 0x7F, 0xC7, 0xB7, 0xDB, 0x36, 0xAE, 0x3D, 0x0E, 0x33, 0x5E, 0x7D, 0xA8, - 0xFE, 0xDF, 0x1A, 0xD6, 0xA5, 0xFB, 0x01, 0x78, 0xDB, 0xE1, 0x5C, 0xBA, 0xB4, 0x84, 0x6B, 0xBF, - 0x12, 0x07, 0x8A, 0xE1, 0xB2, 0xF2, 0x25, 0xCC, 0xA8, 0x5D, 0x65, 0x69, 0x77, 0x6D, 0xDA, 0x3F, - 0x78, 0xA0, 0xE0, 0x90, 0xDE, 0xD5, 0xEE, 0xB7, 0x7F, 0xF0, 0x59, 0xDD, 0x76, 0xDF, 0xF6, 0xD5, - 0xF1, 0x47, 0xC4, 0xAB, 0x7F, 0x15, 0x31, 0x96, 0x4F, 0x85, 0xA7, 0xC2, 0x7A, 0x6D, 0xD7, 0xD9, - 0x2E, 0x70, 0xF3, 0x84, 0x46, 0x58, 0x71, 0xB3, 0x70, 0x3E, 0x60, 0x3F, 0x31, 0x01, 0x78, 0xEB, - 0x40, 0x1F, 0x33, 0x68, 0xDF, 0xB1, 0x28, 0xD5, 0xFF, 0x00, 0x64, 0xDF, 0x81, 0x7E, 0x32, 0x8F, - 0xC1, 0x7E, 0x3D, 0xBA, 0xD4, 0xFE, 0x23, 0xF8, 0xFB, 0x50, 0xF0, 0xFD, 0xDD, 0xDC, 0x16, 0xCC, - 0xD6, 0x7A, 0x95, 0xAA, 0x1B, 0x45, 0xB6, 0x86, 0xD3, 0x8F, 0x9A, 0xE1, 0x9C, 0x6A, 0x03, 0x8C, - 0xE4, 0xC3, 0x8E, 0xD5, 0xD4, 0x78, 0xF3, 0xF6, 0x05, 0xD2, 0xB4, 0x9B, 0xFF, 0x00, 0xDA, 0xC2, - 0x1B, 0x1F, 0x05, 0x7C, 0x46, 0xB7, 0x6F, 0x84, 0x26, 0xDA, 0xE3, 0xC3, 0xE6, 0xEA, 0xD5, 0x95, - 0x34, 0x9B, 0x73, 0x78, 0xBE, 0x6F, 0xF6, 0x8B, 0x11, 0x85, 0x73, 0x6A, 0xDB, 0x94, 0x1E, 0xA7, - 0x3E, 0x95, 0x63, 0xC2, 0x3F, 0xF0, 0x51, 0xAD, 0x7B, 0xC2, 0xDF, 0xB3, 0x17, 0xEC, 0xE1, 0xE0, - 0x68, 0x75, 0xA6, 0x8E, 0xDF, 0xE1, 0x0F, 0x8D, 0xAE, 0x3C, 0x4E, 0x6D, 0xFC, 0x89, 0xBF, 0xD0, - 0x1F, 0xED, 0x0D, 0x2C, 0x72, 0x13, 0xB7, 0x0D, 0x9F, 0xB4, 0x4E, 0x70, 0x84, 0x9F, 0x6A, 0xDA, - 0xD7, 0x3F, 0xE0, 0xA7, 0xFA, 0xE7, 0x89, 0x5B, 0xF6, 0xB4, 0x8A, 0xF7, 0xC4, 0x05, 0xA1, 0xF8, - 0xE7, 0x12, 0x00, 0x3E, 0xCF, 0x37, 0xFC, 0x4C, 0x99, 0x09, 0x41, 0x8F, 0x97, 0xE5, 0xFD, 0xD8, - 0x5F, 0xBF, 0xB6, 0x80, 0x21, 0xF8, 0x7D, 0xFF, 0x00, 0x04, 0xF1, 0xF0, 0xAE, 0xA7, 0xF1, 0xDB, - 0xF6, 0x51, 0xD1, 0x75, 0x6F, 0x07, 0x7C, 0x58, 0x8F, 0x4A, 0xF8, 0xB9, 0xA1, 0x7D, 0xB7, 0xC4, - 0x76, 0xD1, 0x69, 0xEC, 0x2F, 0x6F, 0xA6, 0x59, 0xA6, 0x0F, 0x2D, 0x82, 0xE3, 0x26, 0x1F, 0x2C, - 0xC0, 0xC1, 0xB9, 0x18, 0x39, 0xAE, 0x03, 0x59, 0xFD, 0x89, 0x6D, 0x74, 0x3F, 0xD8, 0xD3, 0xE3, - 0x5F, 0x8D, 0xA6, 0xF0, 0x8F, 0x8F, 0xA2, 0xD6, 0x7C, 0x03, 0xF1, 0x0A, 0xCB, 0xC3, 0x76, 0x37, - 0xB2, 0xDA, 0x3A, 0xE9, 0xF6, 0x16, 0x8D, 0xF6, 0x95, 0xB8, 0x8E, 0xE8, 0xED, 0xC0, 0x9C, 0x3F, - 0xD8, 0xD7, 0x9C, 0x60, 0xC8, 0x07, 0xF1, 0x57, 0xB2, 0xF8, 0x57, 0xFE, 0x0A, 0xC9, 0xAA, 0x45, - 0xFB, 0x40, 0xFE, 0xCA, 0xFE, 0x2C, 0xBF, 0xF1, 0x2F, 0x97, 0x27, 0xC1, 0xAF, 0x0D, 0x9D, 0x1A, - 0xE2, 0x73, 0x6D, 0x39, 0xFE, 0xCE, 0x05, 0x7C, 0xBD, 0x84, 0x04, 0xCB, 0x7C, 0x88, 0x9F, 0x70, - 0x30, 0xAE, 0x1B, 0xC7, 0x1F, 0xF0, 0x52, 0xED, 0x77, 0xC5, 0xBF, 0xB0, 0xE7, 0xC6, 0x5F, 0x00, - 0x4F, 0xAE, 0x79, 0x97, 0x9F, 0x15, 0x3E, 0x24, 0x1F, 0x13, 0xDF, 0x59, 0xF9, 0x32, 0xE6, 0xEE, - 0x12, 0xD1, 0x4C, 0x65, 0xDD, 0xB7, 0x60, 0xFD, 0xEC, 0x11, 0xF0, 0x48, 0x6E, 0x3A, 0x50, 0x06, - 0xC6, 0xAB, 0xFF, 0x00, 0x04, 0xD5, 0xD3, 0xB4, 0xAF, 0xDA, 0xD5, 0x7C, 0x0E, 0xDF, 0x0E, 0x7E, - 0x2C, 0xB5, 0xB9, 0xF8, 0x51, 0x17, 0x89, 0xCD, 0x88, 0xB3, 0x73, 0x7C, 0xBA, 0xAB, 0x69, 0x81, - 0xDA, 0x76, 0x18, 0x05, 0x6D, 0x05, 0xE8, 0x75, 0xC9, 0xEC, 0x31, 0x5E, 0x5F, 0xFF, 0x00, 0x0C, - 0x6D, 0x69, 0x7D, 0xFB, 0x16, 0xFC, 0x1F, 0xF1, 0xE5, 0xA7, 0x85, 0x3C, 0x78, 0xDA, 0xC7, 0x8B, - 0xBC, 0x77, 0x7B, 0xE1, 0x9D, 0x4E, 0xE7, 0xEC, 0x6C, 0x6C, 0xB5, 0x28, 0xF1, 0x09, 0xB5, 0x8A, - 0xCB, 0x8F, 0xDE, 0x4B, 0xF2, 0xDD, 0x2B, 0x63, 0xF8, 0x94, 0x0E, 0xD5, 0xEE, 0xDE, 0x23, 0xFF, - 0x00, 0x82, 0xCA, 0xF8, 0x9E, 0xF3, 0xF6, 0xD9, 0xD5, 0xFE, 0x24, 0xC5, 0xE2, 0x7D, 0xED, 0x77, - 0xF0, 0xD4, 0xF8, 0x41, 0x2E, 0xBC, 0x89, 0xC0, 0x6F, 0xDC, 0x16, 0xF2, 0xB0, 0x57, 0x76, 0x7C, - 0xD6, 0x61, 0x9C, 0x6D, 0xF7, 0xC7, 0x35, 0xE7, 0x1E, 0x1E, 0xFF, 0x00, 0x82, 0x91, 0xEB, 0x1A, - 0x2F, 0xEC, 0x4B, 0xF0, 0x57, 0xC0, 0x09, 0xAF, 0x05, 0xB9, 0xF8, 0x61, 0xF1, 0x08, 0xF8, 0x9E, - 0x0B, 0x4F, 0x26, 0x5C, 0xDA, 0x20, 0x79, 0x24, 0x59, 0x77, 0x6D, 0xDA, 0x7E, 0x79, 0x5F, 0x85, - 0x25, 0xBD, 0xA8, 0x03, 0xA2, 0xF1, 0x77, 0xFC, 0x13, 0x93, 0xC2, 0xF6, 0x3F, 0x1C, 0x3F, 0x6A, - 0xDF, 0x0E, 0xE9, 0xDE, 0x0D, 0xF8, 0xAD, 0x25, 0xAF, 0xC2, 0x7F, 0x0D, 0x2E, 0xAF, 0xE1, 0xB8, - 0x64, 0xB3, 0x6F, 0xB4, 0xE9, 0xAE, 0x5A, 0x17, 0x2F, 0xA8, 0x0D, 0xBF, 0x2C, 0x62, 0x23, 0x39, - 0xFA, 0x21, 0xE7, 0x8C, 0xD7, 0x82, 0xFE, 0xD1, 0xFF, 0x00, 0xB3, 0x9D, 0xBF, 0xC2, 0x6F, 0xD9, - 0xAB, 0xE0, 0x8F, 0x8A, 0x21, 0xF0, 0x9F, 0x8D, 0xB4, 0x3B, 0xEF, 0x1A, 0xE9, 0x57, 0xB7, 0x3A, - 0xAE, 0xA3, 0xAC, 0x5A, 0xB4, 0x5A, 0x76, 0xAF, 0x28, 0xBA, 0x66, 0xB7, 0x7B, 0x06, 0x23, 0x0F, - 0x18, 0xB4, 0x92, 0xDC, 0xB1, 0x19, 0xF9, 0x98, 0xD7, 0xD3, 0xDE, 0x2D, 0xFF, 0x00, 0x82, 0xBA, - 0xEA, 0x37, 0x5F, 0x19, 0xBF, 0x6B, 0x2F, 0x14, 0xDA, 0x78, 0xA1, 0x64, 0xB8, 0xF8, 0xCD, 0xE1, - 0xF8, 0x74, 0x2B, 0x49, 0x7E, 0xCD, 0x38, 0xFE, 0xD1, 0x84, 0x29, 0x89, 0xD4, 0x65, 0x32, 0x9F, - 0xBB, 0x92, 0x4F, 0xBF, 0xB6, 0xBE, 0x64, 0xFD, 0xA0, 0xBF, 0x6D, 0x9F, 0x14, 0x7E, 0xD1, 0xBF, - 0xB2, 0xF7, 0xC2, 0xFF, 0x00, 0x00, 0xF8, 0x8B, 0x51, 0xFB, 0x4D, 0x9F, 0xC2, 0xBF, 0x3A, 0xC3, - 0x46, 0xB7, 0xC3, 0x7F, 0xA3, 0xDA, 0x34, 0x36, 0xE8, 0x39, 0x3C, 0x75, 0x8B, 0x18, 0x07, 0xB5, - 0x00, 0x78, 0x45, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, - 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, - 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, - 0x51, 0x40, 0x05, 0x14, 0x57, 0xD0, 0x1F, 0xB3, 0x17, 0xEC, 0xB7, 0xF0, 0xB7, 0xE3, 0x3C, 0x5E, - 0x0F, 0xFF, 0x00, 0x84, 0xB3, 0xE3, 0xDF, 0x86, 0x7C, 0x07, 0x7B, 0xE2, 0x2D, 0x7D, 0x34, 0x9D, - 0x43, 0x4D, 0xBA, 0xD1, 0xEE, 0xE4, 0x9B, 0x44, 0xB5, 0x66, 0xC7, 0xDB, 0xE5, 0xB8, 0x71, 0x1D, - 0xA7, 0x94, 0x07, 0x24, 0x79, 0xDB, 0x87, 0xA5, 0x00, 0x78, 0x3E, 0x9B, 0xA7, 0xCB, 0xAB, 0xEA, - 0x36, 0xF6, 0x90, 0xA9, 0x92, 0x6B, 0x99, 0x16, 0x28, 0xD4, 0x7F, 0x13, 0x31, 0xC0, 0x1F, 0x99, - 0xAF, 0x5A, 0x3A, 0x75, 0xA7, 0x86, 0xF4, 0xE4, 0xD3, 0x2C, 0x16, 0x33, 0x0C, 0x38, 0x13, 0xCE, - 0x17, 0xE6, 0xBB, 0x90, 0x75, 0x62, 0x7A, 0xED, 0xCF, 0xDD, 0x1D, 0x31, 0xEF, 0x5D, 0x37, 0xC7, - 0xBF, 0x80, 0x9E, 0x03, 0xFD, 0x9C, 0xBF, 0x69, 0x89, 0x7C, 0x3F, 0xE0, 0x7F, 0x88, 0xD6, 0x5F, - 0x14, 0xB4, 0xCD, 0x3D, 0x66, 0x92, 0x1D, 0x6F, 0x4F, 0x48, 0x92, 0xD6, 0x72, 0xB1, 0xBE, 0x0A, - 0xEC, 0x92, 0x4E, 0x73, 0x83, 0xD6, 0xB9, 0x89, 0x17, 0x3F, 0x8D, 0x69, 0x08, 0xF5, 0x29, 0x22, - 0xAC, 0x8B, 0x8F, 0xC2, 0xA0, 0x91, 0x6A, 0xD3, 0x8A, 0xF6, 0x0F, 0x80, 0xDF, 0xF0, 0x4F, 0x2F, - 0x8C, 0xFF, 0x00, 0xB4, 0xFF, 0x00, 0x87, 0x9F, 0x57, 0xF0, 0x3F, 0x80, 0xB5, 0x4D, 0x63, 0x4B, - 0x46, 0xDA, 0xB7, 0x92, 0xCF, 0x6F, 0x65, 0x04, 0xA7, 0xD1, 0x1E, 0xE2, 0x48, 0xC4, 0x98, 0xEF, - 0xB0, 0x9C, 0x77, 0xAD, 0xA8, 0xE1, 0xEA, 0xD5, 0x97, 0x2D, 0x28, 0xB9, 0x3E, 0xC9, 0x37, 0xF9, - 0x1B, 0x61, 0xF0, 0xB5, 0xAB, 0xCB, 0x92, 0x84, 0x1C, 0x9F, 0x64, 0x9B, 0x7F, 0x81, 0xE1, 0xD2, - 0x2F, 0xEB, 0x50, 0x4A, 0xB5, 0xF6, 0x26, 0x9F, 0xFF, 0x00, 0x04, 0x20, 0xFD, 0xAB, 0xB5, 0xA3, - 0x8B, 0x5F, 0x84, 0xF2, 0x4B, 0xBB, 0xA6, 0x3C, 0x47, 0xA4, 0x0F, 0xE7, 0x75, 0x5A, 0x9F, 0xF1, - 0x0E, 0xDF, 0xED, 0x91, 0x32, 0x6E, 0x5F, 0x83, 0x17, 0x2C, 0xA4, 0x75, 0x1E, 0x24, 0xD1, 0x8F, - 0xFE, 0xDD, 0xD1, 0x53, 0x0F, 0x56, 0x9B, 0xB5, 0x48, 0xB5, 0xEA, 0x9A, 0x0A, 0xD8, 0x5A, 0xF4, - 0x9D, 0xAA, 0x41, 0xAF, 0x54, 0xD7, 0xE6, 0x7C, 0x3F, 0x20, 0xAA, 0xF2, 0x0C, 0x1A, 0xFB, 0x57, - 0x55, 0xFF, 0x00, 0x83, 0x7D, 0xBF, 0x6B, 0xFD, 0x29, 0x0B, 0x5C, 0x7C, 0x1C, 0xBA, 0x8C, 0x0E, - 0xB9, 0xF1, 0x16, 0x90, 0x7F, 0x95, 0xDD, 0x73, 0x1A, 0x97, 0xFC, 0x11, 0x1B, 0xF6, 0xA1, 0xD3, - 0x09, 0x13, 0xFC, 0x2B, 0x9E, 0x32, 0x3A, 0xFF, 0x00, 0xC4, 0xFB, 0x4B, 0x3F, 0xCA, 0xE6, 0xA7, - 0xD8, 0xD4, 0xDF, 0x95, 0xFD, 0xCC, 0xBA, 0x58, 0x0C, 0x4D, 0x4F, 0xE1, 0xD3, 0x93, 0xF4, 0x4D, - 0xFE, 0x87, 0xC9, 0x52, 0x2E, 0x2A, 0x09, 0x16, 0xBE, 0xA1, 0xBE, 0xFF, 0x00, 0x82, 0x3C, 0xFE, - 0xD1, 0xD6, 0x43, 0xF7, 0xBF, 0x0D, 0xA7, 0x5C, 0x7F, 0xD4, 0x6B, 0x4D, 0x3F, 0xCA, 0xE2, 0xB2, - 0xAE, 0x7F, 0xE0, 0x93, 0x9F, 0xB4, 0x14, 0x04, 0xEE, 0xF8, 0x77, 0x70, 0x31, 0xFF, 0x00, 0x51, - 0x6B, 0x03, 0xFF, 0x00, 0xB5, 0xEB, 0x1F, 0x67, 0x2E, 0xCC, 0xED, 0xA7, 0xC3, 0xF9, 0xAC, 0xFE, - 0x0C, 0x35, 0x47, 0xE9, 0x09, 0x3F, 0xD0, 0xF9, 0xB5, 0xC7, 0x7A, 0x89, 0x86, 0x0D, 0x7D, 0x18, - 0xDF, 0xF0, 0x4A, 0x8F, 0x8F, 0xCC, 0x70, 0x3E, 0x1E, 0x5E, 0x1C, 0xFA, 0x6A, 0x76, 0x3F, 0xFC, - 0x7A, 0xB4, 0xB4, 0x4F, 0xF8, 0x23, 0x5F, 0xED, 0x2B, 0xE2, 0x67, 0xD9, 0x63, 0xF0, 0xBE, 0xFA, - 0x76, 0x6E, 0x98, 0xD5, 0x74, 0xF5, 0xCF, 0xE7, 0x38, 0xAD, 0x23, 0x87, 0xAA, 0xF6, 0x8B, 0xFB, - 0x99, 0x55, 0x78, 0x6B, 0x37, 0xA7, 0x1E, 0x6A, 0x98, 0x5A, 0x89, 0x77, 0x70, 0x92, 0x5F, 0x8A, - 0x3E, 0x5D, 0x61, 0x83, 0x51, 0x3A, 0xF3, 0x8E, 0xD5, 0xEF, 0x9F, 0xB5, 0x37, 0xFC, 0x13, 0x2B, - 0xE3, 0xCF, 0xEC, 0x5B, 0xE1, 0xCB, 0x1D, 0x63, 0xE2, 0x6F, 0xC3, 0x1F, 0x11, 0x78, 0x5F, 0x45, - 0xD4, 0x8E, 0xDB, 0x7D, 0x49, 0xC4, 0x57, 0x56, 0x4C, 0xDF, 0xDD, 0x33, 0x40, 0xEF, 0x1A, 0xB7, - 0xFB, 0x2C, 0xC0, 0xFB, 0x57, 0x83, 0x30, 0xC8, 0xAC, 0x5A, 0x69, 0xD9, 0x9E, 0x33, 0x4D, 0x3B, - 0x32, 0xB4, 0xB6, 0xEB, 0xF7, 0xBA, 0x0E, 0xF8, 0xED, 0xEF, 0x55, 0x9D, 0x76, 0x36, 0x2A, 0xFD, - 0x7A, 0xA7, 0xEC, 0x77, 0xF0, 0x03, 0xC1, 0xBF, 0xB4, 0x87, 0xC5, 0xDD, 0x3F, 0xC3, 0x3E, 0x2C, - 0xF8, 0x8D, 0x61, 0xF0, 0xCE, 0xCA, 0xEC, 0xED, 0x97, 0x59, 0xD5, 0x21, 0x8D, 0xAC, 0xED, 0xF9, - 0xC7, 0x25, 0xA5, 0x4E, 0xD8, 0xEE, 0x28, 0x11, 0xE3, 0x74, 0x57, 0xD1, 0x1F, 0xB4, 0xC7, 0xEC, - 0x85, 0xF0, 0xE7, 0xE0, 0xA5, 0xA7, 0x8E, 0xA4, 0xF0, 0xDF, 0xC7, 0xDF, 0x04, 0xF8, 0xE6, 0xEB, - 0xC2, 0x7A, 0xF7, 0xF6, 0x46, 0x9F, 0xA7, 0x59, 0xD8, 0xDD, 0x2D, 0xCF, 0x88, 0xA0, 0xCA, 0x03, - 0x7B, 0x6F, 0x22, 0x2C, 0x96, 0xDE, 0x48, 0xDC, 0xC7, 0xE6, 0x98, 0x13, 0xB0, 0xE0, 0x74, 0xCF, - 0xCE, 0xF4, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, - 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, - 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x57, 0xDD, 0x9F, 0xF0, 0x4D, - 0xCF, 0x01, 0xF8, 0xF3, 0x54, 0xD0, 0x3E, 0x1C, 0xEA, 0x5A, 0x1F, 0xEC, 0xA7, 0xE0, 0xAF, 0x8B, - 0x5A, 0x5C, 0x9E, 0x34, 0x8E, 0x1B, 0x5D, 0x77, 0x53, 0x13, 0x2C, 0x9A, 0xB5, 0xCE, 0xEE, 0x2C, - 0x26, 0x73, 0x31, 0x84, 0x42, 0x3B, 0xEE, 0x84, 0xF1, 0xD4, 0xD7, 0xC2, 0x75, 0xF5, 0x4F, 0xEC, - 0x43, 0x2F, 0xC0, 0xDB, 0x58, 0x7C, 0x13, 0x27, 0x8E, 0xBE, 0x26, 0x7C, 0x6A, 0xF0, 0xA6, 0xBA, - 0xBE, 0x27, 0x4F, 0xED, 0x2B, 0x4F, 0x0C, 0xE9, 0xAA, 0xF6, 0x76, 0xF6, 0x39, 0x1F, 0xBE, 0x82, - 0x64, 0x97, 0xCD, 0x17, 0x04, 0x7A, 0x44, 0xD8, 0xC7, 0x43, 0x40, 0x1E, 0xD3, 0xFF, 0x00, 0x05, - 0x3A, 0xF0, 0xBF, 0x8E, 0x34, 0x5F, 0x8C, 0x77, 0x12, 0x78, 0xAB, 0xF6, 0x6F, 0xF0, 0x3F, 0xC0, - 0xE8, 0xFE, 0xD4, 0xEB, 0xBB, 0x40, 0x92, 0x27, 0x50, 0xE6, 0x09, 0xF1, 0x08, 0x31, 0x90, 0xBB, - 0x48, 0x04, 0xFD, 0xC1, 0xF7, 0x05, 0x7C, 0xA2, 0xEB, 0x5E, 0xE7, 0xFB, 0x6D, 0xDD, 0x7C, 0x19, - 0xBC, 0xF8, 0xD5, 0x70, 0xDF, 0x0C, 0x3C, 0x75, 0xF1, 0x8F, 0xC6, 0x2C, 0x64, 0x93, 0x0F, 0xE3, - 0x40, 0xC4, 0x18, 0xBC, 0x99, 0x77, 0x36, 0x64, 0x44, 0x93, 0x7E, 0x76, 0xE3, 0x23, 0xA1, 0x6A, - 0xF0, 0xF7, 0x5F, 0xD2, 0xB5, 0xA7, 0xB1, 0x48, 0xF4, 0xBF, 0xD8, 0xA7, 0xE1, 0x36, 0x9B, 0xF1, - 0xBF, 0xF6, 0xA6, 0xF0, 0x7F, 0x86, 0xB5, 0xAC, 0xB6, 0x93, 0x79, 0x71, 0x2C, 0xF7, 0x88, 0x33, - 0xFB, 0xE8, 0xAD, 0xED, 0xE5, 0xB8, 0x68, 0xF8, 0xE4, 0x07, 0x11, 0x6D, 0xCF, 0x6D, 0xD5, 0xFA, - 0x15, 0xF1, 0x2F, 0xF6, 0xDD, 0xD5, 0xB4, 0x7F, 0x16, 0xC9, 0xE1, 0xDF, 0x0F, 0x4A, 0x34, 0x5D, - 0x23, 0x47, 0x22, 0xD2, 0x18, 0x2D, 0x80, 0x8C, 0x28, 0x4F, 0x97, 0x68, 0x03, 0xEE, 0x81, 0x8C, - 0x00, 0x3D, 0x2B, 0xE1, 0xEF, 0xF8, 0x26, 0x5C, 0x26, 0x5F, 0xDB, 0x77, 0xC1, 0xEA, 0x3A, 0x98, - 0x35, 0x43, 0xFF, 0x00, 0x94, 0xBB, 0xBA, 0xEB, 0xBE, 0x34, 0x78, 0xA9, 0xAC, 0x7E, 0x3B, 0xF8, - 0x9D, 0x77, 0x91, 0xB7, 0x55, 0xB8, 0xFF, 0x00, 0xD1, 0xAD, 0x5F, 0xD1, 0xBE, 0x09, 0xCF, 0x09, - 0x47, 0x07, 0x5A, 0xB5, 0x6A, 0x6A, 0x52, 0xE7, 0x4A, 0xED, 0x5F, 0x4E, 0x55, 0x6D, 0xFE, 0x67, - 0xEA, 0xDC, 0x27, 0x8F, 0xFA, 0xAE, 0x47, 0x28, 0xD2, 0x56, 0x95, 0x4A, 0xB2, 0x52, 0x7D, 0x5C, - 0x63, 0x18, 0x5A, 0x37, 0xED, 0x79, 0x48, 0xFB, 0x73, 0xC0, 0x1F, 0xB5, 0xA7, 0x8B, 0x24, 0x54, - 0x78, 0xFC, 0x43, 0xA9, 0xA9, 0xF4, 0x17, 0x0D, 0xFE, 0x35, 0xEB, 0xBE, 0x19, 0xFD, 0xAF, 0x3C, - 0x6B, 0x24, 0x08, 0xA7, 0xC4, 0x9A, 0xB1, 0x5C, 0x7F, 0xCF, 0xD3, 0xFF, 0x00, 0x8D, 0x7E, 0x7D, - 0x7C, 0x32, 0xF8, 0x92, 0x62, 0x68, 0xF3, 0x21, 0xF7, 0xE6, 0xBD, 0xE7, 0xC1, 0x5E, 0x3B, 0x49, - 0x96, 0x36, 0x0F, 0xC1, 0x1C, 0x8C, 0xD7, 0xEE, 0xD9, 0x9E, 0x4B, 0x82, 0xAF, 0x4F, 0xDB, 0x42, - 0x9C, 0x5A, 0x7E, 0x48, 0xF0, 0x78, 0x8B, 0x03, 0x29, 0xC7, 0xDA, 0x41, 0xBB, 0x7C, 0xCF, 0xA5, - 0x7C, 0x41, 0xFB, 0x40, 0xF8, 0xCB, 0x55, 0xB7, 0x27, 0xFB, 0x7F, 0x52, 0x61, 0x8E, 0xF3, 0xB1, - 0xFE, 0x75, 0xE0, 0xBF, 0xB4, 0x07, 0xC5, 0xFF, 0x00, 0x1E, 0x59, 0x59, 0x49, 0x25, 0xBF, 0x88, - 0x35, 0x04, 0xF9, 0x73, 0xF2, 0xBD, 0x76, 0x7E, 0x1D, 0xF1, 0x0A, 0x5D, 0x44, 0xA0, 0xB6, 0x54, - 0xFB, 0xD5, 0x1F, 0x8B, 0xBE, 0x1C, 0x8F, 0x57, 0xF0, 0xC3, 0xBA, 0xA8, 0x6F, 0x96, 0xBE, 0x3E, - 0x79, 0x66, 0x1A, 0x12, 0xE5, 0x74, 0xE3, 0xF7, 0x23, 0xE1, 0xB2, 0xDC, 0x4D, 0x5A, 0x38, 0x94, - 0xA4, 0xD9, 0xF1, 0x3F, 0x88, 0x7F, 0x6B, 0x2F, 0x89, 0x56, 0x9A, 0xCC, 0xB1, 0x9F, 0x14, 0x6A, - 0x64, 0x03, 0x8C, 0x33, 0x82, 0x3F, 0x95, 0x63, 0xEA, 0x3F, 0xB6, 0x0F, 0xC4, 0x8B, 0x5E, 0x7F, - 0xE1, 0x22, 0xBA, 0x60, 0x7D, 0x51, 0x4F, 0xF4, 0xAB, 0x5F, 0x16, 0x7C, 0x1A, 0xDA, 0x5F, 0x88, - 0x65, 0x7D, 0x84, 0x02, 0xC7, 0xB5, 0x71, 0x1A, 0x96, 0x9C, 0x93, 0x46, 0x46, 0x06, 0x71, 0x5F, - 0x9E, 0xF1, 0x3E, 0x4D, 0x66, 0xE7, 0x49, 0x5B, 0xE4, 0x7E, 0xCF, 0x97, 0x62, 0xA7, 0x52, 0x95, - 0xE1, 0x2B, 0x33, 0xA5, 0xB3, 0xFD, 0xB6, 0xBE, 0x22, 0xDA, 0xCC, 0x18, 0xEB, 0xF3, 0x1C, 0x1E, - 0x7F, 0x76, 0x9F, 0xE1, 0x5E, 0x93, 0xF0, 0xFB, 0xFE, 0x0A, 0x25, 0xF1, 0x02, 0xCC, 0x28, 0xFE, - 0xDF, 0x9D, 0x18, 0x0E, 0xAA, 0xAA, 0xA4, 0x7E, 0x42, 0xBE, 0x6A, 0xD5, 0x34, 0xB3, 0x6F, 0x21, - 0xDA, 0x38, 0xAC, 0xF4, 0xBE, 0x7D, 0x31, 0x99, 0xC1, 0x60, 0x05, 0x7E, 0x5B, 0x56, 0xA5, 0x5A, - 0x32, 0xB3, 0x3D, 0x8C, 0x36, 0x75, 0x8A, 0xC3, 0xBD, 0x64, 0xEC, 0x7E, 0xB4, 0x7F, 0xC1, 0x33, - 0xFF, 0x00, 0xE0, 0xAD, 0x97, 0xDF, 0x15, 0xBE, 0x26, 0x37, 0xC0, 0xFF, 0x00, 0x8C, 0x96, 0xF6, - 0xBE, 0x39, 0xF8, 0x7F, 0xF1, 0x1A, 0x37, 0xD2, 0xA5, 0xB7, 0xD4, 0x22, 0x59, 0x02, 0x16, 0x53, - 0xF2, 0xB7, 0xAA, 0x9C, 0x70, 0x7A, 0xAB, 0x00, 0x41, 0xAF, 0xC6, 0x5F, 0xDB, 0xDF, 0xE0, 0x66, - 0x97, 0xFB, 0x32, 0xFE, 0xDA, 0xFF, 0x00, 0x15, 0x3E, 0x1E, 0xE8, 0x77, 0x53, 0x5E, 0x68, 0xBE, - 0x0D, 0xF1, 0x45, 0xFE, 0x93, 0x63, 0x3C, 0xA3, 0x0E, 0xF0, 0x45, 0x3B, 0x2C, 0x65, 0xBD, 0xF6, - 0x81, 0x5E, 0xB7, 0xFB, 0x21, 0x78, 0xE2, 0x4B, 0x5F, 0xDB, 0x13, 0xE1, 0xEC, 0xCB, 0x29, 0x06, - 0x1D, 0x72, 0xD5, 0xB3, 0xFF, 0x00, 0x6D, 0x05, 0x79, 0xD7, 0xFC, 0x14, 0xB7, 0x54, 0x3A, 0xF7, - 0xFC, 0x14, 0x03, 0xE3, 0x1D, 0xE1, 0x3B, 0x9A, 0xEB, 0xC5, 0x97, 0xF2, 0x67, 0xD7, 0x33, 0x35, - 0x7C, 0xD6, 0x75, 0xC9, 0x52, 0x31, 0xAF, 0x6D, 0x5B, 0xB3, 0xF3, 0x3E, 0x07, 0x8C, 0xB0, 0xF4, - 0xF1, 0x14, 0x23, 0x9A, 0x45, 0x24, 0xDC, 0xDC, 0x1D, 0xB4, 0xBD, 0xA2, 0xA4, 0x9E, 0x9D, 0x77, - 0xFB, 0x8F, 0x0A, 0x71, 0x83, 0x5F, 0x71, 0xFF, 0x00, 0xC1, 0x2E, 0x3C, 0x1F, 0xE2, 0xAD, 0x4B, - 0x5A, 0xF0, 0xBD, 0xC6, 0x97, 0xFB, 0x28, 0xF8, 0x7F, 0xE3, 0x55, 0xAC, 0x86, 0x5F, 0x2E, 0x6B, - 0xE9, 0x02, 0x7F, 0x69, 0x62, 0x77, 0x04, 0xB6, 0xF6, 0xD9, 0xF2, 0x90, 0x53, 0xA7, 0xF0, 0x57, - 0xC3, 0xCC, 0x32, 0x2B, 0xEB, 0x7F, 0xF8, 0x26, 0xFE, 0xB3, 0xF0, 0x87, 0x46, 0xF1, 0x7F, 0x87, - 0x26, 0xF1, 0xA7, 0xC7, 0x2F, 0x8B, 0xDF, 0x0A, 0xAF, 0x55, 0xA4, 0xFB, 0x55, 0xCF, 0x86, 0xA1, - 0x94, 0x25, 0x91, 0xF3, 0x9B, 0x6F, 0x96, 0xF1, 0xA3, 0xB7, 0x2B, 0xB5, 0x8E, 0x17, 0xEF, 0x13, - 0x5E, 0x01, 0xF9, 0xE9, 0xD1, 0x7F, 0xC1, 0x43, 0x7C, 0x0D, 0xE2, 0x8D, 0x37, 0xC3, 0x1F, 0x14, - 0x6F, 0xAF, 0xBF, 0x63, 0xCD, 0x2F, 0xE1, 0x0E, 0x9F, 0x17, 0x8B, 0x7C, 0xBB, 0x9D, 0x7E, 0x0F, - 0xB5, 0x49, 0xFF, 0x00, 0x08, 0xE4, 0xA5, 0x93, 0xFD, 0x02, 0x37, 0x0E, 0x2D, 0xB6, 0x37, 0x00, - 0x61, 0x0F, 0xDF, 0x38, 0x35, 0xF0, 0x9D, 0x7D, 0x7D, 0xFB, 0x78, 0xDF, 0x7C, 0x21, 0xD5, 0x6E, - 0x7E, 0x24, 0x5D, 0x78, 0x57, 0xF6, 0x87, 0xF8, 0xB5, 0xF1, 0x33, 0x5A, 0x9B, 0xC4, 0xA5, 0xF4, - 0xDB, 0x3D, 0x7F, 0x46, 0x94, 0x5B, 0xEB, 0x76, 0xA5, 0x97, 0x37, 0x77, 0x17, 0x52, 0x4C, 0xAD, - 0xE7, 0x0F, 0x98, 0xE0, 0xDB, 0x82, 0x76, 0x8E, 0x99, 0xAF, 0x90, 0x68, 0x00, 0xA2, 0x8A, 0x28, - 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, - 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, - 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xAF, 0xD1, 0x0F, 0xF8, 0x25, 0xBC, 0xBF, 0x1A, 0x9F, 0xC1, 0xBF, - 0x0C, 0x21, 0xF0, 0x17, 0x8C, 0xBF, 0x67, 0xCD, 0x27, 0x4F, 0x3E, 0x3B, 0x8C, 0xE9, 0x96, 0xBE, - 0x27, 0x93, 0x4F, 0x7D, 0x66, 0xD6, 0xF7, 0x7F, 0x13, 0x4B, 0x1B, 0x2F, 0xDA, 0x4D, 0xBE, 0x79, - 0x18, 0x6C, 0x73, 0xC5, 0x7E, 0x77, 0xD7, 0xD6, 0x9F, 0xB0, 0xD7, 0x86, 0xFE, 0x16, 0xEB, 0x56, - 0x9E, 0x05, 0x5F, 0x18, 0x7E, 0xCF, 0x3F, 0x17, 0x3E, 0x25, 0x5F, 0x5C, 0x78, 0xAA, 0x38, 0x6F, - 0x75, 0x0F, 0x0F, 0xEB, 0x12, 0x47, 0x69, 0xAC, 0x5B, 0x16, 0xFF, 0x00, 0x8F, 0x18, 0xAD, 0xD6, - 0x01, 0x99, 0x8F, 0x4C, 0x8B, 0x85, 0x27, 0xDA, 0x80, 0x3D, 0xFB, 0xFE, 0x0A, 0xD6, 0xDF, 0x18, - 0xA3, 0xF8, 0xE3, 0x73, 0x0F, 0xC5, 0x3F, 0x1A, 0xFC, 0x20, 0xF1, 0x3C, 0xC2, 0xED, 0xCB, 0x47, - 0xE1, 0x18, 0xE3, 0x8A, 0x41, 0x27, 0x91, 0x3E, 0x1B, 0x68, 0x1B, 0xF6, 0x01, 0xB8, 0x1C, 0x9C, - 0x64, 0xAD, 0x7C, 0x6E, 0xE3, 0xBD, 0x7B, 0xC7, 0xED, 0xCF, 0xE1, 0x2F, 0x87, 0x5E, 0x16, 0xF8, - 0xD5, 0x34, 0x7E, 0x03, 0xFD, 0x9F, 0xFE, 0x23, 0xFC, 0x17, 0xB7, 0x12, 0xC8, 0x15, 0x3C, 0x51, - 0x75, 0x3B, 0x3A, 0xA7, 0x93, 0x2E, 0x62, 0x29, 0x23, 0xC9, 0x86, 0x3C, 0x1C, 0xF9, 0x87, 0xEE, - 0x1A, 0xF0, 0xA9, 0x17, 0xF5, 0xAD, 0x69, 0xEC, 0x54, 0x4F, 0x7E, 0xFF, 0x00, 0x82, 0x55, 0xDB, - 0x89, 0xFF, 0x00, 0x6F, 0x3F, 0x05, 0x21, 0xE4, 0x35, 0xBE, 0xAC, 0x7F, 0xF2, 0x93, 0x79, 0x55, - 0xBF, 0x69, 0x49, 0x5A, 0xD7, 0xE3, 0xD7, 0x8A, 0xD8, 0x6E, 0x18, 0xD5, 0x2E, 0x3F, 0xF4, 0x6B, - 0x56, 0xBF, 0xFC, 0x12, 0x3E, 0xD3, 0xED, 0xBF, 0xF0, 0x50, 0x7F, 0x02, 0xC5, 0xDD, 0xAD, 0xB5, - 0x8F, 0xFD, 0x33, 0xDE, 0x9A, 0x8B, 0xF6, 0xB2, 0xD1, 0x24, 0x83, 0xE3, 0x77, 0x8A, 0x5B, 0x1D, - 0x75, 0x3B, 0x83, 0xD3, 0xFE, 0x9A, 0xB5, 0x7E, 0xFF, 0x00, 0xE1, 0x4E, 0x0E, 0x75, 0x72, 0x6C, - 0x55, 0x48, 0x6F, 0x1A, 0x91, 0xFF, 0x00, 0xD2, 0x4F, 0xB8, 0xC9, 0xE7, 0xFF, 0x00, 0x09, 0x71, - 0x8F, 0xFD, 0x3C, 0x9B, 0xFF, 0x00, 0xC9, 0x69, 0x9C, 0xEF, 0x83, 0xFC, 0x62, 0x6D, 0x24, 0x4F, - 0x9F, 0x1F, 0x8D, 0x7B, 0x37, 0x80, 0xBE, 0x27, 0xF9, 0x28, 0x80, 0xC9, 0xBB, 0x8F, 0x5A, 0xF9, - 0x7C, 0x5E, 0xBD, 0x9C, 0xBD, 0x48, 0xDA, 0x6B, 0xA4, 0xF0, 0xD7, 0x8D, 0xE4, 0xB5, 0x65, 0x1B, - 0xCF, 0xCB, 0xEF, 0x5F, 0xAE, 0xE4, 0x7C, 0x50, 0xA8, 0xFF, 0x00, 0xB3, 0xD7, 0xD8, 0xF4, 0xD6, - 0x21, 0x4A, 0x1E, 0xCE, 0x67, 0xDD, 0x9F, 0x0D, 0xFE, 0x26, 0x25, 0xEA, 0x2A, 0xF9, 0xC0, 0x9C, - 0xFA, 0xD7, 0xAD, 0x69, 0xFA, 0x9C, 0x7A, 0xE6, 0x8E, 0xD0, 0xB1, 0x0C, 0x08, 0xE2, 0xBC, 0x4B, - 0xFE, 0x09, 0xFB, 0xE1, 0x1D, 0x03, 0x5B, 0xF8, 0x13, 0xF1, 0x8B, 0xE2, 0xC7, 0x8A, 0x2D, 0x64, - 0xD7, 0xB4, 0xFF, 0x00, 0x85, 0x36, 0x5A, 0x7A, 0xDA, 0x68, 0x62, 0x79, 0x21, 0x8F, 0x50, 0xBD, - 0xBF, 0x9D, 0xA1, 0x85, 0xE7, 0x68, 0xCA, 0xC9, 0xF6, 0x78, 0xB6, 0x33, 0x38, 0x46, 0x52, 0x49, - 0x51, 0xB8, 0x0C, 0xE7, 0xEA, 0x9F, 0xD8, 0x6B, 0xC4, 0xDE, 0x19, 0xF8, 0xF7, 0x65, 0xE2, 0x0D, - 0x4B, 0x59, 0xF0, 0x57, 0x86, 0x34, 0xDB, 0x7F, 0x0A, 0xF8, 0x2F, 0x50, 0xD4, 0xDE, 0xE2, 0x49, - 0xAE, 0x2D, 0x2C, 0x6F, 0xAE, 0xCC, 0xC9, 0x15, 0x93, 0x31, 0x33, 0x05, 0x40, 0x24, 0x7D, 0x84, - 0xEE, 0xC3, 0x04, 0x39, 0xE7, 0x9A, 0x59, 0xC6, 0x71, 0x87, 0x8A, 0xA9, 0x56, 0x8C, 0x5C, 0xA3, - 0x4D, 0xA5, 0x26, 0xAD, 0xF1, 0x34, 0xA4, 0x92, 0xBB, 0x4D, 0xBB, 0x4A, 0x3F, 0x7A, 0xDF, 0x5B, - 0x7C, 0x06, 0x6B, 0x85, 0x50, 0xAD, 0xCF, 0x4F, 0xA3, 0xFF, 0x00, 0x82, 0x7C, 0x81, 0xF1, 0xFB, - 0xE1, 0xD2, 0xCB, 0x2C, 0xAE, 0xB1, 0xF7, 0x38, 0xAF, 0x9A, 0x3C, 0x5F, 0xA3, 0x4D, 0xA6, 0x5D, - 0x36, 0xD0, 0x70, 0x2B, 0xF6, 0x2B, 0xE2, 0x8F, 0xC3, 0xFF, 0x00, 0x84, 0xB7, 0x1F, 0x0A, 0x7E, - 0x28, 0x78, 0x93, 0x49, 0xD1, 0x74, 0x4D, 0x72, 0x3F, 0x0E, 0xF8, 0x4F, 0x49, 0x80, 0xCA, 0x97, - 0x17, 0x32, 0xD9, 0x5A, 0x78, 0x82, 0xE1, 0xF6, 0x4C, 0xD6, 0xCF, 0xE6, 0x82, 0xF0, 0x63, 0x24, - 0x06, 0x2C, 0xBB, 0x87, 0x07, 0x68, 0xC5, 0x7C, 0x09, 0xFB, 0x5B, 0xF8, 0x5F, 0x46, 0xD3, 0xBC, - 0x1F, 0xE0, 0x3D, 0x2E, 0xCF, 0xC3, 0x9A, 0x4E, 0x95, 0xAC, 0xAE, 0x86, 0xB7, 0xFA, 0xB5, 0xE5, - 0xB1, 0x9C, 0xCF, 0x7E, 0xF7, 0x12, 0x3C, 0x90, 0x99, 0x03, 0xC8, 0xCA, 0xB8, 0xB6, 0x30, 0x70, - 0x8A, 0xBF, 0x31, 0x62, 0x7A, 0xE0, 0x78, 0xBF, 0x5C, 0xA3, 0x99, 0x47, 0x91, 0x53, 0x92, 0xD6, - 0xDA, 0xA4, 0xAD, 0xEE, 0x29, 0xEA, 0xAF, 0x7B, 0x24, 0xD2, 0x7E, 0x6D, 0x74, 0xD4, 0xFA, 0xCE, - 0x1F, 0xCD, 0x17, 0x2A, 0x84, 0xBF, 0xAD, 0x0F, 0x91, 0x75, 0x0D, 0x48, 0x0C, 0xEF, 0x00, 0x7D, - 0x6B, 0x8A, 0xF1, 0x8E, 0xBE, 0x51, 0x59, 0x62, 0xE7, 0xDC, 0x57, 0xA7, 0x6B, 0xDE, 0x04, 0x96, - 0x59, 0xD8, 0x2A, 0x1D, 0xA6, 0xB9, 0xCD, 0x7B, 0xE1, 0xAB, 0x45, 0x6C, 0xCC, 0x63, 0x24, 0x9F, - 0x6A, 0xFC, 0xBF, 0x3E, 0xE1, 0xBC, 0x47, 0x34, 0xB9, 0x56, 0x87, 0xD4, 0x62, 0xDD, 0x5A, 0x90, - 0x7E, 0xCC, 0xE6, 0x3F, 0x65, 0x0B, 0xB9, 0x07, 0xED, 0x4B, 0xE0, 0x77, 0x20, 0xFF, 0x00, 0xC8, - 0x6A, 0xDB, 0xFF, 0x00, 0x46, 0x2D, 0x64, 0x7E, 0xDF, 0x24, 0xB7, 0xED, 0xAB, 0xF1, 0x45, 0x8F, - 0x53, 0xE2, 0x5B, 0xDF, 0xFD, 0x1A, 0xD5, 0xE8, 0xBF, 0xB3, 0x07, 0x82, 0x24, 0x83, 0xF6, 0xA3, - 0xF0, 0x37, 0xEE, 0xCE, 0x3F, 0xB6, 0x6D, 0x8F, 0x4F, 0xFA, 0x68, 0x2B, 0x80, 0xFF, 0x00, 0x82, - 0x82, 0x41, 0xE4, 0x7E, 0xDB, 0x9F, 0x15, 0x23, 0x3C, 0x15, 0xF1, 0x35, 0xE8, 0xFF, 0x00, 0xC8, - 0xAD, 0x5F, 0x92, 0xE7, 0x18, 0x79, 0xD1, 0xE5, 0xA7, 0x3E, 0xEC, 0xF9, 0xFC, 0xDE, 0x9C, 0xE9, - 0xF0, 0xF2, 0x53, 0xDF, 0xDB, 0xFF, 0x00, 0xED, 0x87, 0x8C, 0xB0, 0xC1, 0xAF, 0xD0, 0xDF, 0xF8, - 0x23, 0xAE, 0x95, 0xF1, 0xCB, 0x50, 0xF1, 0x37, 0x83, 0xD3, 0xE1, 0xAF, 0xFC, 0x28, 0xDB, 0x84, - 0x3E, 0x77, 0xD8, 0xE0, 0xF1, 0x5A, 0xC4, 0xD3, 0x7F, 0xC7, 0xC4, 0x9B, 0xBC, 0xD0, 0x14, 0xC9, - 0xF7, 0xF7, 0x63, 0xFD, 0x9D, 0xB5, 0xF9, 0xE8, 0xE3, 0x22, 0xBE, 0xAC, 0xFF, 0x00, 0x82, 0x76, - 0xF8, 0x77, 0xE0, 0xCE, 0xB7, 0xE2, 0xEF, 0x0F, 0xC7, 0xF1, 0x03, 0xE0, 0xFF, 0x00, 0xC6, 0x6F, - 0x88, 0x12, 0x48, 0x5F, 0xED, 0x7F, 0xF0, 0x8B, 0x4F, 0x26, 0xCB, 0xAF, 0xDE, 0xB6, 0xDF, 0x29, - 0x23, 0xD8, 0xFC, 0x2E, 0xD5, 0x38, 0x7F, 0xBC, 0xA6, 0xBC, 0x33, 0xF3, 0xD3, 0xD5, 0xFF, 0x00, - 0xE0, 0xA9, 0xBA, 0x37, 0xC7, 0x0D, 0x33, 0xC3, 0xDF, 0x18, 0x17, 0xC7, 0xFA, 0x5F, 0xEC, 0xEB, - 0xA7, 0xD8, 0xA7, 0x8D, 0x00, 0xD5, 0xFF, 0x00, 0xE1, 0x1A, 0x7D, 0x39, 0x75, 0x83, 0x76, 0x5E, - 0x3C, 0x1B, 0x55, 0xDC, 0x2E, 0xFE, 0xCE, 0x7E, 0x5C, 0xFC, 0xA0, 0x63, 0x76, 0x46, 0x32, 0x6B, - 0xF3, 0x96, 0xBE, 0xB2, 0xFD, 0xB8, 0x74, 0x5F, 0x81, 0xBA, 0x4C, 0xBF, 0x11, 0x23, 0xF0, 0x2F, - 0xC2, 0xBF, 0x8D, 0xDE, 0x11, 0xD6, 0x6D, 0xFC, 0x4A, 0x63, 0xD2, 0xE5, 0xF1, 0x25, 0xEA, 0xAD, - 0x96, 0x8F, 0x6B, 0x95, 0xDD, 0x6B, 0x71, 0x0B, 0x23, 0xCA, 0x66, 0x1F, 0x30, 0x05, 0xA6, 0xCF, - 0xCC, 0xB9, 0xCE, 0x30, 0x7E, 0x4D, 0xA0, 0x02, 0x8A, 0x28, 0xA0, 0x02, 0x8A, 0x28, 0xA0, 0x02, - 0x8A, 0x28, 0xA0, 0x02, 0x8A, 0x28, 0xA0, 0x02, 0x8A, 0x28, 0xA0, 0x02, 0x8A, 0x28, 0xA0, 0x02, - 0x8A, 0x28, 0xA0, 0x02, 0x8A, 0x28, 0xA0, 0x02, 0x8A, 0x28, 0xA0, 0x02, 0x8A, 0x28, 0xA0, 0x02, - 0xBE, 0xD8, 0xFF, 0x00, 0x82, 0x7A, 0x7C, 0x45, 0x5F, 0x0E, 0xF8, 0x7F, 0xC0, 0x36, 0x9F, 0xF0, - 0xD7, 0xBA, 0x87, 0xC1, 0xBF, 0x2B, 0xC5, 0xA9, 0x33, 0x68, 0x5F, 0xD9, 0x77, 0x92, 0xDB, 0x68, - 0x8B, 0xBB, 0x9D, 0x4B, 0xCD, 0xE6, 0xD8, 0x9F, 0xF6, 0x58, 0x7D, 0x6B, 0xE2, 0x7A, 0xFB, 0xCB, - 0xFE, 0x09, 0xA5, 0xE0, 0x1F, 0x88, 0x1A, 0xF7, 0x87, 0x7E, 0x1B, 0xDE, 0x68, 0x7F, 0xB2, 0xEF, - 0xC3, 0x3F, 0x8B, 0x1A, 0x4C, 0x9E, 0x35, 0x8E, 0x1B, 0x5D, 0x67, 0x57, 0x66, 0x8E, 0xF3, 0x55, - 0xB9, 0xDD, 0xC5, 0x8C, 0xCE, 0x67, 0x31, 0x88, 0x01, 0xEB, 0xBA, 0xDC, 0x8C, 0x75, 0x26, 0x80, - 0x26, 0xFF, 0x00, 0x82, 0x84, 0x78, 0xCA, 0xD7, 0xC5, 0x5F, 0x1B, 0xE7, 0x30, 0xFE, 0xD3, 0x97, - 0x3F, 0xB4, 0x0B, 0x19, 0xA4, 0x6F, 0x31, 0xAC, 0x4C, 0x11, 0xB8, 0xF2, 0x66, 0xFD, 0xF6, 0xE5, - 0xFD, 0xDE, 0x46, 0x71, 0x81, 0xCF, 0xCF, 0x5F, 0x3B, 0xBA, 0xF5, 0x15, 0xF5, 0x57, 0xFC, 0x14, - 0xE7, 0xC2, 0x1E, 0x36, 0xF0, 0xE7, 0xC6, 0xD9, 0xDB, 0xC5, 0x5F, 0xB3, 0xCF, 0xC3, 0xBF, 0x82, - 0x63, 0xED, 0x2E, 0xA5, 0x7C, 0x35, 0x2C, 0x52, 0xED, 0x7F, 0x22, 0x6C, 0x45, 0xBA, 0x3D, 0xAA, - 0x54, 0x80, 0x49, 0xF9, 0x07, 0xDC, 0x15, 0xF2, 0xBC, 0x83, 0x22, 0xB5, 0xA7, 0xB1, 0x51, 0x3E, - 0xA1, 0xFF, 0x00, 0x82, 0x2A, 0xE9, 0x67, 0x58, 0xFF, 0x00, 0x82, 0x97, 0x7C, 0x3B, 0xB7, 0xDB, - 0x93, 0x25, 0xAE, 0xB7, 0xFA, 0x68, 0x97, 0xE7, 0xFA, 0x57, 0x4D, 0xFB, 0x5E, 0xF8, 0x45, 0x97, - 0xE3, 0x07, 0x89, 0x8B, 0x27, 0x5D, 0x46, 0xE3, 0xB7, 0xFD, 0x34, 0x6A, 0x4F, 0xF8, 0x37, 0xF3, - 0x4B, 0x1A, 0xBF, 0xFC, 0x15, 0xC3, 0xE1, 0x65, 0xB1, 0x19, 0x13, 0x5B, 0x6B, 0xE3, 0x1E, 0xFF, - 0x00, 0xD8, 0x1A, 0x91, 0xAF, 0x71, 0xFD, 0xAE, 0xFE, 0x16, 0xFD, 0xAB, 0xE2, 0x7F, 0x88, 0x8E, - 0xC3, 0xBC, 0x5F, 0x4F, 0xDB, 0xFE, 0x9A, 0x35, 0x7F, 0x51, 0xFD, 0x1D, 0xEA, 0x53, 0xAB, 0x4B, - 0x1B, 0x82, 0xA9, 0xB4, 0xB9, 0x5F, 0xDE, 0x9A, 0xFD, 0x0F, 0x47, 0x0F, 0x9B, 0x2C, 0x3A, 0xA7, - 0x45, 0xEC, 0xE5, 0x27, 0xF7, 0xA8, 0xFF, 0x00, 0x91, 0xF9, 0xF9, 0xE2, 0x7D, 0x1C, 0xDB, 0x4E, - 0xDF, 0x29, 0x1C, 0xD6, 0x7E, 0x9F, 0x6C, 0xC9, 0x20, 0x27, 0x38, 0xCD, 0x7A, 0xC7, 0xC4, 0x9F, - 0x87, 0xF2, 0xD8, 0x5C, 0x3E, 0xE4, 0x38, 0xCF, 0xA5, 0x70, 0xEF, 0xA2, 0x79, 0x3D, 0x31, 0xC5, - 0x7B, 0x1C, 0x4F, 0x90, 0xD5, 0xC1, 0xE3, 0x25, 0xA6, 0x87, 0xD5, 0xC7, 0xDF, 0xD5, 0x1D, 0xFF, - 0x00, 0xC0, 0x1F, 0xDA, 0x1B, 0xC5, 0x9F, 0x01, 0xE6, 0xD5, 0x1B, 0xC3, 0x3A, 0xB4, 0xB6, 0x10, - 0xEB, 0xD6, 0x6D, 0xA7, 0x6A, 0x96, 0xAC, 0x8B, 0x2D, 0xB6, 0xA5, 0x6C, 0xC4, 0x13, 0x14, 0xD1, - 0xB0, 0x2A, 0xEB, 0x90, 0x0F, 0x23, 0x20, 0x8E, 0x08, 0xAF, 0x6A, 0xF0, 0x47, 0xFC, 0x14, 0x1B, - 0xE2, 0x2D, 0xBD, 0x96, 0xB1, 0xA5, 0xCB, 0xAE, 0x22, 0xE9, 0x9E, 0x22, 0xB2, 0x87, 0x4D, 0xBD, - 0xB5, 0x8A, 0xD2, 0x28, 0xE1, 0x7B, 0x58, 0x7F, 0xD5, 0x42, 0xAA, 0x17, 0x08, 0x88, 0x7E, 0x60, - 0x17, 0x1F, 0x37, 0x3C, 0x9A, 0xF1, 0x7F, 0x86, 0x9F, 0xB3, 0x47, 0x8B, 0x3E, 0x23, 0xFC, 0x27, - 0xF1, 0x37, 0x8E, 0x34, 0xA8, 0x74, 0xD1, 0xE1, 0x7F, 0x06, 0xDC, 0x5B, 0x5A, 0xEB, 0x37, 0xB7, - 0x37, 0xD1, 0xC0, 0x2C, 0x64, 0xB9, 0x62, 0xB6, 0xE1, 0x95, 0x8E, 0x4F, 0x98, 0xC1, 0x95, 0x70, - 0x0E, 0x4A, 0x91, 0xDA, 0xBB, 0x2D, 0x63, 0xF6, 0x34, 0xF8, 0x8D, 0xE0, 0x4F, 0x89, 0xBA, 0xAF, - 0x84, 0xF5, 0xCD, 0x26, 0xDB, 0x47, 0xD7, 0x34, 0x5D, 0x13, 0xFE, 0x12, 0x39, 0xD2, 0xF2, 0xF6, - 0x38, 0xA2, 0x7D, 0x3F, 0xCB, 0x12, 0x7D, 0xA6, 0x39, 0x09, 0xDA, 0xE9, 0xB1, 0x81, 0xF9, 0x4E, - 0x79, 0xC7, 0x5E, 0x28, 0xC9, 0x73, 0x2C, 0xBA, 0x53, 0x74, 0x71, 0x4A, 0x0E, 0x5D, 0x6F, 0x6B, - 0xE9, 0x67, 0x7F, 0x97, 0xBB, 0xE9, 0xA7, 0x91, 0xC7, 0x8C, 0xA3, 0x4A, 0x71, 0x71, 0x76, 0xBF, - 0xF5, 0xFF, 0x00, 0x00, 0xFA, 0x0B, 0xE1, 0x7F, 0xED, 0x6B, 0xE2, 0x0F, 0x86, 0xDA, 0x26, 0xA1, - 0x16, 0x87, 0xA9, 0x24, 0x16, 0xBA, 0xD5, 0xBA, 0xDB, 0xDF, 0xDA, 0xCB, 0x12, 0x4F, 0x6F, 0x76, - 0x8A, 0x77, 0x28, 0x74, 0x70, 0x41, 0x2A, 0x46, 0x41, 0xEA, 0x0D, 0x79, 0x3F, 0xC4, 0x1F, 0x1A, - 0xDF, 0xFC, 0x4A, 0xF1, 0x85, 0xE6, 0xA7, 0xAA, 0x5D, 0xC9, 0x7F, 0x7F, 0x7B, 0x26, 0xE9, 0x66, - 0x7C, 0x65, 0x8F, 0x40, 0x30, 0x38, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x0A, 0x93, 0x55, 0xFD, 0x96, - 0x7E, 0x24, 0xF8, 0x0B, 0xC3, 0x9A, 0x5D, 0xC5, 0xE5, 0x8E, 0x9E, 0xED, 0xAD, 0x68, 0xB6, 0xFE, - 0x21, 0xD3, 0x6D, 0x21, 0xD4, 0x62, 0x96, 0xEF, 0x50, 0xD3, 0xE7, 0x6D, 0xB0, 0xDC, 0x45, 0x08, - 0x3B, 0x9D, 0x5C, 0xF0, 0x30, 0x33, 0xC1, 0xE3, 0x8A, 0xE8, 0x24, 0xFD, 0x93, 0x7C, 0x7D, 0xE0, - 0x7B, 0x1D, 0x7E, 0xFF, 0x00, 0x57, 0xD3, 0x6D, 0x09, 0xF0, 0x84, 0x90, 0x27, 0x88, 0xAC, 0xED, - 0xAF, 0x62, 0xB8, 0xBC, 0xF0, 0xF1, 0x99, 0xB6, 0xC7, 0xF6, 0xB8, 0x94, 0x96, 0x8B, 0x73, 0x7C, - 0xBC, 0xF4, 0x6E, 0x0E, 0x2B, 0xDE, 0xFA, 0xC6, 0x5F, 0x1A, 0xBC, 0xF4, 0xE7, 0x0E, 0x69, 0x69, - 0x74, 0xD5, 0xDE, 0xCB, 0x7D, 0xDE, 0xB6, 0x5F, 0x72, 0xEC, 0x78, 0x38, 0x39, 0x7B, 0x06, 0xD2, - 0x39, 0xAD, 0x3B, 0xE1, 0xE8, 0xBF, 0xB7, 0xDD, 0xE5, 0xE4, 0x9F, 0x6A, 0xA9, 0xE2, 0x0F, 0x84, - 0x05, 0xED, 0xF1, 0xE5, 0x93, 0xF8, 0x57, 0xB5, 0x7C, 0x2F, 0xD0, 0xE0, 0xD4, 0xAD, 0xD1, 0x70, - 0x18, 0x91, 0xE9, 0x5D, 0xB5, 0xC7, 0xC3, 0x14, 0xB9, 0x4C, 0x94, 0x18, 0xC7, 0xA5, 0x74, 0xD6, - 0x8D, 0x27, 0x17, 0x09, 0x9F, 0x6F, 0x91, 0xE6, 0xE9, 0x4B, 0x92, 0xA6, 0xC7, 0xCB, 0xFF, 0x00, - 0xB3, 0xCF, 0xC1, 0x93, 0x07, 0xED, 0x29, 0xE0, 0x97, 0xF2, 0x8E, 0x06, 0xAD, 0x01, 0xCE, 0x3F, - 0xDB, 0x15, 0xF2, 0x27, 0xFC, 0x14, 0xAA, 0xD3, 0xEC, 0x5F, 0xB7, 0xF7, 0xC6, 0x28, 0xB1, 0x83, - 0x0F, 0x8B, 0x2F, 0xD7, 0xFF, 0x00, 0x23, 0x35, 0x7E, 0xB4, 0x7C, 0x0D, 0xF8, 0x59, 0x1B, 0x7E, - 0xD0, 0xBE, 0x11, 0xDB, 0x18, 0xF9, 0x75, 0x28, 0x88, 0xC0, 0xF7, 0xAF, 0xCA, 0xDF, 0xF8, 0x2A, - 0xF5, 0x97, 0xF6, 0x7F, 0xFC, 0x14, 0xAF, 0xE3, 0x9C, 0x18, 0xC7, 0x97, 0xE3, 0x4D, 0x49, 0x31, - 0xF4, 0x9D, 0xAB, 0xF9, 0xBB, 0xC5, 0x2C, 0xB6, 0x18, 0x6C, 0x4D, 0x29, 0x53, 0xFB, 0x49, 0xFE, - 0x67, 0xA3, 0xC7, 0xD2, 0xA3, 0x1C, 0xB3, 0x0F, 0x4E, 0x8F, 0xDA, 0x9C, 0xA5, 0xFF, 0x00, 0x92, - 0xA4, 0x7C, 0xEC, 0xE3, 0x06, 0xBE, 0xE6, 0xFF, 0x00, 0x82, 0x54, 0x78, 0xDF, 0x5D, 0xD0, 0xBC, - 0x43, 0xE1, 0x58, 0xF4, 0xDF, 0xDA, 0xBB, 0x41, 0xF8, 0x32, 0x23, 0x32, 0xEC, 0xB5, 0xD4, 0x6C, - 0xC4, 0xC9, 0xA6, 0xFE, 0xFD, 0xCE, 0x0E, 0xF2, 0xA8, 0x77, 0x1F, 0x9F, 0xAF, 0xF1, 0xD7, 0xC3, - 0x8E, 0xBD, 0x45, 0x7D, 0xAD, 0xFF, 0x00, 0x04, 0xB7, 0xF0, 0x4E, 0xA3, 0xE2, 0x0F, 0x11, 0xF8, - 0x65, 0xA2, 0xFD, 0x93, 0x74, 0xDF, 0x8D, 0xF0, 0x49, 0xE6, 0xE2, 0x49, 0xEE, 0xCC, 0x47, 0x53, - 0xFD, 0xFB, 0x8C, 0x90, 0xE7, 0x67, 0xCA, 0x41, 0x4F, 0xF8, 0x05, 0x7E, 0x52, 0x7E, 0x46, 0x5A, - 0xFF, 0x00, 0x82, 0x91, 0x7C, 0x40, 0xF1, 0x66, 0xB7, 0xE1, 0x5F, 0x8A, 0xB6, 0x7A, 0xA7, 0xED, - 0x79, 0xE1, 0x0F, 0x8A, 0xDA, 0x7D, 0xC7, 0x8B, 0xC4, 0xB2, 0xF8, 0x7B, 0x4D, 0x82, 0x58, 0xE4, - 0xF1, 0x1B, 0x86, 0x8F, 0x1A, 0x82, 0x84, 0x8B, 0xC8, 0x01, 0x71, 0x92, 0x04, 0xA7, 0xFD, 0x5F, - 0x04, 0xF0, 0x2B, 0xE0, 0x3A, 0xFB, 0xAB, 0xFE, 0x0A, 0x15, 0xE0, 0x19, 0x74, 0x4F, 0x0D, 0xFC, - 0x52, 0xBC, 0x6F, 0xD8, 0xDE, 0xE3, 0xE1, 0x08, 0xB7, 0xF1, 0x6F, 0x92, 0xDE, 0x21, 0x4D, 0x4A, - 0xEE, 0x5B, 0x7F, 0x0D, 0x36, 0xF8, 0xFF, 0x00, 0xE2, 0x5E, 0x10, 0x7F, 0xA3, 0x10, 0xD9, 0xC6, - 0x47, 0xFC, 0xF4, 0xE3, 0xB5, 0x7C, 0x2B, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, - 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, - 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, - 0x05, 0x7D, 0x3B, 0xFB, 0x14, 0xEA, 0x7F, 0x01, 0xEC, 0xAE, 0xFC, 0x17, 0xFF, 0x00, 0x09, 0xE6, - 0xB9, 0xF1, 0xCF, 0x47, 0xF1, 0x02, 0x78, 0x92, 0x36, 0xD4, 0x9F, 0xC2, 0xC2, 0x06, 0xB3, 0x5B, - 0x0C, 0xF5, 0xB7, 0x0A, 0x45, 0xC0, 0xB9, 0x3E, 0xA3, 0x38, 0xC7, 0x00, 0xD7, 0xCC, 0x55, 0xFA, - 0x2D, 0xFF, 0x00, 0x04, 0xBA, 0xF8, 0x81, 0xF1, 0xD3, 0x47, 0xF8, 0x71, 0xF0, 0xCE, 0xC7, 0xC0, - 0xBE, 0x3E, 0xF8, 0x0B, 0xA1, 0xF8, 0x76, 0x1F, 0x1F, 0x47, 0x3E, 0x9F, 0x65, 0xE2, 0xA7, 0xB0, - 0xFE, 0xD1, 0xB3, 0xBE, 0xDC, 0x3F, 0xD2, 0x26, 0x0E, 0xA2, 0xE0, 0x5B, 0x8E, 0xC4, 0x38, 0x1C, - 0xF1, 0x8A, 0x00, 0xF3, 0xBF, 0xDB, 0x23, 0x54, 0xF8, 0x2D, 0xA8, 0xFC, 0x73, 0x98, 0xFC, 0x2D, - 0xD6, 0xBE, 0x33, 0xEA, 0xEC, 0x5A, 0x52, 0x1B, 0xC6, 0xCE, 0x5B, 0x31, 0x79, 0x32, 0xE4, 0xFE, - 0xF0, 0x79, 0xBB, 0xF3, 0xB7, 0x19, 0xE3, 0x1B, 0xAB, 0xC8, 0x9D, 0x70, 0x7E, 0xB5, 0xF5, 0xEF, - 0xFC, 0x15, 0x6F, 0xC6, 0x1F, 0x18, 0x3C, 0x45, 0xF1, 0xBA, 0xE2, 0x3F, 0x89, 0xBF, 0x10, 0xBE, - 0x11, 0xF8, 0xBD, 0xBE, 0xD6, 0xEE, 0x62, 0xF0, 0x73, 0xC6, 0x40, 0x90, 0x41, 0x38, 0x0E, 0x07, - 0x2F, 0xB0, 0x02, 0xC0, 0xE4, 0xE3, 0x2C, 0xB5, 0xF2, 0x23, 0x8C, 0x8A, 0xD6, 0x9E, 0xC5, 0x44, - 0xFB, 0x5F, 0xFE, 0x0D, 0xC8, 0x87, 0xCF, 0xFF, 0x00, 0x82, 0xCE, 0x7C, 0x1C, 0x8F, 0xA8, 0x75, - 0xD7, 0x54, 0xFE, 0x3A, 0x0E, 0xA3, 0x5F, 0x6F, 0xFE, 0xD6, 0xBF, 0x0D, 0xC8, 0xF8, 0xA5, 0xAF, - 0xBF, 0x97, 0xC3, 0xDE, 0xCC, 0x7A, 0x7F, 0xB6, 0x6B, 0xE2, 0x4F, 0xF8, 0x37, 0x08, 0x67, 0xFE, - 0x0B, 0x43, 0xF0, 0x63, 0xFE, 0xE3, 0x9F, 0xFA, 0x61, 0xD4, 0x6B, 0xF5, 0x93, 0xF6, 0xA5, 0xF8, - 0x69, 0x1D, 0xD7, 0x8C, 0xF5, 0x57, 0xDA, 0xBF, 0x34, 0xF2, 0x1F, 0xFC, 0x78, 0xD7, 0xED, 0x9E, - 0x0A, 0xE6, 0x8B, 0x05, 0x98, 0x57, 0xBF, 0xDA, 0x51, 0xFC, 0x1B, 0x3E, 0x67, 0x88, 0xAB, 0xCA, - 0x8C, 0xA9, 0x4E, 0x3D, 0xDF, 0xE8, 0x7E, 0x64, 0x7C, 0x55, 0xF8, 0x58, 0xB7, 0x91, 0x4B, 0xFB, - 0xBE, 0x7E, 0x95, 0xF3, 0x8F, 0x8E, 0xFC, 0x0B, 0x2E, 0x87, 0x76, 0xE7, 0x61, 0xDB, 0x9F, 0x4A, - 0xFD, 0x0C, 0xF8, 0xA1, 0xE0, 0x18, 0xAD, 0x1A, 0x4C, 0x85, 0xC5, 0x7C, 0xEB, 0xF1, 0x6B, 0xC0, - 0xB6, 0xD2, 0xC7, 0x26, 0x54, 0x12, 0x6B, 0xFA, 0xDF, 0x15, 0x84, 0xA3, 0x9C, 0x61, 0x39, 0x1F, - 0xC4, 0x96, 0x8C, 0xFD, 0x0B, 0x86, 0x33, 0x48, 0xE2, 0x69, 0x28, 0x49, 0xEA, 0x4F, 0xF0, 0x57, - 0xC7, 0xDF, 0x0C, 0xF4, 0x5F, 0xF8, 0x27, 0xED, 0x9F, 0x80, 0x35, 0x1F, 0x88, 0x1A, 0x1E, 0x8B, - 0xE2, 0x5F, 0x19, 0x7C, 0x51, 0xD3, 0x75, 0xCF, 0x13, 0xC1, 0x3E, 0x97, 0xAA, 0x4D, 0xF6, 0x2D, - 0x0E, 0xCA, 0x07, 0x58, 0x83, 0xB4, 0x56, 0xAC, 0x92, 0x3F, 0x9E, 0xF2, 0x3E, 0xC8, 0xD9, 0xBE, - 0x56, 0x5F, 0xE2, 0xCA, 0xAF, 0x41, 0xF1, 0x5B, 0xF6, 0xF6, 0xF0, 0x3F, 0xC6, 0x8F, 0xD9, 0x73, - 0xC6, 0x3E, 0x17, 0xF1, 0x46, 0xB1, 0x0C, 0xFF, 0x00, 0x11, 0xB4, 0x3D, 0x56, 0x4F, 0x0C, 0x78, - 0x2F, 0xC4, 0xAF, 0x05, 0xD1, 0xB7, 0xB9, 0xF0, 0xB5, 0xE6, 0xA3, 0x1D, 0xDB, 0x2C, 0xC7, 0xCA, - 0xF3, 0x80, 0xB4, 0xFB, 0x2A, 0xA2, 0x82, 0x81, 0xFC, 0xBB, 0x96, 0x55, 0x47, 0x39, 0x03, 0xE4, - 0x3F, 0x89, 0x5A, 0x19, 0xD0, 0x5E, 0x46, 0x84, 0x80, 0x39, 0xAF, 0x0A, 0xF1, 0xA7, 0x89, 0xEE, - 0x23, 0xBC, 0x2A, 0x64, 0x3D, 0x7D, 0x6B, 0xF9, 0xA3, 0x89, 0x32, 0x9A, 0x79, 0x4E, 0x21, 0xD5, - 0xAB, 0xCD, 0xCC, 0xA5, 0xCC, 0xB6, 0xD1, 0xDD, 0x6D, 0xA6, 0xCD, 0x2B, 0x5B, 0xB7, 0x9E, 0xA7, - 0xAD, 0x8D, 0xA7, 0x4E, 0x9B, 0xBB, 0xBD, 0xEF, 0x7F, 0xEB, 0xE5, 0xA1, 0xFA, 0xD3, 0xF1, 0x07, - 0xFE, 0x0A, 0x73, 0xF0, 0xD7, 0xC1, 0x3F, 0xF0, 0x50, 0x2F, 0x86, 0x7A, 0xCF, 0x87, 0x61, 0xF0, - 0xEF, 0x8D, 0x3E, 0x13, 0xFC, 0x38, 0xB6, 0xF0, 0xDE, 0x9B, 0x63, 0xAC, 0x58, 0xE8, 0x93, 0x5B, - 0xEB, 0x31, 0xD9, 0x69, 0xF6, 0x8B, 0x14, 0x91, 0x66, 0xE6, 0x28, 0x65, 0x38, 0x9C, 0xC9, 0x20, - 0x8C, 0xEE, 0x52, 0x02, 0x90, 0xC1, 0x89, 0x03, 0x85, 0xF0, 0xA7, 0xED, 0x31, 0xE0, 0x6F, 0xD9, - 0xC7, 0xE1, 0xDF, 0xED, 0x0B, 0x2D, 0x9F, 0x8F, 0x2D, 0x3E, 0x24, 0xF8, 0xCB, 0xE3, 0x63, 0x7F, - 0x63, 0xE9, 0x86, 0xC2, 0xCA, 0xF6, 0x28, 0x34, 0xFD, 0x39, 0xEE, 0xCD, 0xCC, 0xF7, 0x77, 0x6F, - 0x75, 0x14, 0x47, 0xCF, 0x71, 0xB1, 0x12, 0x24, 0x0E, 0x54, 0xA9, 0x66, 0x6E, 0x40, 0xAF, 0xCE, - 0xCF, 0x00, 0x6B, 0x12, 0x5C, 0x40, 0x77, 0x31, 0xC0, 0x1E, 0xB5, 0xD9, 0xD9, 0xC9, 0xBD, 0x81, - 0x07, 0x9A, 0xF7, 0x78, 0x77, 0x09, 0x83, 0xC6, 0xE1, 0x61, 0xCA, 0xE4, 0xAC, 0xA2, 0x9E, 0xAB, - 0xDE, 0xE4, 0x97, 0x32, 0xBE, 0x9B, 0xDF, 0x7E, 0x5B, 0x5F, 0xA9, 0xE3, 0xE2, 0x70, 0xAB, 0xD9, - 0xA9, 0xC0, 0xFB, 0x37, 0xF6, 0x79, 0xF8, 0x82, 0x97, 0x31, 0xC3, 0x97, 0xE8, 0x3D, 0x6B, 0xE8, - 0x4D, 0x07, 0xC4, 0x2B, 0x76, 0x8B, 0x93, 0x95, 0x35, 0xF9, 0xFF, 0x00, 0xF0, 0x83, 0xC7, 0xB2, - 0x78, 0x6E, 0x74, 0x0C, 0xE4, 0x0C, 0xF1, 0x5F, 0x52, 0xFC, 0x30, 0xF8, 0x9F, 0x1E, 0xA3, 0x6F, - 0x17, 0xEF, 0x06, 0x70, 0x3B, 0xD7, 0xD6, 0x4A, 0x57, 0x7A, 0x9E, 0x5E, 0x17, 0x19, 0x28, 0x55, - 0x3E, 0xAA, 0xFD, 0x97, 0x7C, 0x3B, 0x1E, 0xAB, 0xFB, 0x44, 0xF8, 0x44, 0x80, 0x08, 0x3A, 0x84, - 0x67, 0xF9, 0xD7, 0xE2, 0xFF, 0x00, 0xFC, 0x16, 0x46, 0xD5, 0x6D, 0x7F, 0xE0, 0xAB, 0x1F, 0xB4, - 0x2C, 0x4B, 0xD1, 0x7C, 0x79, 0xAA, 0xA8, 0xFC, 0x2E, 0x1E, 0xBF, 0x68, 0x3F, 0x61, 0x5D, 0x6D, - 0x75, 0x2F, 0xDA, 0x5F, 0xC1, 0xB1, 0xEE, 0xDD, 0xE6, 0x6A, 0x31, 0x8F, 0xE7, 0x5F, 0x8C, 0xFF, - 0x00, 0xF0, 0x59, 0xC5, 0xCF, 0xFC, 0x15, 0x83, 0xF6, 0x88, 0xFF, 0x00, 0xB1, 0xFB, 0x56, 0xFF, - 0x00, 0xD2, 0x97, 0xAF, 0xC0, 0xFC, 0x5E, 0x93, 0xFA, 0xE5, 0x08, 0x74, 0xE5, 0x7F, 0x99, 0xEF, - 0x67, 0xF8, 0xE9, 0x57, 0xA5, 0x46, 0x2D, 0xE8, 0xAF, 0xFA, 0x1F, 0x2E, 0xB8, 0xEF, 0x5F, 0x51, - 0x7F, 0xC1, 0x3B, 0x7C, 0x4F, 0xF0, 0x8B, 0xC3, 0x9E, 0x33, 0xD0, 0x64, 0xF1, 0xD7, 0xC7, 0x2F, - 0x8B, 0x9F, 0x0B, 0x1A, 0x32, 0xFF, 0x00, 0x6A, 0x9B, 0xC3, 0x36, 0xEF, 0xB6, 0xCB, 0xF7, 0xAD, - 0x8F, 0x2D, 0xD1, 0x64, 0x6E, 0x57, 0x6B, 0x1F, 0xDD, 0xF5, 0x26, 0xBE, 0x60, 0x71, 0x83, 0x5F, - 0x7B, 0xFF, 0x00, 0xC1, 0x1F, 0xEF, 0xBE, 0x37, 0x5A, 0x78, 0xD3, 0xC2, 0x67, 0xE1, 0xB6, 0x91, - 0xF0, 0x4F, 0x51, 0x2A, 0x65, 0xFB, 0x18, 0xF1, 0x4F, 0x90, 0x93, 0x37, 0xFA, 0x44, 0x99, 0xF3, - 0x5B, 0xFD, 0x67, 0xDE, 0xDD, 0x8C, 0xFF, 0x00, 0x0E, 0x2B, 0xF2, 0x13, 0xE6, 0x4F, 0x1B, 0xFD, - 0xB9, 0x7C, 0x45, 0xF0, 0x9F, 0x5B, 0x9B, 0xE2, 0x24, 0xBE, 0x11, 0xFD, 0xA0, 0xBE, 0x30, 0x7C, - 0x48, 0xBE, 0xB8, 0xF1, 0x11, 0x7D, 0x32, 0xCF, 0x5F, 0xD1, 0x9C, 0x5A, 0x6B, 0x76, 0xBB, 0x93, - 0xFD, 0x2E, 0xE2, 0xE5, 0xE7, 0x46, 0x13, 0x01, 0xB8, 0xE0, 0xDA, 0x8C, 0x95, 0x5E, 0x99, 0xC8, - 0xF9, 0x2E, 0xBF, 0x45, 0xFF, 0x00, 0xE0, 0xA7, 0xED, 0xF1, 0xC6, 0xE3, 0xC2, 0x9F, 0x18, 0xA6, - 0xF1, 0xF7, 0x81, 0x7F, 0x67, 0xBD, 0x26, 0xC5, 0xFC, 0x6B, 0x9D, 0x63, 0x52, 0xF0, 0xE4, 0x36, - 0x0D, 0xAC, 0x45, 0x77, 0xBE, 0x3C, 0x2D, 0xAC, 0x82, 0x43, 0x73, 0xF6, 0x76, 0x3B, 0x73, 0xF2, - 0xE3, 0x96, 0xCE, 0x39, 0xAF, 0xCE, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x2B, 0xEB, 0xCF, 0xD8, 0x3F, 0x44, 0xF8, 0x7F, 0xAB, 0x59, 0xF8, 0x06, 0x1F, 0x11, 0x7E, 0xCB, - 0xFF, 0x00, 0x11, 0x3E, 0x2C, 0x6A, 0x57, 0x1E, 0x2B, 0x8E, 0x39, 0xF5, 0x5D, 0x1F, 0x5D, 0xB9, - 0x8E, 0xDF, 0x5C, 0x80, 0xB7, 0xFC, 0x83, 0xA3, 0xB5, 0x58, 0x36, 0x19, 0x4F, 0x4C, 0x89, 0x81, - 0x3E, 0xD5, 0xF2, 0x1D, 0x7D, 0x9D, 0xFB, 0x01, 0xF8, 0xF9, 0x3C, 0x3D, 0xE1, 0xFF, 0x00, 0x01, - 0xDB, 0xCB, 0xFB, 0x5E, 0x6B, 0x9F, 0x07, 0x6D, 0xE0, 0xF1, 0x5A, 0x4F, 0x2E, 0x83, 0x1D, 0xA5, - 0xF4, 0x96, 0xBA, 0x22, 0xEE, 0xE7, 0x52, 0x05, 0x58, 0xDB, 0xB3, 0x77, 0xDA, 0x53, 0x3E, 0xB4, - 0x01, 0xD2, 0x7E, 0xDE, 0x5E, 0x13, 0xF0, 0x8F, 0x86, 0x3E, 0x37, 0xCD, 0x1F, 0x85, 0xFF, 0x00, - 0x66, 0x7F, 0x1B, 0x7C, 0x05, 0x8C, 0x4B, 0x20, 0x03, 0x5E, 0xB9, 0xB8, 0x66, 0x55, 0xF2, 0x65, - 0xFD, 0xC9, 0x8E, 0x42, 0xCA, 0x0B, 0x70, 0x72, 0x1B, 0x3F, 0x21, 0xF7, 0xAF, 0x06, 0x61, 0x83, - 0x5E, 0xEF, 0xFB, 0x77, 0x78, 0xD2, 0xC3, 0xC5, 0xBF, 0x1B, 0x26, 0x92, 0xC7, 0xF6, 0x9A, 0xD5, - 0x3E, 0x3F, 0x29, 0x96, 0x46, 0x12, 0x5C, 0x59, 0x49, 0x6E, 0x8E, 0x3C, 0x99, 0x7F, 0x7D, 0xCE, - 0x13, 0x23, 0x38, 0xE0, 0x67, 0xE7, 0x35, 0xE1, 0x4E, 0x32, 0x2B, 0x5A, 0x7B, 0x15, 0x13, 0xED, - 0xAF, 0xF8, 0x37, 0x08, 0x7F, 0xC6, 0xE8, 0xFE, 0x0B, 0xFD, 0x75, 0xCF, 0xFD, 0x30, 0xEA, 0x35, - 0xFB, 0x35, 0xFB, 0x4E, 0xA7, 0xFC, 0x54, 0xFA, 0x98, 0xDB, 0xC8, 0x99, 0xFB, 0x7B, 0x9A, 0xFC, - 0x0A, 0xFF, 0x00, 0x82, 0x54, 0xFE, 0xD6, 0x3A, 0x5F, 0xEC, 0x3B, 0xFF, 0x00, 0x05, 0x07, 0xF8, - 0x63, 0xF1, 0x43, 0x5C, 0xB7, 0x92, 0xE3, 0x44, 0xF0, 0xDE, 0xA1, 0x34, 0x7A, 0x90, 0x8D, 0x4B, - 0x49, 0x1D, 0xAD, 0xD5, 0xAC, 0xF6, 0x73, 0x4A, 0xAA, 0x08, 0xDC, 0xD1, 0xC7, 0x70, 0xCE, 0x06, - 0x79, 0x28, 0x07, 0x7A, 0xFE, 0x93, 0x3E, 0x37, 0xFC, 0x11, 0x8B, 0xE3, 0xFE, 0x85, 0x6F, 0xE3, - 0x6F, 0x87, 0x7A, 0x86, 0x9F, 0xE2, 0xAD, 0x07, 0xC4, 0x50, 0x8B, 0xCB, 0x79, 0xAC, 0x6E, 0x12, - 0x48, 0xE5, 0x0D, 0xCE, 0xE4, 0x60, 0x70, 0xC0, 0x9E, 0xDD, 0x41, 0xC8, 0x35, 0xF7, 0x3C, 0x07, - 0x99, 0xE1, 0xF0, 0x79, 0x83, 0xFA, 0xC4, 0x94, 0x54, 0x92, 0xB3, 0x7A, 0x2D, 0x0F, 0x95, 0xE2, - 0xCC, 0x1D, 0x7A, 0xD8, 0x78, 0xCA, 0x82, 0xBB, 0x8B, 0xD9, 0x6E, 0x7E, 0x6B, 0x7C, 0x69, 0xBA, - 0xF2, 0x9A, 0x5C, 0xF1, 0x8A, 0xF9, 0x9B, 0xE2, 0xB6, 0xB5, 0x14, 0x51, 0x4B, 0xC8, 0x26, 0xBE, - 0xE4, 0xFD, 0xA1, 0x7F, 0x60, 0xDF, 0x8B, 0xFA, 0xCB, 0xC8, 0x74, 0xEF, 0x04, 0x6B, 0x17, 0x2B, - 0xDB, 0xCA, 0x8C, 0x36, 0x7F, 0x5A, 0xF9, 0x0F, 0xE2, 0xE7, 0xFC, 0x13, 0xCF, 0xE3, 0xFC, 0x46, - 0x43, 0xFF, 0x00, 0x0A, 0xBF, 0xC5, 0xAE, 0xA4, 0xF5, 0x5B, 0x32, 0x45, 0x7F, 0x6E, 0xF0, 0x9E, - 0x7D, 0x94, 0x4A, 0x9C, 0x5B, 0xC5, 0x53, 0xBF, 0x6E, 0x78, 0xFF, 0x00, 0x99, 0xC3, 0xC2, 0x19, - 0x96, 0x22, 0x85, 0x55, 0x1A, 0xB0, 0x92, 0xF5, 0x4C, 0xF8, 0xA7, 0xE3, 0xA7, 0x8B, 0x44, 0x62, - 0x45, 0x04, 0x0C, 0x83, 0x5F, 0x39, 0x6B, 0x12, 0x3E, 0xAF, 0xA9, 0x33, 0xF2, 0x54, 0x1E, 0x3D, - 0xEB, 0xEC, 0x9F, 0x8A, 0xFF, 0x00, 0xF0, 0x4D, 0xCF, 0x8F, 0x3A, 0x9F, 0x9B, 0x9F, 0x86, 0x3E, - 0x2B, 0xE8, 0x4F, 0x36, 0x84, 0x57, 0x9C, 0x5A, 0xFF, 0x00, 0xC1, 0x37, 0xFE, 0x33, 0x5A, 0xC8, - 0x12, 0xE3, 0xE1, 0xCF, 0x88, 0xED, 0xCA, 0x9E, 0x7C, 0xC8, 0x02, 0xFF, 0x00, 0x33, 0x5F, 0x85, - 0xF8, 0xAD, 0x1A, 0x98, 0xBC, 0xC9, 0xC6, 0x8B, 0x52, 0x87, 0x46, 0x9A, 0x6B, 0xEF, 0x4C, 0xFD, - 0x92, 0xA5, 0x3A, 0xD8, 0xAB, 0x4A, 0x31, 0x6D, 0x7A, 0x33, 0xCD, 0x7E, 0x1D, 0xD8, 0x32, 0xD9, - 0xB1, 0xC1, 0xE9, 0x5D, 0x6D, 0x81, 0xF2, 0xFF, 0x00, 0x0A, 0xF5, 0x3F, 0x0C, 0xFE, 0xC1, 0xDF, - 0x13, 0x74, 0xBB, 0x02, 0x24, 0xF0, 0x7E, 0xA5, 0x1B, 0x63, 0xA3, 0x6C, 0x07, 0xFF, 0x00, 0x42, - 0xA4, 0x8B, 0xF6, 0x34, 0xF8, 0x9B, 0xE7, 0x32, 0x8F, 0x07, 0xEA, 0xB8, 0xCF, 0x07, 0x09, 0xFF, - 0x00, 0xC5, 0x57, 0x37, 0x0E, 0xDB, 0x07, 0x4E, 0x09, 0xC9, 0x5F, 0xD5, 0x7F, 0x99, 0xE8, 0x4B, - 0x2F, 0xA9, 0x1A, 0x29, 0x34, 0x71, 0xBA, 0x56, 0xA3, 0xB7, 0x1B, 0x4E, 0x08, 0xF7, 0xAF, 0x47, - 0xF8, 0x6D, 0xF1, 0x26, 0x5D, 0x16, 0xE6, 0x35, 0x32, 0x61, 0x46, 0x3A, 0x9A, 0xA9, 0x6B, 0xFB, - 0x15, 0x7C, 0x55, 0x0C, 0x0C, 0x7E, 0x09, 0xD6, 0xE4, 0xFF, 0x00, 0x76, 0x35, 0x3F, 0xC8, 0xD6, - 0xA6, 0x99, 0xFB, 0x12, 0xFC, 0x68, 0xB8, 0xBC, 0x4F, 0x23, 0xE1, 0xCF, 0x8A, 0xE5, 0xE7, 0xF8, - 0x2C, 0xCB, 0x7F, 0x23, 0x5F, 0x63, 0x8A, 0xC5, 0xE1, 0xD7, 0xBF, 0xED, 0x22, 0xBF, 0xED, 0xE5, - 0xFE, 0x67, 0xC4, 0xE3, 0x30, 0x75, 0x29, 0xD5, 0xBA, 0x4C, 0xFB, 0x8F, 0xFE, 0x09, 0xAD, 0xF1, - 0x1C, 0x6B, 0x3F, 0xB5, 0x47, 0xC3, 0xF8, 0xD5, 0xF2, 0xF2, 0x6B, 0x10, 0x2E, 0x3E, 0xA7, 0x15, - 0xF9, 0x81, 0xFF, 0x00, 0x05, 0x99, 0x5C, 0xFF, 0x00, 0xC1, 0x57, 0xBF, 0x68, 0x8F, 0xFB, 0x1F, - 0xB5, 0x6F, 0xFD, 0x29, 0x7A, 0xFD, 0x7A, 0xFF, 0x00, 0x82, 0x48, 0xFE, 0xC0, 0xFE, 0x26, 0xFD, - 0x9E, 0xBC, 0x45, 0x27, 0xC7, 0x0F, 0x8E, 0x26, 0x2F, 0x87, 0x3E, 0x07, 0xF8, 0x7D, 0x03, 0xEA, - 0x61, 0xB5, 0x49, 0x96, 0x17, 0x95, 0xD5, 0x48, 0x0C, 0xC3, 0x3F, 0x2A, 0x8C, 0xF0, 0x3E, 0xF3, - 0x31, 0x00, 0x0C, 0xF5, 0xFC, 0x4B, 0xFD, 0xBC, 0xFE, 0x3C, 0x58, 0xFE, 0xD4, 0xBF, 0xB6, 0x87, - 0xC5, 0x2F, 0x88, 0xFA, 0x5D, 0xAC, 0xD6, 0x7A, 0x6F, 0x8D, 0xBC, 0x4F, 0x7F, 0xAC, 0x5A, 0x41, - 0x29, 0xCC, 0x91, 0x45, 0x34, 0xEC, 0xE8, 0x1B, 0xDF, 0x69, 0x19, 0xAF, 0xE7, 0x6F, 0x12, 0xF3, - 0x8C, 0x2E, 0x3B, 0x32, 0x85, 0x2C, 0x2C, 0xD4, 0xFD, 0x9C, 0x6C, 0xDA, 0xD5, 0x5D, 0xB6, 0xED, - 0x75, 0xA3, 0xB2, 0xED, 0xDC, 0xD3, 0x11, 0x52, 0x4E, 0x11, 0x84, 0xB7, 0x57, 0xFC, 0x6D, 0xFE, - 0x47, 0x8D, 0xBA, 0x73, 0x5F, 0x4C, 0x7F, 0xC1, 0x3E, 0xAC, 0x3E, 0x0B, 0xDF, 0x78, 0xC7, 0x43, - 0x8F, 0xE2, 0x27, 0xC2, 0xEF, 0x8C, 0x3E, 0x32, 0xCB, 0x3F, 0xDB, 0x1B, 0xC2, 0xB7, 0x12, 0x7F, - 0xA4, 0x7E, 0xF5, 0xB1, 0xE5, 0xA4, 0x65, 0x5B, 0x85, 0xDA, 0xA7, 0xE6, 0xEA, 0x0D, 0x7C, 0xD7, - 0x22, 0xD7, 0xDA, 0xFF, 0x00, 0xF0, 0x4A, 0x5F, 0x13, 0xF8, 0xEA, 0xC3, 0xC6, 0x7E, 0x18, 0x87, - 0xC2, 0x9F, 0xB5, 0x07, 0x81, 0xFE, 0x10, 0xCA, 0x86, 0x4F, 0x26, 0xDF, 0x5E, 0x51, 0xE5, 0x69, - 0xD9, 0x9D, 0xC9, 0x0F, 0xE6, 0x05, 0x4F, 0x98, 0xE5, 0xFE, 0xFF, 0x00, 0x47, 0xAF, 0xCE, 0x4E, - 0x33, 0xCB, 0x7F, 0x6D, 0x3B, 0x7F, 0x80, 0x36, 0xFF, 0x00, 0xF0, 0xB1, 0x23, 0xF0, 0x1F, 0xC3, - 0xEF, 0x8E, 0x9E, 0x1B, 0xD7, 0x93, 0xC4, 0x84, 0x69, 0x32, 0x78, 0x96, 0xF6, 0x2F, 0xB0, 0xE9, - 0xB6, 0x7B, 0x97, 0x74, 0x17, 0x31, 0x32, 0x19, 0xBC, 0xFC, 0x6E, 0xC6, 0xE9, 0x49, 0xCB, 0x2E, - 0x7A, 0x60, 0xFC, 0xA5, 0x5F, 0x7E, 0x7F, 0xC1, 0x49, 0x7C, 0x75, 0xF1, 0x2A, 0xF3, 0xC3, 0xDF, - 0x16, 0x34, 0xBF, 0x12, 0x7E, 0xD5, 0x5F, 0x0D, 0xFE, 0x27, 0xE9, 0xB2, 0x78, 0xB8, 0x49, 0x7B, - 0xA1, 0x68, 0xAE, 0x7C, 0xCF, 0x10, 0xCF, 0xBE, 0x32, 0x2F, 0xA1, 0x09, 0x01, 0x8B, 0xCB, 0x04, - 0x03, 0xC4, 0xE7, 0x84, 0x3D, 0x7B, 0xFC, 0x07, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, - 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, - 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, - 0x40, 0x05, 0x7B, 0xE7, 0xEC, 0xDB, 0xFB, 0x6E, 0xD9, 0xFE, 0xCF, 0xB6, 0x1E, 0x0F, 0xB7, 0xB8, - 0xF8, 0x47, 0xF0, 0xAF, 0xC5, 0x8D, 0xE1, 0x3F, 0x11, 0xC7, 0xE2, 0x13, 0xA8, 0x6A, 0x7A, 0x5B, - 0x36, 0xA7, 0xAA, 0x04, 0x39, 0xFB, 0x1C, 0xF3, 0xEF, 0x2A, 0xD6, 0xC7, 0xBA, 0x79, 0x7E, 0x9C, - 0xD7, 0x81, 0xD1, 0x40, 0x1F, 0x46, 0x7C, 0x70, 0xFD, 0xAC, 0x74, 0xBF, 0xDA, 0xD7, 0xF6, 0x9A, - 0xFF, 0x00, 0x84, 0xAA, 0x0F, 0x01, 0xE8, 0x7E, 0x02, 0x9F, 0x56, 0x32, 0x42, 0x2C, 0x74, 0x57, - 0x0B, 0x67, 0x19, 0x74, 0x70, 0xAA, 0xB1, 0xEC, 0x5C, 0x65, 0x8A, 0x8E, 0xB5, 0x9F, 0x5E, 0x0F, - 0x6D, 0x71, 0x25, 0xA4, 0xC9, 0x2C, 0x6C, 0xC9, 0x24, 0x6C, 0x19, 0x58, 0x1C, 0x15, 0x23, 0x90, - 0x6B, 0xDA, 0xBC, 0x21, 0xE2, 0xE8, 0x7E, 0x20, 0xDB, 0xAB, 0x42, 0xC8, 0xBA, 0xAE, 0x3F, 0x7F, - 0x6A, 0x38, 0x69, 0x1B, 0xBB, 0xC6, 0x3B, 0x83, 0xD4, 0xA8, 0xE4, 0x7A, 0x63, 0xA6, 0x90, 0x7D, - 0x0A, 0x89, 0x6D, 0x86, 0x0D, 0x7A, 0x67, 0xC1, 0x3F, 0xDB, 0x5B, 0xE2, 0xFF, 0x00, 0xEC, 0xD1, - 0xA5, 0xCD, 0x63, 0xF0, 0xFF, 0x00, 0xE2, 0x67, 0x8E, 0x3C, 0x1B, 0x63, 0x70, 0xC5, 0xA4, 0xB6, - 0xD2, 0x35, 0x89, 0xED, 0x61, 0x72, 0x7A, 0x9D, 0x8A, 0xC1, 0x73, 0xD3, 0x9C, 0x57, 0x9B, 0x4B, - 0x13, 0x21, 0x21, 0x94, 0xAB, 0x0E, 0x08, 0x23, 0x04, 0x54, 0x4C, 0x32, 0x2B, 0x42, 0x8F, 0xA1, - 0x5B, 0xFE, 0x0A, 0xEF, 0xFB, 0x51, 0x86, 0xC7, 0xFC, 0x2F, 0xEF, 0x8B, 0x1F, 0xF8, 0x51, 0xDC, - 0xFF, 0x00, 0xF1, 0x55, 0x13, 0xFF, 0x00, 0xC1, 0x5E, 0x7F, 0x6A, 0x4C, 0x7F, 0xC9, 0xC0, 0x7C, - 0x58, 0xFF, 0x00, 0xC2, 0x8E, 0xE7, 0xFF, 0x00, 0x8A, 0xAF, 0x9E, 0xDC, 0x64, 0x54, 0x4E, 0x3B, - 0xD1, 0x64, 0x2B, 0x1E, 0xED, 0xA8, 0x7F, 0xC1, 0x56, 0x3F, 0x69, 0x5D, 0x47, 0x3F, 0x68, 0xF8, - 0xE9, 0xF1, 0x3E, 0x6C, 0xF5, 0xDF, 0xAF, 0xDC, 0x1F, 0xFD, 0x9A, 0xB1, 0xAF, 0x7F, 0xE0, 0xA4, - 0x5F, 0x1F, 0xEF, 0x39, 0x9B, 0xE3, 0x17, 0xC4, 0x49, 0x0F, 0x7D, 0xDA, 0xDC, 0xE7, 0xFF, 0x00, - 0x66, 0xAF, 0x1C, 0x75, 0xED, 0x50, 0xB8, 0xEF, 0x5A, 0x46, 0xB5, 0x48, 0xAB, 0x46, 0x4D, 0x7C, - 0xCD, 0xE9, 0xE3, 0x2B, 0xD3, 0xFE, 0x1C, 0xDA, 0xF4, 0x6D, 0x1E, 0xB3, 0x3F, 0xFC, 0x14, 0x27, - 0xE3, 0x9C, 0x99, 0xDD, 0xF1, 0x67, 0xC7, 0xAD, 0xF5, 0xD6, 0x26, 0xFF, 0x00, 0x1A, 0xAC, 0xDF, - 0xF0, 0x50, 0x3F, 0x8E, 0x08, 0x72, 0x3E, 0x2B, 0xF8, 0xEC, 0x67, 0xFE, 0xA2, 0xF2, 0xFF, 0x00, - 0x8D, 0x79, 0x44, 0x8B, 0xFA, 0x54, 0x32, 0x2E, 0x7F, 0x1A, 0x87, 0x56, 0x7F, 0xCC, 0xCD, 0xA5, - 0x9A, 0x63, 0x5E, 0xF5, 0x65, 0xFF, 0x00, 0x81, 0x3F, 0xF3, 0x3D, 0x82, 0x2F, 0xF8, 0x28, 0xFF, - 0x00, 0xC7, 0xCB, 0x3F, 0xF5, 0x3F, 0x18, 0x3E, 0x20, 0xC7, 0x8F, 0xEE, 0xEB, 0x33, 0x0F, 0xEB, - 0x57, 0x2D, 0x7F, 0xE0, 0xAA, 0x9F, 0xB4, 0xA6, 0x96, 0x73, 0x6F, 0xF1, 0xCB, 0xE2, 0x6C, 0x24, - 0x1F, 0xE0, 0xD7, 0xA7, 0x1F, 0xFB, 0x35, 0x78, 0x64, 0x82, 0xA0, 0x91, 0x7F, 0x4A, 0x89, 0x4A, - 0x52, 0xD2, 0x4E, 0xE6, 0x12, 0xC6, 0x62, 0x25, 0xA4, 0xA7, 0x27, 0xF3, 0x67, 0xA6, 0x7C, 0x7B, - 0xFD, 0xBA, 0xFE, 0x34, 0x7E, 0xD4, 0x1E, 0x1E, 0xB7, 0xD2, 0x7E, 0x22, 0x7C, 0x52, 0xF1, 0xD7, - 0x8D, 0x34, 0xBB, 0x39, 0x3C, 0xE8, 0x6C, 0xF5, 0x7D, 0x62, 0x7B, 0xA8, 0x23, 0x7E, 0x9B, 0x82, - 0x33, 0x15, 0xCF, 0xBE, 0x2B, 0xC8, 0x24, 0x5A, 0xB7, 0x22, 0xE3, 0xF0, 0xA8, 0x24, 0x5A, 0xCD, - 0x45, 0x45, 0x5A, 0x27, 0x31, 0x4E, 0x55, 0xAE, 0xFF, 0x00, 0xF6, 0x46, 0xF8, 0xDF, 0xF0, 0xDF, - 0xE0, 0x2F, 0xC5, 0x9B, 0x0F, 0x11, 0x78, 0xEB, 0xE1, 0xCF, 0xFC, 0x2C, 0xCD, 0x3E, 0xD5, 0x89, - 0xB9, 0xD1, 0xAF, 0x2E, 0xA2, 0x8A, 0xCE, 0xE7, 0xE6, 0xC8, 0xFB, 0xD1, 0x39, 0x1C, 0x60, 0x77, - 0xEF, 0x5E, 0x79, 0xAF, 0x3C, 0x9A, 0x75, 0xA0, 0x66, 0x46, 0x4F, 0x34, 0x7C, 0x84, 0x8C, 0x67, - 0xE9, 0xEB, 0x5C, 0xB9, 0x39, 0x35, 0x20, 0x7D, 0x05, 0xFB, 0x4A, 0xFE, 0xD3, 0xFF, 0x00, 0x0A, - 0x7E, 0x2E, 0x41, 0xE3, 0xC4, 0xF0, 0x8F, 0xC0, 0x7F, 0x0F, 0xF8, 0x22, 0xE7, 0xC4, 0xDE, 0x20, - 0x3A, 0xB6, 0x8D, 0xA8, 0x41, 0xAC, 0xDC, 0xBC, 0xDE, 0x1C, 0xB3, 0x3B, 0x3F, 0xE2, 0x5F, 0x1C, - 0x0B, 0xB2, 0xDD, 0xE3, 0xF9, 0x5B, 0xE6, 0x31, 0xEE, 0x1B, 0xCF, 0x3C, 0x0A, 0xF9, 0xF6, 0x8A, - 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, - 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, - 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA7, 0x23, - 0x14, 0x60, 0x54, 0xF2, 0x3A, 0x11, 0xDA, 0x9B, 0x45, 0x00, 0x7D, 0x55, 0xFB, 0x2D, 0x7E, 0xCD, - 0x7F, 0x13, 0xFF, 0x00, 0x69, 0x3F, 0xD9, 0x2F, 0xE2, 0xD7, 0x8F, 0xBC, 0x37, 0xAA, 0xDE, 0xC9, - 0xA7, 0xFC, 0x1E, 0x86, 0x1D, 0x4B, 0x56, 0x92, 0x49, 0x5A, 0x43, 0x15, 0xA9, 0x82, 0xE1, 0xC8, - 0x19, 0xCE, 0x3F, 0xD4, 0xFE, 0x95, 0xF5, 0x17, 0x82, 0xFF, 0x00, 0xE0, 0x9A, 0x1F, 0x10, 0x35, - 0x1F, 0xDA, 0x57, 0xF6, 0x59, 0xF0, 0x9D, 0xC2, 0x79, 0xD1, 0xFC, 0x61, 0xF0, 0x69, 0xF1, 0x34, - 0xD0, 0x96, 0x6C, 0x5F, 0x46, 0x22, 0xF3, 0x8B, 0x31, 0x07, 0x20, 0xED, 0x96, 0x2F, 0xBA, 0x40, - 0xE2, 0xBE, 0x11, 0xF8, 0x19, 0xF1, 0xCB, 0x4B, 0xF8, 0x6B, 0xF0, 0x1B, 0xE2, 0xD7, 0x87, 0x6F, - 0x2F, 0x7C, 0x5D, 0x6D, 0xAB, 0x78, 0xC3, 0x4C, 0xB3, 0xB6, 0xD1, 0x57, 0x4A, 0xD4, 0x1E, 0xDE, - 0xC0, 0xCC, 0xB7, 0x71, 0x9B, 0x8F, 0xB6, 0xC6, 0x18, 0x09, 0xA3, 0x6B, 0x43, 0x71, 0x1A, 0xA9, - 0x0C, 0x03, 0x48, 0x0E, 0x3B, 0xD7, 0xAF, 0x78, 0x03, 0xF6, 0xF5, 0xF0, 0xCF, 0x87, 0xBE, 0x37, - 0x7E, 0xCF, 0x1A, 0xFE, 0xA1, 0x79, 0xF1, 0x2A, 0x7D, 0x27, 0xE1, 0x4F, 0x87, 0x5B, 0x4E, 0xD6, - 0x56, 0x2D, 0x6E, 0x45, 0xBA, 0x7B, 0xBD, 0xF7, 0x2C, 0x0D, 0x9B, 0xEF, 0xFD, 0xD4, 0x38, 0x6B, - 0x55, 0x28, 0x08, 0x04, 0x44, 0x46, 0x39, 0xA7, 0x76, 0x07, 0x75, 0xAA, 0x7E, 0xC9, 0x1E, 0x34, - 0xD3, 0x3F, 0xE0, 0x9E, 0x3E, 0x38, 0xF8, 0xAF, 0x27, 0xFC, 0xC0, 0x7E, 0x26, 0x0F, 0x09, 0xC5, - 0x77, 0xD5, 0xA2, 0x41, 0x22, 0xC2, 0xD1, 0x63, 0xEE, 0x91, 0xE6, 0x30, 0xE4, 0x8C, 0xFB, 0xD7, - 0xBC, 0xDE, 0xFF, 0x00, 0xC1, 0x26, 0xFC, 0x71, 0x73, 0xFB, 0x70, 0xF8, 0x9F, 0xE1, 0x8C, 0x1A, - 0x6C, 0x5E, 0x60, 0xF8, 0x54, 0x7C, 0x5B, 0xA7, 0x5B, 0x65, 0x8A, 0xC7, 0x39, 0x44, 0x45, 0x97, - 0x39, 0xC9, 0x3E, 0x61, 0x27, 0x04, 0x95, 0xE7, 0xA5, 0x7C, 0x41, 0xA4, 0x7E, 0xD7, 0x3E, 0x1F, - 0x1F, 0xB0, 0x87, 0xC4, 0x4F, 0x87, 0xBA, 0x83, 0x78, 0xD2, 0xE3, 0xC6, 0x5E, 0x2A, 0xF1, 0xC5, - 0x97, 0x88, 0xAC, 0x26, 0x5D, 0x4D, 0xC6, 0x93, 0x0D, 0xB4, 0x6B, 0x21, 0x9B, 0xCC, 0x83, 0x76, - 0x3E, 0xD0, 0xCE, 0xC0, 0xEF, 0x0B, 0x93, 0xC7, 0x3F, 0x2D, 0x7A, 0xDE, 0xA7, 0xFF, 0x00, 0x05, - 0x2C, 0xF0, 0x8C, 0xBF, 0xB5, 0xDE, 0xA9, 0xE3, 0x8B, 0x2D, 0x43, 0xE2, 0xB5, 0xAE, 0x87, 0x71, - 0xF0, 0xA6, 0x5F, 0x07, 0x5B, 0x31, 0xD6, 0xA4, 0x6D, 0x44, 0x6A, 0x67, 0x4D, 0x30, 0xC6, 0xED, - 0x27, 0x99, 0x93, 0x6C, 0x2E, 0xB0, 0xFB, 0x73, 0xD0, 0x74, 0xCF, 0x14, 0x73, 0x30, 0x34, 0xBC, - 0x1F, 0xFB, 0x10, 0xF8, 0xE7, 0xC5, 0x9F, 0xB2, 0xCF, 0xEC, 0xD3, 0xE3, 0xC8, 0x6D, 0xE3, 0x78, - 0x7E, 0x31, 0x78, 0xE6, 0xE3, 0xC2, 0xC6, 0x73, 0x1F, 0x37, 0xAF, 0xF6, 0x97, 0x8A, 0x38, 0xC8, - 0xC6, 0x06, 0x3E, 0xCF, 0x38, 0xCA, 0x80, 0x7D, 0xEB, 0x67, 0xC4, 0x3F, 0xF0, 0x4F, 0xBF, 0x1A, - 0x78, 0x5B, 0xFE, 0x1A, 0xEA, 0x4B, 0xDD, 0x3E, 0x1F, 0x27, 0xE0, 0x3C, 0x71, 0xED, 0x3E, 0x5F, - 0xFC, 0x83, 0xD9, 0xC9, 0x90, 0x63, 0xFB, 0xDF, 0xBB, 0x2B, 0xF7, 0xF3, 0x5F, 0x3D, 0xE9, 0x5F, - 0xB6, 0x5D, 0xAE, 0x9B, 0xFB, 0x2D, 0xFC, 0x17, 0xF0, 0x6C, 0x7A, 0xEF, 0x8E, 0xED, 0xF5, 0x4F, - 0x87, 0x7E, 0x39, 0xD4, 0x3C, 0x43, 0x78, 0xB0, 0x6A, 0x0E, 0x96, 0x76, 0xF6, 0x92, 0x7D, 0x90, - 0xDB, 0xB5, 0xA2, 0xEE, 0xFD, 0xDD, 0xC2, 0xB0, 0xBD, 0x25, 0x80, 0x1C, 0xCF, 0x9C, 0xF2, 0x6B, - 0xA8, 0xF1, 0xB7, 0xED, 0xDF, 0xA2, 0x6A, 0x97, 0xFF, 0x00, 0xB5, 0x25, 0xC6, 0x9F, 0xAE, 0x7C, - 0x47, 0x67, 0xF8, 0xC0, 0x6D, 0xE0, 0xF0, 0xF8, 0xBA, 0xD4, 0x9D, 0xD6, 0x7B, 0x61, 0x78, 0xAD, - 0x2A, 0xEA, 0x20, 0xBE, 0x25, 0x7F, 0xB3, 0x0D, 0x8A, 0x4E, 0xEC, 0x1C, 0xFA, 0xD1, 0x76, 0x07, - 0xBD, 0x78, 0x67, 0xFE, 0x09, 0xB1, 0xE2, 0xD6, 0xFD, 0xA1, 0x7F, 0x65, 0x3F, 0x08, 0xDF, 0xE8, - 0xF0, 0x48, 0xFF, 0x00, 0x19, 0xFC, 0x34, 0x75, 0xBB, 0x88, 0x76, 0x10, 0x35, 0x00, 0x13, 0xCC, - 0x2C, 0x48, 0xE5, 0x7E, 0x47, 0x8F, 0xEE, 0x15, 0x15, 0xC1, 0xF8, 0xEB, 0xF6, 0x11, 0xF1, 0xA7, - 0x84, 0x3F, 0x61, 0xAF, 0x8D, 0x3F, 0x10, 0x66, 0xD3, 0x60, 0x4B, 0xCF, 0x84, 0xFF, 0x00, 0x12, - 0x4F, 0x85, 0xEF, 0xAE, 0xFC, 0xA1, 0x9B, 0x68, 0x43, 0x43, 0x09, 0x8B, 0x6E, 0x36, 0x9F, 0xDE, - 0xCF, 0x1F, 0x24, 0x6E, 0xE7, 0xAD, 0x73, 0xFF, 0x00, 0x0F, 0xBF, 0xE0, 0xA0, 0xDE, 0x11, 0xD1, - 0xBE, 0x37, 0xFE, 0xCB, 0x5A, 0xE6, 0xA9, 0xAC, 0x7C, 0x56, 0x93, 0x48, 0xF8, 0x43, 0xA3, 0x0B, - 0x2F, 0x11, 0x4B, 0x16, 0xAC, 0xDF, 0x6D, 0x49, 0x8C, 0xF3, 0x33, 0xAD, 0x83, 0xEF, 0xCC, 0x70, - 0xF9, 0x7E, 0x4A, 0x85, 0xC8, 0x18, 0x52, 0x3B, 0x57, 0x0D, 0xAC, 0x7E, 0xDA, 0x36, 0x9A, 0xB7, - 0xEC, 0x7B, 0xF1, 0x93, 0xC1, 0x33, 0x78, 0x87, 0xC7, 0xD2, 0x6B, 0x1E, 0x3E, 0xF8, 0x81, 0x65, - 0xE2, 0x4B, 0x2B, 0x79, 0x2F, 0x9D, 0xB4, 0xFB, 0x9B, 0x45, 0x17, 0x2D, 0x3B, 0x5D, 0x2E, 0xFF, - 0x00, 0x9A, 0x76, 0x73, 0x68, 0xC0, 0x90, 0x72, 0x62, 0xCE, 0x7E, 0x51, 0x4A, 0xE0, 0x7D, 0x2B, - 0xE2, 0x6F, 0xF8, 0x25, 0x57, 0x8F, 0xEC, 0x7F, 0x6D, 0xBD, 0x5F, 0xE1, 0xAC, 0x5A, 0x1C, 0x2A, - 0x6D, 0x3E, 0x1A, 0x9F, 0x18, 0x25, 0xB7, 0x96, 0x48, 0x53, 0xE4, 0x15, 0xF3, 0x72, 0x7E, 0x6F, - 0xF5, 0xCA, 0xC7, 0x19, 0xDB, 0xED, 0x8E, 0x2B, 0xCD, 0x7C, 0x3F, 0xFB, 0x0F, 0x78, 0xC3, 0x5B, - 0xFD, 0x89, 0x7E, 0x0A, 0xFC, 0x40, 0x4D, 0x26, 0x16, 0xB9, 0xF8, 0xA1, 0xF1, 0x08, 0xF8, 0x62, - 0x0B, 0xBF, 0x28, 0x66, 0xED, 0x0B, 0xC9, 0x1A, 0xC5, 0xB7, 0x1B, 0x47, 0xCF, 0x13, 0xF2, 0xA0, - 0x1F, 0x7A, 0xAF, 0xA9, 0x7F, 0xC1, 0x49, 0xB4, 0xDD, 0x47, 0xF6, 0xAF, 0x5F, 0x1C, 0x9F, 0x1D, - 0x7C, 0x5C, 0x4B, 0x75, 0xF8, 0x57, 0x17, 0x85, 0xFE, 0xD8, 0xBA, 0x8B, 0x0B, 0xE6, 0xD5, 0x17, - 0x4C, 0x11, 0xB4, 0x6C, 0xDB, 0x89, 0x6B, 0x33, 0x78, 0x5D, 0xF0, 0x7B, 0x36, 0x71, 0x5E, 0x6A, - 0xBF, 0xB6, 0x4D, 0xAE, 0x9F, 0xFB, 0x19, 0xFC, 0x23, 0xF0, 0x1D, 0xA7, 0x89, 0xBC, 0x7C, 0xBA, - 0xBF, 0x84, 0x7C, 0x77, 0x7B, 0xE2, 0x7D, 0x4E, 0x0F, 0xB6, 0xB0, 0xB1, 0xD3, 0xE3, 0xC4, 0x22, - 0xD6, 0x4B, 0x11, 0x9F, 0xDD, 0xCB, 0xF3, 0x5D, 0x33, 0x11, 0x8F, 0x99, 0x81, 0xA2, 0xE0, 0x7D, - 0x25, 0xE2, 0xEF, 0xF8, 0x26, 0x77, 0x8A, 0x2D, 0xFE, 0x33, 0xFE, 0xD6, 0x1E, 0x16, 0xB4, 0xF0, - 0xFD, 0xBA, 0x5C, 0x7C, 0x18, 0xF0, 0xFC, 0x3A, 0xE5, 0x9C, 0x5B, 0x49, 0xFE, 0xCE, 0x84, 0xA9, - 0x95, 0xDC, 0x67, 0x97, 0xFD, 0xDC, 0x72, 0x7D, 0xFD, 0xD5, 0xF3, 0x3F, 0xC7, 0xAF, 0xD9, 0xCB, - 0xE2, 0x07, 0xEC, 0xDB, 0xFB, 0x32, 0x7C, 0x31, 0xF8, 0x81, 0xAF, 0xC3, 0x15, 0x9D, 0xAF, 0xC5, - 0x2F, 0x3A, 0xFF, 0x00, 0x45, 0xB9, 0x16, 0xF1, 0x83, 0x71, 0x68, 0xB0, 0x5B, 0xB8, 0xFE, 0x1C, - 0x70, 0x65, 0xEA, 0x39, 0xE6, 0xBD, 0x6B, 0xC5, 0x9F, 0xF0, 0x52, 0x2F, 0x0E, 0xDF, 0x7C, 0x6E, - 0xFD, 0xAA, 0xBC, 0x45, 0xA6, 0xF8, 0xCB, 0xE2, 0xC4, 0x76, 0xFF, 0x00, 0x16, 0xBC, 0x36, 0xBA, - 0x3F, 0x87, 0x66, 0x92, 0xF4, 0xFD, 0xAB, 0x51, 0x60, 0x61, 0x42, 0x9A, 0x89, 0xDD, 0xF3, 0x47, - 0xE5, 0x09, 0xC7, 0x5E, 0x8E, 0x78, 0xE7, 0x15, 0xE0, 0xFF, 0x00, 0xB4, 0x9F, 0xED, 0x22, 0x9F, - 0x17, 0x3F, 0x66, 0xBF, 0x82, 0x7E, 0x16, 0x5F, 0x17, 0xF8, 0xDB, 0x5E, 0xBB, 0xF0, 0x4E, 0x93, - 0x79, 0x6D, 0xA9, 0x69, 0x9A, 0xBD, 0xC9, 0x97, 0x4D, 0xD1, 0xA5, 0x37, 0x4C, 0xB0, 0x25, 0x82, - 0x9F, 0xB9, 0x19, 0xB4, 0x8E, 0xDC, 0x30, 0x1C, 0x6E, 0x53, 0x45, 0xC0, 0xF1, 0x5D, 0x47, 0x56, - 0xB8, 0xD6, 0x6E, 0xDA, 0x7B, 0xA9, 0xE4, 0xB8, 0x99, 0xBA, 0xBC, 0x8C, 0x58, 0xFD, 0x3E, 0x95, - 0x56, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x03, 0xFF, 0xD9, -} ; - -const char sleep_jpg[] PROGMEM = -{ - 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x00, 0x60, - 0x00, 0x60, 0x00, 0x00, 0xFF, 0xE1, 0x00, 0x22, 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x4D, 0x4D, - 0x00, 0x2A, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x02, 0x01, 0x01, - 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x05, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x06, 0x04, 0x04, 0x03, 0x05, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x08, - 0x09, 0x0B, 0x09, 0x08, 0x08, 0x0A, 0x08, 0x07, 0x07, 0x0A, 0x0D, 0x0A, 0x0A, 0x0B, 0x0C, 0x0C, - 0x0C, 0x0C, 0x07, 0x09, 0x0E, 0x0F, 0x0D, 0x0C, 0x0E, 0x0B, 0x0C, 0x0C, 0x0C, 0xFF, 0xDB, 0x00, - 0x43, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x06, 0x03, 0x03, 0x06, 0x0C, 0x08, 0x07, 0x08, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0xB5, 0x01, 0x40, 0x03, 0x01, 0x22, 0x00, 0x02, - 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, - 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, - 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, - 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, - 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, - 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, - 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, - 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, - 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, - 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, - 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, - 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xC4, 0x00, - 0x1F, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, - 0x00, 0xB5, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, - 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, - 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, - 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, - 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, - 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, - 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, - 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, - 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, - 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, - 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, - 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, - 0x00, 0x3F, 0x00, 0xFE, 0x7F, 0xE8, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xAB, 0x56, 0xFA, 0x1D, 0xED, 0xDD, 0xBF, 0x9D, 0x0D, 0x9D, - 0xD4, 0xB1, 0x0E, 0xAE, 0x91, 0x33, 0x2F, 0xE7, 0x8A, 0xAA, 0x46, 0xD3, 0x55, 0x28, 0x49, 0x2B, - 0xB4, 0x2B, 0xA7, 0xB1, 0x63, 0x4C, 0xD3, 0x2E, 0x35, 0x9D, 0x46, 0x0B, 0x4B, 0x38, 0x25, 0xB9, - 0xBA, 0xBA, 0x91, 0x62, 0x86, 0x28, 0x94, 0xB3, 0xC8, 0xE4, 0xE0, 0x28, 0x03, 0x92, 0x49, 0x3D, - 0x2B, 0xF4, 0xF7, 0xF6, 0x5C, 0xFF, 0x00, 0x83, 0x57, 0xBE, 0x33, 0xFC, 0x67, 0xF0, 0x7D, 0x9E, - 0xB7, 0xE3, 0x4F, 0x11, 0xF8, 0x67, 0xE1, 0xCC, 0x57, 0xD1, 0xAC, 0xD1, 0xD8, 0x5E, 0x33, 0x5C, - 0xDF, 0x2A, 0x30, 0xC8, 0xDF, 0x1C, 0x79, 0x31, 0x92, 0x3B, 0x37, 0x35, 0xF1, 0xE7, 0xFC, 0x12, - 0xC7, 0xC2, 0x4B, 0xE3, 0x5F, 0xDB, 0xF3, 0xE1, 0x8D, 0x93, 0x28, 0x75, 0x5D, 0x66, 0x2B, 0x82, - 0x08, 0xCE, 0x3C, 0xB3, 0xBF, 0xFF, 0x00, 0x65, 0xAF, 0xE9, 0xD9, 0x3E, 0x31, 0xB8, 0x92, 0x4F, - 0x98, 0xFD, 0xE3, 0xDF, 0xF0, 0xAF, 0xD0, 0xB8, 0x67, 0x85, 0xA1, 0x8C, 0xC0, 0xFD, 0x71, 0xAE, - 0x66, 0xE5, 0x28, 0xD9, 0xDE, 0xC9, 0x25, 0x17, 0x7D, 0x1A, 0xBB, 0x6D, 0xBD, 0xF4, 0xD3, 0x63, - 0xE6, 0xB3, 0x8C, 0xE6, 0x58, 0x7A, 0xFE, 0xC1, 0x69, 0xA2, 0x77, 0xF5, 0x6F, 0xFC, 0x8F, 0xC5, - 0x3F, 0xDA, 0x6B, 0xFE, 0x0D, 0x54, 0xF8, 0xC9, 0xF0, 0x8F, 0xC2, 0x37, 0x9A, 0xC7, 0x82, 0x7C, - 0x51, 0xE1, 0x7F, 0x88, 0x9F, 0x63, 0x8C, 0xCA, 0xFA, 0x7D, 0xB1, 0x6B, 0x5B, 0xD9, 0x14, 0x0C, - 0x9F, 0x2D, 0x64, 0xC6, 0xF6, 0xC7, 0xF0, 0x8E, 0x6B, 0xF2, 0xF7, 0x5A, 0xD1, 0x6E, 0xFC, 0x39, - 0xAB, 0x5D, 0x69, 0xF7, 0xF6, 0xF3, 0x59, 0xDF, 0x59, 0x4A, 0xD0, 0xCF, 0x04, 0xC8, 0x52, 0x48, - 0x5D, 0x4E, 0x19, 0x58, 0x1E, 0x41, 0x04, 0x63, 0x15, 0xFD, 0x73, 0xDE, 0x7C, 0x65, 0x91, 0x64, - 0x5F, 0x98, 0xFD, 0xE1, 0xDF, 0xDE, 0xBF, 0x9A, 0x3F, 0xF8, 0x2B, 0xDF, 0x84, 0x47, 0x83, 0xBF, - 0xE0, 0xA2, 0x3F, 0x13, 0x21, 0x55, 0xDA, 0x97, 0x5A, 0x9B, 0x5E, 0x80, 0x06, 0x07, 0xEF, 0x46, - 0xF3, 0xF9, 0x92, 0x4D, 0x57, 0x13, 0x70, 0xAC, 0x30, 0x78, 0x1F, 0xAE, 0xC6, 0x3C, 0xAD, 0x49, - 0x2B, 0x2B, 0xD9, 0xA6, 0x9E, 0xBA, 0xB7, 0x66, 0x9A, 0x5B, 0x69, 0xAE, 0xC4, 0xE4, 0xF9, 0xD4, - 0xB1, 0x15, 0xFD, 0x84, 0x9D, 0xF4, 0x6E, 0xFE, 0x96, 0xFF, 0x00, 0x33, 0xE6, 0xAA, 0x28, 0xAB, - 0x13, 0xE9, 0x57, 0x56, 0xB0, 0x2C, 0xB2, 0x5B, 0x5C, 0x47, 0x13, 0x7D, 0xD7, 0x68, 0xC8, 0x53, - 0xF4, 0x35, 0xF9, 0xD2, 0x8B, 0x7B, 0x1F, 0x51, 0x72, 0xBD, 0x14, 0x51, 0x48, 0x02, 0x8A, 0x28, - 0xA0, 0x02, 0x8A, 0x28, 0xA0, 0x02, 0x8A, 0x28, 0xA0, 0x02, 0x8A, 0x28, 0xA0, 0x02, 0x8A, 0x28, - 0xA0, 0x02, 0x8A, 0x28, 0xA0, 0x02, 0x8A, 0x28, 0xA0, 0x02, 0x8A, 0x28, 0xA0, 0x02, 0x8A, 0x28, - 0xA0, 0x02, 0x8A, 0x28, 0xA0, 0x02, 0x8A, 0x28, 0xA0, 0x02, 0x8A, 0x28, 0xA0, 0x02, 0x8A, 0x28, - 0xA0, 0x02, 0x8A, 0x92, 0xD6, 0xD6, 0x4B, 0xDB, 0x88, 0xE1, 0x86, 0x37, 0x96, 0x69, 0x98, 0x22, - 0x22, 0x0D, 0xCC, 0xEC, 0x4E, 0x00, 0x03, 0xB9, 0x26, 0xBF, 0x45, 0xBF, 0xE0, 0x9F, 0x9F, 0xF0, - 0x4B, 0x1D, 0x2B, 0x4D, 0x8A, 0xC7, 0xC6, 0x1F, 0x16, 0x2D, 0x56, 0xEE, 0x46, 0xDB, 0x3D, 0x9E, - 0x80, 0xE7, 0xE5, 0x1D, 0xC3, 0x5C, 0x7B, 0x7F, 0xB3, 0xDE, 0xBE, 0xAB, 0x85, 0x78, 0x43, 0x1F, - 0x9F, 0xE2, 0x1D, 0x1C, 0x1C, 0x7D, 0xD8, 0xFC, 0x52, 0x7B, 0x45, 0x7E, 0xAD, 0xF4, 0x5F, 0x92, - 0xBB, 0x3C, 0x8C, 0xE3, 0x3B, 0xC3, 0x65, 0xB4, 0xBD, 0xA6, 0x21, 0xEA, 0xF6, 0x5D, 0x5F, 0xF5, - 0xDC, 0xF9, 0xD7, 0xF6, 0x2A, 0xFF, 0x00, 0x82, 0x5A, 0xFC, 0x52, 0xFD, 0xB6, 0xEE, 0xE1, 0xBB, - 0xD1, 0x74, 0xD8, 0xF4, 0x1F, 0x0A, 0xEF, 0xDB, 0x2E, 0xBF, 0xAB, 0x6E, 0x86, 0xD7, 0x03, 0xAF, - 0x94, 0x30, 0x5E, 0x66, 0xFF, 0x00, 0x70, 0x11, 0x91, 0x82, 0x45, 0x7E, 0xB1, 0x7E, 0xC9, 0x1F, - 0xF0, 0x43, 0x1F, 0x81, 0x3F, 0xB3, 0xBC, 0x56, 0xB7, 0xFE, 0x26, 0xB5, 0xB8, 0xF8, 0x99, 0xE2, - 0x28, 0x76, 0xBB, 0x4F, 0xAA, 0xFE, 0xE7, 0x4F, 0x8D, 0xC1, 0xCE, 0x52, 0xD9, 0x0F, 0x3F, 0xF0, - 0x37, 0x39, 0xF4, 0xAF, 0x4A, 0xD3, 0x7E, 0x2D, 0x5B, 0xE8, 0x7A, 0x6C, 0x16, 0x76, 0x71, 0x41, - 0x69, 0x67, 0x6A, 0x82, 0x38, 0x60, 0x85, 0x04, 0x71, 0xC4, 0xA3, 0x80, 0x15, 0x47, 0x00, 0x0A, - 0x9D, 0x7E, 0x39, 0xB6, 0x7F, 0xD6, 0x1F, 0xCE, 0xBF, 0x7D, 0xCA, 0xBC, 0x28, 0xC3, 0xE0, 0xA2, - 0x9A, 0x87, 0x3C, 0xFF, 0x00, 0x9A, 0x5A, 0xFD, 0xCB, 0x65, 0xF2, 0xD7, 0xCD, 0x9F, 0x9A, 0xE3, - 0xB8, 0xDA, 0xAD, 0x79, 0x59, 0x3E, 0x58, 0xF6, 0x5F, 0xAB, 0xDD, 0xFE, 0x47, 0xBE, 0xE8, 0xFE, - 0x1C, 0xF0, 0x56, 0x87, 0xA6, 0xAD, 0x95, 0x9F, 0x84, 0xFC, 0x27, 0x6B, 0x66, 0xAB, 0xB4, 0x41, - 0x1E, 0x8B, 0x6A, 0x10, 0x0F, 0x4C, 0x79, 0x7F, 0xCE, 0xBF, 0x3F, 0xBF, 0xE0, 0xBA, 0x7F, 0xF0, - 0x4F, 0xAF, 0x86, 0x3E, 0x26, 0xFD, 0x98, 0xB5, 0xAF, 0x8A, 0x1E, 0x19, 0xF0, 0xFE, 0x97, 0xE1, - 0x5F, 0x17, 0xF8, 0x54, 0xC7, 0x2C, 0xF2, 0x69, 0xB0, 0x8B, 0x78, 0x35, 0x48, 0x18, 0x90, 0xCB, - 0x24, 0x4B, 0xF2, 0xEF, 0x1D, 0x43, 0x28, 0x1D, 0x79, 0xAF, 0x45, 0xF8, 0x95, 0xFF, 0x00, 0x05, - 0x1A, 0xF0, 0x67, 0xC2, 0xBB, 0x9F, 0xB2, 0xDF, 0xEB, 0x11, 0xDD, 0xEA, 0x4C, 0x70, 0xB6, 0x56, - 0x6C, 0x25, 0x95, 0x8F, 0xA1, 0xEC, 0x3F, 0x1A, 0xF2, 0xEF, 0xDA, 0xD7, 0xE2, 0x96, 0xB1, 0xFB, - 0x55, 0x7E, 0xC4, 0x3F, 0x12, 0xEF, 0x2F, 0x52, 0x3D, 0x2F, 0x41, 0xD3, 0x74, 0x49, 0x2F, 0x2D, - 0x6C, 0x62, 0x72, 0xF3, 0x5D, 0x4C, 0x08, 0x08, 0xF3, 0x3F, 0x4C, 0x2E, 0x4E, 0x14, 0x77, 0x26, - 0xBA, 0xF3, 0x8E, 0x0B, 0x4B, 0x2F, 0xAF, 0x52, 0xAC, 0x57, 0x2C, 0x63, 0x26, 0xFD, 0x6D, 0xA6, - 0x9D, 0xEF, 0x6B, 0x76, 0xDC, 0xC3, 0x01, 0x9E, 0xC9, 0x62, 0x69, 0xA8, 0x5D, 0x39, 0x35, 0xF9, - 0xEA, 0x7C, 0x1D, 0xFF, 0x00, 0x04, 0x40, 0xD1, 0xD6, 0xE7, 0xFE, 0x0A, 0x0B, 0xE0, 0xB9, 0xD8, - 0x67, 0xC9, 0x13, 0xBA, 0x9F, 0x7F, 0x2C, 0xE2, 0xBF, 0x74, 0x86, 0xA1, 0x75, 0x1B, 0x36, 0xE8, - 0xDB, 0xEF, 0x1E, 0x9F, 0x53, 0x5F, 0x8A, 0xBF, 0xF0, 0x43, 0xEF, 0x06, 0x6B, 0x1A, 0xCF, 0xED, - 0xD9, 0xE0, 0x58, 0x74, 0x5D, 0x1F, 0x51, 0xD6, 0xAE, 0x9A, 0xE8, 0x89, 0x62, 0xB3, 0x84, 0xC8, - 0xD1, 0x43, 0xE5, 0xB6, 0xF9, 0x5B, 0x1F, 0x75, 0x13, 0x82, 0xCC, 0x78, 0x02, 0xBF, 0x7D, 0xAF, - 0xFE, 0x16, 0x5C, 0x59, 0xCE, 0xCB, 0x25, 0xB3, 0x0C, 0xB1, 0xDA, 0x40, 0xCA, 0xB7, 0x3D, 0x8F, - 0x7A, 0xF9, 0x3E, 0x0A, 0xC5, 0x50, 0xC3, 0x65, 0x34, 0xA9, 0xC9, 0xAB, 0xBB, 0xBB, 0x75, 0xDD, - 0xAF, 0xD0, 0xF5, 0xF8, 0x8A, 0x8D, 0x5A, 0xD8, 0xD9, 0x4D, 0x6C, 0xAC, 0xBF, 0x03, 0xCA, 0x2F, - 0xF5, 0x1B, 0xB9, 0x4A, 0xED, 0x8D, 0xBE, 0xF0, 0xEB, 0xF5, 0x15, 0xF8, 0x99, 0xFF, 0x00, 0x05, - 0xDE, 0xD1, 0x96, 0x0F, 0xF8, 0x28, 0x0F, 0x8A, 0x2E, 0x54, 0x7F, 0xAE, 0xB4, 0xB4, 0x66, 0xC7, - 0xAF, 0x94, 0x33, 0x5F, 0xD1, 0xBF, 0x85, 0x7F, 0x64, 0x7B, 0xEF, 0x14, 0xF8, 0x5E, 0x6D, 0x55, - 0xA4, 0x8E, 0xD6, 0x1B, 0x75, 0x32, 0x31, 0x90, 0xED, 0x55, 0x51, 0xC9, 0x24, 0xF6, 0x00, 0x0A, - 0xFE, 0x73, 0xBF, 0xE0, 0xB0, 0xFE, 0x2C, 0xD3, 0x7E, 0x2E, 0x7E, 0xD7, 0x9E, 0x3C, 0xD5, 0xB4, - 0x5B, 0x85, 0xBD, 0xD3, 0xED, 0xEF, 0x4D, 0x84, 0x37, 0x0B, 0xF7, 0x65, 0xF2, 0x62, 0x44, 0x2C, - 0xBF, 0xEC, 0x96, 0x0D, 0x83, 0xDC, 0x73, 0x55, 0xC5, 0x99, 0x86, 0x17, 0x1F, 0x94, 0x62, 0x29, - 0xD0, 0xB3, 0x74, 0xED, 0x7F, 0x26, 0xE4, 0xAD, 0xFA, 0xFE, 0x23, 0xC8, 0xF0, 0xB5, 0xB0, 0xD8, - 0xDA, 0x72, 0x9F, 0xDA, 0xBF, 0xDD, 0x6F, 0xF3, 0xB1, 0xF7, 0x97, 0xFC, 0x11, 0xFB, 0xFE, 0x09, - 0x89, 0xF0, 0xA7, 0xE1, 0x87, 0xEC, 0xFB, 0xE1, 0x7F, 0x88, 0x5E, 0x29, 0xD0, 0xF4, 0xDF, 0x1A, - 0x78, 0xD3, 0xC5, 0x16, 0x8B, 0xA8, 0xAC, 0xBA, 0x94, 0x62, 0x7B, 0x4D, 0x2E, 0x37, 0xCE, 0xD8, - 0xA2, 0x88, 0xFC, 0x8C, 0xD8, 0x1F, 0x33, 0xB8, 0x3D, 0xB0, 0x05, 0x7D, 0xCD, 0xAB, 0x78, 0x3B, - 0xC0, 0x7A, 0xFE, 0x94, 0xD6, 0x37, 0xDE, 0x0D, 0xF0, 0x7D, 0xDD, 0x91, 0x5D, 0xA6, 0x09, 0x34, - 0x4B, 0x5F, 0x2F, 0x1E, 0x98, 0x11, 0xF1, 0xF8, 0x57, 0xC9, 0xBA, 0x36, 0xA3, 0xAE, 0xFE, 0xCB, - 0x1F, 0xB3, 0xE7, 0x81, 0x6E, 0xB4, 0x18, 0xED, 0x75, 0x6F, 0x0D, 0x5F, 0xE8, 0x16, 0x97, 0x6F, - 0xA6, 0x4D, 0x29, 0x86, 0xE3, 0x4F, 0x91, 0xA3, 0xCC, 0x9E, 0x4B, 0xF4, 0x64, 0x6C, 0x67, 0x69, - 0x23, 0x07, 0xEB, 0x51, 0x78, 0x03, 0xFE, 0x0A, 0x15, 0xE1, 0x5F, 0x88, 0x17, 0xDF, 0x61, 0x8F, - 0x52, 0xFE, 0xCC, 0xD5, 0x73, 0xB5, 0xAC, 0xAF, 0x88, 0x8A, 0x4C, 0xFA, 0x29, 0xFB, 0xAD, 0xF8, - 0x1A, 0xFA, 0xFC, 0xA7, 0x82, 0x28, 0xCB, 0x07, 0x4E, 0xA6, 0x16, 0x2B, 0x91, 0xA5, 0x6E, 0xFF, - 0x00, 0x3F, 0x3E, 0xEF, 0xA9, 0xE1, 0xE3, 0x73, 0xEA, 0xBE, 0xDE, 0x71, 0xAA, 0xDF, 0x32, 0x7A, - 0xFF, 0x00, 0x5D, 0x8C, 0x6F, 0xDA, 0xCF, 0xFE, 0x08, 0x4B, 0xF0, 0x33, 0xE3, 0xEC, 0x57, 0x57, - 0xDE, 0x10, 0x8E, 0xEB, 0xE1, 0x9F, 0x88, 0x25, 0x0C, 0xC8, 0xFA, 0x76, 0x6E, 0x34, 0xE7, 0x72, - 0x73, 0xF3, 0xDB, 0xB9, 0xDC, 0x07, 0xFB, 0x8E, 0x31, 0xE9, 0x5F, 0x94, 0x7F, 0xB6, 0x77, 0xFC, - 0x13, 0x13, 0xE2, 0x97, 0xEC, 0x4B, 0x7B, 0x25, 0xC7, 0x88, 0x34, 0xB8, 0xF5, 0x7F, 0x0C, 0xEF, - 0xDB, 0x0E, 0xBD, 0xA5, 0x16, 0x9A, 0xCD, 0x87, 0x6F, 0x33, 0x80, 0xD1, 0x37, 0xB3, 0x80, 0x33, - 0xD0, 0x9A, 0xFD, 0xB6, 0x4F, 0x8E, 0x4C, 0x0F, 0xFA, 0xC3, 0xF9, 0xD4, 0x7A, 0xC7, 0xC5, 0x6B, - 0x5F, 0x12, 0x69, 0x37, 0x16, 0x1A, 0x84, 0x16, 0xF7, 0xD6, 0x37, 0x48, 0x63, 0x9E, 0xDE, 0xE1, - 0x04, 0x91, 0xCA, 0xA7, 0x82, 0x19, 0x4F, 0x04, 0x57, 0x9F, 0x9B, 0x78, 0x4D, 0x87, 0xC6, 0x45, - 0xBE, 0x4E, 0x49, 0xFF, 0x00, 0x34, 0x74, 0xFB, 0xD6, 0xCF, 0xF3, 0xF3, 0x47, 0x56, 0x07, 0x8D, - 0xAA, 0xE1, 0xDD, 0x9B, 0xE6, 0x8F, 0x67, 0xFA, 0x3D, 0xD7, 0xE5, 0xE4, 0x7F, 0x38, 0xB4, 0x57, - 0xE8, 0xA7, 0xED, 0xF7, 0xFF, 0x00, 0x04, 0xA9, 0xD3, 0x6E, 0x56, 0xFB, 0xC5, 0xDF, 0x09, 0x6D, - 0xFC, 0x9D, 0xBB, 0xA7, 0xBB, 0xF0, 0xFA, 0x9C, 0xF1, 0xD4, 0x98, 0x3E, 0x9F, 0xDD, 0xAF, 0xCE, - 0xFB, 0x8B, 0x79, 0x2D, 0x2E, 0x1A, 0x29, 0x23, 0x68, 0xE4, 0x8D, 0x8A, 0xB2, 0xB0, 0xC3, 0x29, - 0x1C, 0x10, 0x47, 0xAD, 0x7E, 0x01, 0xC5, 0x3C, 0x23, 0x8F, 0xC8, 0x71, 0x3E, 0xC3, 0x19, 0x1D, - 0x25, 0xAC, 0x64, 0xB6, 0x92, 0xF2, 0xEC, 0xD7, 0x55, 0xBA, 0xF4, 0x69, 0xBF, 0xD3, 0x32, 0x7C, - 0xEB, 0x0D, 0x99, 0x52, 0xF6, 0x98, 0x77, 0xAA, 0xDD, 0x75, 0x5F, 0xD7, 0x46, 0x47, 0x45, 0x14, - 0x57, 0xCB, 0x9E, 0xB0, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, - 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, - 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x39, 0x17, - 0x71, 0xA0, 0x0F, 0xB4, 0x3F, 0xE0, 0x99, 0x3F, 0xB3, 0x76, 0x9F, 0x69, 0xA3, 0x5C, 0x7C, 0x56, - 0xF1, 0x2D, 0xB4, 0x33, 0x43, 0x6B, 0x73, 0xF6, 0x1D, 0x02, 0xDE, 0x65, 0xCA, 0xC9, 0x72, 0x3E, - 0x67, 0x9F, 0x07, 0xAE, 0xC0, 0x30, 0x3D, 0xCF, 0xB5, 0x7D, 0x9C, 0xDF, 0x17, 0xB7, 0x1C, 0x99, - 0xB2, 0x4F, 0x27, 0xE6, 0xEB, 0x5E, 0x11, 0xF1, 0xAB, 0x56, 0x1F, 0xB3, 0xF7, 0xC1, 0x4F, 0x83, - 0x7E, 0x10, 0xB3, 0x85, 0x9A, 0x18, 0x7C, 0x30, 0x35, 0x29, 0x0A, 0xB6, 0xD0, 0xF2, 0xCE, 0xE0, - 0xB1, 0x3E, 0xA7, 0xB5, 0x79, 0x8C, 0x9F, 0x1C, 0xEF, 0x71, 0xF2, 0xC2, 0x7F, 0x16, 0xAF, 0xED, - 0xDE, 0x08, 0xC0, 0xE0, 0xB2, 0x6C, 0x9E, 0x96, 0x12, 0x2E, 0xD2, 0xB5, 0xE7, 0xA6, 0xAE, 0x4F, - 0x76, 0xFF, 0x00, 0x25, 0xE4, 0x92, 0x3F, 0x9E, 0xF8, 0x82, 0xB6, 0x37, 0x30, 0xC7, 0x4E, 0xBA, - 0x8D, 0xE3, 0x7B, 0x47, 0x5E, 0x8B, 0x45, 0xFD, 0x77, 0x3E, 0xC3, 0xB4, 0xF8, 0xA1, 0x36, 0xAD, - 0x7D, 0x0D, 0xAD, 0xA9, 0x69, 0xAE, 0x2E, 0x1C, 0x47, 0x1C, 0x69, 0xCB, 0x3B, 0x1E, 0xD5, 0xF3, - 0xBF, 0xED, 0xAB, 0xFB, 0x5D, 0xEA, 0x1A, 0x6E, 0xA9, 0x75, 0xE0, 0xDF, 0x0E, 0x6A, 0x0C, 0xAF, - 0x6A, 0x7C, 0xAD, 0x52, 0xF6, 0x16, 0xFF, 0x00, 0x96, 0x9D, 0xE1, 0x43, 0xED, 0xDC, 0xFA, 0xF1, - 0x5B, 0x3F, 0xB2, 0x1F, 0xC5, 0x2B, 0xCB, 0xCD, 0x43, 0xE2, 0x1E, 0xB4, 0xF1, 0xC6, 0xD7, 0x9E, - 0x11, 0xF0, 0x65, 0xF6, 0xA9, 0x62, 0xB8, 0x24, 0x89, 0xCB, 0x24, 0x41, 0xC7, 0xFB, 0xA1, 0xC9, - 0xAF, 0x90, 0x6F, 0xB7, 0x49, 0x99, 0x26, 0x91, 0xA4, 0x92, 0x4F, 0x9E, 0x47, 0x63, 0x96, 0x76, - 0x3C, 0x92, 0x4F, 0xA9, 0x24, 0x9A, 0xF5, 0x73, 0x2C, 0xF3, 0x47, 0x4E, 0x83, 0xB2, 0xEA, 0xF6, - 0xFE, 0xBD, 0x4D, 0xF2, 0x5C, 0x9E, 0xF2, 0xF6, 0x98, 0xB4, 0xB4, 0xD9, 0x6F, 0xF7, 0xFF, 0x00, - 0x91, 0x73, 0xC0, 0xB2, 0xC9, 0x71, 0xE3, 0x6B, 0x59, 0x26, 0x76, 0x92, 0x46, 0x7C, 0x97, 0x63, - 0x96, 0x27, 0xEB, 0x5F, 0xA1, 0xDA, 0xC7, 0x8A, 0x34, 0xFF, 0x00, 0x0C, 0xFF, 0x00, 0xC1, 0x3B, - 0xFE, 0x20, 0x0B, 0xFB, 0x88, 0xED, 0xCE, 0xA5, 0xA3, 0xFD, 0x86, 0xD9, 0x58, 0xE1, 0xA7, 0x99, - 0xC8, 0xDA, 0xAA, 0x3B, 0x9E, 0x0D, 0x7E, 0x78, 0x78, 0x30, 0x6C, 0xD6, 0xA3, 0xB8, 0x57, 0xDA, - 0xD1, 0x9E, 0x38, 0xAF, 0x42, 0xF8, 0x95, 0xE3, 0x4B, 0xCD, 0x6B, 0xC2, 0x51, 0xC3, 0x7D, 0xA8, - 0x5C, 0x5D, 0x45, 0x6E, 0xBF, 0xBA, 0x89, 0xDF, 0xF7, 0x71, 0xFB, 0x85, 0x1C, 0x67, 0xDF, 0xAD, - 0x7E, 0x7F, 0x9D, 0x71, 0xA6, 0x0B, 0x0F, 0x94, 0xD7, 0xC1, 0x42, 0xF3, 0x9C, 0xEF, 0xAF, 0x45, - 0x75, 0x6D, 0x5B, 0xDF, 0xE4, 0x9F, 0xA9, 0xF4, 0x92, 0xCA, 0x2A, 0x62, 0x71, 0xF4, 0xAB, 0xA6, - 0x94, 0x61, 0xF7, 0xEF, 0xD0, 0xEA, 0x3F, 0xE0, 0x98, 0x5E, 0x39, 0xD6, 0xBE, 0x12, 0xFE, 0xD1, - 0x5A, 0x0E, 0xB5, 0xE1, 0xFD, 0x4A, 0xEB, 0x49, 0xD4, 0xA1, 0x96, 0x58, 0x04, 0xD6, 0xEF, 0xB5, - 0x9A, 0x37, 0x86, 0x45, 0x74, 0x3E, 0xAA, 0xC3, 0x82, 0x0F, 0x07, 0xF2, 0xAF, 0xD0, 0x0D, 0x0E, - 0xFF, 0x00, 0xE3, 0x07, 0xED, 0xAF, 0xF1, 0xFF, 0x00, 0x52, 0xF1, 0x47, 0x86, 0x7E, 0x33, 0x78, - 0x83, 0xE1, 0xA5, 0xE4, 0xEE, 0xB0, 0x49, 0x65, 0x61, 0x19, 0x36, 0x00, 0xA7, 0xCA, 0x0A, 0xC4, - 0xA4, 0x01, 0xFF, 0x00, 0xD7, 0xAF, 0xCC, 0xFF, 0x00, 0xD8, 0x97, 0xC6, 0x76, 0xBE, 0x1B, 0xF8, - 0xC3, 0xA7, 0xDC, 0xDD, 0x48, 0x23, 0xB7, 0xB7, 0x99, 0x9C, 0x9F, 0xF8, 0x0B, 0x00, 0x3F, 0x5A, - 0xFD, 0x4A, 0xFF, 0x00, 0x82, 0x62, 0x5C, 0x7D, 0xA7, 0xC5, 0xF7, 0x33, 0x1F, 0x95, 0x6E, 0xAE, - 0x5A, 0x44, 0x07, 0xD0, 0x9E, 0x2B, 0xCF, 0xF0, 0xFE, 0x38, 0x0A, 0xD9, 0x15, 0x4A, 0x9E, 0xEB, - 0xAD, 0x4D, 0x49, 0x74, 0xE6, 0x49, 0xB4, 0xF6, 0xEA, 0xBE, 0xF4, 0x79, 0x19, 0xF2, 0xC6, 0x52, - 0xE2, 0x0E, 0x69, 0x5F, 0xD9, 0x4A, 0x30, 0xB7, 0xF2, 0xDE, 0xF3, 0x4E, 0xDD, 0x2F, 0x66, 0xAF, - 0xD6, 0xD6, 0x1D, 0xF1, 0xBB, 0xE2, 0x2F, 0xC7, 0xFF, 0x00, 0xD9, 0x73, 0xC1, 0x3E, 0x20, 0xD0, - 0xFC, 0x41, 0xF1, 0xEB, 0xC4, 0x9E, 0x32, 0xB5, 0xD5, 0x20, 0x68, 0xA7, 0x41, 0x6C, 0xB0, 0x82, - 0xA4, 0x60, 0x80, 0xCD, 0xB9, 0x97, 0x3F, 0xEC, 0x91, 0x5F, 0x92, 0xFF, 0x00, 0x13, 0xAC, 0xD7, - 0x50, 0xB4, 0xD4, 0x4E, 0xDF, 0x9A, 0x4B, 0xE9, 0x47, 0x27, 0x24, 0x92, 0x89, 0xD4, 0xD7, 0xEB, - 0xB7, 0xFC, 0x15, 0x0A, 0x5D, 0xF7, 0x17, 0xB1, 0xAF, 0xCC, 0xC5, 0x4E, 0x00, 0xE4, 0x9A, 0xFC, - 0x89, 0xF8, 0xA6, 0xB2, 0x68, 0x9A, 0x93, 0x41, 0x31, 0x0A, 0xD2, 0xDC, 0xB4, 0xCC, 0xA0, 0xFD, - 0xDC, 0xE0, 0x63, 0xF4, 0xFD, 0x6B, 0xA3, 0x8D, 0xE5, 0x80, 0xC3, 0x70, 0xC4, 0x54, 0x79, 0x63, - 0x56, 0xAB, 0x8B, 0x69, 0x59, 0x37, 0xE7, 0x65, 0xD1, 0x77, 0xDB, 0x63, 0xB3, 0x87, 0x23, 0x56, - 0xA6, 0x6D, 0x52, 0x6E, 0xEE, 0x31, 0x56, 0x5B, 0xD9, 0x6B, 0xB2, 0xFF, 0x00, 0x80, 0x7E, 0xAF, - 0x78, 0xBF, 0xE2, 0x36, 0x97, 0xF1, 0x57, 0xF6, 0x23, 0xF8, 0x7F, 0xAA, 0x68, 0xF3, 0xA4, 0xF6, - 0xE9, 0xA0, 0xC3, 0x69, 0x28, 0x53, 0x96, 0x82, 0x68, 0xD4, 0xAB, 0xA3, 0x0E, 0xC4, 0x71, 0xD7, - 0xD6, 0xBF, 0x29, 0x7F, 0x68, 0x89, 0xA4, 0xB4, 0xF1, 0xB9, 0x92, 0x27, 0x78, 0xE4, 0x59, 0x18, - 0xAB, 0x29, 0xC1, 0x15, 0xEA, 0x3F, 0x0A, 0xFC, 0x7B, 0xA9, 0x78, 0x77, 0xC1, 0x8D, 0x06, 0x97, - 0xAB, 0x5E, 0x59, 0xC3, 0x70, 0x37, 0x49, 0x02, 0x49, 0x98, 0x9C, 0xFA, 0x94, 0x39, 0x19, 0xF7, - 0xEB, 0x5E, 0x51, 0xF1, 0x85, 0x2E, 0x35, 0xCD, 0x50, 0xDC, 0x3C, 0x91, 0xBC, 0x8A, 0x49, 0x3C, - 0x6D, 0x2D, 0x5D, 0xDC, 0x3F, 0xC7, 0x18, 0x0A, 0x99, 0x34, 0x70, 0x35, 0x1B, 0x85, 0x45, 0x6D, - 0xF6, 0x7A, 0xDF, 0x46, 0xB6, 0xF9, 0xD8, 0xDB, 0xFB, 0x1E, 0xB5, 0x1C, 0xCD, 0xE2, 0x74, 0x70, - 0x77, 0xF5, 0x3D, 0x8B, 0xF6, 0x48, 0xFD, 0xB3, 0x35, 0xAB, 0x2B, 0xA8, 0xBC, 0x31, 0xAF, 0x5D, - 0x4D, 0xA8, 0x24, 0xA3, 0x6E, 0x9D, 0x2C, 0x8F, 0xFB, 0xC0, 0xE0, 0x71, 0x11, 0x63, 0xEB, 0xDB, - 0x3D, 0xEB, 0xD5, 0x6F, 0x7F, 0x6E, 0x05, 0xD3, 0xEE, 0x64, 0x85, 0xB4, 0xBB, 0xC5, 0x9A, 0x16, - 0x28, 0xEB, 0x24, 0x81, 0x4A, 0xB0, 0xEA, 0x0F, 0x15, 0xF0, 0x95, 0xB4, 0x92, 0x59, 0xB8, 0x96, - 0x36, 0x68, 0xA6, 0x84, 0xF9, 0x88, 0xE0, 0xE1, 0x91, 0x87, 0x20, 0x8F, 0xA1, 0x02, 0xBD, 0xD7, - 0xF6, 0x96, 0xD5, 0xE1, 0x8B, 0x5F, 0xF0, 0xDE, 0xAD, 0x1C, 0x7E, 0x5D, 0xC7, 0x89, 0x3C, 0x3F, - 0x6B, 0xA8, 0xDD, 0xA2, 0xF1, 0x89, 0x8E, 0xE4, 0x66, 0xFF, 0x00, 0x81, 0x6D, 0x06, 0xBE, 0xFB, - 0x2C, 0xCE, 0x9B, 0xA2, 0xD5, 0x67, 0x7B, 0x5A, 0xCF, 0xBA, 0x67, 0x97, 0x9A, 0x64, 0x94, 0x9E, - 0x21, 0x4E, 0x92, 0xB7, 0x35, 0xF4, 0x5D, 0xD7, 0xF9, 0xAF, 0xEB, 0x53, 0xDA, 0xE7, 0xFD, 0xBB, - 0xEF, 0x23, 0x60, 0xD0, 0x69, 0x71, 0xAB, 0x29, 0xC8, 0x2D, 0x31, 0xE2, 0xBE, 0x7F, 0xFD, 0xBF, - 0x7E, 0x0A, 0x69, 0xFF, 0x00, 0x13, 0x3E, 0x17, 0x58, 0xFC, 0x73, 0xF0, 0xCD, 0x8D, 0xBD, 0x82, - 0xDE, 0x6A, 0x0D, 0xA3, 0xF8, 0xAE, 0xC2, 0xDD, 0x70, 0x96, 0xD7, 0xD8, 0xDF, 0x1D, 0xC0, 0x03, - 0xB4, 0xC8, 0x72, 0x4F, 0xF7, 0x83, 0x57, 0x16, 0xDE, 0x2A, 0x6C, 0x74, 0x6F, 0xCE, 0xBE, 0x8A, - 0xFD, 0x90, 0x61, 0x4F, 0x8C, 0x1F, 0xB2, 0x8F, 0xED, 0x1D, 0xE0, 0xDB, 0xAE, 0x6D, 0xE5, 0xF0, - 0xCC, 0x1A, 0xCC, 0x1B, 0xB9, 0x11, 0xDC, 0xDB, 0xCA, 0x76, 0xB0, 0xF7, 0xDA, 0x48, 0xCD, 0x7C, - 0xCF, 0x1F, 0x61, 0xF0, 0xD9, 0xCE, 0x4D, 0x53, 0x0A, 0xD7, 0xBC, 0xBD, 0xE8, 0xBE, 0xD2, 0x5B, - 0x7F, 0xC1, 0xF2, 0xB9, 0xD5, 0x90, 0xE1, 0xEA, 0x65, 0xD8, 0xB8, 0xE2, 0x23, 0xB6, 0xCF, 0xCD, - 0x3F, 0xEB, 0xEF, 0x3F, 0x39, 0xE8, 0xA0, 0x8C, 0x51, 0x5F, 0xC5, 0xFB, 0x1F, 0xB4, 0x05, 0x14, - 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, - 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, - 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x68, 0x68, 0x16, 0x5F, 0x6C, 0xB9, 0x55, 0xF7, 0x1F, - 0xCE, 0xB3, 0xEB, 0xA4, 0xF0, 0x05, 0xAF, 0xDA, 0x2E, 0xEB, 0x7C, 0x32, 0xBD, 0x44, 0x99, 0x9D, - 0x5F, 0x85, 0x9F, 0xB5, 0x5F, 0x1D, 0x7F, 0xE0, 0x94, 0xDE, 0x3C, 0xFD, 0xA2, 0x3F, 0x64, 0xBF, - 0x05, 0xFC, 0x46, 0xD1, 0x56, 0xDE, 0x48, 0x7C, 0x1B, 0xA0, 0x0B, 0x4D, 0x42, 0x16, 0x38, 0x90, - 0xC4, 0x22, 0x8A, 0x54, 0x70, 0x3B, 0x80, 0x09, 0xCD, 0x7E, 0x71, 0xEA, 0x5A, 0x4B, 0xE9, 0x97, - 0xB3, 0x5B, 0x4A, 0x31, 0x2D, 0xBB, 0x94, 0x61, 0xEE, 0x2B, 0xF6, 0x5B, 0xE3, 0x8F, 0xED, 0x31, - 0xE2, 0x9F, 0x84, 0x5F, 0xB3, 0x26, 0x83, 0xE1, 0xDD, 0x23, 0x54, 0x6B, 0x5F, 0x0C, 0xF8, 0xCF, - 0xC2, 0x96, 0x56, 0x77, 0xE6, 0x31, 0xF3, 0x5B, 0xC8, 0xD6, 0xCA, 0xAB, 0x32, 0x9F, 0x7E, 0x87, - 0xD7, 0x02, 0xBF, 0x28, 0x7E, 0x28, 0xFC, 0x09, 0xF1, 0x47, 0xC3, 0xAD, 0x69, 0xBF, 0xB7, 0x2C, - 0xEE, 0x23, 0x4B, 0xB2, 0x66, 0x86, 0xE9, 0x90, 0x88, 0xEE, 0xD0, 0x9E, 0x1D, 0x0F, 0x70, 0x6B, - 0xFB, 0x07, 0x27, 0x78, 0xDA, 0x94, 0x15, 0x6C, 0x57, 0x2D, 0xA5, 0x6E, 0x4B, 0x6F, 0x6B, 0x2D, - 0xFC, 0xEF, 0x7D, 0xBA, 0x1F, 0x81, 0x62, 0xAB, 0x51, 0xA3, 0x8B, 0x74, 0xA1, 0x37, 0x64, 0xB5, - 0x4E, 0xDF, 0x15, 0xDF, 0xC3, 0xD6, 0xCD, 0x5B, 0x7B, 0xEB, 0x7B, 0x16, 0xBF, 0x64, 0x9F, 0x1F, - 0x5B, 0x7C, 0x2C, 0xF8, 0xBB, 0x1D, 0xE6, 0xAB, 0x19, 0x9B, 0xC3, 0xDA, 0xD5, 0x9C, 0xFA, 0x26, - 0xAF, 0x1E, 0x39, 0x6B, 0x4B, 0x85, 0x0A, 0xCD, 0xFF, 0x00, 0x01, 0x60, 0xAD, 0xFF, 0x00, 0x01, - 0xAF, 0x17, 0xFD, 0xA4, 0xBE, 0x15, 0xEA, 0x5F, 0x02, 0x3E, 0x25, 0xDD, 0x68, 0x77, 0xFF, 0x00, - 0xBC, 0xB2, 0x53, 0xE6, 0xE9, 0x97, 0xA9, 0xCC, 0x3A, 0x8D, 0xA9, 0xFF, 0x00, 0x57, 0x2A, 0x37, - 0x43, 0x95, 0xC6, 0x47, 0x62, 0x08, 0xAF, 0x56, 0xF0, 0xFE, 0x8E, 0x25, 0x0A, 0x36, 0xE0, 0x7A, - 0x62, 0xBD, 0x32, 0x3F, 0x0C, 0xE9, 0xFF, 0x00, 0x16, 0x7C, 0x02, 0xBE, 0x16, 0xF1, 0x55, 0xA3, - 0x6A, 0x1A, 0x4C, 0x24, 0x9B, 0x39, 0xC7, 0xFC, 0x7C, 0x69, 0xCC, 0x7B, 0xC6, 0xDD, 0x76, 0xFB, - 0x0A, 0xF5, 0xB3, 0xBE, 0x15, 0xAD, 0x8E, 0xC0, 0xB8, 0xE1, 0xE7, 0xCB, 0x53, 0x7D, 0x76, 0x7E, - 0x4F, 0xAF, 0xF5, 0xB3, 0x1E, 0x07, 0x88, 0xA1, 0x87, 0xC4, 0xA8, 0xD5, 0x57, 0x8B, 0xED, 0xBA, - 0xF3, 0x47, 0xC4, 0x96, 0x5E, 0x37, 0x8F, 0x4A, 0x6F, 0x95, 0xAA, 0x87, 0x8B, 0x3E, 0x2A, 0xCB, - 0xA9, 0x5B, 0x18, 0x95, 0xBE, 0x5E, 0x9D, 0x6B, 0xDE, 0x7E, 0x2B, 0x7F, 0xC1, 0x27, 0xFC, 0x75, - 0x69, 0x04, 0xDA, 0x97, 0x81, 0x6E, 0xAD, 0xFC, 0x61, 0xA5, 0x80, 0x5C, 0x42, 0x8E, 0x12, 0xF2, - 0x31, 0xE8, 0x57, 0xA3, 0x1F, 0xA5, 0x7C, 0xD1, 0xE3, 0xBF, 0x84, 0x1E, 0x2C, 0xF8, 0x6D, 0xA8, - 0x49, 0x6B, 0xE2, 0x0F, 0x0F, 0x6B, 0x1A, 0x44, 0xF1, 0x9C, 0x32, 0xDD, 0x5A, 0x3C, 0x78, 0xFC, - 0x71, 0x8A, 0xFE, 0x59, 0xE2, 0x6C, 0xBF, 0x3A, 0xC0, 0xD4, 0x74, 0xF1, 0xD4, 0x65, 0x05, 0xDF, - 0x78, 0xBF, 0x49, 0x2D, 0x3F, 0x1B, 0x9F, 0xAF, 0x65, 0x15, 0xF0, 0x18, 0x88, 0x29, 0x61, 0xA6, - 0xA5, 0xF9, 0xFD, 0xCF, 0x53, 0xA5, 0xF8, 0x41, 0xE3, 0xE5, 0xD0, 0xF5, 0xC8, 0xE4, 0x76, 0xC7, - 0xCD, 0x9E, 0xB5, 0xF7, 0x67, 0xC0, 0x4F, 0xDA, 0x56, 0xC6, 0xCA, 0xC2, 0x1F, 0xF4, 0xC6, 0x86, - 0x45, 0x51, 0xCA, 0x48, 0x54, 0x8F, 0xCA, 0xBF, 0x33, 0x2D, 0xEE, 0x1A, 0xD9, 0xF2, 0xA6, 0xBA, - 0x0D, 0x23, 0xE2, 0x56, 0xA1, 0xA4, 0x2F, 0xEE, 0xE6, 0x91, 0x7E, 0x86, 0xBE, 0x67, 0x0B, 0x99, - 0x4E, 0x8A, 0xB2, 0x3D, 0x2C, 0x46, 0x06, 0x35, 0x75, 0x67, 0xE9, 0x77, 0xC6, 0x5F, 0xDA, 0x6F, - 0x4D, 0xB9, 0xD2, 0x25, 0x66, 0xBC, 0x69, 0xE4, 0x65, 0x3F, 0x33, 0xCB, 0xB8, 0xFE, 0xA6, 0xBE, - 0x0F, 0xF8, 0xE3, 0xF1, 0x35, 0x35, 0xED, 0x7E, 0x49, 0x22, 0x6C, 0xFC, 0xD9, 0xC8, 0x35, 0xC2, - 0xEA, 0xBF, 0x14, 0xB5, 0x2D, 0x56, 0x3D, 0xB2, 0x4D, 0x23, 0x0F, 0x73, 0x5C, 0xED, 0xD5, 0xEC, - 0x97, 0x6F, 0xB9, 0xDB, 0x39, 0xA7, 0x89, 0xCC, 0xA7, 0x55, 0x59, 0x86, 0x1F, 0x2F, 0x8D, 0x27, - 0x74, 0x7A, 0xB7, 0x83, 0x3E, 0x38, 0x4D, 0xA5, 0x59, 0x08, 0x59, 0xCE, 0xDE, 0x9D, 0x6A, 0xEE, - 0xA9, 0xF1, 0x26, 0x3D, 0x68, 0x67, 0x76, 0x49, 0xAF, 0x3A, 0xF0, 0x47, 0xC2, 0x8F, 0x14, 0xFC, - 0x42, 0xBE, 0x5B, 0x6D, 0x07, 0x41, 0xD5, 0xF5, 0x49, 0xA4, 0x38, 0x55, 0xB6, 0xB6, 0x77, 0xCF, - 0xE3, 0x8C, 0x57, 0xBE, 0xF8, 0x13, 0xFE, 0x09, 0xBD, 0xE3, 0x08, 0x2D, 0xE3, 0xBE, 0xF1, 0xA5, - 0xD4, 0x3E, 0x17, 0xB1, 0x38, 0x6F, 0xB3, 0x96, 0x12, 0x5E, 0x4A, 0x3D, 0x02, 0xFF, 0x00, 0x09, - 0xAF, 0xA8, 0xE1, 0x7C, 0xA7, 0x3D, 0xCC, 0x6A, 0x2A, 0x78, 0x0A, 0x12, 0x92, 0xEF, 0x6B, 0x45, - 0x7A, 0xC9, 0xD9, 0x7E, 0x37, 0x3C, 0xCC, 0xDB, 0x11, 0x97, 0xE1, 0xA0, 0xE5, 0x89, 0x9A, 0x8F, - 0x97, 0x5F, 0xB9, 0x6A, 0x71, 0xBF, 0x0F, 0xBC, 0x3B, 0x2F, 0xC4, 0x3F, 0x13, 0xC3, 0x63, 0x1B, - 0x79, 0x76, 0xEC, 0x77, 0x5D, 0xCE, 0x7E, 0xED, 0xBC, 0x23, 0xEF, 0x31, 0x3F, 0x4C, 0x81, 0xEA, - 0x4D, 0x75, 0xFF, 0x00, 0x1A, 0x3C, 0x69, 0xFF, 0x00, 0x09, 0xBF, 0x8F, 0x25, 0xB9, 0x89, 0x5A, - 0x2B, 0x0B, 0x58, 0x63, 0xB1, 0xB1, 0x43, 0xFC, 0x10, 0x44, 0x30, 0xBF, 0x99, 0xC9, 0xFC, 0x6B, - 0xB8, 0xD4, 0x3C, 0x13, 0xA7, 0xF8, 0x37, 0x44, 0xFE, 0xCA, 0xD1, 0x6D, 0x5A, 0xD2, 0xC5, 0x7F, - 0xD6, 0x33, 0x1C, 0xCD, 0x74, 0x7D, 0x5D, 0xBF, 0xA5, 0x79, 0xEF, 0x89, 0xB4, 0x66, 0x72, 0x55, - 0x15, 0x99, 0xF3, 0xC0, 0x03, 0xAD, 0x7F, 0x51, 0xE5, 0xBC, 0x31, 0x88, 0xCB, 0x32, 0xD5, 0x4A, - 0xB4, 0xF9, 0xA7, 0xBC, 0xAD, 0xB2, 0xF2, 0x8D, 0xF5, 0xB2, 0xEF, 0xA5, 0xDF, 0x44, 0x7E, 0x75, - 0x4F, 0x36, 0xA7, 0x8A, 0xC4, 0x73, 0xC3, 0x48, 0xAD, 0x17, 0x7F, 0x36, 0xFC, 0xDD, 0x97, 0xA2, - 0xF5, 0x39, 0xF6, 0x7A, 0xFA, 0xD3, 0xFE, 0x09, 0xF7, 0xA1, 0x4F, 0xA7, 0x7C, 0x0F, 0xF8, 0x9D, - 0x78, 0xAA, 0xDE, 0x77, 0x8A, 0xEC, 0x9F, 0x47, 0xB5, 0x5C, 0x72, 0xEB, 0x1C, 0x32, 0x4B, 0x21, - 0xFA, 0x0F, 0xE9, 0x5F, 0x29, 0x69, 0xBE, 0x1D, 0xBA, 0xD4, 0x35, 0x34, 0xB7, 0x91, 0x1A, 0xD9, - 0x73, 0x99, 0x24, 0x90, 0x61, 0x63, 0x5E, 0xE4, 0xD7, 0xDD, 0x1F, 0xB0, 0xDC, 0xF6, 0x7A, 0x8E, - 0x95, 0xAB, 0x2D, 0xBE, 0xD8, 0xF4, 0xAD, 0x07, 0x41, 0xBE, 0x86, 0xD5, 0x4F, 0xF1, 0x48, 0xD6, - 0xD2, 0x6F, 0x94, 0xFB, 0x9F, 0xEB, 0x5B, 0x61, 0xF0, 0x8E, 0x74, 0x2A, 0xD7, 0x92, 0xD2, 0x31, - 0x95, 0xBC, 0xDD, 0xBF, 0x42, 0x73, 0x6C, 0x52, 0x87, 0x25, 0x18, 0x7D, 0xA6, 0xAF, 0xE9, 0x7F, - 0xD4, 0xFC, 0xA4, 0xD4, 0xA0, 0xFB, 0x3D, 0xDB, 0xA8, 0xE8, 0xAC, 0x40, 0xFC, 0xEA, 0xBD, 0x6B, - 0x78, 0xA6, 0xD3, 0xEC, 0xD7, 0x6D, 0x8E, 0xEC, 0x7F, 0x9D, 0x64, 0xD7, 0xF1, 0x16, 0x25, 0x25, - 0x55, 0xA4, 0x7E, 0xCD, 0x4E, 0x57, 0x8A, 0x61, 0x45, 0x14, 0x56, 0x06, 0x81, 0x45, 0x14, 0x50, - 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, - 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, - 0x01, 0x5D, 0x47, 0xC3, 0x6B, 0xA5, 0x87, 0x53, 0x55, 0x6E, 0x8D, 0xC1, 0xAE, 0x5E, 0xAE, 0xE8, - 0xB7, 0xFF, 0x00, 0x61, 0xBB, 0x56, 0xF4, 0x39, 0xAD, 0xA8, 0x4B, 0x96, 0x69, 0xB2, 0x2A, 0x2B, - 0xC6, 0xC7, 0xEC, 0x3F, 0x87, 0x7F, 0x68, 0xE8, 0x7E, 0x36, 0xFE, 0xC3, 0x3E, 0x15, 0x76, 0x99, - 0x64, 0xD5, 0x3C, 0x2F, 0x6E, 0x34, 0x5B, 0xE5, 0x27, 0x2C, 0x0C, 0x67, 0x31, 0xB7, 0xD1, 0x90, - 0xF1, 0xFE, 0xE1, 0xAF, 0x54, 0xFF, 0x00, 0x82, 0x89, 0xFE, 0xD8, 0x1A, 0x3F, 0x8F, 0x3F, 0x60, - 0x9F, 0x02, 0xF8, 0x7F, 0x5A, 0xF0, 0xDE, 0x96, 0x75, 0xED, 0x08, 0xC7, 0x15, 0xA6, 0xA3, 0x6B, - 0x12, 0xC2, 0x4D, 0xB0, 0x4C, 0x15, 0x20, 0x74, 0x72, 0xDC, 0x93, 0xD0, 0xFA, 0x0A, 0xFC, 0x9C, - 0xF8, 0x2F, 0xFB, 0x44, 0xDC, 0x78, 0x06, 0x2B, 0x98, 0x16, 0x46, 0x7D, 0x3F, 0x52, 0x8C, 0x45, - 0x79, 0x08, 0x3F, 0x7B, 0x07, 0x2A, 0xE3, 0xFD, 0xA5, 0x3F, 0xA1, 0x22, 0xBD, 0xF3, 0xF6, 0xBD, - 0xF8, 0xD3, 0x6B, 0xE3, 0x5F, 0x83, 0xDE, 0x15, 0x4B, 0x0B, 0xC8, 0xEE, 0x61, 0x31, 0x8C, 0x94, - 0x6C, 0xF3, 0x8E, 0xF5, 0xFD, 0x5B, 0xC3, 0xB9, 0x8E, 0x13, 0x32, 0xCB, 0x30, 0xB3, 0xFF, 0x00, - 0x97, 0xB4, 0x1B, 0xD3, 0xAD, 0xED, 0x64, 0xFC, 0xD3, 0x5B, 0xF9, 0x9F, 0xCD, 0x9C, 0x59, 0x92, - 0xE6, 0x30, 0xE2, 0x1A, 0x11, 0xA0, 0xDA, 0xA3, 0x59, 0xB5, 0x3B, 0x2D, 0x1C, 0x74, 0x95, 0x9F, - 0x66, 0x9A, 0xBA, 0x6A, 0xCF, 0xB3, 0xDC, 0x8B, 0xE1, 0xDC, 0x96, 0x9E, 0x28, 0x88, 0x49, 0xA7, - 0xCE, 0xB7, 0x2A, 0xBF, 0x7E, 0x3E, 0x93, 0x45, 0xFE, 0xF2, 0xF5, 0xFC, 0x46, 0x45, 0x7B, 0x67, - 0xC3, 0xDF, 0x0F, 0x2C, 0xA1, 0x30, 0xA3, 0xD2, 0xBE, 0x09, 0xD1, 0xBC, 0x41, 0x79, 0xA6, 0xEA, - 0x11, 0xCD, 0x65, 0x71, 0x35, 0xBD, 0xC2, 0x30, 0x28, 0xF1, 0xB9, 0x56, 0x53, 0xF5, 0xAF, 0xA4, - 0x3E, 0x08, 0x7E, 0xD8, 0xDA, 0xCF, 0x86, 0xDE, 0x18, 0xBC, 0x47, 0xA4, 0xC7, 0xAF, 0x5B, 0x2F, - 0x5B, 0x8B, 0x66, 0x10, 0xDD, 0xA8, 0xF5, 0x3F, 0xC2, 0xFF, 0x00, 0x8E, 0x2B, 0xF4, 0xFC, 0xBB, - 0x35, 0xF6, 0xEB, 0x92, 0x51, 0xD5, 0x75, 0x5B, 0x1D, 0xD8, 0xEE, 0x1B, 0x74, 0x67, 0xED, 0x29, - 0xCA, 0xFE, 0x4F, 0x47, 0xFD, 0x7D, 0xC7, 0xDB, 0x3F, 0x0E, 0x3C, 0x2C, 0xD0, 0x4F, 0x1C, 0x90, - 0x99, 0x21, 0x93, 0x8C, 0x3A, 0x31, 0x56, 0xFC, 0xC7, 0x35, 0xF4, 0x17, 0x82, 0x3C, 0x39, 0x3F, - 0x8A, 0x2C, 0x56, 0xD7, 0x56, 0xB3, 0xD3, 0x75, 0xCB, 0x36, 0xFB, 0xD0, 0xEA, 0x76, 0x31, 0x5D, - 0x2B, 0x7F, 0xDF, 0x4B, 0x9F, 0xD6, 0xBE, 0x75, 0xF8, 0x03, 0xFB, 0x51, 0xFC, 0x35, 0xF1, 0xB4, - 0x96, 0xF1, 0x8F, 0x12, 0x43, 0xA2, 0xDC, 0xB6, 0x33, 0x6D, 0xAC, 0xC6, 0x6D, 0x18, 0x1F, 0x4D, - 0xE7, 0xF7, 0x67, 0xFE, 0xFA, 0xAF, 0xB9, 0x3E, 0x0D, 0xF8, 0x7A, 0xD7, 0x5E, 0xD3, 0xE1, 0x9B, - 0x4D, 0x9A, 0xD7, 0x52, 0x85, 0x80, 0x22, 0x4B, 0x49, 0x56, 0xE1, 0x08, 0xFA, 0xA1, 0x22, 0xBC, - 0x5E, 0x28, 0xC7, 0xD3, 0xA7, 0x4B, 0x96, 0xA4, 0x6F, 0x7E, 0xEB, 0xFC, 0xC3, 0x2B, 0xC1, 0xD4, - 0x75, 0x34, 0xBA, 0xF4, 0x3C, 0xDB, 0x51, 0xFF, 0x00, 0x82, 0x6C, 0x7C, 0x09, 0xF8, 0xA3, 0x27, - 0x99, 0xAF, 0xFC, 0x17, 0xF0, 0x2D, 0xD4, 0xB2, 0x72, 0xCD, 0x67, 0x04, 0x96, 0x04, 0x9F, 0xFB, - 0x62, 0xCA, 0x2B, 0x22, 0xF3, 0xFE, 0x08, 0x3D, 0xFB, 0x2A, 0xEB, 0x2C, 0xD2, 0x7F, 0xC2, 0x9F, - 0x92, 0xD4, 0xB0, 0xE9, 0x6D, 0xE2, 0x3B, 0xD4, 0x51, 0xF4, 0x05, 0xCD, 0x7D, 0xAF, 0xE0, 0xCF, - 0x01, 0xE4, 0x2E, 0xE8, 0x59, 0x78, 0xFE, 0x25, 0xC5, 0x7A, 0x46, 0x87, 0xF0, 0xC4, 0x5D, 0xA2, - 0xAA, 0xC6, 0x0B, 0x1A, 0xFE, 0x7C, 0xCD, 0xAA, 0xE5, 0x9C, 0xEE, 0x53, 0xA3, 0x1F, 0xB8, 0xFD, - 0x2B, 0x03, 0x1C, 0x5F, 0x2A, 0x51, 0x9B, 0xFB, 0xCF, 0xCD, 0x83, 0xFF, 0x00, 0x04, 0x2E, 0xFD, - 0x96, 0x7C, 0x38, 0x77, 0xC7, 0xF0, 0x7D, 0xAE, 0x0A, 0xF3, 0x8B, 0x9F, 0x10, 0xDE, 0x48, 0xA7, - 0xFF, 0x00, 0x1E, 0x15, 0x04, 0xDF, 0xF0, 0x4F, 0x0F, 0x82, 0x7F, 0x0C, 0x58, 0xC9, 0xE1, 0xDF, - 0x83, 0x5E, 0x05, 0xB3, 0x92, 0x3E, 0x51, 0xAE, 0xA0, 0x96, 0xF8, 0xAF, 0xFD, 0xFD, 0x73, 0x5F, - 0xA4, 0x1E, 0x32, 0xF8, 0x57, 0xFD, 0x9F, 0x09, 0xDF, 0x18, 0x5E, 0x3B, 0xD7, 0x8B, 0x7C, 0x42, - 0xF0, 0x11, 0x26, 0x4F, 0x2E, 0x16, 0x93, 0x39, 0xE1, 0x57, 0x71, 0xFD, 0x2B, 0xD0, 0xE1, 0xDA, - 0x99, 0x57, 0x3A, 0x9C, 0x28, 0xC7, 0xEE, 0x39, 0xB3, 0x48, 0xE3, 0x79, 0x5C, 0x65, 0x51, 0x9F, - 0x0C, 0xF8, 0xEB, 0x47, 0xB9, 0xD0, 0x6C, 0xDE, 0xD3, 0x4E, 0x86, 0xC7, 0x45, 0xB5, 0x5F, 0xBB, - 0x0E, 0x9B, 0x65, 0x1D, 0xA2, 0x8F, 0xFB, 0xE1, 0x73, 0xFA, 0xD7, 0xCE, 0xDF, 0x13, 0x3C, 0x26, - 0xCD, 0x34, 0x92, 0x36, 0xE7, 0x91, 0xB3, 0x97, 0x62, 0x59, 0x8F, 0xD4, 0x9E, 0x6B, 0xEE, 0x4F, - 0x8D, 0xFE, 0x0A, 0x4D, 0x16, 0xCE, 0x5B, 0x8B, 0xE6, 0xB7, 0xD3, 0xA1, 0x50, 0x4B, 0x49, 0x79, - 0x32, 0x5B, 0xA0, 0xFC, 0x5C, 0x81, 0x5F, 0x10, 0xFC, 0x7C, 0xFD, 0xA2, 0x7E, 0x1D, 0x78, 0x25, - 0xE6, 0x8E, 0x6F, 0x11, 0xDB, 0xEA, 0xF7, 0x4A, 0x4F, 0xFA, 0x36, 0x90, 0x86, 0xED, 0x89, 0xF4, - 0xDE, 0x3F, 0x76, 0x3F, 0xEF, 0xAA, 0xFE, 0x90, 0xC8, 0x31, 0xF4, 0x2A, 0x61, 0xD7, 0x22, 0x4A, - 0xDD, 0x97, 0xF9, 0x1F, 0x98, 0xE3, 0x30, 0x75, 0xDD, 0x5E, 0x5D, 0x5B, 0x3E, 0x7D, 0xF8, 0x87, - 0xE1, 0x96, 0xB7, 0x77, 0x6F, 0x2C, 0xED, 0xF5, 0xC5, 0x73, 0x5F, 0x09, 0x35, 0x9F, 0x0B, 0xFC, - 0x3E, 0xF8, 0xCF, 0xA2, 0xDF, 0x78, 0xD2, 0x35, 0x6D, 0x1E, 0x3B, 0x85, 0x69, 0xAD, 0xCF, 0xFA, - 0xC9, 0x57, 0x3C, 0xE1, 0x7B, 0x7D, 0x4D, 0x7E, 0xB2, 0xFE, 0xC2, 0xBF, 0x0B, 0x7E, 0x0A, 0xFE, - 0xD1, 0x3F, 0xB1, 0xD5, 0xD6, 0xAD, 0x7D, 0xA0, 0xDF, 0x2E, 0xAD, 0xAD, 0x59, 0xDE, 0xC4, 0xF7, - 0x33, 0x48, 0xBE, 0x66, 0x95, 0x2C, 0x28, 0x19, 0x18, 0x1E, 0x84, 0xB0, 0x3D, 0x06, 0x71, 0x8A, - 0xFC, 0x11, 0xF8, 0x8D, 0x72, 0xCB, 0xE3, 0xED, 0x6A, 0x3F, 0x36, 0x69, 0x96, 0x0B, 0xE9, 0xA2, - 0x56, 0x91, 0xB7, 0x33, 0x2A, 0xB9, 0x03, 0x27, 0xE8, 0x2B, 0xC5, 0xC4, 0xF1, 0x75, 0x2C, 0x4C, - 0xAB, 0x61, 0x21, 0x4A, 0x51, 0x70, 0x7C, 0xAD, 0xBD, 0x13, 0xDF, 0x55, 0xF7, 0x79, 0x77, 0x3D, - 0x9C, 0xAF, 0x86, 0xE7, 0x5E, 0x9F, 0xBF, 0x51, 0x72, 0xC9, 0x74, 0xD5, 0xAF, 0x2F, 0xEA, 0xFD, - 0x8F, 0xAA, 0xBF, 0xE0, 0xA8, 0x5F, 0x1A, 0x7E, 0x16, 0xFC, 0x48, 0xF1, 0x7D, 0xAC, 0x9F, 0x0B, - 0xF4, 0xE1, 0xA4, 0xD8, 0xEC, 0x5D, 0xD1, 0x21, 0xEF, 0x8E, 0x49, 0x23, 0xDE, 0xB0, 0xBE, 0x0F, - 0x7C, 0x65, 0x5F, 0x84, 0x1F, 0xB2, 0xBF, 0x89, 0xAE, 0x96, 0x5D, 0x9A, 0x86, 0xAD, 0x68, 0x74, - 0xCB, 0x30, 0x0F, 0xCC, 0xCF, 0x2F, 0xDE, 0x3F, 0x40, 0x80, 0xE7, 0xFD, 0xE1, 0x5F, 0x2B, 0x4B, - 0x77, 0xB1, 0x0B, 0x33, 0x60, 0x0E, 0xE7, 0xB5, 0x3B, 0x5C, 0xF8, 0x89, 0x36, 0xA7, 0xA6, 0x5A, - 0xDA, 0xB4, 0x85, 0x6D, 0x2C, 0x90, 0xAC, 0x51, 0xE7, 0xB9, 0xEA, 0xC7, 0xDC, 0xFF, 0x00, 0x20, - 0x2B, 0xE2, 0xB3, 0x8E, 0x24, 0xA7, 0x95, 0x65, 0xB5, 0xA1, 0x7F, 0x7E, 0x70, 0xE4, 0x8A, 0x6E, - 0xEF, 0x5D, 0x1B, 0x6D, 0xF6, 0x5F, 0x7B, 0xB1, 0xF4, 0x98, 0x1E, 0x17, 0x54, 0xE9, 0xD2, 0xC3, - 0xC5, 0xB9, 0x28, 0xCF, 0x99, 0xB7, 0x65, 0xD5, 0xBB, 0x24, 0xB4, 0x4A, 0xFA, 0x24, 0xB6, 0x47, - 0x23, 0xE3, 0x67, 0x5F, 0xB6, 0x6D, 0x5F, 0xE1, 0xE2, 0xB0, 0x6A, 0xE6, 0xB3, 0x7A, 0x6F, 0x2E, - 0x99, 0xBD, 0xEA, 0x9D, 0x7F, 0x27, 0xD7, 0x97, 0x35, 0x46, 0xD1, 0xFA, 0xB5, 0x35, 0x68, 0xD8, - 0x28, 0xA2, 0x8A, 0xC4, 0xA0, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, - 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, - 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA0, 0x1C, 0x51, 0x45, 0x00, 0x68, 0x69, - 0x7A, 0xA3, 0x5A, 0xB6, 0x37, 0x57, 0x41, 0x61, 0xE2, 0xE9, 0xAD, 0x55, 0x63, 0xF3, 0x19, 0xE0, - 0x07, 0x3E, 0x59, 0x6E, 0x07, 0xD3, 0xD2, 0xB8, 0xF0, 0x70, 0x6A, 0x68, 0xEE, 0x4A, 0x1A, 0xFA, - 0x4C, 0x97, 0x88, 0x2B, 0x60, 0x6A, 0x29, 0x41, 0xD8, 0xE3, 0xAF, 0x83, 0x85, 0x4D, 0xD1, 0xEC, - 0x1F, 0x0F, 0xEE, 0xAD, 0xF5, 0xA9, 0xF2, 0x92, 0x29, 0x93, 0xBC, 0x64, 0xFC, 0xC3, 0xF0, 0xAF, - 0x68, 0xF0, 0x2E, 0x81, 0xBC, 0xA7, 0x19, 0x66, 0xF6, 0xAF, 0x92, 0xB4, 0x9D, 0x58, 0xDB, 0xCE, - 0xAE, 0xAC, 0xCB, 0x22, 0xF4, 0x65, 0x38, 0x23, 0xF1, 0xAF, 0x5E, 0xF8, 0x5D, 0xFB, 0x49, 0x6A, - 0xDE, 0x11, 0x9A, 0x25, 0xB8, 0x8E, 0xDF, 0x54, 0xB7, 0x53, 0xF7, 0x65, 0xF9, 0x64, 0x03, 0xD9, - 0xC7, 0xF5, 0x06, 0xBF, 0xA3, 0xB8, 0x3B, 0xC4, 0xFC, 0xBB, 0xD9, 0xAA, 0x58, 0xE8, 0xF2, 0xBF, - 0xE6, 0x5A, 0xAF, 0x9A, 0xDD, 0x7C, 0xAF, 0xF2, 0x3F, 0x3D, 0xE2, 0x6E, 0x1E, 0xC5, 0x54, 0x8B, - 0x96, 0x19, 0xDF, 0xC9, 0xE9, 0xF7, 0x3D, 0xBF, 0x23, 0xEC, 0xDF, 0x87, 0x1F, 0x0D, 0xED, 0xB5, - 0x25, 0x8D, 0x6E, 0x2D, 0xA3, 0x9B, 0x77, 0x50, 0xCB, 0x9A, 0xF7, 0xAF, 0x85, 0xDF, 0x03, 0x2D, - 0x74, 0xD9, 0xE3, 0x9B, 0x4D, 0x9F, 0x52, 0xD1, 0xE6, 0x04, 0x11, 0x25, 0x8D, 0xD3, 0xC2, 0x41, - 0xFC, 0x0D, 0x7C, 0xDB, 0xF0, 0x17, 0xF6, 0xD7, 0xF0, 0x1D, 0xE3, 0xC3, 0x16, 0xAF, 0xF6, 0xED, - 0x0E, 0x63, 0x8C, 0xB4, 0xD1, 0x79, 0xD0, 0x8F, 0xF8, 0x12, 0xF3, 0xFF, 0x00, 0x8E, 0xD7, 0xDB, - 0x5F, 0x00, 0x7E, 0x26, 0xF8, 0x17, 0xC7, 0x82, 0x16, 0xD3, 0x7C, 0x59, 0xE1, 0xDB, 0x80, 0x71, - 0x85, 0x37, 0x89, 0x13, 0xFE, 0x4F, 0xB4, 0xD7, 0xE9, 0x95, 0x38, 0x8B, 0x2D, 0xC4, 0x52, 0x73, - 0xA1, 0x5A, 0x32, 0x5D, 0xAF, 0xAF, 0xDC, 0xF5, 0xFB, 0xD1, 0xF9, 0xBA, 0xCA, 0xF1, 0x54, 0x67, - 0xCB, 0x52, 0x0D, 0x3F, 0x4F, 0xD7, 0x63, 0xD5, 0xBE, 0x11, 0x5C, 0xFC, 0x47, 0xF0, 0xF7, 0x94, - 0xBA, 0x6F, 0xC4, 0xCF, 0x19, 0x43, 0x1A, 0xF0, 0x12, 0x5B, 0xC6, 0x95, 0x47, 0xFD, 0xF5, 0x9A, - 0xFA, 0xB7, 0xE0, 0x6F, 0xC6, 0x4F, 0x88, 0x9E, 0x1E, 0x9D, 0x64, 0xD5, 0xFC, 0x5D, 0x77, 0xAC, - 0x43, 0xB7, 0x1B, 0x67, 0x85, 0x77, 0x0F, 0xC7, 0x15, 0xE6, 0x5F, 0x0D, 0x7C, 0x27, 0x0D, 0xD5, - 0xBC, 0x72, 0x5A, 0xB4, 0x37, 0x2B, 0x81, 0x86, 0x86, 0x45, 0x91, 0x4F, 0xFD, 0xF2, 0x4D, 0x7A, - 0xA6, 0x8D, 0xE1, 0x7B, 0x88, 0x61, 0x18, 0x86, 0x7F, 0xFB, 0xF6, 0xDF, 0xE1, 0x5F, 0x90, 0xF1, - 0x2D, 0x6C, 0x16, 0x26, 0xF0, 0x95, 0x38, 0x6B, 0xD7, 0x96, 0x37, 0xFB, 0xED, 0x73, 0xED, 0x32, - 0x9A, 0x78, 0x9A, 0x56, 0x97, 0x3C, 0xBE, 0xF7, 0xFE, 0x65, 0x5F, 0xDA, 0x4B, 0xE3, 0xB7, 0x8F, - 0xBC, 0x51, 0x64, 0xA9, 0xA1, 0xF8, 0x9A, 0xE3, 0x44, 0x8E, 0x35, 0x3B, 0x9A, 0x18, 0x57, 0x7B, - 0x1F, 0xAE, 0x2B, 0xE2, 0x5F, 0x8D, 0x73, 0x7C, 0x49, 0xF1, 0x1B, 0x4A, 0x9A, 0x8F, 0xC4, 0xEF, - 0x1B, 0x49, 0x1B, 0x64, 0x14, 0x8A, 0xF5, 0xE1, 0x53, 0xF8, 0x29, 0x15, 0xF6, 0xBF, 0x89, 0xBC, - 0x2B, 0x71, 0x34, 0x4D, 0xBA, 0x09, 0x8F, 0xFD, 0xB3, 0x3F, 0xE1, 0x5E, 0x25, 0xF1, 0x5F, 0xC1, - 0x56, 0xF6, 0xF0, 0x48, 0xF7, 0x4D, 0x05, 0xB2, 0x80, 0x7E, 0x69, 0x64, 0x58, 0xC0, 0xFF, 0x00, - 0xBE, 0x88, 0xAE, 0xCE, 0x11, 0xAD, 0x81, 0xC2, 0x25, 0x08, 0xD3, 0x85, 0xBB, 0xF2, 0xC6, 0xFF, - 0x00, 0x7D, 0xAE, 0x61, 0x9D, 0x47, 0x15, 0x55, 0xB9, 0xF3, 0xCB, 0xEF, 0x67, 0xE7, 0x5F, 0xC5, - 0x3F, 0x80, 0xB1, 0x6A, 0x37, 0x12, 0x4F, 0xA8, 0x5E, 0xEA, 0xDA, 0xA4, 0xDC, 0x92, 0xF7, 0x97, - 0x4F, 0x2B, 0x1F, 0xCC, 0xD7, 0x86, 0x78, 0xEF, 0xE1, 0x94, 0x1A, 0x21, 0x7F, 0x26, 0x15, 0x5C, - 0x1F, 0xEE, 0xD7, 0xD9, 0x3F, 0x1E, 0xFE, 0x23, 0x78, 0x1F, 0xC0, 0xDE, 0x68, 0xD4, 0xBC, 0x55, - 0xA0, 0x5B, 0xE3, 0x3C, 0x2D, 0xDA, 0xCA, 0xDF, 0x92, 0x6E, 0x35, 0xF1, 0x87, 0xC6, 0xEF, 0xDB, - 0x03, 0xC0, 0xF0, 0x4B, 0x34, 0x7A, 0x48, 0xBE, 0xD6, 0xA4, 0x5C, 0x80, 0xD1, 0xC7, 0xE4, 0xC2, - 0x7F, 0xE0, 0x4D, 0xCF, 0xFE, 0x3B, 0x5F, 0xB0, 0x4F, 0x89, 0x32, 0xCC, 0x35, 0x2E, 0x7C, 0x45, - 0x68, 0xC5, 0x76, 0xBA, 0xBF, 0xDC, 0xB5, 0xFB, 0x91, 0xF2, 0x14, 0xF2, 0xFC, 0x6D, 0x59, 0x5A, - 0x94, 0x24, 0xFF, 0x00, 0x2F, 0xBF, 0x63, 0xDA, 0x7E, 0x00, 0x7E, 0xD8, 0x29, 0xF0, 0x13, 0xE0, - 0x12, 0xE8, 0x6B, 0x70, 0xD1, 0xCD, 0x24, 0xF7, 0x44, 0xAA, 0xB6, 0x30, 0x1E, 0x35, 0x51, 0xFC, - 0xAB, 0xF3, 0x9F, 0xE2, 0x65, 0xFC, 0x36, 0xFE, 0x23, 0xBC, 0xBA, 0x67, 0x1B, 0xEE, 0x67, 0x79, - 0x36, 0x83, 0xC9, 0xCB, 0x13, 0x5A, 0x5F, 0x10, 0xBE, 0x3F, 0x6A, 0x1E, 0x25, 0x92, 0x45, 0x8D, - 0x21, 0xD3, 0xED, 0xD8, 0xF0, 0x91, 0x7C, 0xCC, 0x7E, 0xAC, 0x7F, 0xA0, 0x15, 0xE5, 0x7A, 0xBE, - 0xAC, 0x6E, 0xA6, 0x66, 0x66, 0x66, 0x66, 0xEA, 0x49, 0xC9, 0x35, 0xF8, 0xDF, 0x17, 0x78, 0x8D, - 0x95, 0xC2, 0x2E, 0x19, 0x7C, 0x6F, 0x2B, 0xB7, 0xCC, 0xF4, 0xFB, 0x96, 0xEF, 0xE7, 0x63, 0xF4, - 0xAE, 0x15, 0xE1, 0xDC, 0x56, 0x1D, 0xBA, 0x98, 0x97, 0xBF, 0x42, 0xC6, 0xA7, 0xAE, 0x3D, 0xE3, - 0xFC, 0xC7, 0x6A, 0x2F, 0x45, 0xCD, 0x66, 0xDD, 0xEA, 0x2C, 0xE3, 0x19, 0xE2, 0xAA, 0xC9, 0x39, - 0x7A, 0x8C, 0x9C, 0xD7, 0xF3, 0xCE, 0x73, 0x9F, 0x56, 0xC6, 0xD4, 0x72, 0x9B, 0xBD, 0xCF, 0xD1, - 0x69, 0xE1, 0xE3, 0x14, 0x04, 0xE4, 0xD1, 0x45, 0x15, 0xF3, 0xA7, 0x48, 0x51, 0x45, 0x14, 0x00, - 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, - 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, - 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0xE5, 0x72, 0xA6, 0xAD, - 0xD9, 0xEA, 0xEF, 0x6E, 0x47, 0x35, 0x4A, 0x8A, 0xD2, 0x9D, 0x69, 0xC1, 0xDE, 0x2C, 0x99, 0x41, - 0x4B, 0x46, 0x75, 0xDA, 0x3F, 0x8E, 0x9A, 0xD8, 0x8C, 0x9A, 0xEB, 0x34, 0x2F, 0x8A, 0x30, 0xC0, - 0xCA, 0xDC, 0x2B, 0x7A, 0xA9, 0xDA, 0x7F, 0x31, 0x5E, 0x4A, 0x1B, 0x14, 0xF5, 0x9D, 0x97, 0xF8, - 0x8D, 0x7A, 0x54, 0x73, 0x9C, 0x4C, 0x36, 0x67, 0x2C, 0xF0, 0x34, 0xA5, 0xD0, 0xFA, 0x77, 0xC2, - 0x3F, 0xB5, 0x26, 0xB3, 0xE1, 0xC5, 0x5F, 0xB0, 0x78, 0x93, 0x5D, 0xB1, 0xDB, 0xD3, 0xC8, 0xD4, - 0x65, 0x4C, 0x7E, 0x1B, 0xAB, 0xBB, 0xD3, 0xFF, 0x00, 0xE0, 0xA1, 0x3F, 0x10, 0x74, 0xD8, 0x42, - 0xC3, 0xF1, 0x17, 0xC6, 0x11, 0xA8, 0x18, 0x00, 0x6A, 0x6E, 0x7F, 0x9D, 0x7C, 0x56, 0x9A, 0x8C, - 0xA9, 0xD1, 0x8D, 0x3F, 0xFB, 0x66, 0xE3, 0xFB, 0xED, 0xF9, 0xD7, 0x62, 0xE2, 0x4C, 0x45, 0xAC, - 0xD9, 0xCF, 0xFD, 0x97, 0x48, 0xFB, 0x2F, 0x56, 0xFF, 0x00, 0x82, 0x81, 0x7C, 0x40, 0xD5, 0xA3, - 0x65, 0xB8, 0xF8, 0x89, 0xE2, 0xF9, 0x55, 0x86, 0x08, 0x3A, 0x9B, 0x8F, 0xE5, 0x5E, 0x7D, 0xE3, - 0x0F, 0xDA, 0x57, 0x58, 0xF1, 0x26, 0xEF, 0xB7, 0xF8, 0x8B, 0x5C, 0xBE, 0xDD, 0xD7, 0xCF, 0xD4, - 0x25, 0x7C, 0xFF, 0x00, 0xE3, 0xD5, 0xF3, 0xAF, 0xF6, 0xCD, 0xC7, 0xF7, 0xDB, 0xF3, 0xA6, 0x3E, - 0xA1, 0x33, 0xF5, 0x63, 0x4F, 0xFD, 0x64, 0xC4, 0xDA, 0xC9, 0x87, 0xF6, 0x55, 0x2B, 0xDC, 0xF4, - 0x9D, 0x6F, 0xE2, 0x34, 0x33, 0xBB, 0x37, 0xDE, 0x63, 0xDC, 0xF2, 0x4F, 0xE2, 0x6B, 0x97, 0xD5, - 0x7C, 0x66, 0xD7, 0x39, 0xC5, 0x73, 0x2D, 0x33, 0x37, 0xF1, 0x53, 0x49, 0xCD, 0x71, 0x56, 0xCE, - 0x71, 0x13, 0xDD, 0x9D, 0x14, 0xF0, 0x34, 0xA3, 0xD0, 0xB9, 0x77, 0xAA, 0x3D, 0xC1, 0xEB, 0x55, - 0x19, 0x8B, 0x1A, 0x4A, 0x2B, 0xCD, 0xA9, 0x5A, 0x73, 0xD6, 0x4C, 0xEA, 0x8C, 0x54, 0x76, 0x0A, - 0x28, 0xA2, 0xB3, 0x28, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, - 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0xFF, 0xD9, -} ; - -const char return_jpg[] PROGMEM = -{ - 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x00, 0x60, - 0x00, 0x60, 0x00, 0x00, 0xFF, 0xE1, 0x00, 0x22, 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x4D, 0x4D, - 0x00, 0x2A, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x02, 0x01, 0x01, - 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x05, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x06, 0x04, 0x04, 0x03, 0x05, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x08, - 0x09, 0x0B, 0x09, 0x08, 0x08, 0x0A, 0x08, 0x07, 0x07, 0x0A, 0x0D, 0x0A, 0x0A, 0x0B, 0x0C, 0x0C, - 0x0C, 0x0C, 0x07, 0x09, 0x0E, 0x0F, 0x0D, 0x0C, 0x0E, 0x0B, 0x0C, 0x0C, 0x0C, 0xFF, 0xDB, 0x00, - 0x43, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x06, 0x03, 0x03, 0x06, 0x0C, 0x08, 0x07, 0x08, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0xB5, 0x01, 0x40, 0x03, 0x01, 0x22, 0x00, 0x02, - 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, - 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, - 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, - 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, - 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, - 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, - 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, - 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, - 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, - 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, - 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, - 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xC4, 0x00, - 0x1F, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, - 0x00, 0xB5, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, - 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, - 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, - 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, - 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, - 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, - 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, - 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, - 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, - 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, - 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, - 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, - 0x00, 0x3F, 0x00, 0xFE, 0x7F, 0xE8, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xAE, 0xE3, 0xE0, 0x5F, 0xEC, 0xFD, 0xE3, 0x4F, 0xDA, 0x63, - 0xE2, 0x25, 0xA7, 0x85, 0x7C, 0x09, 0xE1, 0xBD, 0x53, 0xC4, 0xFA, 0xE5, 0xE1, 0x01, 0x2D, 0xAC, - 0xE2, 0x2F, 0xE5, 0xA9, 0x60, 0xBB, 0xDD, 0xBE, 0xEC, 0x69, 0xB9, 0x94, 0x6E, 0x62, 0x06, 0x58, - 0x0C, 0xE4, 0x8A, 0xFD, 0x76, 0xFD, 0x8B, 0x7F, 0xE0, 0x84, 0x5F, 0x0E, 0xFF, 0x00, 0x65, 0x36, - 0xB3, 0xF1, 0x27, 0xC7, 0x79, 0xED, 0xBE, 0x20, 0xF8, 0xE2, 0x1C, 0x4D, 0x0F, 0x83, 0xEC, 0xA4, - 0xFF, 0x00, 0x89, 0x55, 0x83, 0x65, 0x19, 0x7E, 0xD5, 0x26, 0x33, 0x23, 0x00, 0x18, 0x15, 0x23, - 0x90, 0xF8, 0x28, 0xA4, 0x07, 0xAF, 0x57, 0x2D, 0xC9, 0xF1, 0x38, 0xD9, 0x5A, 0x92, 0xD3, 0xBF, - 0x4F, 0xEB, 0xFA, 0x76, 0x3C, 0x5C, 0xE3, 0x3F, 0xC1, 0xE5, 0xB0, 0xBD, 0x79, 0x7B, 0xDF, 0xCA, - 0xB7, 0xFF, 0x00, 0x81, 0xF3, 0xF9, 0x5C, 0xF8, 0x1F, 0xFE, 0x09, 0xFD, 0xFF, 0x00, 0x04, 0x79, - 0xF8, 0xA9, 0xFB, 0x7A, 0xC8, 0xBA, 0xB5, 0xAD, 0xAC, 0x5E, 0x0D, 0xF8, 0x7F, 0x6F, 0x87, 0xBC, - 0xF1, 0x4E, 0xB4, 0x86, 0x1B, 0x55, 0x4C, 0xA8, 0x3E, 0x4A, 0x9C, 0x19, 0x8F, 0xCD, 0x91, 0x82, - 0x14, 0xED, 0x60, 0x18, 0xB0, 0xDA, 0x7B, 0x6F, 0xF8, 0x2B, 0x67, 0xFC, 0x13, 0x4F, 0xE1, 0x5F, - 0xEC, 0x0F, 0xE0, 0x2F, 0x03, 0xDC, 0xF8, 0x27, 0xE2, 0x07, 0x88, 0x3C, 0x51, 0xAF, 0x6B, 0x57, - 0x33, 0x5A, 0xEA, 0x16, 0xBA, 0x9D, 0x9A, 0xC7, 0x15, 0xCA, 0xA4, 0x48, 0xCF, 0x71, 0x6C, 0xCA, - 0xAA, 0x42, 0x46, 0xEC, 0x11, 0x96, 0x40, 0x18, 0xF9, 0xD1, 0x95, 0xCE, 0xD7, 0x35, 0xFB, 0x09, - 0xE3, 0x0F, 0x89, 0x97, 0xFE, 0x2C, 0xB6, 0xB6, 0xB3, 0x7F, 0xB3, 0xD8, 0xE9, 0x56, 0x0A, 0x12, - 0xCF, 0x4D, 0xB3, 0x8C, 0x43, 0x69, 0x68, 0xA3, 0x8F, 0x91, 0x07, 0x7F, 0xF6, 0x9B, 0x2C, 0x7B, - 0x93, 0x5F, 0x85, 0x3F, 0xF0, 0x55, 0x4F, 0xDA, 0x83, 0xFE, 0x1A, 0x63, 0xF6, 0xAD, 0xD5, 0x8D, - 0x8D, 0xD0, 0xB8, 0xF0, 0xDF, 0x84, 0xB7, 0x68, 0xBA, 0x51, 0x8E, 0x5D, 0xF0, 0xCA, 0x23, 0x63, - 0xE7, 0x4E, 0xB8, 0x25, 0x4F, 0x99, 0x2E, 0xEC, 0x38, 0xFB, 0xC8, 0x91, 0x7A, 0x57, 0xD3, 0xE7, - 0x19, 0x26, 0x13, 0x2D, 0xCB, 0xEF, 0x3F, 0x7A, 0xA4, 0x9A, 0x4B, 0xF3, 0x6F, 0xEE, 0xED, 0xDD, - 0x5D, 0xB3, 0xE3, 0xF2, 0x1E, 0x22, 0xC7, 0xE6, 0xF9, 0xA2, 0xE5, 0xF7, 0x68, 0xC1, 0x36, 0xD2, - 0xFB, 0x92, 0x6F, 0xAB, 0x6F, 0x5D, 0x7B, 0x3B, 0x24, 0x7C, 0xCB, 0x45, 0x14, 0x57, 0xC1, 0x9F, - 0xA5, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, - 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, - 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, - 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x57, - 0xB5, 0x7E, 0xC7, 0x3F, 0xB0, 0x37, 0xC5, 0x0F, 0xDB, 0xB7, 0xC7, 0x63, 0x43, 0xF8, 0x77, 0xE1, - 0xD9, 0xF5, 0x21, 0x19, 0x22, 0xEF, 0x52, 0x9F, 0x30, 0xE9, 0xF6, 0x00, 0x00, 0x49, 0x96, 0x6C, - 0x10, 0x38, 0x20, 0xE0, 0x02, 0xD8, 0x39, 0xC6, 0x01, 0x23, 0x4A, 0x34, 0x67, 0x56, 0x5C, 0x94, - 0xD5, 0xD9, 0x95, 0x6A, 0xF4, 0xE8, 0xC1, 0xD4, 0xAB, 0x25, 0x18, 0xAE, 0xAC, 0xF1, 0x50, 0x0B, - 0x1A, 0xFD, 0x05, 0xFD, 0x81, 0x3F, 0xE0, 0x82, 0x9E, 0x37, 0xFD, 0xA2, 0x74, 0xAB, 0x4F, 0x19, - 0xFC, 0x56, 0xBC, 0x9B, 0xE1, 0x4F, 0xC3, 0x76, 0x7F, 0x96, 0x5B, 0xC8, 0x4A, 0xEA, 0xDA, 0xA0, - 0x0D, 0xB4, 0xAD, 0xBD, 0xBB, 0x0D, 0xC0, 0xF0, 0xDC, 0xB2, 0x92, 0x36, 0xF2, 0xA1, 0x48, 0x7A, - 0xFB, 0xAF, 0xF6, 0x36, 0xFF, 0x00, 0x82, 0x5C, 0xFC, 0x19, 0xFF, 0x00, 0x82, 0x78, 0x1B, 0x7D, - 0x5E, 0xF1, 0x6C, 0xBE, 0x2F, 0x7C, 0x53, 0xB7, 0xF9, 0xBF, 0xB4, 0xEF, 0x21, 0x1F, 0xD8, 0xDA, - 0x3C, 0x9B, 0x9B, 0xFD, 0x44, 0x79, 0x3E, 0x63, 0x01, 0xB7, 0xE6, 0x0C, 0x46, 0x4B, 0x7C, 0xEC, - 0x0E, 0xDA, 0xF6, 0x8F, 0x18, 0xFC, 0x40, 0xD5, 0x3C, 0x71, 0xAA, 0x1B, 0xDD, 0x52, 0xF2, 0x5B, - 0xC9, 0xC2, 0x84, 0x4D, 0xD8, 0x09, 0x12, 0x8E, 0x88, 0x8A, 0x30, 0xA8, 0xA3, 0xFB, 0xAA, 0x00, - 0xF6, 0xAF, 0xBF, 0xC9, 0xB8, 0x35, 0xBB, 0x55, 0xC6, 0x7D, 0xDF, 0xD7, 0xFC, 0x37, 0xAA, 0x3F, - 0x30, 0xCF, 0xB8, 0xFE, 0xD7, 0xA3, 0x97, 0x7F, 0xE0, 0x4F, 0xF4, 0x5D, 0x3F, 0x3F, 0x46, 0x37, - 0xE0, 0xFF, 0x00, 0x87, 0xBC, 0x0F, 0xFB, 0x23, 0x7C, 0x3B, 0xFF, 0x00, 0x84, 0x47, 0xE0, 0xCF, - 0x86, 0x61, 0xF0, 0x66, 0x90, 0xC3, 0x17, 0x7A, 0x97, 0x0D, 0xAB, 0xEA, 0x8D, 0xC6, 0x5E, 0x59, - 0xF2, 0x59, 0x73, 0x80, 0x30, 0x18, 0x9C, 0x28, 0x05, 0x88, 0x18, 0xAA, 0x33, 0x5E, 0xF5, 0xE6, - 0xA9, 0x4D, 0x7B, 0x55, 0x26, 0xBC, 0xAF, 0xD0, 0xA8, 0xD0, 0xA7, 0x46, 0x3C, 0x94, 0xD5, 0x91, - 0xF9, 0x6D, 0x7C, 0x45, 0x4A, 0xD3, 0x75, 0x2A, 0xBB, 0xB6, 0x78, 0x9F, 0xFC, 0x14, 0x9F, 0xF6, - 0xA3, 0x3F, 0xB3, 0x2F, 0xEC, 0xB5, 0xAD, 0x6A, 0x56, 0x77, 0x4D, 0x6F, 0xE2, 0x0D, 0x70, 0x7F, - 0x63, 0xE8, 0xC6, 0x37, 0x2B, 0x24, 0x73, 0xCA, 0xAD, 0xBA, 0x65, 0x23, 0x95, 0x31, 0x46, 0x1E, - 0x40, 0x7A, 0x6E, 0x54, 0x19, 0xF9, 0x85, 0x7E, 0x1D, 0x93, 0x93, 0x5F, 0xB5, 0xFF, 0x00, 0xB6, - 0x4F, 0xFC, 0x13, 0xBE, 0xD3, 0xF6, 0xF7, 0xD4, 0xF4, 0x99, 0x35, 0x4F, 0x8B, 0x11, 0x78, 0x06, - 0xDF, 0xC3, 0xB0, 0xBA, 0x59, 0xD9, 0xCF, 0xE1, 0xF9, 0x75, 0x1B, 0x7B, 0x99, 0x65, 0x39, 0x92, - 0x57, 0x92, 0x27, 0x0E, 0x84, 0x04, 0x89, 0x40, 0xD8, 0xFC, 0x64, 0x8C, 0x73, 0x9F, 0x96, 0x3C, - 0x73, 0xFF, 0x00, 0x06, 0xEF, 0xFC, 0x4A, 0xB5, 0xB9, 0x98, 0xF8, 0x37, 0xE2, 0x47, 0xC2, 0x3F, - 0x19, 0x5B, 0xA8, 0x3E, 0x52, 0x3E, 0xB1, 0x26, 0x8F, 0x79, 0x31, 0xEC, 0x04, 0x57, 0x51, 0xA2, - 0xE4, 0xF1, 0x80, 0x24, 0x3F, 0xD6, 0xBF, 0x39, 0xE2, 0xCC, 0x1E, 0x3F, 0x15, 0x8A, 0xE6, 0x8D, - 0x37, 0xC9, 0x15, 0x65, 0xB7, 0xAB, 0x7B, 0xF7, 0xD3, 0xE4, 0x8F, 0xD5, 0xF8, 0x23, 0x32, 0xCA, - 0xB0, 0x58, 0x2E, 0x49, 0xD5, 0x4A, 0xA4, 0xDD, 0xE5, 0x74, 0xF4, 0xE8, 0x95, 0xED, 0x6D, 0x16, - 0xBB, 0xF5, 0x67, 0xE7, 0xBD, 0x15, 0xF4, 0x47, 0xC6, 0x8F, 0xF8, 0x25, 0x5F, 0xED, 0x11, 0xFB, - 0x3F, 0x59, 0xB5, 0xE7, 0x89, 0xBE, 0x11, 0x78, 0xC6, 0x1D, 0x3C, 0x64, 0xFD, 0xBE, 0xC6, 0xD3, - 0xFB, 0x4A, 0xD0, 0x81, 0xC9, 0x6F, 0x3A, 0xD8, 0xC8, 0x98, 0xC7, 0x39, 0x27, 0x8E, 0xF5, 0xF3, - 0xF5, 0xE5, 0xAC, 0xB6, 0x37, 0x0F, 0x0C, 0xD1, 0xC9, 0x0C, 0xB1, 0x9D, 0xAC, 0xAE, 0xA5, 0x59, - 0x4F, 0xA1, 0x07, 0xA5, 0x7C, 0x2D, 0x4A, 0x15, 0x29, 0xBB, 0x54, 0x8B, 0x5E, 0xAA, 0xC7, 0xE9, - 0x34, 0x71, 0x34, 0x6B, 0x2B, 0xD1, 0x9A, 0x92, 0xF2, 0x69, 0xFE, 0x44, 0x14, 0x51, 0x45, 0x64, - 0x6C, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, - 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, - 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, - 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x7D, 0xD1, 0xFF, 0x00, 0x04, 0x6F, 0xFF, - 0x00, 0x82, 0x65, 0x68, 0x7F, 0xB6, 0x9E, 0xBB, 0xAF, 0x78, 0xDB, 0xE2, 0x25, 0xC5, 0xE5, 0xA7, - 0xC2, 0xDF, 0x03, 0xC8, 0x91, 0x5E, 0x45, 0x67, 0x20, 0x5B, 0x8D, 0x66, 0xE9, 0xB6, 0xB0, 0xB4, - 0x42, 0x08, 0x65, 0x24, 0x32, 0x64, 0xFC, 0xBC, 0x3E, 0x77, 0x61, 0x1F, 0x1F, 0xAE, 0x8B, 0xE3, - 0x1D, 0x3F, 0xC1, 0xBE, 0x03, 0x83, 0xC1, 0xBE, 0x07, 0xD0, 0xB4, 0xBF, 0x03, 0x78, 0x26, 0xC8, - 0x6C, 0x87, 0x48, 0xD2, 0xA3, 0x11, 0x2C, 0xA3, 0x73, 0x36, 0x66, 0x70, 0x01, 0x91, 0x89, 0x72, - 0x48, 0xE1, 0x32, 0x72, 0x14, 0x1A, 0xFC, 0xE9, 0xFF, 0x00, 0x82, 0x11, 0xFE, 0xD0, 0x7A, 0x2D, - 0x97, 0xC0, 0xCF, 0x1B, 0x7C, 0x31, 0x96, 0xF1, 0xAD, 0xF5, 0xE9, 0xBC, 0x41, 0x0F, 0x88, 0xE1, - 0xB7, 0x72, 0xA1, 0x6F, 0xA0, 0xFB, 0x31, 0x81, 0x8A, 0x73, 0x92, 0xD1, 0x91, 0xF3, 0x0C, 0x74, - 0x99, 0x48, 0x27, 0x9C, 0x7D, 0xCB, 0x35, 0xF7, 0xBD, 0x7E, 0xBD, 0xC2, 0x78, 0x3C, 0x34, 0x70, - 0x51, 0xAF, 0x04, 0x9C, 0x9E, 0xEF, 0xAD, 0xFF, 0x00, 0xE0, 0x74, 0xED, 0xBF, 0x9B, 0xFC, 0x27, - 0x8E, 0x33, 0x0C, 0x5C, 0xF3, 0x19, 0xE1, 0xEA, 0xB6, 0xA3, 0x1D, 0x97, 0x4B, 0x5B, 0x7F, 0x9F, - 0x57, 0xF2, 0x7B, 0x24, 0xAE, 0xCD, 0x7B, 0xEF, 0x55, 0x66, 0xBE, 0xF7, 0xAA, 0x33, 0x5F, 0x7F, - 0xB5, 0x55, 0x26, 0xBF, 0xEB, 0xCD, 0x7D, 0x67, 0x33, 0x3E, 0x26, 0xE5, 0xE9, 0xAF, 0xBD, 0xEA, - 0xA4, 0xD7, 0xDF, 0xED, 0x55, 0x19, 0xAF, 0xBD, 0xEA, 0xAC, 0xD7, 0xDF, 0xED, 0x56, 0x7C, 0xC2, - 0xB9, 0x76, 0x6B, 0xEE, 0xBC, 0xD5, 0x49, 0xAF, 0xBD, 0xEA, 0x94, 0xD7, 0xDE, 0xF5, 0x52, 0x6B, - 0xEF, 0x7A, 0x39, 0x99, 0x27, 0x41, 0xE1, 0xFF, 0x00, 0x88, 0x3A, 0xC7, 0x82, 0x2E, 0x9E, 0x7D, - 0x1B, 0x57, 0xD4, 0xF4, 0x89, 0xDF, 0xEF, 0x49, 0x65, 0x75, 0x25, 0xBB, 0xB7, 0xD4, 0xA1, 0x06, - 0xA8, 0xFC, 0x4D, 0xD5, 0x3C, 0x37, 0xF1, 0xE6, 0xD5, 0xAD, 0xFE, 0x25, 0x78, 0x03, 0xC0, 0x3F, - 0x10, 0xE3, 0xF2, 0x8C, 0x2B, 0x3E, 0xAD, 0xA3, 0xC7, 0x15, 0xFC, 0x40, 0xF5, 0x65, 0xBC, 0xB6, - 0xF2, 0xAE, 0x03, 0x7B, 0x99, 0x0F, 0x3C, 0xF5, 0xE6, 0xB0, 0xA6, 0xBE, 0xF7, 0xAA, 0xB3, 0x5E, - 0xFB, 0xD6, 0x15, 0xA8, 0x53, 0xAA, 0xAD, 0x52, 0x29, 0xFA, 0xA3, 0x6A, 0x38, 0x9A, 0xB4, 0xA5, - 0xCD, 0x4A, 0x4D, 0x3F, 0x26, 0x78, 0x3F, 0xC6, 0x5F, 0xF8, 0x23, 0xAF, 0xC0, 0xBF, 0x8B, 0x0B, - 0x34, 0xFE, 0x04, 0xF1, 0x37, 0x89, 0xBE, 0x12, 0x6A, 0xCC, 0xA0, 0xA5, 0x96, 0xB0, 0x0E, 0xBF, - 0xA2, 0x31, 0xEE, 0x04, 0xD1, 0xAA, 0xDD, 0x45, 0x93, 0xEA, 0x92, 0xE0, 0x77, 0xE3, 0x9F, 0x8A, - 0xBF, 0x6A, 0x4F, 0xF8, 0x26, 0x17, 0xC6, 0x0F, 0xD9, 0x2F, 0x4B, 0x93, 0x59, 0xD7, 0x3C, 0x3B, - 0x1E, 0xB9, 0xE1, 0x15, 0x72, 0x8B, 0xE2, 0x6F, 0x0E, 0xDC, 0x2E, 0xAB, 0xA4, 0x12, 0x06, 0x70, - 0xF3, 0x45, 0x9F, 0x25, 0xB0, 0x47, 0xCB, 0x28, 0x46, 0xF6, 0xAF, 0xD4, 0x29, 0xAF, 0x7D, 0xEA, - 0xD7, 0x86, 0x3E, 0x22, 0x6A, 0xDE, 0x04, 0xD4, 0x5E, 0xEB, 0x47, 0xD4, 0x6E, 0xB4, 0xF9, 0xA5, - 0x8C, 0xC3, 0x2F, 0x94, 0xFF, 0x00, 0x2C, 0xF1, 0x9C, 0x6E, 0x8E, 0x45, 0x3F, 0x2C, 0x88, 0x70, - 0x32, 0x8C, 0x0A, 0x9E, 0xE0, 0xD7, 0xCC, 0x63, 0xF8, 0x47, 0x05, 0x59, 0x37, 0x4B, 0xDC, 0x7E, - 0x5B, 0x7D, 0xC7, 0xD8, 0xE5, 0x7C, 0x7D, 0x98, 0xE1, 0x9A, 0x8D, 0x77, 0xED, 0x23, 0xE7, 0xBF, - 0xDF, 0xFE, 0x77, 0x3F, 0x0A, 0xE8, 0xAF, 0xD6, 0xFF, 0x00, 0xDA, 0x57, 0xF6, 0x11, 0xF8, 0x47, - 0xFB, 0x5C, 0xC3, 0x73, 0x7D, 0x15, 0x95, 0x87, 0xC2, 0x5F, 0x1F, 0xDC, 0x65, 0xC6, 0xAF, 0xA4, - 0xDA, 0xB7, 0xF6, 0x0E, 0xA7, 0x21, 0x39, 0x3F, 0x6B, 0xB2, 0x40, 0x4C, 0x0C, 0xC7, 0x71, 0x32, - 0xDB, 0x0C, 0x64, 0x8C, 0xC5, 0x80, 0x4D, 0x7E, 0x6D, 0xFE, 0xD3, 0x1F, 0xB2, 0xAF, 0x8D, 0xBF, - 0x64, 0x8F, 0x88, 0x1F, 0xF0, 0x8E, 0xF8, 0xDF, 0x49, 0x3A, 0x7D, 0xCC, 0xC8, 0x67, 0xB2, 0xBB, - 0x86, 0x55, 0xB8, 0xB1, 0xD5, 0x20, 0xDC, 0x54, 0x5C, 0x5B, 0x4E, 0x84, 0xA4, 0xB1, 0x1C, 0x70, - 0xCA, 0x7E, 0xA0, 0x1E, 0x2B, 0xF3, 0xFC, 0xCF, 0x23, 0xC4, 0xE0, 0x9D, 0xEA, 0x2B, 0xC7, 0xBA, - 0xDB, 0xFE, 0x01, 0xFA, 0xBE, 0x47, 0xC4, 0xD8, 0x2C, 0xCE, 0x36, 0xA2, 0xED, 0x3F, 0xE5, 0x7B, - 0xFC, 0xBB, 0xAF, 0xC7, 0xC8, 0xF3, 0x4A, 0x28, 0xA2, 0xBC, 0x73, 0xE8, 0x42, 0x8A, 0x28, 0xA0, - 0x02, 0x8A, 0x28, 0xA0, 0x02, 0x8A, 0x28, 0xA0, 0x02, 0x8A, 0x28, 0xA0, 0x02, 0x8A, 0x28, 0xA0, - 0x02, 0x8A, 0x28, 0xA0, 0x02, 0x8A, 0x28, 0xA0, 0x02, 0x8A, 0x28, 0xA0, 0x02, 0x8A, 0x28, 0xA0, - 0x02, 0x8A, 0x28, 0xA0, 0x02, 0x8A, 0x28, 0xA0, 0x02, 0x8A, 0x28, 0xA0, 0x0D, 0x9F, 0x05, 0xF8, - 0xCF, 0x54, 0xF8, 0x75, 0xE2, 0x9B, 0x1D, 0x6F, 0x44, 0xBF, 0xB8, 0xD3, 0x35, 0x6D, 0x36, 0x61, - 0x3D, 0xAD, 0xD4, 0x0D, 0xB6, 0x48, 0x5C, 0x77, 0x1F, 0xC8, 0x83, 0xC1, 0x04, 0x83, 0x90, 0x6B, - 0xF5, 0x67, 0xF6, 0x06, 0xFF, 0x00, 0x82, 0x8F, 0xE9, 0xBF, 0xB5, 0x2E, 0x97, 0x1F, 0x87, 0x7C, - 0x44, 0xF6, 0x9A, 0x4F, 0x8F, 0x6D, 0x63, 0x24, 0xC2, 0xBF, 0xBB, 0x83, 0x58, 0x45, 0x04, 0x99, - 0x20, 0x04, 0xF0, 0xE1, 0x41, 0x2F, 0x1F, 0x50, 0x01, 0x65, 0xCA, 0xEE, 0x09, 0xF9, 0x1B, 0x57, - 0xB4, 0xAD, 0x5A, 0xE7, 0x40, 0xD4, 0xED, 0xAF, 0x6C, 0xEE, 0x67, 0xB4, 0xBD, 0xB3, 0x95, 0x67, - 0x82, 0x78, 0x24, 0x31, 0xC9, 0x0C, 0x8A, 0x43, 0x2B, 0xAB, 0x0C, 0x15, 0x60, 0x40, 0x20, 0x8E, - 0x41, 0x15, 0xED, 0x64, 0xD9, 0xD5, 0x6C, 0xBE, 0xAF, 0x34, 0x35, 0x8B, 0xDE, 0x3D, 0x1F, 0xF9, - 0x3E, 0xCC, 0xF9, 0xFE, 0x20, 0xE1, 0xDC, 0x3E, 0x6B, 0x47, 0x92, 0xA6, 0x93, 0x5F, 0x0C, 0xBA, - 0xAF, 0x2F, 0x35, 0xDD, 0x7D, 0xD6, 0x67, 0xEF, 0xCC, 0xD7, 0xDE, 0xF5, 0x56, 0x6B, 0xEC, 0x57, - 0xC8, 0x1F, 0xB0, 0x57, 0xFC, 0x14, 0x7A, 0xDF, 0xE3, 0x9D, 0xA5, 0xB7, 0x84, 0xFC, 0x69, 0x73, - 0x6D, 0x65, 0xE3, 0x28, 0x80, 0x4B, 0x6B, 0xA3, 0xB6, 0x28, 0x75, 0xA1, 0xD3, 0x80, 0x30, 0xAB, - 0x37, 0xAA, 0x0E, 0x1B, 0xAA, 0x81, 0xCA, 0x8F, 0xA9, 0xA6, 0xBE, 0xFF, 0x00, 0x6A, 0xBF, 0x5F, - 0xC0, 0xE6, 0x14, 0x71, 0x94, 0x55, 0x6A, 0x0E, 0xE9, 0xFD, 0xE9, 0xF6, 0x7E, 0x67, 0xE0, 0x39, - 0xA6, 0x59, 0x89, 0xC0, 0x57, 0x78, 0x7C, 0x4A, 0xB3, 0x5F, 0x73, 0x5D, 0xD7, 0x75, 0xFD, 0x6E, - 0x5E, 0x9A, 0xFA, 0xAA, 0x4D, 0x7D, 0xEF, 0x54, 0x66, 0xBD, 0xF7, 0xAA, 0xB3, 0xDE, 0xFB, 0xD7, - 0x59, 0xE6, 0x97, 0x66, 0xBC, 0xAA, 0xB3, 0x5E, 0xFB, 0xD7, 0xC1, 0x3A, 0xCF, 0xFC, 0x15, 0xCB, - 0xC5, 0x5E, 0x18, 0xF1, 0x05, 0xF6, 0x9D, 0xAA, 0x78, 0x1F, 0x49, 0x4B, 0xAD, 0x3E, 0x69, 0x2D, - 0x67, 0x89, 0x6F, 0x64, 0x46, 0x8E, 0x54, 0x62, 0xAC, 0x09, 0x2A, 0x7A, 0x10, 0x45, 0x5C, 0xD3, - 0x7F, 0xE0, 0xB1, 0xF0, 0xCA, 0xEA, 0xB7, 0xDE, 0x01, 0x96, 0x35, 0xEE, 0xD0, 0x6B, 0x02, 0x43, - 0xFF, 0x00, 0x7C, 0x98, 0x57, 0xF9, 0xD7, 0xCE, 0xAE, 0x2A, 0xCB, 0x6F, 0x67, 0x52, 0xDE, 0xB1, - 0x97, 0xF9, 0x1F, 0x5B, 0x2E, 0x07, 0xCE, 0x52, 0xE6, 0x8D, 0x24, 0xD7, 0x94, 0xA3, 0xFE, 0x67, - 0xDC, 0x13, 0x5E, 0xD5, 0x49, 0xAF, 0xB9, 0xAF, 0x97, 0xBC, 0x25, 0xFF, 0x00, 0x05, 0x58, 0xF8, - 0x73, 0xE2, 0x29, 0x16, 0x3D, 0x4A, 0xDF, 0xC4, 0x1A, 0x0B, 0x63, 0x2D, 0x24, 0xF6, 0xAB, 0x34, - 0x40, 0xFB, 0x18, 0x99, 0x98, 0xFF, 0x00, 0xDF, 0x02, 0xBD, 0x7B, 0xC0, 0x1F, 0xB4, 0x1F, 0x83, - 0x7E, 0x2D, 0x24, 0x7F, 0xF0, 0x8E, 0x78, 0x97, 0x49, 0xD5, 0x26, 0x96, 0x33, 0x28, 0xB7, 0x8E, - 0x70, 0xB7, 0x2A, 0x83, 0xA9, 0x68, 0x5B, 0x12, 0x2F, 0xFC, 0x09, 0x45, 0x7A, 0x18, 0x7C, 0xD3, - 0x09, 0x88, 0x76, 0xA3, 0x51, 0x37, 0xDA, 0xFA, 0xFD, 0xDB, 0x9E, 0x36, 0x33, 0x25, 0xC7, 0xE1, - 0x55, 0xF1, 0x14, 0x65, 0x15, 0xDE, 0xDA, 0x7D, 0xFB, 0x1D, 0xCC, 0xD7, 0xB5, 0x56, 0x6B, 0xDA, - 0xAA, 0xF3, 0x33, 0x9E, 0xB4, 0xCA, 0xEE, 0x3C, 0xB2, 0x59, 0x2E, 0x4B, 0xD3, 0x7C, 0x55, 0xA4, - 0xE8, 0x7F, 0x16, 0x7E, 0x1A, 0xDD, 0x78, 0x1B, 0xC7, 0x1A, 0x69, 0xD7, 0xBC, 0x21, 0x78, 0xC6, - 0x58, 0xE1, 0xDC, 0x16, 0xEB, 0x47, 0xB8, 0x3C, 0x7D, 0xAE, 0xCA, 0x43, 0xFE, 0xAA, 0x6E, 0xCC, - 0x3E, 0xE4, 0xAB, 0xC3, 0x83, 0x84, 0x64, 0x65, 0x15, 0x9D, 0x5A, 0x50, 0xA9, 0x17, 0x0A, 0x8A, - 0xE9, 0xF4, 0x35, 0xA3, 0x5A, 0xA5, 0x19, 0xAA, 0x94, 0x9B, 0x52, 0x5A, 0xA6, 0x8F, 0xCC, 0x8F, - 0xDB, 0x2B, 0xF6, 0x44, 0xD5, 0xBF, 0x64, 0x5F, 0x89, 0x89, 0xA6, 0xDC, 0x4D, 0xFD, 0xA9, 0xE1, - 0xDD, 0x62, 0x23, 0x79, 0xA0, 0x6B, 0x09, 0x1E, 0xD8, 0xB5, 0x3B, 0x6C, 0xE0, 0x9C, 0x7F, 0x0C, - 0xA8, 0xD9, 0x49, 0x13, 0xAA, 0xB2, 0x91, 0xE9, 0x5E, 0x35, 0x5F, 0xAF, 0x9F, 0x1A, 0x7E, 0x11, - 0x69, 0xFF, 0x00, 0xB4, 0x6F, 0xC1, 0xAD, 0x5B, 0xC0, 0xBA, 0xAB, 0x47, 0x17, 0xDB, 0x09, 0xBB, - 0xD1, 0xAE, 0xE4, 0xDB, 0x8D, 0x2B, 0x51, 0x55, 0xC4, 0x72, 0x82, 0xC4, 0x04, 0x49, 0x00, 0x11, - 0x4A, 0x72, 0x32, 0x84, 0x31, 0xC9, 0x8D, 0x45, 0x7E, 0x49, 0xEB, 0xFA, 0x1D, 0xE7, 0x86, 0x35, - 0xBB, 0xCD, 0x36, 0xFE, 0x09, 0x2D, 0x6F, 0x74, 0xF9, 0xDE, 0xDA, 0xE2, 0x17, 0x18, 0x68, 0xA4, - 0x46, 0x2A, 0xCA, 0x7D, 0xC1, 0x04, 0x57, 0xE4, 0xBC, 0x45, 0x93, 0x7D, 0x46, 0xBD, 0xE1, 0xF0, - 0x4B, 0x6F, 0x2F, 0x23, 0xF7, 0xEE, 0x11, 0xE2, 0x25, 0x9A, 0x61, 0xAD, 0x53, 0xF8, 0x90, 0xF8, - 0xBC, 0xFB, 0x3F, 0x9F, 0x5F, 0x3F, 0x54, 0x67, 0xD1, 0x45, 0x15, 0xF3, 0xA7, 0xD6, 0x85, 0x14, - 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, - 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, - 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x05, 0x14, 0x51, 0x40, 0x12, 0xDB, - 0x5C, 0x49, 0x69, 0x32, 0xC9, 0x1B, 0x34, 0x72, 0x46, 0xC1, 0x95, 0x94, 0xE1, 0x94, 0x8E, 0x41, - 0x07, 0xB1, 0xAF, 0xD0, 0x1F, 0xD8, 0x43, 0xFE, 0x0A, 0x32, 0x3C, 0x6C, 0x96, 0xBE, 0x0B, 0xF8, - 0x81, 0x7C, 0x91, 0xEB, 0x0A, 0x16, 0x1D, 0x37, 0x58, 0x99, 0xB0, 0xBA, 0x8F, 0x61, 0x0C, 0xE4, - 0xF0, 0x26, 0xF4, 0x73, 0xC4, 0x9D, 0x0E, 0x1F, 0x06, 0x4F, 0xCF, 0x9A, 0x2B, 0xD3, 0xCA, 0xF3, - 0x6A, 0xF8, 0x0A, 0xDE, 0xD6, 0x8B, 0xD3, 0xAA, 0xE8, 0xD7, 0xF5, 0xB3, 0xE8, 0x78, 0xF9, 0xDE, - 0x47, 0x86, 0xCD, 0x30, 0xFE, 0xC6, 0xBA, 0xD7, 0xA4, 0x96, 0xF1, 0x7E, 0x5F, 0xAA, 0xEB, 0xF7, - 0x33, 0xF7, 0x0A, 0x6B, 0xEF, 0x7A, 0xAB, 0x35, 0xF7, 0xFB, 0x55, 0xF0, 0xDF, 0xEC, 0x4F, 0xFF, - 0x00, 0x05, 0x0C, 0x92, 0xDB, 0xEC, 0x7E, 0x11, 0xF1, 0xF5, 0xF6, 0xE8, 0x78, 0x87, 0x4F, 0xD6, - 0xA7, 0x7E, 0x63, 0xEC, 0x23, 0xB8, 0x63, 0xDB, 0xA0, 0x12, 0x1E, 0x9F, 0xC5, 0xC7, 0xCC, 0x3E, - 0xCE, 0x96, 0xEC, 0xBF, 0x4A, 0xFD, 0x77, 0x2D, 0xCD, 0x28, 0xE3, 0xA9, 0x7B, 0x5A, 0x2F, 0xD5, - 0x75, 0x4F, 0xB3, 0x3F, 0x9F, 0x73, 0xAC, 0x97, 0x13, 0x96, 0x62, 0x3D, 0x86, 0x21, 0x7A, 0x3E, - 0x8D, 0x77, 0x5F, 0xAA, 0xE8, 0x7E, 0x60, 0xFE, 0xDD, 0xFE, 0x05, 0x4F, 0x01, 0x7E, 0xD5, 0x1E, - 0x2C, 0x86, 0x14, 0x99, 0x6D, 0xB5, 0x2B, 0x85, 0xD5, 0x23, 0x2F, 0xFC, 0x66, 0x75, 0x12, 0x48, - 0x47, 0xB0, 0x95, 0xA4, 0x03, 0xE9, 0x8A, 0xF1, 0xBA, 0xFB, 0x13, 0xFE, 0x0A, 0xCB, 0xE0, 0x8F, - 0x2B, 0xC4, 0x3E, 0x13, 0xF1, 0x2A, 0x06, 0x6F, 0xB4, 0x5B, 0xCB, 0xA6, 0x4C, 0x71, 0xC2, 0x79, - 0x6D, 0xE6, 0x46, 0x3F, 0x1F, 0x32, 0x5F, 0xFB, 0xE6, 0xBE, 0x3B, 0xAF, 0xC9, 0x73, 0xDC, 0x37, - 0xB0, 0xC7, 0xD5, 0x87, 0x9D, 0xFE, 0x4F, 0x5F, 0xD4, 0xFD, 0xF3, 0x85, 0xF1, 0x9F, 0x5A, 0xCA, - 0xA8, 0x55, 0x7B, 0xF2, 0xD9, 0xFA, 0xC7, 0x47, 0xF9, 0x5C, 0x2A, 0x48, 0x66, 0x68, 0x24, 0x57, - 0x46, 0x65, 0x75, 0x39, 0x0C, 0x0E, 0x08, 0x35, 0x1D, 0x15, 0xE4, 0x9E, 0xF1, 0xF4, 0x27, 0xC0, - 0x8F, 0xF8, 0x28, 0x6F, 0x8D, 0x3E, 0x14, 0xCF, 0x0D, 0xB6, 0xB5, 0x71, 0x27, 0x8B, 0x34, 0x5C, - 0xE1, 0xE3, 0xBC, 0x90, 0x9B, 0xB8, 0x87, 0xAA, 0x4C, 0x72, 0xC7, 0xE8, 0xFB, 0x87, 0x18, 0x1B, - 0x7A, 0xD7, 0xDD, 0xDF, 0x07, 0xBE, 0x34, 0xF8, 0x77, 0xE3, 0x9F, 0x84, 0x63, 0xD6, 0x7C, 0x3B, - 0x7C, 0xB7, 0x50, 0x30, 0x02, 0x68, 0x5C, 0x6D, 0x9E, 0xD2, 0x42, 0x32, 0x63, 0x95, 0x3F, 0x85, - 0x87, 0xE2, 0x0E, 0x32, 0xA4, 0x8C, 0x1A, 0xFC, 0x8D, 0xAF, 0x40, 0xFD, 0x9F, 0xBE, 0x3C, 0x6A, - 0xDF, 0xB3, 0xD7, 0xC4, 0x4B, 0x5D, 0x73, 0x4D, 0x66, 0x92, 0xDF, 0x21, 0x6F, 0xAC, 0x8C, 0x85, - 0x63, 0xBE, 0x87, 0x3C, 0xA1, 0xEB, 0x83, 0xDD, 0x5B, 0x07, 0x6B, 0x60, 0xE0, 0x8C, 0x83, 0xF5, - 0x99, 0x27, 0x13, 0xD6, 0xC3, 0x4D, 0x52, 0xC4, 0x37, 0x2A, 0x7E, 0x7A, 0xB5, 0xE9, 0xFE, 0x5F, - 0x71, 0xF0, 0x9C, 0x49, 0xC1, 0x38, 0x6C, 0x6D, 0x39, 0x56, 0xC1, 0xC5, 0x42, 0xAA, 0xD7, 0x4D, - 0x14, 0xBC, 0x9A, 0xD9, 0x37, 0xDF, 0xBE, 0xFD, 0xD7, 0xEA, 0xE5, 0x15, 0x9B, 0xE0, 0xEF, 0x16, - 0xD8, 0xF8, 0xF3, 0xC2, 0xBA, 0x7E, 0xB5, 0xA6, 0xCD, 0xE7, 0xE9, 0xFA, 0xA5, 0xBA, 0x5C, 0xDB, - 0xBE, 0x30, 0x4A, 0x30, 0x04, 0x64, 0x76, 0x3D, 0x88, 0x3C, 0x82, 0x08, 0xAD, 0x2A, 0xFD, 0x4A, - 0x32, 0x52, 0x4A, 0x51, 0xD9, 0x9F, 0x86, 0x4E, 0x12, 0x84, 0x9C, 0x64, 0xAC, 0xD6, 0x8C, 0x2B, - 0xF3, 0xC7, 0xFE, 0x0A, 0x65, 0xF0, 0xD1, 0x7C, 0x19, 0xF1, 0xFD, 0x75, 0x98, 0x22, 0xD9, 0x6B, - 0xE2, 0x9B, 0x55, 0xBB, 0x66, 0x03, 0x00, 0xDC, 0x21, 0xD9, 0x2F, 0xE2, 0x40, 0x8D, 0x89, 0xEE, - 0x5C, 0x9A, 0xFD, 0x0E, 0xAF, 0x93, 0xFF, 0x00, 0xE0, 0xAC, 0x5E, 0x17, 0x86, 0xEF, 0xE1, 0xAF, - 0x85, 0x75, 0xA2, 0xCC, 0x27, 0xB1, 0xD4, 0xE4, 0xB2, 0x51, 0xD8, 0xAC, 0xD1, 0x17, 0x39, 0xFA, - 0x18, 0x17, 0xF3, 0x35, 0xF3, 0xFC, 0x55, 0x87, 0x55, 0x72, 0xE9, 0x3E, 0xB1, 0xB3, 0x5F, 0x93, - 0xFC, 0x1B, 0x3E, 0xBB, 0x81, 0x71, 0x8E, 0x86, 0x6F, 0x4E, 0x3D, 0x26, 0x9C, 0x5F, 0xDD, 0x75, - 0xF8, 0xA4, 0x7C, 0x27, 0x45, 0x14, 0x57, 0xE4, 0x27, 0xF4, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, - 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, - 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, - 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x57, - 0xD6, 0x1F, 0xB1, 0x4F, 0xED, 0xE5, 0x27, 0x80, 0x12, 0xCF, 0xC2, 0x3E, 0x34, 0xB9, 0x69, 0xB4, - 0x15, 0x0B, 0x0E, 0x9F, 0xA8, 0x3F, 0xCC, 0xFA, 0x68, 0xE0, 0x2C, 0x52, 0x1E, 0xF0, 0x8E, 0xC7, - 0xAA, 0x74, 0xE5, 0x70, 0x13, 0xE4, 0xFA, 0x2B, 0xBB, 0x2F, 0xCC, 0x2B, 0x60, 0xEA, 0xAA, 0xD4, - 0x5E, 0xBF, 0x83, 0x5D, 0x99, 0xE6, 0xE6, 0xD9, 0x4E, 0x1B, 0x31, 0xC3, 0xBC, 0x3E, 0x25, 0x5D, - 0x74, 0x7D, 0x53, 0xEE, 0x9F, 0x7F, 0xCF, 0x66, 0x7E, 0x8D, 0xFF, 0x00, 0xC1, 0x47, 0x74, 0x6B, - 0x3F, 0x15, 0xFE, 0xCA, 0x77, 0x3A, 0x92, 0xC9, 0x1C, 0xCB, 0xA6, 0xDD, 0xDA, 0x5F, 0xDA, 0x4B, - 0x13, 0x86, 0x49, 0x37, 0xB8, 0x8B, 0x20, 0x8E, 0x0A, 0x94, 0x98, 0x9E, 0x38, 0x3C, 0x1A, 0xFC, - 0xE4, 0xAD, 0xA9, 0xBC, 0x7B, 0xAE, 0x5C, 0x78, 0x4E, 0x3D, 0x0E, 0x5D, 0x6B, 0x54, 0x93, 0x45, - 0x85, 0xFC, 0xC8, 0xF4, 0xF6, 0xBA, 0x90, 0xDA, 0xA3, 0x72, 0x77, 0x08, 0xF3, 0xB4, 0x1E, 0x4F, - 0x6E, 0xE6, 0xB1, 0x6B, 0xAB, 0x3B, 0xCC, 0xE3, 0x8F, 0xAE, 0xAB, 0xC6, 0x3C, 0xBA, 0x24, 0xFA, - 0xEA, 0x8E, 0x1E, 0x1B, 0xC9, 0x67, 0x95, 0xE1, 0x5E, 0x16, 0x73, 0xE7, 0xF7, 0x9B, 0x4E, 0xD6, - 0xD1, 0xDB, 0x4F, 0xC2, 0xFF, 0x00, 0x30, 0xA2, 0x8A, 0x2B, 0xC7, 0x3E, 0x80, 0x28, 0xA2, 0x8A, - 0x00, 0xFB, 0xF7, 0xFE, 0x09, 0x67, 0xF1, 0x06, 0x5F, 0x10, 0x7C, 0x19, 0xD5, 0xBC, 0x3F, 0x33, - 0x33, 0x9F, 0x0E, 0x5F, 0xEE, 0x87, 0x9E, 0x12, 0x19, 0xC1, 0x70, 0xBF, 0xF7, 0xF1, 0x65, 0x3F, - 0xF0, 0x2A, 0xFA, 0x7E, 0xBE, 0x3C, 0xFF, 0x00, 0x82, 0x49, 0xE9, 0x93, 0x43, 0xA3, 0xF8, 0xEA, - 0xF1, 0x83, 0x79, 0x33, 0xCD, 0x65, 0x0A, 0x1E, 0xC5, 0x91, 0x67, 0x2D, 0xFA, 0x48, 0xBF, 0x98, - 0xAF, 0xB0, 0xEB, 0xF6, 0x4E, 0x1B, 0xA9, 0x29, 0xE5, 0xB4, 0x9C, 0xB7, 0xB3, 0x5F, 0x24, 0xDA, - 0x5F, 0x85, 0x8F, 0xE7, 0x5E, 0x32, 0xA7, 0x0A, 0x79, 0xCD, 0x78, 0xD3, 0xDA, 0xE9, 0xFC, 0xDC, - 0x53, 0x7F, 0x8B, 0x61, 0x5F, 0x32, 0x7F, 0xC1, 0x56, 0x08, 0x1F, 0xB3, 0xFE, 0x8B, 0xEB, 0xFF, - 0x00, 0x09, 0x04, 0x3F, 0xFA, 0x4D, 0x73, 0x5F, 0x4D, 0xD7, 0xC6, 0x9F, 0xF0, 0x56, 0x6F, 0x1B, - 0xFC, 0xBE, 0x10, 0xF0, 0xDC, 0x53, 0x2F, 0xFC, 0xB7, 0xD4, 0xEE, 0x61, 0xFE, 0x21, 0xD2, 0x38, - 0x5B, 0xE9, 0xFE, 0xBC, 0x7E, 0x14, 0x71, 0x25, 0x48, 0xC3, 0x2E, 0xAA, 0xE5, 0xD5, 0x25, 0xF7, - 0xB4, 0x3E, 0x0D, 0xA3, 0x2A, 0x99, 0xC5, 0x05, 0x1E, 0x8D, 0xBF, 0x92, 0x4D, 0x9F, 0x17, 0x51, - 0x45, 0x15, 0xF8, 0xD9, 0xFD, 0x12, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, - 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, - 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, - 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, - 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x01, 0xB8, 0xE0, 0x51, 0x5F, 0x5E, 0x7F, 0xC1, 0x3E, - 0x7F, 0x63, 0xC9, 0xF5, 0xCD, 0x5A, 0xC7, 0xE2, 0x07, 0x89, 0x20, 0x68, 0x74, 0xEB, 0x37, 0x13, - 0xE8, 0xD6, 0x92, 0x29, 0x0D, 0x75, 0x28, 0x39, 0x5B, 0x86, 0x1D, 0xA3, 0x53, 0xCA, 0x7F, 0x79, - 0x86, 0x7E, 0xE8, 0x1B, 0xFB, 0xB2, 0xEC, 0xBE, 0xAE, 0x36, 0xBA, 0xA3, 0x49, 0x7A, 0xBE, 0x89, - 0x77, 0x7F, 0xD7, 0x91, 0xE5, 0xE7, 0x19, 0xB5, 0x0C, 0xBB, 0x0D, 0x2C, 0x4D, 0x77, 0xB6, 0xCB, - 0xAB, 0x7D, 0x12, 0xFE, 0xB4, 0x5A, 0x9F, 0x47, 0x7E, 0xC6, 0xDF, 0x06, 0x24, 0xF8, 0x1D, 0xF0, - 0x17, 0x4B, 0xD3, 0x2E, 0xA3, 0x30, 0xEA, 0x97, 0xC5, 0xB5, 0x1D, 0x45, 0x0F, 0x54, 0x9A, 0x40, - 0x3E, 0x43, 0xEE, 0x88, 0xA8, 0x87, 0xD4, 0xA1, 0x3D, 0xEB, 0xD4, 0xA8, 0xA2, 0xBF, 0x6C, 0xC3, - 0x61, 0xE1, 0x42, 0x94, 0x68, 0xD3, 0xDA, 0x2A, 0xC8, 0xFE, 0x6C, 0xC6, 0x62, 0xAA, 0x62, 0x6B, - 0xCF, 0x11, 0x57, 0xE2, 0x93, 0x6D, 0xFC, 0xFF, 0x00, 0xAD, 0x06, 0xCD, 0x32, 0x5B, 0x42, 0xF2, - 0x48, 0xEB, 0x1C, 0x71, 0xA9, 0x66, 0x66, 0x38, 0x0A, 0x07, 0x52, 0x4F, 0x61, 0x5F, 0x95, 0xFF, - 0x00, 0xB5, 0x67, 0xC6, 0x2F, 0xF8, 0x5E, 0x9F, 0x1C, 0xB5, 0xAD, 0x72, 0x19, 0x19, 0xB4, 0xEF, - 0x30, 0x5A, 0xE9, 0xE1, 0x81, 0x18, 0xB6, 0x8F, 0xE5, 0x43, 0x83, 0xC8, 0xDC, 0x72, 0xE4, 0x1E, - 0x85, 0xCD, 0x7D, 0x31, 0xFF, 0x00, 0x05, 0x11, 0xFD, 0xAE, 0x60, 0xD2, 0xF4, 0x7B, 0xCF, 0x87, - 0xBE, 0x1D, 0xBA, 0xF3, 0x6F, 0xAF, 0x07, 0x97, 0xAD, 0x5C, 0xC4, 0xFF, 0x00, 0x2D, 0xB4, 0x5D, - 0xED, 0xC1, 0x1D, 0x5D, 0xBA, 0x3F, 0x60, 0xB9, 0x5E, 0x4B, 0x1D, 0xBF, 0x0E, 0xD7, 0xE7, 0x9C, - 0x5F, 0x9B, 0x46, 0xAC, 0xD6, 0x0E, 0x93, 0xBA, 0x8B, 0xBB, 0xF5, 0xED, 0xF2, 0xEB, 0xE7, 0xE8, - 0x7E, 0xB9, 0xE1, 0xEE, 0x41, 0x3C, 0x3D, 0x37, 0x98, 0x57, 0x56, 0x73, 0x56, 0x8A, 0xFE, 0xEE, - 0xF7, 0xF9, 0xE9, 0x6F, 0x25, 0x7D, 0x98, 0x51, 0x45, 0x15, 0xF1, 0x07, 0xE9, 0x61, 0x45, 0x14, - 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, - 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, - 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x62, 0x80, 0x0A, 0x29, - 0xC2, 0x36, 0x27, 0xEE, 0x9F, 0xCA, 0x97, 0xEC, 0xF2, 0x7F, 0x71, 0xBF, 0x2A, 0x00, 0x65, 0x15, - 0x22, 0xDA, 0x48, 0xFD, 0x23, 0x63, 0xF4, 0x14, 0xEF, 0xB0, 0x5C, 0x7F, 0xCF, 0x19, 0x3F, 0xEF, - 0x9A, 0x00, 0x86, 0x8A, 0x9B, 0xEC, 0x17, 0x1F, 0xF3, 0xC6, 0x4F, 0xFB, 0xE6, 0x8F, 0xB0, 0x5C, - 0x7F, 0xCF, 0x19, 0x3F, 0xEF, 0x9A, 0x00, 0x86, 0xA6, 0xB6, 0xB7, 0x92, 0xF2, 0xE1, 0x23, 0x8D, - 0x5E, 0x49, 0x24, 0x60, 0xAA, 0x8A, 0x32, 0xCC, 0x4F, 0x00, 0x01, 0xDC, 0xD7, 0xA9, 0xFC, 0x0B, - 0xFD, 0x8F, 0x3C, 0x6D, 0xF1, 0xEA, 0xE2, 0x19, 0x34, 0xFD, 0x3D, 0xF4, 0xFD, 0x1D, 0x88, 0xDF, - 0xAA, 0x5F, 0x2B, 0x45, 0x6F, 0x8C, 0x90, 0x76, 0x71, 0xBA, 0x53, 0x95, 0x23, 0x08, 0x08, 0x07, - 0x01, 0x8A, 0xF5, 0xAF, 0xB9, 0x7F, 0x67, 0x5F, 0xD8, 0xC7, 0xC2, 0x5F, 0xB3, 0xC4, 0x71, 0x5E, - 0x5B, 0xC4, 0x75, 0x8F, 0x10, 0x85, 0xC3, 0xEA, 0x97, 0x68, 0x37, 0xA1, 0x23, 0x0C, 0x21, 0x4E, - 0x44, 0x4A, 0x79, 0xE8, 0x4B, 0x61, 0x88, 0x2C, 0x45, 0x7D, 0x06, 0x55, 0xC3, 0xB8, 0xAC, 0x63, - 0x52, 0xB7, 0x2C, 0x3B, 0xBF, 0xD1, 0x75, 0xFC, 0xBC, 0xCF, 0x95, 0xCF, 0x78, 0xC3, 0x03, 0x96, - 0xA7, 0x0B, 0xF3, 0xD4, 0xFE, 0x55, 0xFA, 0xBD, 0x97, 0xE7, 0xE4, 0x78, 0x5F, 0xEC, 0x91, 0xFF, - 0x00, 0x04, 0xEA, 0x92, 0x79, 0xAD, 0x3C, 0x49, 0xF1, 0x0A, 0xD8, 0x47, 0x6E, 0xA0, 0x4D, 0x6D, - 0xA1, 0x38, 0xF9, 0xE4, 0x3F, 0xC2, 0xD7, 0x3E, 0x83, 0xBF, 0x95, 0xD4, 0xE4, 0x6E, 0xC6, 0x0A, - 0x1F, 0xB4, 0x22, 0x85, 0x61, 0x8D, 0x51, 0x15, 0x51, 0x10, 0x05, 0x55, 0x51, 0x80, 0xA0, 0x76, - 0x15, 0x9F, 0xE2, 0x9F, 0x19, 0x69, 0x1E, 0x07, 0xD3, 0x85, 0xDE, 0xB3, 0xAA, 0x69, 0xFA, 0x4D, - 0xA9, 0x6D, 0x82, 0x5B, 0xBB, 0x84, 0x85, 0x19, 0xBD, 0x01, 0x62, 0x32, 0x7D, 0xAB, 0xE7, 0xFF, - 0x00, 0x8B, 0x7F, 0xF0, 0x53, 0x0F, 0x08, 0x78, 0x32, 0x39, 0x20, 0xF0, 0xDD, 0x9D, 0xF7, 0x8A, - 0x2F, 0x86, 0x40, 0x90, 0x23, 0x5A, 0xDA, 0x29, 0xE4, 0x72, 0xEE, 0x37, 0xB6, 0x0F, 0xF7, 0x53, - 0x04, 0x7F, 0x15, 0x7E, 0x85, 0x42, 0x39, 0x76, 0x51, 0x47, 0x91, 0xC9, 0x47, 0xBD, 0xFE, 0x27, - 0xF2, 0xDF, 0xFC, 0x8F, 0xC8, 0xF1, 0x33, 0xCD, 0xF8, 0x83, 0x11, 0xED, 0x14, 0x1C, 0xBA, 0x2B, - 0x2B, 0x46, 0x3F, 0x37, 0xA2, 0xF5, 0x6E, 0xEF, 0xA9, 0xF4, 0x75, 0xDD, 0xDC, 0x56, 0x16, 0xB2, - 0x5C, 0x4D, 0x2C, 0x70, 0xC3, 0x0A, 0x97, 0x92, 0x49, 0x18, 0x2A, 0xA2, 0x81, 0x92, 0x49, 0x3C, - 0x00, 0x3D, 0x6B, 0xE4, 0x5F, 0xDA, 0xC3, 0xFE, 0x0A, 0x33, 0x6F, 0x63, 0x6B, 0x71, 0xE1, 0xFF, - 0x00, 0x87, 0x97, 0x1F, 0x68, 0xBB, 0x62, 0x62, 0xB9, 0xD6, 0xC0, 0xCC, 0x50, 0xAE, 0x30, 0x45, - 0xBE, 0x7E, 0xF3, 0x67, 0xFE, 0x5A, 0x1F, 0x94, 0x63, 0xE5, 0xDD, 0x90, 0xCB, 0xF3, 0x9F, 0xC7, - 0x2F, 0xDA, 0xB7, 0xC7, 0x1F, 0x1F, 0xA5, 0x68, 0xF5, 0x8D, 0x41, 0xAD, 0xF4, 0xBD, 0xD9, 0x4D, - 0x32, 0xCF, 0x30, 0xDA, 0x0F, 0xBA, 0x46, 0xE5, 0xC9, 0x32, 0x10, 0x54, 0x10, 0x5C, 0xB6, 0x0E, - 0x71, 0x8C, 0xE2, 0xBC, 0xBC, 0xC6, 0xC0, 0xFD, 0xD3, 0xF9, 0x57, 0xCA, 0x67, 0x1C, 0x5D, 0x3A, - 0xA9, 0xD2, 0xC1, 0xAE, 0x55, 0xFC, 0xCF, 0x7F, 0x97, 0x6F, 0xCF, 0xD0, 0xFB, 0xCE, 0x1F, 0xF0, - 0xFE, 0x9D, 0x09, 0x2A, 0xF9, 0x8B, 0x53, 0x92, 0xDA, 0x2B, 0xE1, 0x5E, 0xBD, 0xFD, 0x36, 0xF5, - 0x26, 0xB8, 0xB9, 0x92, 0xF6, 0xE1, 0xA5, 0x99, 0x9E, 0x49, 0x64, 0x62, 0xEE, 0xCE, 0x72, 0xCC, - 0x4F, 0x24, 0x92, 0x7A, 0x93, 0x55, 0xE8, 0xC5, 0x15, 0xF1, 0x07, 0xE9, 0x41, 0x45, 0x14, 0x50, - 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, - 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, - 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x5D, 0xD7, 0xC0, 0xBF, 0x84, 0xD1, 0xFC, - 0x59, 0xF1, 0x4C, 0x76, 0x12, 0x5D, 0xC5, 0x68, 0xAE, 0x71, 0xB9, 0xCE, 0x05, 0x70, 0xB5, 0x73, - 0x48, 0xD6, 0xEE, 0xB4, 0x2B, 0x91, 0x35, 0xAC, 0xD2, 0x43, 0x20, 0xE8, 0x54, 0xE2, 0x80, 0x3E, - 0xFC, 0xF8, 0x6F, 0xFF, 0x00, 0x04, 0x93, 0xD0, 0xF5, 0xF8, 0x62, 0x92, 0xE3, 0xC4, 0x76, 0x67, - 0x70, 0x19, 0x02, 0x41, 0x5E, 0xB7, 0xE1, 0x7F, 0xF8, 0x23, 0x0F, 0x83, 0x01, 0x0D, 0x3E, 0xAF, - 0x04, 0x8A, 0x7A, 0x61, 0xC5, 0x7E, 0x6E, 0x68, 0x9F, 0xB4, 0xD7, 0x8C, 0xB4, 0x11, 0x88, 0x35, - 0xAB, 0xC5, 0x0B, 0xD3, 0xF7, 0x86, 0xBA, 0xAD, 0x37, 0xF6, 0xF4, 0xF8, 0x87, 0xA7, 0x04, 0x0B, - 0xAD, 0xDD, 0x1D, 0x9D, 0x3E, 0x73, 0x40, 0x1F, 0xA5, 0x3A, 0x2F, 0xFC, 0x11, 0x87, 0xC0, 0x56, - 0xD7, 0x01, 0xE4, 0xBB, 0x89, 0xD3, 0xD3, 0x70, 0xAE, 0x92, 0x2F, 0xF8, 0x23, 0xAF, 0xC3, 0x89, - 0x17, 0xFE, 0x59, 0x37, 0xD0, 0x8A, 0xFC, 0xD4, 0x83, 0xFE, 0x0A, 0x6B, 0xF1, 0x2A, 0x18, 0x95, - 0x7F, 0xB5, 0xA6, 0x20, 0x0C, 0x7D, 0xE3, 0x5B, 0x9A, 0x2F, 0xFC, 0x15, 0x8B, 0xE2, 0x3E, 0x95, - 0x0E, 0xC3, 0x7F, 0x23, 0xFB, 0x96, 0x34, 0x01, 0xFA, 0x4B, 0xA0, 0xFF, 0x00, 0xC1, 0x23, 0x3E, - 0x1B, 0x69, 0x44, 0xEF, 0x86, 0x39, 0x3E, 0xB8, 0xAD, 0x51, 0xFF, 0x00, 0x04, 0xA8, 0xF8, 0x62, - 0x5B, 0xFE, 0x3C, 0xE0, 0xFD, 0x2B, 0xF3, 0x57, 0xFE, 0x1E, 0xF5, 0xF1, 0x1F, 0xFE, 0x7F, 0x9B, - 0xF3, 0x35, 0x2D, 0x8F, 0xFC, 0x16, 0x07, 0xE2, 0x2D, 0xBD, 0xC2, 0xBB, 0xDD, 0xB3, 0x80, 0x7A, - 0x64, 0xD0, 0x07, 0xE9, 0x4A, 0xFF, 0x00, 0xC1, 0x28, 0x7E, 0x1A, 0x37, 0xFC, 0xB8, 0xC5, 0xF9, - 0x0A, 0x13, 0xFE, 0x09, 0x57, 0xF0, 0xFA, 0xC6, 0x68, 0xE6, 0xB3, 0xB4, 0xB1, 0x8E, 0x78, 0xDB, - 0x70, 0x69, 0xED, 0x96, 0x74, 0xFF, 0x00, 0xBE, 0x49, 0x03, 0x35, 0xF9, 0xEF, 0x17, 0xFC, 0x16, - 0xB7, 0xC7, 0x51, 0xA0, 0x19, 0xCE, 0x29, 0xDF, 0xF0, 0xFB, 0x0F, 0x1D, 0x56, 0x94, 0xAA, 0x4A, - 0x9C, 0xD5, 0x48, 0x6E, 0xB5, 0x5D, 0x7F, 0x07, 0xA1, 0x95, 0x7A, 0x30, 0xAD, 0x4D, 0xD2, 0xA9, - 0xB3, 0xD1, 0xD9, 0xB5, 0xF8, 0xA6, 0x9A, 0xF9, 0x33, 0xF4, 0xE2, 0xCF, 0xF6, 0x2F, 0xB5, 0x89, - 0x71, 0x36, 0xB7, 0x33, 0xE3, 0x80, 0x23, 0x89, 0x63, 0x03, 0xF3, 0xCD, 0x17, 0xFF, 0x00, 0xB1, - 0x36, 0x95, 0x7F, 0x19, 0x59, 0x35, 0x8D, 0x45, 0x14, 0xFF, 0x00, 0xCF, 0x39, 0xBC, 0xB3, 0xF9, - 0xAE, 0x0D, 0x7E, 0x63, 0xFF, 0x00, 0xC3, 0xEC, 0x3C, 0x75, 0x4D, 0x97, 0xFE, 0x0B, 0x5B, 0xE3, - 0xA9, 0x10, 0x8C, 0xE3, 0x35, 0xE8, 0xD7, 0xCF, 0x31, 0xF5, 0xB4, 0x9D, 0x59, 0x7C, 0x9D, 0xBF, - 0x05, 0x63, 0xC8, 0xC2, 0xF0, 0xD6, 0x57, 0x87, 0xD6, 0x95, 0x08, 0xDF, 0xBB, 0x57, 0x7F, 0x7B, - 0xB9, 0xFA, 0x19, 0xA8, 0xFF, 0x00, 0xC1, 0x2E, 0xBE, 0x1D, 0x6A, 0xD7, 0x8F, 0x71, 0x75, 0x1F, - 0xDA, 0xAE, 0x24, 0x39, 0x79, 0x66, 0x7D, 0xEE, 0xE7, 0xD4, 0x93, 0xC9, 0xAC, 0x2D, 0x7B, 0xFE, - 0x09, 0x19, 0xF0, 0xDB, 0x55, 0x23, 0x64, 0x31, 0xC7, 0xF4, 0xC5, 0x7E, 0x77, 0xDF, 0x7F, 0xC1, - 0x60, 0x7E, 0x22, 0xDC, 0x5C, 0x33, 0xA5, 0xDB, 0x20, 0x27, 0xA6, 0x4D, 0x45, 0xFF, 0x00, 0x0F, - 0x7A, 0xF8, 0x8F, 0xFF, 0x00, 0x3F, 0xCD, 0xF9, 0x9A, 0xF2, 0xDB, 0x6D, 0xDD, 0x9E, 0xDC, 0x52, - 0x4A, 0xCB, 0x63, 0xEF, 0xF9, 0x7F, 0xE0, 0x8E, 0xBF, 0x0E, 0x23, 0x5F, 0xF9, 0x64, 0xBF, 0x52, - 0x2B, 0x9B, 0xD6, 0xBF, 0xE0, 0x8C, 0x3E, 0x02, 0xB9, 0xB8, 0x2F, 0x1D, 0xDC, 0x48, 0x9E, 0x9B, - 0x85, 0x7C, 0x29, 0xAD, 0x7F, 0xC1, 0x58, 0xBE, 0x23, 0xEA, 0xB0, 0xEC, 0x17, 0xF2, 0x27, 0xB8, - 0x63, 0x58, 0x73, 0xFF, 0x00, 0xC1, 0x4D, 0x7E, 0x25, 0x4D, 0x13, 0x2F, 0xF6, 0xB4, 0xC0, 0x11, - 0x8F, 0xBC, 0x69, 0x0C, 0xFB, 0x5F, 0xC5, 0x1F, 0xF0, 0x46, 0x1F, 0x06, 0x12, 0x5A, 0x0D, 0x5E, - 0x08, 0xD4, 0x75, 0xCB, 0x8A, 0xF2, 0x4F, 0x89, 0x1F, 0xF0, 0x49, 0x3D, 0x0F, 0x40, 0x86, 0x59, - 0x2D, 0xFC, 0x47, 0x66, 0x36, 0x83, 0x80, 0x64, 0x15, 0xF3, 0x0E, 0xA5, 0xFB, 0x7A, 0x7C, 0x43, - 0xD4, 0x43, 0x86, 0xD6, 0xEE, 0x86, 0xFE, 0xBF, 0x39, 0xAE, 0x57, 0x5B, 0xFD, 0xA6, 0xBC, 0x65, - 0xAF, 0x0C, 0x4F, 0xAD, 0x5E, 0x30, 0x6E, 0xBF, 0xBC, 0x34, 0x01, 0x0F, 0xC7, 0x4F, 0x84, 0xD1, - 0xFC, 0x26, 0xF1, 0x4C, 0x96, 0x11, 0xDD, 0xC5, 0x76, 0xA8, 0x71, 0xB9, 0x0E, 0x45, 0x70, 0xB5, - 0x73, 0x57, 0xD6, 0xEE, 0xB5, 0xDB, 0x93, 0x35, 0xD4, 0xD2, 0x4D, 0x21, 0xEA, 0x58, 0xE6, 0xA9, - 0xD0, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, - 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, - 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, - 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, - 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, - 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, - 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, - 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, - 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, - 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, - 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, - 0x50, 0x01, 0x45, 0x14, 0x50, 0x07, 0xFF, 0xD9, -} ; - -const char about_jpg[] PROGMEM = -{ - 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x00, 0x60, - 0x00, 0x60, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x05, 0x03, 0x03, 0x03, 0x03, 0x03, 0x06, 0x04, - 0x04, 0x03, 0x05, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x08, 0x09, 0x0B, 0x09, 0x08, - 0x08, 0x0A, 0x08, 0x07, 0x07, 0x0A, 0x0D, 0x0A, 0x0A, 0x0B, 0x0C, 0x0C, 0x0C, 0x0C, 0x07, 0x09, - 0x0E, 0x0F, 0x0D, 0x0C, 0x0E, 0x0B, 0x0C, 0x0C, 0x0C, 0xFF, 0xDB, 0x00, 0x43, 0x01, 0x02, 0x02, - 0x02, 0x03, 0x03, 0x03, 0x06, 0x03, 0x03, 0x06, 0x0C, 0x08, 0x07, 0x08, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xFF, 0xC0, - 0x00, 0x11, 0x08, 0x00, 0xB5, 0x01, 0x40, 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, - 0x01, 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, - 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, - 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, - 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, - 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, - 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, - 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, - 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, - 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, - 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, - 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, - 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xC4, 0x00, 0x1F, 0x01, 0x00, 0x03, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, 0x11, 0x00, - 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, - 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, - 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, - 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, - 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, - 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, - 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, - 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, - 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, - 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, - 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, - 0xFA, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3F, 0x00, 0xFE, - 0x7F, 0xE8, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, - 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA9, 0x0F, - 0x5E, 0xF4, 0xD2, 0x39, 0xEE, 0x45, 0x3B, 0x00, 0xDA, 0x2B, 0x5F, 0xC3, 0x7E, 0x12, 0xD5, 0x3C, - 0x63, 0xAA, 0xAD, 0x8E, 0x91, 0xA7, 0xDF, 0x6A, 0x77, 0x92, 0x7D, 0xD8, 0x6D, 0x60, 0x69, 0xA4, - 0x6F, 0xA2, 0xA8, 0x26, 0xBD, 0x53, 0xC2, 0x7F, 0xB0, 0xA7, 0x8F, 0xFC, 0x4A, 0xD1, 0xB5, 0xD5, - 0xA5, 0x86, 0x89, 0x0C, 0x83, 0x22, 0x4B, 0xEB, 0xB5, 0x04, 0x7D, 0x52, 0x3D, 0xEE, 0xBF, 0x42, - 0xA2, 0xBD, 0x5C, 0xBF, 0x22, 0xCC, 0x31, 0xCE, 0xD8, 0x4A, 0x32, 0x9F, 0xA4, 0x5B, 0x4B, 0xD5, - 0xF4, 0x3C, 0xFC, 0x76, 0x6D, 0x82, 0xC1, 0xAB, 0xE2, 0xAA, 0xC6, 0x1F, 0xE2, 0x69, 0x7E, 0x67, - 0x8A, 0x51, 0x5F, 0x51, 0x68, 0xDF, 0xF0, 0x4E, 0x1B, 0x86, 0x65, 0x1A, 0xA7, 0x8B, 0x6C, 0xED, - 0xC6, 0x46, 0xEF, 0xB3, 0x59, 0x34, 0xF8, 0xE7, 0x9C, 0x6F, 0x68, 0xFB, 0x7D, 0x33, 0xED, 0xD6, - 0xB4, 0x47, 0xFC, 0x13, 0x7B, 0x4B, 0xF2, 0x33, 0xFF, 0x00, 0x09, 0xC5, 0xD1, 0x97, 0x07, 0xA6, - 0x8E, 0xBB, 0x73, 0xCE, 0x3F, 0xE5, 0xBE, 0x7D, 0x33, 0x5F, 0x51, 0x4F, 0xC3, 0x1E, 0x24, 0x9C, - 0x79, 0xA3, 0x87, 0xFB, 0xE5, 0x14, 0xFE, 0xE7, 0x23, 0xE7, 0x6A, 0x78, 0x81, 0x90, 0x42, 0x5C, - 0xB2, 0xC4, 0x7D, 0xD1, 0x93, 0x5F, 0x7A, 0x8B, 0x3E, 0x4C, 0xA2, 0xBE, 0xA6, 0xD5, 0xBF, 0xE0, - 0x9B, 0x4F, 0x1A, 0x7F, 0xA0, 0x78, 0xCA, 0x2B, 0x86, 0xE3, 0x89, 0xF4, 0xE3, 0x08, 0xE8, 0x3F, - 0xBB, 0x23, 0xF7, 0xCF, 0xE0, 0x01, 0xEF, 0x81, 0xC6, 0xF8, 0xAF, 0xF6, 0x05, 0xF1, 0xD6, 0x84, - 0x37, 0x58, 0x8D, 0x23, 0x5B, 0x40, 0xBB, 0x8F, 0xD9, 0x2E, 0xF6, 0x30, 0xFC, 0x25, 0x08, 0x49, - 0xF6, 0x19, 0xF6, 0xCD, 0x71, 0xE3, 0x3C, 0x3F, 0xE2, 0x0C, 0x32, 0xBD, 0x4C, 0x2C, 0xBE, 0x56, - 0x97, 0xFE, 0x92, 0xD9, 0xD5, 0x85, 0xE3, 0x6C, 0x8F, 0x10, 0xED, 0x4F, 0x13, 0x1F, 0x9D, 0xE3, - 0xFF, 0x00, 0xA5, 0x58, 0xF0, 0xBA, 0x2B, 0xA0, 0xF1, 0x77, 0xC3, 0xDD, 0x6F, 0xC0, 0x1A, 0x8A, - 0xDA, 0xEB, 0x7A, 0x4E, 0xA1, 0xA5, 0xDC, 0x38, 0xDC, 0xA9, 0x75, 0x03, 0x46, 0x5C, 0x7A, 0x8C, - 0x8E, 0x47, 0xB8, 0xC8, 0x35, 0x86, 0x41, 0x2B, 0x83, 0x5F, 0x27, 0x5B, 0x0F, 0x52, 0x94, 0xB9, - 0x2A, 0xC5, 0xC5, 0xAD, 0xD3, 0x56, 0xB1, 0xF4, 0xD4, 0xEA, 0x42, 0x71, 0xE6, 0x83, 0xBA, 0x7B, - 0x35, 0xA9, 0x1D, 0x14, 0xF3, 0xD0, 0xD2, 0x77, 0xFF, 0x00, 0x3C, 0x56, 0x76, 0x28, 0x6D, 0x14, - 0x51, 0x52, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, - 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, - 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, - 0x14, 0x50, 0x01, 0x45, 0x48, 0x48, 0x02, 0xBD, 0x8B, 0xF6, 0x6D, 0xFD, 0x94, 0x35, 0x3F, 0x8D, - 0xB7, 0x11, 0xEA, 0x57, 0xFE, 0x76, 0x95, 0xE1, 0x88, 0xD8, 0xEE, 0xBA, 0xDB, 0xFB, 0xCB, 0xB2, - 0x0E, 0x0A, 0x42, 0x0F, 0x04, 0xE4, 0x10, 0x5C, 0xFC, 0xAB, 0xCF, 0xDE, 0x23, 0x61, 0xF4, 0xB2, - 0xBC, 0xA7, 0x17, 0x98, 0xE2, 0x23, 0x85, 0xC1, 0xC1, 0xCA, 0x72, 0xE8, 0xBF, 0x37, 0xD9, 0x77, - 0x6F, 0x43, 0x8B, 0x30, 0xCC, 0x70, 0xD8, 0x1A, 0x12, 0xC4, 0xE2, 0xE4, 0xA3, 0x08, 0xEE, 0xDF, - 0xE4, 0xBB, 0xBF, 0x24, 0x70, 0x3F, 0x0F, 0x7E, 0x16, 0xEB, 0xDF, 0x15, 0xF5, 0xBF, 0xEC, 0xFD, - 0x07, 0x4F, 0x9F, 0x50, 0xB8, 0x03, 0x7B, 0xED, 0xC2, 0xC7, 0x0A, 0xFF, 0x00, 0x79, 0xDC, 0xE1, - 0x51, 0x7B, 0x65, 0x88, 0x19, 0xC0, 0xEA, 0x40, 0xAF, 0xAB, 0x3E, 0x09, 0xFF, 0x00, 0xC1, 0x3B, - 0xB4, 0xCB, 0x6B, 0xCB, 0x7F, 0xED, 0xEF, 0xB4, 0x78, 0xA3, 0x56, 0x94, 0xE5, 0x34, 0xFB, 0x20, - 0xEB, 0x6E, 0x0F, 0xA1, 0x2A, 0x04, 0x8F, 0x8F, 0x51, 0xB0, 0x7B, 0x11, 0xCD, 0x7D, 0x7F, 0xFB, - 0x1A, 0xFE, 0xC0, 0xB7, 0xDF, 0x11, 0xA2, 0xB4, 0xD2, 0x7C, 0x39, 0xA5, 0xAE, 0x87, 0xE1, 0xD5, - 0xC4, 0xAF, 0x28, 0x5C, 0x34, 0xE3, 0xA1, 0x95, 0x9D, 0xB2, 0x4F, 0xA7, 0x98, 0xF9, 0xEC, 0xA8, - 0x1B, 0x1B, 0x47, 0xE8, 0xBF, 0xC2, 0xCF, 0x80, 0x7E, 0x05, 0xFD, 0x90, 0xB4, 0x38, 0xD3, 0x4D, - 0xB1, 0x86, 0xFB, 0xC4, 0x12, 0x20, 0x66, 0xB8, 0x65, 0xCC, 0xCC, 0x7F, 0xBD, 0x93, 0x92, 0x8B, - 0xE8, 0x4E, 0x5B, 0x93, 0x8E, 0x38, 0x1F, 0xB5, 0x60, 0x78, 0x47, 0x26, 0xC8, 0x54, 0x5E, 0x3D, - 0x7D, 0x67, 0x13, 0xFC, 0xAB, 0xE0, 0x8B, 0xF3, 0xEF, 0xF3, 0xD3, 0xC8, 0xFC, 0x9B, 0x19, 0xC5, - 0x19, 0xAE, 0x72, 0xDA, 0xC0, 0xBF, 0xAB, 0xE1, 0xFF, 0x00, 0x99, 0xFC, 0x72, 0x5F, 0xA7, 0xCB, - 0x5F, 0x33, 0xE3, 0x3F, 0xD9, 0xF3, 0xFE, 0x09, 0x33, 0xE2, 0xED, 0x63, 0x41, 0x89, 0xF5, 0x35, - 0xD1, 0xFE, 0x1B, 0xF8, 0x6D, 0x40, 0x90, 0xC2, 0xD0, 0x2F, 0x9D, 0x8E, 0x3E, 0x6F, 0x25, 0x0A, - 0xA8, 0x27, 0xFB, 0xCE, 0xC0, 0x9E, 0xBC, 0xD7, 0xBA, 0x78, 0x6B, 0xF6, 0x1C, 0xF8, 0x2F, 0xF0, - 0xDD, 0x15, 0x66, 0xD3, 0xF5, 0xAF, 0x1E, 0x5F, 0xC7, 0xF7, 0xA5, 0xD4, 0x2F, 0x5E, 0xDE, 0xD4, - 0x37, 0xB2, 0x43, 0xB3, 0x23, 0xD9, 0xB7, 0x8F, 0x7A, 0xF5, 0x3F, 0x13, 0x78, 0xB2, 0xFB, 0xC5, - 0x97, 0x1B, 0xAF, 0x26, 0x3E, 0x5A, 0x9C, 0xA4, 0x49, 0xC4, 0x71, 0xFD, 0x07, 0x73, 0xEE, 0x72, - 0x7D, 0xEB, 0x25, 0xE3, 0x3F, 0xE0, 0x6B, 0xD9, 0xAD, 0x9D, 0x63, 0xEA, 0xC7, 0x91, 0x4B, 0xD9, - 0xC5, 0x6D, 0x18, 0x7B, 0xA9, 0x7C, 0xD6, 0xBF, 0x89, 0xE6, 0xD1, 0xC9, 0xF0, 0x54, 0xDF, 0x34, - 0xA3, 0xED, 0x24, 0xF7, 0x94, 0xDF, 0x33, 0x7F, 0x7E, 0x85, 0x1B, 0x2F, 0x0A, 0xF8, 0x6F, 0x41, - 0x00, 0x68, 0xFE, 0x0D, 0xF0, 0x4E, 0x8A, 0xAB, 0xC6, 0xEB, 0x5D, 0x12, 0xDB, 0xCE, 0x23, 0xDE, - 0x57, 0x46, 0x72, 0x7D, 0xF3, 0x4F, 0xBB, 0x8D, 0x6E, 0xD3, 0x6C, 0xAA, 0xB2, 0x47, 0xFD, 0xC2, - 0x83, 0x68, 0xFC, 0x3A, 0x54, 0xEC, 0xDB, 0x6A, 0x29, 0x2E, 0x23, 0x03, 0x77, 0x98, 0x9B, 0x7A, - 0x1E, 0x7A, 0x1A, 0xF3, 0xA5, 0x52, 0x5B, 0xCA, 0x5F, 0x3B, 0xB3, 0xD3, 0x8D, 0x18, 0x2D, 0x23, - 0x15, 0x6F, 0x4F, 0xF2, 0x39, 0xBD, 0x67, 0xE1, 0x5F, 0x85, 0xF5, 0xE0, 0x56, 0xFB, 0xC3, 0x7A, - 0x05, 0xD6, 0xEF, 0xE2, 0x93, 0x4F, 0x85, 0x9B, 0xF0, 0x6D, 0xB9, 0x1F, 0x81, 0xAF, 0x3D, 0xF1, - 0x9F, 0xEC, 0x43, 0xE0, 0x4F, 0x13, 0x46, 0xCD, 0x67, 0x6B, 0x7D, 0xA0, 0xDC, 0x37, 0x3B, 0xEC, - 0x6E, 0x4B, 0x21, 0x3D, 0xB2, 0x92, 0xEF, 0x18, 0xF6, 0x5D, 0xBF, 0x51, 0x5E, 0xC6, 0xEB, 0xB8, - 0x71, 0xF5, 0x06, 0x99, 0x5D, 0x58, 0x7C, 0xD3, 0x17, 0x45, 0xDE, 0x95, 0x46, 0xBE, 0x6C, 0xCE, - 0xB6, 0x5B, 0x85, 0xAC, 0xAD, 0x56, 0x9A, 0x6B, 0xD1, 0x1F, 0x15, 0xFC, 0x5C, 0xFD, 0x85, 0xBC, - 0x4F, 0xE1, 0xCD, 0x3A, 0x75, 0xB2, 0x82, 0xCF, 0xC6, 0xBA, 0x33, 0x0C, 0xC9, 0x6F, 0x1C, 0x03, - 0xCF, 0x65, 0xEA, 0x77, 0x5A, 0xC8, 0x59, 0x64, 0x1D, 0xB0, 0x8C, 0xEC, 0x7F, 0xB9, 0x5F, 0x18, - 0xFC, 0x54, 0xFD, 0x85, 0x7C, 0x27, 0xE3, 0x3F, 0xB5, 0x36, 0x8B, 0xE6, 0xF8, 0x5F, 0x56, 0x85, - 0xCC, 0x6F, 0x0E, 0x19, 0xAD, 0x96, 0x51, 0x9C, 0xAC, 0xB0, 0xB7, 0xEF, 0x22, 0x6C, 0xE0, 0x1D, - 0xA4, 0x6D, 0x03, 0xFD, 0x59, 0x3C, 0x57, 0xEC, 0xE4, 0x83, 0x03, 0xFD, 0xDA, 0xF3, 0xCF, 0x8E, - 0x1F, 0xB3, 0x67, 0x86, 0x7E, 0x3C, 0x5A, 0x79, 0x9A, 0x84, 0x2D, 0xA7, 0xEB, 0x91, 0xC7, 0xB2, - 0xDB, 0x59, 0xB3, 0x01, 0x6E, 0xE1, 0xC7, 0x45, 0x7E, 0xD3, 0x46, 0x3F, 0xE7, 0x9B, 0xE4, 0x63, - 0x3B, 0x4A, 0x93, 0xBA, 0xBD, 0x1A, 0xD9, 0x8E, 0x0B, 0x32, 0x8F, 0xB1, 0xCE, 0xE8, 0x46, 0x6B, - 0xA4, 0xD2, 0xB4, 0x97, 0xCD, 0x7F, 0x5D, 0xCF, 0x3E, 0x9E, 0x55, 0x8A, 0xC0, 0x4B, 0xDB, 0x64, - 0xF5, 0x9D, 0x37, 0xFC, 0xAD, 0xDE, 0x2F, 0xE4, 0xCF, 0xE7, 0xFB, 0xE2, 0xC7, 0xC0, 0xEF, 0x12, - 0x7C, 0x16, 0xD5, 0x16, 0xDF, 0x5C, 0xD3, 0xDA, 0x38, 0x67, 0x24, 0x41, 0x75, 0x19, 0xF3, 0x2D, - 0xEE, 0x31, 0xD7, 0x6B, 0x8E, 0x33, 0xD0, 0x95, 0x38, 0x60, 0x08, 0xC8, 0x19, 0xAE, 0x2F, 0xA0, - 0x18, 0xFC, 0xEB, 0xF5, 0x63, 0xE3, 0xD7, 0xC1, 0x2D, 0x43, 0xE1, 0x5E, 0xA3, 0x27, 0x86, 0xFC, - 0x75, 0xA7, 0x59, 0x5F, 0x69, 0x1A, 0xB6, 0x52, 0xD2, 0xFF, 0x00, 0xCB, 0xDD, 0xA7, 0xEA, 0x80, - 0x73, 0xDF, 0x98, 0xA6, 0x5E, 0xBB, 0x49, 0xDC, 0xA7, 0x95, 0x38, 0x2A, 0xC7, 0xE2, 0x9F, 0xDA, - 0x6F, 0xF6, 0x37, 0xBA, 0xF8, 0x6A, 0xD3, 0xEB, 0xBE, 0x19, 0x59, 0x6F, 0xBC, 0x3D, 0xCB, 0xCF, - 0x6E, 0x72, 0xF3, 0xE9, 0x9D, 0xCE, 0x7F, 0xBF, 0x10, 0xEC, 0xFD, 0x57, 0xA3, 0x74, 0x0E, 0xDF, - 0x9B, 0x71, 0x77, 0x86, 0xB5, 0x30, 0x34, 0x7F, 0xB4, 0x32, 0xA9, 0x7B, 0x6A, 0x1B, 0xBF, 0xE6, - 0x8A, 0xF3, 0xB6, 0xEB, 0xCC, 0xFB, 0x7E, 0x1C, 0xE3, 0xA8, 0x62, 0x6A, 0xAC, 0x06, 0x67, 0x1F, - 0x65, 0x5B, 0xA7, 0xF2, 0xCB, 0xD1, 0xF4, 0x7E, 0x4F, 0xE4, 0x7C, 0xF7, 0x45, 0x49, 0xD0, 0x9E, - 0xD4, 0x84, 0x6D, 0x18, 0x35, 0xF9, 0x55, 0x8F, 0xD0, 0x86, 0x51, 0x45, 0x14, 0x80, 0x28, 0xA2, - 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, - 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA2, - 0x8A, 0x00, 0x28, 0xA2, 0x8A, 0x00, 0x28, 0xA7, 0xF7, 0xE9, 0x5D, 0x87, 0xC1, 0x7F, 0x85, 0x77, - 0xDF, 0x19, 0xBC, 0x7F, 0x63, 0xA0, 0xD9, 0xFE, 0xE9, 0x66, 0x26, 0x5B, 0x89, 0xF6, 0xEE, 0x16, - 0xB0, 0xAF, 0x2F, 0x21, 0x19, 0x1D, 0x07, 0x00, 0x64, 0x6E, 0x62, 0xAB, 0x9C, 0x91, 0x5D, 0x18, - 0x5C, 0x25, 0x5C, 0x45, 0x68, 0xE1, 0xE8, 0xAB, 0xCA, 0x4D, 0x24, 0xBB, 0xB6, 0x65, 0x5E, 0xBD, - 0x3A, 0x34, 0xA5, 0x5A, 0xAB, 0xB4, 0x62, 0x9B, 0x6F, 0xB2, 0x5B, 0x9D, 0xCF, 0xEC, 0x9B, 0xFB, - 0x33, 0x3F, 0xC6, 0x8D, 0x61, 0xB5, 0x4D, 0x59, 0x26, 0x8B, 0xC3, 0x3A, 0x7B, 0xED, 0x99, 0x95, - 0xB6, 0x35, 0xEC, 0xB8, 0x04, 0x42, 0x87, 0xB6, 0x01, 0x05, 0xC8, 0xE4, 0x29, 0x03, 0x82, 0xCA, - 0x6B, 0xF5, 0x67, 0xF6, 0x2F, 0xFD, 0x8D, 0x25, 0xF8, 0x9F, 0xA9, 0x69, 0x8D, 0x3D, 0x8C, 0x36, - 0xDA, 0x44, 0x51, 0xA9, 0xB3, 0xB2, 0xF2, 0xF6, 0xC0, 0x90, 0x2E, 0x00, 0x96, 0x45, 0x1D, 0x22, - 0x1C, 0x6D, 0x4E, 0xB2, 0x1C, 0x7F, 0x09, 0xF9, 0xB8, 0x2F, 0xD9, 0x1F, 0xF6, 0x73, 0xB2, 0xD6, - 0x4D, 0x8D, 0x9D, 0xB5, 0x8B, 0x2F, 0x86, 0x7C, 0x3F, 0xB2, 0xDA, 0x1B, 0x6C, 0x6E, 0x6B, 0xC9, - 0x49, 0xC8, 0x8C, 0x9C, 0x0D, 0xC5, 0x98, 0xEF, 0x90, 0xF7, 0x2F, 0xD0, 0x6F, 0xC8, 0xFD, 0x22, - 0xF0, 0xD7, 0xD9, 0x3E, 0x04, 0x7C, 0x3F, 0xE4, 0x43, 0x26, 0xA9, 0x77, 0x83, 0x26, 0x3F, 0xE5, - 0xAC, 0xB8, 0xE1, 0x07, 0xFD, 0x33, 0x41, 0x9E, 0x9E, 0xE7, 0x82, 0xD5, 0xFD, 0x25, 0x83, 0xCB, - 0xA8, 0xF0, 0xBE, 0x03, 0xEA, 0x78, 0x6F, 0x7B, 0x13, 0x51, 0x2E, 0x79, 0x76, 0xBF, 0xD9, 0x5E, - 0x48, 0xFC, 0x17, 0x15, 0x8E, 0xA9, 0xC4, 0x78, 0xEF, 0xAD, 0x57, 0x6E, 0x38, 0x7A, 0x6D, 0xF2, - 0x47, 0x6B, 0xDB, 0xED, 0x3F, 0x37, 0xF8, 0x6C, 0x8E, 0xA1, 0xAF, 0x34, 0x7F, 0x81, 0x3E, 0x14, - 0x87, 0x45, 0xD1, 0x23, 0x8F, 0xED, 0x92, 0x28, 0x67, 0x77, 0x00, 0xB3, 0x1C, 0x63, 0xCD, 0x93, - 0xD4, 0xFF, 0x00, 0x75, 0x7A, 0x01, 0xEC, 0x30, 0x78, 0x2B, 0xAD, 0x4F, 0xED, 0xB7, 0x12, 0x4D, - 0x34, 0x8D, 0x34, 0xD2, 0x36, 0xE7, 0x76, 0x39, 0x66, 0x3E, 0xA6, 0xB9, 0x6B, 0xCF, 0x15, 0x4D, - 0xA8, 0xDE, 0x49, 0x71, 0x3C, 0xAD, 0x2C, 0xD3, 0x31, 0x67, 0x72, 0x7A, 0x9A, 0x8C, 0xEB, 0x98, - 0xFE, 0x23, 0xF9, 0xD7, 0x97, 0x47, 0x07, 0xCB, 0x79, 0xC9, 0xDE, 0x4F, 0x56, 0xFB, 0x9E, 0x9D, - 0x4C, 0x42, 0x93, 0x51, 0x8E, 0x89, 0x6C, 0x74, 0xE6, 0xEE, 0x31, 0xDF, 0xF0, 0xA4, 0xFB, 0x74, - 0x7F, 0xFD, 0x6A, 0xE5, 0xDB, 0x5D, 0xCF, 0xF1, 0x54, 0x72, 0x6B, 0x98, 0x1F, 0x7A, 0xB6, 0x78, - 0x7E, 0xAC, 0x88, 0x54, 0xD6, 0xC7, 0xDA, 0x1F, 0xB1, 0x17, 0xEC, 0x89, 0xA5, 0xF8, 0xDB, 0xC3, - 0xD1, 0xF8, 0xCB, 0xC5, 0x56, 0xA2, 0xFA, 0xD6, 0xE2, 0x42, 0x34, 0xBD, 0x3E, 0x65, 0xCC, 0x2E, - 0x8A, 0xC5, 0x4C, 0xD2, 0xAF, 0xF1, 0x82, 0xC0, 0x85, 0x43, 0xF2, 0xE0, 0x6E, 0x21, 0xB7, 0x2E, - 0xDF, 0xAF, 0x2C, 0xF4, 0x9B, 0x5B, 0x1D, 0x35, 0x2D, 0x21, 0xB6, 0xB7, 0x86, 0xD6, 0x34, 0xD8, - 0xB0, 0xA4, 0x61, 0x63, 0x55, 0xF4, 0x0A, 0x38, 0xC7, 0xB5, 0x71, 0xBF, 0xB3, 0x05, 0xED, 0xBD, - 0xFF, 0x00, 0xEC, 0xE7, 0xE0, 0x59, 0x2D, 0x48, 0xFB, 0x3B, 0x68, 0x36, 0x41, 0x39, 0xC9, 0x5C, - 0x40, 0x80, 0x83, 0xEE, 0x08, 0x20, 0xFB, 0x83, 0x5D, 0xE8, 0x19, 0xFE, 0x95, 0xF8, 0x3E, 0x71, - 0x98, 0x56, 0xC4, 0xE2, 0xE7, 0x3A, 0xAF, 0xAB, 0x49, 0x74, 0x49, 0x74, 0x3F, 0x5C, 0xCA, 0xF0, - 0x74, 0xE8, 0x61, 0xE2, 0xA0, 0xB5, 0x69, 0x36, 0xFB, 0x9F, 0x2E, 0x7E, 0xD9, 0x7F, 0xB1, 0x96, - 0x8D, 0xA9, 0xF8, 0x3B, 0x51, 0xF1, 0x67, 0x84, 0xF4, 0xF8, 0x74, 0xBD, 0x5B, 0x4B, 0x46, 0xBA, - 0xBB, 0xB3, 0xB5, 0x8C, 0x47, 0x05, 0xFC, 0x4A, 0x33, 0x21, 0x11, 0x8E, 0x16, 0x50, 0x01, 0x60, - 0x54, 0x7C, 0xF8, 0x20, 0x82, 0x58, 0x30, 0xF8, 0x8C, 0xDF, 0x46, 0x4E, 0x77, 0x70, 0xDC, 0xD7, - 0xEB, 0xAD, 0xFD, 0xD4, 0x36, 0x96, 0x13, 0x4B, 0x70, 0xC9, 0x1C, 0x11, 0xA3, 0x34, 0xAC, 0xE7, - 0x0A, 0xAA, 0x07, 0x24, 0xE7, 0xB6, 0x2B, 0xF1, 0x77, 0xFB, 0x72, 0x32, 0x9F, 0xBA, 0xDC, 0xB1, - 0x75, 0x40, 0x4F, 0x21, 0x7B, 0x7E, 0x95, 0xF7, 0x7C, 0x13, 0x8B, 0xAD, 0x88, 0xA5, 0x3A, 0x35, - 0x1D, 0xD4, 0x6D, 0x66, 0xF7, 0xD6, 0xFA, 0x7E, 0x07, 0xCC, 0x71, 0x26, 0x1E, 0x9D, 0x1A, 0x91, - 0xA9, 0x05, 0x6E, 0x6B, 0xDF, 0xB6, 0x96, 0x3A, 0xA7, 0xBE, 0x8F, 0x3D, 0x6A, 0x13, 0xA8, 0x46, - 0x0F, 0x5A, 0xE6, 0x1F, 0x5C, 0xF9, 0x7E, 0xF0, 0xA8, 0x9F, 0x5E, 0xC7, 0x7A, 0xFB, 0x6F, 0x62, - 0x7C, 0xEC, 0x6A, 0x74, 0x34, 0x7E, 0x20, 0xF8, 0x3B, 0x43, 0xF8, 0xA1, 0xE1, 0x3B, 0xDD, 0x0B, - 0x5E, 0xB2, 0x8B, 0x50, 0xD2, 0xEF, 0xD7, 0x12, 0xC2, 0xFC, 0x10, 0x47, 0x47, 0x53, 0xD5, 0x5D, - 0x4F, 0x21, 0x87, 0x20, 0xD7, 0xC0, 0x7F, 0x19, 0x7E, 0x16, 0xEA, 0x5F, 0xB3, 0x57, 0x8E, 0xD7, - 0x41, 0xD5, 0x25, 0x6D, 0x43, 0xC3, 0xFA, 0x80, 0x66, 0xD1, 0xB5, 0x59, 0x14, 0x62, 0x78, 0xFA, - 0x18, 0xA5, 0xED, 0xBD, 0x72, 0x03, 0x0E, 0x98, 0x20, 0xFD, 0xD6, 0x18, 0xFB, 0x99, 0xF5, 0xEF, - 0x7A, 0xE4, 0x3E, 0x35, 0xFC, 0x3F, 0xD2, 0xFE, 0x38, 0xFC, 0x3A, 0xBE, 0xF0, 0xEE, 0xAB, 0x85, - 0x8E, 0xE3, 0xF7, 0x96, 0xD7, 0x1B, 0x77, 0x3D, 0x94, 0xEA, 0x0E, 0xC9, 0x97, 0xDC, 0x64, 0x82, - 0x38, 0xDC, 0xAC, 0xCB, 0x91, 0x9A, 0xF7, 0xF2, 0x1C, 0xD6, 0xAE, 0x06, 0xAD, 0xA5, 0xAD, 0x39, - 0x68, 0xD3, 0xDB, 0xEE, 0x3C, 0xCC, 0xDF, 0x2B, 0xA5, 0x8E, 0xA3, 0xCB, 0x25, 0xEF, 0x2D, 0x53, - 0xEA, 0x8F, 0xC7, 0xCF, 0xDB, 0x07, 0xF6, 0x58, 0x8F, 0xC0, 0x2F, 0x37, 0x8A, 0x3C, 0x3D, 0x6E, - 0x57, 0x44, 0x95, 0xB1, 0x7B, 0x6A, 0xA3, 0x3F, 0xD9, 0xD2, 0x31, 0xC2, 0xB2, 0xFF, 0x00, 0xD3, - 0x26, 0x62, 0x14, 0x67, 0xEE, 0xB1, 0x03, 0xA1, 0x51, 0x5F, 0x3B, 0xC9, 0x13, 0x23, 0x32, 0x9D, - 0xB9, 0x07, 0x1C, 0x1C, 0x8F, 0xCC, 0x71, 0x5F, 0xA4, 0xDA, 0x75, 0xB5, 0xCE, 0x8D, 0xAB, 0x6A, - 0xDE, 0x0D, 0xF1, 0x2D, 0xB4, 0x66, 0xFA, 0xC7, 0xCC, 0xB3, 0x9A, 0x19, 0x7E, 0x78, 0xEE, 0x22, - 0xC6, 0x19, 0x79, 0xFB, 0xCA, 0x50, 0xE4, 0x1F, 0xE2, 0x46, 0x07, 0xEB, 0xF1, 0x1F, 0xED, 0x35, - 0xF0, 0x2A, 0x4F, 0x81, 0xFF, 0x00, 0x11, 0x24, 0xB5, 0x87, 0xCD, 0x93, 0x47, 0xD4, 0x14, 0xDC, - 0x69, 0xB3, 0xBF, 0x3B, 0x93, 0x38, 0x31, 0xB1, 0xE9, 0xBD, 0x0F, 0xCA, 0x7D, 0x46, 0xD6, 0xC0, - 0x0E, 0x31, 0xF9, 0xF7, 0x89, 0xBC, 0x13, 0x4F, 0x05, 0x25, 0x9B, 0xE5, 0xCB, 0xF7, 0x35, 0x37, - 0x4B, 0x68, 0xC9, 0xFE, 0x8F, 0xF0, 0x67, 0xD2, 0xF0, 0x1F, 0x13, 0xD5, 0xC4, 0x73, 0x65, 0x59, - 0x83, 0xFD, 0xF5, 0x3D, 0x9F, 0xF3, 0xC7, 0xFC, 0xD7, 0x5F, 0x2D, 0x77, 0xB9, 0xE5, 0xF4, 0x50, - 0x46, 0x0D, 0x15, 0xF8, 0xE1, 0xFA, 0x50, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, - 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, - 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0xF0, - 0xB9, 0x38, 0xEB, 0xE9, 0x5F, 0x76, 0xFE, 0xC1, 0x5F, 0x02, 0x26, 0xF0, 0xF7, 0x81, 0x2C, 0xEE, - 0x96, 0xDC, 0xC9, 0xE2, 0x0F, 0x1A, 0x3C, 0x65, 0x01, 0xFF, 0x00, 0x96, 0x56, 0xE4, 0xFE, 0xE5, - 0x33, 0xD8, 0x36, 0x4C, 0xAC, 0x73, 0x82, 0x0A, 0x64, 0x02, 0x95, 0xF1, 0xFF, 0x00, 0xC1, 0x2F, - 0x87, 0xAD, 0xF1, 0x57, 0xE2, 0xA6, 0x8B, 0xA1, 0x66, 0x45, 0x8F, 0x50, 0xB9, 0x51, 0x3B, 0x22, - 0xE5, 0x92, 0x15, 0xCB, 0xCA, 0xC3, 0xFD, 0xD8, 0xD5, 0x8F, 0xE1, 0x5F, 0xAF, 0x1F, 0xB2, 0x17, - 0x82, 0x61, 0xBA, 0xF1, 0x25, 0xE6, 0xB1, 0xE4, 0x47, 0x15, 0xAE, 0x8F, 0x08, 0xB7, 0xB4, 0x45, - 0x5C, 0x2C, 0x6E, 0xE0, 0x8F, 0x97, 0xD9, 0x63, 0x05, 0x71, 0xFE, 0xD8, 0xAF, 0xDA, 0x3C, 0x25, - 0xC9, 0xE0, 0xA7, 0x5B, 0x3B, 0xAD, 0x1D, 0x29, 0x69, 0x1B, 0xFF, 0x00, 0x33, 0xFF, 0x00, 0x25, - 0x6F, 0xBC, 0xFC, 0xAB, 0xC4, 0xCC, 0xD2, 0x4E, 0x14, 0xB2, 0x8A, 0x2E, 0xCE, 0xAB, 0xBC, 0xBF, - 0xC2, 0xBA, 0x7C, 0xDF, 0xE4, 0x7D, 0x4F, 0xFB, 0x33, 0x7C, 0x38, 0xB3, 0xF0, 0x3E, 0x9D, 0x69, - 0x1C, 0x2A, 0x0D, 0xBE, 0x8F, 0x1F, 0x97, 0x1B, 0x91, 0x8F, 0x3A, 0x76, 0xE5, 0xE4, 0x3E, 0xFC, - 0x93, 0xED, 0xB9, 0x71, 0xD0, 0x52, 0x78, 0xFB, 0xE2, 0x39, 0xF1, 0x67, 0x88, 0x1E, 0x44, 0x93, - 0x36, 0xB0, 0x66, 0x3B, 0x71, 0xD8, 0xAF, 0x76, 0xFF, 0x00, 0x81, 0x1E, 0x7E, 0x98, 0x1D, 0xAA, - 0x3F, 0x17, 0xF8, 0xA0, 0x78, 0x2B, 0xE1, 0xCA, 0xDA, 0xC6, 0xDB, 0x2E, 0x2F, 0x07, 0x93, 0xEE, - 0x0B, 0x72, 0xE7, 0xF0, 0x19, 0x1F, 0x88, 0xAF, 0x33, 0x8F, 0x5E, 0xC7, 0xF1, 0x0A, 0xFB, 0x6A, - 0x74, 0xE5, 0x89, 0xAF, 0x2C, 0x55, 0x4D, 0x5F, 0x43, 0xE6, 0xA4, 0xA3, 0x87, 0xA1, 0x1C, 0x3C, - 0x3E, 0x67, 0x74, 0xBA, 0xEE, 0x7B, 0x8A, 0x1B, 0x5D, 0xDA, 0x3A, 0x8F, 0xCE, 0xB8, 0xA5, 0xF1, - 0x06, 0x3F, 0x8A, 0x86, 0xD7, 0xF2, 0x7E, 0xF5, 0x75, 0x3C, 0x3D, 0xCE, 0x3E, 0x77, 0x73, 0xB0, - 0x6D, 0x77, 0x07, 0xEF, 0x54, 0x72, 0x6B, 0x99, 0xEF, 0xDA, 0xB8, 0xE7, 0xD7, 0xBF, 0xDA, 0xA8, - 0xDB, 0x5D, 0xDA, 0x3E, 0xF0, 0xA9, 0xF6, 0x26, 0xD1, 0xAA, 0x7D, 0xBD, 0xFB, 0x01, 0x7F, 0xC1, - 0x43, 0xF4, 0xEF, 0x82, 0x5A, 0x68, 0xF0, 0x6F, 0x8D, 0x1E, 0x64, 0xF0, 0xCF, 0x98, 0xF2, 0xE9, - 0xFA, 0x8C, 0x71, 0x34, 0xAD, 0xA6, 0x33, 0x9D, 0xCF, 0x14, 0x88, 0xB9, 0x66, 0x85, 0x98, 0x96, - 0x0C, 0xA0, 0xB2, 0xB3, 0x10, 0x41, 0x52, 0x0C, 0x7F, 0x70, 0x59, 0xFE, 0xD4, 0xDF, 0x0D, 0x6F, - 0xF4, 0x66, 0xD4, 0x21, 0xF1, 0xFF, 0x00, 0x83, 0x5A, 0xC5, 0x46, 0x5A, 0x7F, 0xED, 0x9B, 0x70, - 0x89, 0xEC, 0xC4, 0xBF, 0xCA, 0x47, 0x70, 0x70, 0x45, 0x7E, 0x1D, 0xBE, 0xBF, 0x8F, 0xE2, 0xA8, - 0xA4, 0xD6, 0xD4, 0x9C, 0xF1, 0xBB, 0xD7, 0x15, 0xF0, 0xF9, 0xB7, 0x00, 0xE1, 0x71, 0x95, 0xDD, - 0x7A, 0x72, 0x70, 0x72, 0xD5, 0xDB, 0x55, 0x7E, 0xFE, 0x47, 0xD6, 0x65, 0xBC, 0x55, 0x5F, 0x0F, - 0x49, 0x51, 0x92, 0x52, 0x4B, 0x6B, 0xEE, 0x97, 0x63, 0xF4, 0x3F, 0xF6, 0xF6, 0xFF, 0x00, 0x82, - 0x97, 0x68, 0x7E, 0x26, 0xF0, 0x6E, 0xA1, 0xE0, 0x7F, 0x87, 0xB7, 0x7F, 0xDA, 0x0B, 0xAA, 0x23, - 0x5B, 0x6A, 0xBA, 0xCA, 0x82, 0x90, 0x2C, 0x07, 0x21, 0xE1, 0xB7, 0x27, 0x06, 0x42, 0xE3, 0x21, - 0xA4, 0xC6, 0xC0, 0x84, 0xED, 0x2C, 0xCD, 0xB9, 0x3E, 0x1C, 0x7D, 0x77, 0x8F, 0xBD, 0xFA, 0xD7, - 0x1C, 0xDE, 0x20, 0xC1, 0x3F, 0x35, 0x47, 0x26, 0xBD, 0x9C, 0xFC, 0xD5, 0xEF, 0xE4, 0xFC, 0x3F, - 0x87, 0xCB, 0xA8, 0xFB, 0x1C, 0x3F, 0x5D, 0xDB, 0xDD, 0xBF, 0x33, 0xCF, 0xC7, 0xE6, 0x95, 0xB1, - 0x75, 0x7D, 0xAD, 0x5E, 0x9B, 0x2E, 0x89, 0x1D, 0x83, 0x6B, 0xDC, 0xFD, 0xEA, 0x89, 0xF5, 0xDE, - 0x9F, 0x35, 0x71, 0xCF, 0xAF, 0xE3, 0xF8, 0xAA, 0x39, 0x35, 0xDC, 0xFF, 0x00, 0x15, 0x7A, 0x7E, - 0xC0, 0xC2, 0x35, 0x0E, 0xBD, 0xB5, 0xEC, 0x7F, 0x15, 0x44, 0xFA, 0xF6, 0x7F, 0x8B, 0xBD, 0x71, - 0xFF, 0x00, 0xDB, 0xDF, 0xED, 0x54, 0x52, 0x6B, 0xDC, 0x1F, 0x9B, 0xBD, 0x4F, 0xB1, 0xD6, 0xE6, - 0xF1, 0xA9, 0x6D, 0x51, 0xE5, 0x1F, 0xB7, 0x47, 0x81, 0x4C, 0xA9, 0xA6, 0x78, 0xE3, 0x4D, 0x1E, - 0x5D, 0xE6, 0x9E, 0xC9, 0x6B, 0x7E, 0xC8, 0x3E, 0xF2, 0x67, 0xF7, 0x32, 0x1F, 0xF7, 0x5B, 0xE4, - 0x39, 0xEA, 0x1D, 0x07, 0x41, 0x5E, 0x0B, 0xF1, 0xF3, 0xE1, 0xD4, 0x3F, 0xB4, 0x37, 0xC0, 0xC9, - 0x92, 0xCE, 0x11, 0x26, 0xA9, 0x6E, 0xA6, 0xF3, 0x4F, 0x00, 0x65, 0xD6, 0x64, 0xE1, 0xE2, 0x1D, - 0xCE, 0xF0, 0x19, 0x00, 0xE0, 0x16, 0xF2, 0xC9, 0xE8, 0x2B, 0xEB, 0xEF, 0x15, 0xC1, 0x6D, 0xE3, - 0x1F, 0x0C, 0xEA, 0x1A, 0x4D, 0xDB, 0x7F, 0xA3, 0xEA, 0x56, 0xEF, 0x6E, 0xE7, 0x19, 0xD9, 0xB8, - 0x60, 0x30, 0xF7, 0x53, 0x82, 0x3D, 0xC0, 0xAF, 0x94, 0xBE, 0x13, 0x5F, 0xDC, 0x68, 0x7A, 0xCE, - 0xA3, 0xA1, 0xDD, 0x1F, 0x2E, 0x68, 0x64, 0x62, 0x13, 0x3F, 0x72, 0x54, 0x3B, 0x1C, 0x7E, 0x83, - 0xFE, 0xF9, 0x35, 0xF5, 0x99, 0x5E, 0x1E, 0x9E, 0x63, 0x97, 0xD4, 0xCA, 0xF1, 0x3B, 0x49, 0x35, - 0xFE, 0x5F, 0x73, 0x3E, 0x63, 0x3C, 0x75, 0x30, 0x75, 0xE9, 0xE6, 0x98, 0x7D, 0x27, 0x4D, 0xA9, - 0x7A, 0xAE, 0xA9, 0xFA, 0xAD, 0xFE, 0x67, 0xE7, 0x93, 0x44, 0x63, 0xFB, 0xDD, 0x8E, 0x08, 0xA8, - 0xEB, 0xD5, 0xBF, 0x6B, 0xEF, 0x87, 0x03, 0xE1, 0xBF, 0xC7, 0x2D, 0x5A, 0x38, 0x21, 0xF2, 0x6C, - 0x35, 0x5C, 0x6A, 0x36, 0xAA, 0x06, 0xD5, 0x55, 0x97, 0x25, 0x94, 0x0E, 0xCA, 0xB2, 0x07, 0x51, - 0xEC, 0x82, 0xBC, 0xB7, 0x18, 0xAF, 0xE4, 0x5C, 0xD3, 0x2F, 0xA9, 0x82, 0xC5, 0xD4, 0xC2, 0x55, - 0xDE, 0x12, 0x71, 0x7F, 0x26, 0x7F, 0x40, 0xE0, 0x31, 0x90, 0xC5, 0xE1, 0xA9, 0xE2, 0xA9, 0x6D, - 0x34, 0x9A, 0xF9, 0xAB, 0x91, 0xD1, 0x45, 0x15, 0xE7, 0x9D, 0x41, 0x45, 0x14, 0x50, 0x01, 0x45, - 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, - 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x07, 0xD2, - 0xDF, 0xF0, 0x4E, 0x8F, 0x08, 0xAE, 0xA3, 0xE3, 0x5D, 0x7F, 0x5C, 0x91, 0x15, 0x97, 0x4A, 0xB3, - 0x5B, 0x58, 0xD8, 0xFF, 0x00, 0x04, 0x93, 0x31, 0x20, 0x8F, 0xF8, 0x04, 0x72, 0x0F, 0xF8, 0x15, - 0x7E, 0xAE, 0x7E, 0xCD, 0x1A, 0x02, 0xF8, 0x7B, 0xE1, 0x46, 0x98, 0xEC, 0xBB, 0x5F, 0x50, 0x91, - 0xEF, 0x65, 0xF7, 0xDC, 0xD8, 0x5F, 0xCD, 0x11, 0x2B, 0xF3, 0x7F, 0xFE, 0x09, 0xE7, 0xA1, 0x2E, - 0x9F, 0xF0, 0x6B, 0x54, 0xD4, 0x30, 0x04, 0x97, 0xFA, 0xA3, 0xC4, 0x7D, 0x4A, 0xC5, 0x14, 0x64, - 0x1F, 0xA6, 0x65, 0x61, 0xF5, 0x06, 0xBF, 0x52, 0x3C, 0x1B, 0x60, 0x34, 0xBF, 0x0B, 0x69, 0x56, - 0x20, 0x63, 0xEC, 0xB6, 0x90, 0xC0, 0x3E, 0xAA, 0x8A, 0xBF, 0xD2, 0xBF, 0xA7, 0x38, 0x5B, 0x06, - 0xB0, 0xDC, 0x2B, 0x86, 0xA7, 0x1D, 0x1D, 0x49, 0x39, 0x3F, 0xBE, 0xDF, 0x95, 0x8F, 0xE7, 0xFE, - 0x21, 0xC4, 0x3C, 0x47, 0x13, 0x57, 0x9C, 0xB5, 0x54, 0xD4, 0x62, 0xBE, 0xE4, 0xDF, 0xE3, 0x73, - 0x97, 0xF8, 0xCD, 0xE3, 0x5F, 0xB6, 0x78, 0x9E, 0x3B, 0x51, 0x26, 0xE4, 0xB1, 0x88, 0x64, 0x67, - 0xF8, 0xDF, 0x0C, 0x7F, 0xF1, 0xDD, 0x95, 0xC9, 0xAF, 0x88, 0x71, 0xFC, 0x55, 0xE2, 0xDE, 0x3A, - 0xF8, 0xB1, 0xAA, 0x5C, 0xFC, 0x4A, 0xD7, 0xAE, 0xAD, 0xEF, 0x19, 0xAD, 0xA5, 0xD4, 0x26, 0xF2, - 0xA3, 0x70, 0x1D, 0x44, 0x61, 0xC8, 0x40, 0x33, 0xD3, 0xE5, 0x03, 0xA6, 0x2A, 0x5D, 0x3F, 0xE3, - 0x3C, 0xC0, 0x01, 0x75, 0x68, 0x8C, 0x7B, 0xB4, 0x4F, 0xB7, 0xF4, 0x39, 0xFE, 0x75, 0xF6, 0x74, - 0xB2, 0x0A, 0xD4, 0xE8, 0xC5, 0x42, 0xCF, 0x4B, 0x9E, 0x1D, 0x4C, 0xF2, 0x84, 0xEA, 0x37, 0x3B, - 0xAF, 0xEB, 0xC8, 0xF6, 0x61, 0xE2, 0x3D, 0xA3, 0xEF, 0x52, 0xB7, 0x88, 0xBF, 0xDA, 0xCD, 0x79, - 0x75, 0xBF, 0xC5, 0x9D, 0x3E, 0x61, 0xF3, 0x49, 0x34, 0x27, 0xFE, 0x9A, 0x27, 0xFF, 0x00, 0x13, - 0x9A, 0xBD, 0x0F, 0x8E, 0xAC, 0xEE, 0x07, 0xC9, 0x79, 0x01, 0xF6, 0xF3, 0x00, 0x3F, 0x91, 0xAC, - 0xA7, 0x97, 0xD5, 0x8F, 0xC5, 0x16, 0x6B, 0x4F, 0x19, 0x46, 0x6E, 0xF1, 0x92, 0x67, 0xA0, 0x3F, - 0x88, 0x3F, 0xDA, 0xFC, 0xEB, 0xEB, 0xFF, 0x00, 0xD9, 0x76, 0xDF, 0xF6, 0x44, 0xF8, 0x8D, 0xE1, - 0xCF, 0x0D, 0xE8, 0xDE, 0x34, 0xBC, 0xF1, 0x46, 0x85, 0xE3, 0x0B, 0xAB, 0x58, 0x20, 0xBE, 0xB9, - 0xBD, 0xBA, 0xB8, 0xB6, 0xD3, 0xE7, 0xBD, 0x20, 0x09, 0x36, 0x4A, 0x85, 0xA3, 0x8D, 0x0B, 0x67, - 0x06, 0x42, 0x8B, 0x8C, 0x74, 0x38, 0x15, 0xF0, 0x40, 0xF1, 0x0E, 0xF5, 0xCA, 0xB6, 0xE1, 0xEA, - 0x0E, 0x6A, 0x39, 0xB5, 0xE5, 0x91, 0x19, 0x5B, 0xE6, 0x56, 0x18, 0x39, 0xEE, 0x2B, 0xC5, 0xCD, - 0x32, 0x79, 0x62, 0xE9, 0x72, 0xC6, 0xAC, 0xE9, 0xB5, 0xD6, 0x2E, 0xCF, 0xE7, 0xDD, 0x79, 0x1E, - 0xBE, 0x5D, 0x98, 0x2A, 0x15, 0x39, 0xB9, 0x23, 0x34, 0xFA, 0x3D, 0x7E, 0xE3, 0xF7, 0x08, 0x7F, - 0xC1, 0x19, 0x3E, 0x07, 0xB0, 0xCF, 0xF6, 0x6F, 0x88, 0xB0, 0x7F, 0xEA, 0x37, 0x3F, 0xF8, 0xD3, - 0x7F, 0xE1, 0xCC, 0x1F, 0x03, 0x49, 0xFF, 0x00, 0x90, 0x6F, 0x88, 0xBF, 0xF0, 0x77, 0x3F, 0xF8, - 0xD7, 0x69, 0xFF, 0x00, 0x04, 0xC8, 0xF8, 0x91, 0x75, 0xF1, 0x67, 0xF6, 0x0E, 0xF8, 0x65, 0xAC, - 0x5F, 0x4D, 0x25, 0xC5, 0xD7, 0xF6, 0x42, 0xD8, 0xCB, 0x2C, 0x8C, 0x59, 0xE5, 0x6B, 0x57, 0x7B, - 0x6D, 0xCC, 0x4F, 0x25, 0x8F, 0x93, 0x92, 0x4F, 0x24, 0x92, 0x6B, 0xDF, 0x02, 0xED, 0x61, 0x5F, - 0xCE, 0x38, 0xBC, 0xF3, 0x36, 0xA1, 0x5E, 0x78, 0x79, 0xE2, 0x27, 0x78, 0xB6, 0xBE, 0x27, 0xD1, - 0xD8, 0xFD, 0x9B, 0x0B, 0x96, 0xE0, 0x2B, 0x52, 0x8D, 0x58, 0xD2, 0x8D, 0xA4, 0x93, 0xDB, 0xB9, - 0xF1, 0x0F, 0xC7, 0x2F, 0xF8, 0x24, 0x6F, 0xC1, 0x9F, 0x00, 0x7C, 0x17, 0xF1, 0x76, 0xBB, 0xA7, - 0xE9, 0xBA, 0xF2, 0xDF, 0x68, 0xDA, 0x35, 0xE5, 0xFD, 0xB3, 0x3E, 0xB1, 0x3B, 0x2A, 0xCB, 0x14, - 0x0E, 0xE8, 0x48, 0xCE, 0x08, 0x0C, 0xA3, 0x83, 0xD6, 0xBF, 0x24, 0x57, 0xC4, 0x25, 0xE1, 0x56, - 0xDD, 0xF7, 0x80, 0xAF, 0xE8, 0x2B, 0xF6, 0xB5, 0x7F, 0x2F, 0xF6, 0x56, 0xF8, 0x94, 0xDF, 0xDD, - 0xF0, 0xB6, 0xA6, 0x7F, 0xF2, 0x52, 0x5A, 0xFE, 0x6F, 0xA2, 0xF1, 0x07, 0xEE, 0x13, 0xE6, 0xE8, - 0xA2, 0xBF, 0x4E, 0xF0, 0xDF, 0x19, 0x89, 0xC6, 0xD2, 0xAC, 0xF1, 0x53, 0x94, 0xF9, 0x5C, 0x6D, - 0x76, 0xDD, 0xB7, 0x3E, 0x2F, 0x8B, 0xB0, 0xB4, 0x30, 0xF5, 0x29, 0xAA, 0x11, 0x51, 0xBD, 0xEF, - 0x6D, 0x3A, 0xD8, 0xEC, 0xDF, 0x5F, 0xE7, 0xEF, 0x54, 0x6F, 0xAF, 0x7F, 0xB5, 0xFA, 0xD7, 0x1B, - 0xFD, 0xB8, 0xC7, 0xBB, 0x1A, 0xA7, 0x79, 0xE3, 0x3B, 0x5B, 0x30, 0x7C, 0xEB, 0xBB, 0x78, 0x7F, - 0xDF, 0x95, 0x57, 0xF9, 0x9A, 0xFD, 0x3B, 0xEA, 0xD2, 0x7B, 0x23, 0xE4, 0xD5, 0x54, 0x77, 0x27, - 0x5F, 0xC8, 0xFB, 0xD5, 0x14, 0x9A, 0xF7, 0x1F, 0x7A, 0xBC, 0xD2, 0xFF, 0x00, 0xE3, 0x06, 0x8F, - 0x62, 0x0E, 0xEB, 0xE5, 0x91, 0xBB, 0x08, 0x91, 0x9F, 0x3F, 0x8E, 0x31, 0xFA, 0xD6, 0x06, 0xA9, - 0xFB, 0x43, 0x5B, 0xC6, 0x3F, 0xD1, 0x6C, 0x6E, 0xA6, 0x3D, 0x33, 0x2B, 0x88, 0xC7, 0xE9, 0xBB, - 0xFA, 0x56, 0x91, 0xCB, 0xAA, 0xCB, 0x68, 0x8F, 0xEB, 0x94, 0xE3, 0xBB, 0x3D, 0x95, 0xB5, 0xEF, - 0xF6, 0xAB, 0xE7, 0xCF, 0x89, 0xC7, 0xFE, 0x11, 0x6F, 0x8F, 0xAD, 0x78, 0xBF, 0x24, 0x37, 0x92, - 0xC5, 0x72, 0x7E, 0x92, 0x0D, 0x92, 0x1F, 0xC5, 0xB7, 0x9A, 0xA5, 0xAE, 0xFC, 0x73, 0xD7, 0xEF, - 0xD5, 0x96, 0x09, 0x20, 0xB1, 0x5F, 0xFA, 0x63, 0x1E, 0x5B, 0xFE, 0xFA, 0x6C, 0xFE, 0x63, 0x15, - 0xC5, 0xEA, 0xFA, 0xA5, 0xD6, 0xAD, 0x76, 0x66, 0xBA, 0xB9, 0x9E, 0xEA, 0x63, 0x81, 0xE6, 0x4B, - 0x21, 0x76, 0xFC, 0xCD, 0x7B, 0x19, 0x6E, 0x5F, 0x3C, 0x3C, 0xFD, 0xA3, 0x39, 0x31, 0x98, 0x88, - 0x62, 0x29, 0xBA, 0x7D, 0x2D, 0x6F, 0x91, 0x91, 0xFF, 0x00, 0x05, 0x1B, 0xF0, 0x70, 0xB8, 0xF0, - 0x9F, 0x87, 0x75, 0xF8, 0xD3, 0xE6, 0xB3, 0xB9, 0x92, 0xC2, 0x66, 0xC7, 0x24, 0x48, 0xBE, 0x64, - 0x63, 0xF0, 0x29, 0x2F, 0xFD, 0xF5, 0xF9, 0xFC, 0x90, 0x0E, 0x14, 0x6E, 0xFF, 0x00, 0xF5, 0x57, - 0xDE, 0x1F, 0xB6, 0x26, 0x9F, 0xFF, 0x00, 0x09, 0x27, 0xEC, 0xC1, 0xAD, 0x5D, 0x30, 0xFD, 0xE5, - 0xA8, 0xB5, 0xBC, 0x8C, 0x75, 0xC1, 0x32, 0xC6, 0x84, 0x8F, 0xF8, 0x0C, 0xAD, 0xF8, 0x66, 0xBE, - 0x0E, 0xDD, 0xF2, 0xFF, 0x00, 0x4A, 0xFE, 0x77, 0xF1, 0x7B, 0x02, 0xA8, 0x71, 0x0C, 0xAA, 0x47, - 0x4F, 0x69, 0x18, 0xCB, 0xF0, 0xB7, 0xE8, 0x7E, 0x85, 0xE1, 0x7E, 0x31, 0xD6, 0xC8, 0xE3, 0x4E, - 0x5B, 0xD3, 0x94, 0xA3, 0xF8, 0xDF, 0xF5, 0x23, 0xA2, 0x8A, 0x2B, 0xF2, 0xD3, 0xF4, 0x20, 0xA2, - 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, - 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, - 0x8A, 0x28, 0x03, 0xEE, 0x5F, 0xD8, 0x48, 0x81, 0xFB, 0x3B, 0xC5, 0xB5, 0x55, 0x7F, 0xE2, 0x61, - 0x71, 0xB8, 0x8F, 0xE2, 0x38, 0x8F, 0x93, 0xF8, 0x60, 0x7E, 0x15, 0xFA, 0x7B, 0x71, 0x76, 0xBA, - 0x74, 0x52, 0x4F, 0xFC, 0x36, 0xEA, 0x64, 0xFC, 0x14, 0x67, 0xFA, 0x57, 0xE6, 0x07, 0xEC, 0x13, - 0x74, 0x2E, 0xFF, 0x00, 0x67, 0xCD, 0x8A, 0xC0, 0x9B, 0x7D, 0x52, 0xE2, 0x3D, 0xB8, 0xE4, 0x0F, - 0x2E, 0x06, 0xCF, 0x4E, 0xFB, 0xCF, 0xA9, 0xE3, 0xD3, 0x15, 0xFA, 0x4B, 0x3E, 0xA3, 0xFD, 0xAD, - 0xF0, 0xFA, 0x5B, 0x94, 0x39, 0xFB, 0x56, 0x96, 0xD2, 0xAF, 0xBE, 0xE8, 0x49, 0x1F, 0xCE, 0xBF, - 0xAB, 0xB2, 0xBB, 0x4B, 0x20, 0xCB, 0xDA, 0xDB, 0x96, 0xDF, 0x97, 0xF9, 0x1F, 0xCE, 0xB8, 0xCB, - 0xC3, 0x3D, 0xC7, 0x45, 0xEE, 0xE5, 0x73, 0xE1, 0x78, 0x6E, 0x37, 0xAE, 0xE6, 0xCB, 0x33, 0x72, - 0x4F, 0xBD, 0x4B, 0x1C, 0x80, 0x35, 0x50, 0xB7, 0x97, 0xE4, 0x5F, 0xA5, 0x4C, 0x92, 0xFA, 0x57, - 0xEB, 0x14, 0xE5, 0xEE, 0xD9, 0x9F, 0x01, 0x52, 0x1E, 0xF3, 0x2E, 0x09, 0xF0, 0x29, 0xA2, 0x66, - 0x2B, 0x55, 0xCC, 0xE5, 0x45, 0x02, 0x72, 0xD4, 0xEE, 0x8C, 0xF9, 0x09, 0xCC, 0xCC, 0xA7, 0x20, - 0xED, 0xF7, 0xA7, 0xFF, 0x00, 0x6B, 0xDD, 0x45, 0xF7, 0x2E, 0x6E, 0x17, 0xE9, 0x21, 0x15, 0x4D, - 0xA4, 0xCF, 0x53, 0x51, 0xBC, 0x95, 0x32, 0x51, 0x66, 0xB0, 0xE6, 0x5B, 0x1F, 0xD1, 0x57, 0xFC, - 0x10, 0xC6, 0xE5, 0xAE, 0xBF, 0xE0, 0x96, 0xBF, 0x0B, 0xE4, 0x91, 0x9A, 0x49, 0x08, 0xD5, 0x32, - 0xCC, 0x72, 0x4F, 0xFC, 0x4D, 0x6F, 0x07, 0x5A, 0xFA, 0xE0, 0x8C, 0xFF, 0x00, 0x3A, 0xF9, 0x17, - 0xFE, 0x08, 0x47, 0xCF, 0xFC, 0x12, 0xB7, 0xE1, 0x6F, 0xD3, 0x55, 0xFF, 0x00, 0xD3, 0xAD, 0xE5, - 0x7D, 0x79, 0x5F, 0xC2, 0xBC, 0x4F, 0xFF, 0x00, 0x23, 0x8C, 0x5F, 0xFD, 0x7C, 0x9F, 0xFE, 0x94, - 0xCF, 0xEB, 0x1E, 0x1F, 0xFF, 0x00, 0x91, 0x66, 0x1F, 0xFC, 0x11, 0xFF, 0x00, 0xD2, 0x51, 0xE5, - 0xFF, 0x00, 0xB6, 0x9C, 0xA6, 0x3F, 0xD8, 0xF7, 0xE2, 0xA3, 0x29, 0x2A, 0xCB, 0xE1, 0x0D, 0x58, - 0x82, 0x3A, 0x83, 0xF6, 0x39, 0xAB, 0xF9, 0x5D, 0x3A, 0xFD, 0xF3, 0xC0, 0xB9, 0xBD, 0xBC, 0x23, - 0x03, 0x8F, 0x39, 0xB1, 0xFC, 0xEB, 0xFA, 0x9F, 0xFD, 0xB5, 0xFF, 0x00, 0xE4, 0xCE, 0x3E, 0x2B, - 0xFF, 0x00, 0xD8, 0x9F, 0xAB, 0xFF, 0x00, 0xE9, 0x14, 0xD5, 0xFC, 0xA8, 0x2B, 0xE2, 0x05, 0xFA, - 0x0A, 0xFD, 0xA7, 0xC1, 0x18, 0xA7, 0x47, 0x15, 0x7E, 0xF1, 0xFC, 0x99, 0xF9, 0xBF, 0x89, 0x8D, - 0xAA, 0xD4, 0x6D, 0xD9, 0xFE, 0x68, 0x5B, 0xBB, 0xB9, 0x2E, 0x8F, 0xEF, 0x24, 0x92, 0x4F, 0xF7, - 0x98, 0x9A, 0xA7, 0x37, 0xDE, 0xA9, 0x64, 0x7A, 0xAA, 0xEF, 0xB4, 0x67, 0xF2, 0xAF, 0xDD, 0xAC, - 0x91, 0xF9, 0xA5, 0x3B, 0x91, 0xDC, 0x38, 0xC7, 0x4A, 0xAB, 0x33, 0x11, 0xF8, 0x54, 0x8E, 0x72, - 0xDF, 0x4A, 0xAF, 0x2B, 0x56, 0x72, 0x3B, 0x29, 0xC4, 0x86, 0x53, 0x96, 0xAA, 0xD3, 0x3F, 0xCD, - 0x53, 0xC8, 0xFD, 0x7F, 0x2A, 0xA9, 0x2B, 0x13, 0x91, 0xE9, 0xCD, 0x66, 0xF4, 0x3B, 0xE9, 0xC6, - 0xE7, 0x75, 0xFB, 0x43, 0xB8, 0xFF, 0x00, 0x86, 0x4C, 0xD6, 0xF7, 0x2A, 0xBE, 0xED, 0x2A, 0xD7, - 0x82, 0x33, 0xCE, 0xE8, 0x79, 0xFC, 0x0F, 0x3F, 0x51, 0x5F, 0x01, 0x7D, 0xDF, 0xFF, 0x00, 0x5D, - 0x7D, 0xFF, 0x00, 0xFB, 0x50, 0x49, 0xFD, 0x8B, 0xFB, 0x2A, 0xF8, 0x89, 0x5B, 0x6A, 0xB4, 0x76, - 0x36, 0xD0, 0x00, 0x46, 0x72, 0x4C, 0xD0, 0xC6, 0x40, 0xF7, 0x00, 0x93, 0xCF, 0xA7, 0xAD, 0x7E, - 0x7F, 0xB0, 0xDA, 0x6B, 0xF9, 0xD7, 0xC6, 0xD6, 0xBF, 0xB5, 0xE8, 0x47, 0xAA, 0xA4, 0xBF, 0xF4, - 0xA9, 0x1F, 0x7B, 0xE1, 0x3E, 0xB9, 0x6D, 0x69, 0xF4, 0x75, 0x65, 0xF9, 0x44, 0x4A, 0x28, 0xA2, - 0xBF, 0x17, 0x3F, 0x52, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, - 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, - 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x3E, 0xB6, 0xFF, 0x00, 0x82, 0x6F, 0xF8, 0x91, - 0x66, 0xD0, 0x7C, 0x51, 0xA4, 0xBC, 0x80, 0x34, 0x33, 0x41, 0x79, 0x0A, 0x77, 0x21, 0x83, 0xA4, - 0x87, 0xF0, 0xDB, 0x1F, 0xE7, 0xF4, 0xAF, 0xD3, 0x6F, 0x81, 0xFA, 0xAA, 0xF8, 0xA7, 0xE1, 0x3E, - 0x92, 0x92, 0x37, 0xFA, 0xB8, 0x4D, 0x94, 0x9D, 0xF6, 0xEC, 0x25, 0x06, 0x7F, 0xE0, 0x1B, 0x4F, - 0xE3, 0x5F, 0x8E, 0xFF, 0x00, 0xB1, 0x1F, 0x8E, 0xBF, 0xE1, 0x0B, 0xF8, 0xF9, 0xA7, 0xDB, 0xC9, - 0x27, 0x97, 0x6D, 0xAF, 0xC6, 0xFA, 0x5C, 0xA7, 0x6E, 0x4B, 0x33, 0xE1, 0xA2, 0x1F, 0x8C, 0xC9, - 0x18, 0xCF, 0xA1, 0x35, 0xFA, 0x97, 0xFB, 0x28, 0x78, 0xB0, 0x43, 0x2E, 0xA5, 0xA2, 0xC8, 0xD8, - 0xDF, 0x8B, 0xD8, 0x07, 0xB8, 0xC2, 0xC9, 0xF8, 0xE3, 0x61, 0xFC, 0x0D, 0x7F, 0x4D, 0x70, 0x16, - 0x2D, 0x63, 0x38, 0x59, 0x53, 0x8F, 0xC5, 0x42, 0x4D, 0x7C, 0x9E, 0xB7, 0xFC, 0x7F, 0x03, 0xF0, - 0x7E, 0x2E, 0xC2, 0xBC, 0x27, 0x13, 0xFB, 0x47, 0xF0, 0xD7, 0x8A, 0xFB, 0xD6, 0x8F, 0xF2, 0xFC, - 0x4F, 0x97, 0xCA, 0xB5, 0xB3, 0x34, 0x72, 0x7C, 0xB2, 0x46, 0x76, 0x30, 0x3D, 0x88, 0xE0, 0xD3, - 0x84, 0xBF, 0x2D, 0x74, 0x1F, 0x1B, 0xBC, 0x3D, 0xFF, 0x00, 0x08, 0xB7, 0xC5, 0xEF, 0x12, 0x59, - 0xED, 0xF2, 0xD5, 0x6F, 0xE4, 0x96, 0x31, 0xE9, 0x1C, 0x87, 0xCC, 0x4F, 0xFC, 0x75, 0xD6, 0xB9, - 0x81, 0x21, 0x55, 0xEB, 0xC9, 0xAF, 0xD8, 0x68, 0x54, 0xE7, 0xA6, 0xA7, 0xDD, 0x5C, 0xFC, 0xFA, - 0xBD, 0x1E, 0x59, 0xB8, 0xBE, 0x8E, 0xC5, 0xA1, 0x36, 0x28, 0x69, 0xF2, 0x3B, 0xD5, 0x5D, 0xF4, - 0x79, 0xAD, 0x5B, 0x5D, 0x9C, 0xFE, 0xCC, 0xB0, 0x65, 0xC8, 0xA8, 0xDA, 0x5E, 0x2A, 0x12, 0xE5, - 0xBF, 0x8A, 0x9A, 0xD2, 0x62, 0x97, 0x33, 0x2E, 0x30, 0x3F, 0xA3, 0x8F, 0xF8, 0x20, 0xE9, 0xDD, - 0xFF, 0x00, 0x04, 0xAA, 0xF8, 0x5B, 0xF4, 0xD5, 0x7F, 0xF4, 0xED, 0x79, 0x5F, 0x5F, 0x57, 0xC7, - 0xDF, 0xF0, 0x41, 0x93, 0xFF, 0x00, 0x1A, 0xA6, 0xF8, 0x55, 0xF4, 0xD5, 0x7F, 0xF4, 0xED, 0x7B, - 0x5F, 0x60, 0x81, 0x83, 0x5F, 0xC3, 0x5C, 0x4D, 0xFF, 0x00, 0x23, 0x8C, 0x5F, 0xFD, 0x7D, 0x9F, - 0xFE, 0x94, 0xCF, 0xEA, 0x8C, 0x85, 0x5B, 0x2D, 0xC3, 0xFF, 0x00, 0x82, 0x3F, 0xFA, 0x4A, 0x3C, - 0xB7, 0xF6, 0xDA, 0x3F, 0xF1, 0x87, 0x3F, 0x16, 0x3F, 0xEC, 0x4F, 0xD5, 0xFF, 0x00, 0xF4, 0x8A, - 0x6A, 0xFE, 0x52, 0x55, 0xFF, 0x00, 0x74, 0xBF, 0x41, 0x5F, 0xD5, 0xA7, 0xED, 0xB9, 0xFF, 0x00, - 0x26, 0x6D, 0xF1, 0x6B, 0xFE, 0xC4, 0xED, 0x5F, 0xFF, 0x00, 0x48, 0xA6, 0xAF, 0xE5, 0x15, 0x24, - 0xCC, 0x0B, 0xFE, 0xE8, 0xFC, 0x2B, 0xF6, 0x9F, 0x04, 0x7F, 0x83, 0x8A, 0xF5, 0x8F, 0xE4, 0xCF, - 0xCE, 0x7C, 0x4A, 0x57, 0xAD, 0x45, 0xF9, 0x3F, 0xD0, 0x24, 0x7D, 0xC6, 0xAB, 0x4F, 0x2E, 0xFA, - 0x7C, 0xD2, 0xED, 0x18, 0xAA, 0xCC, 0xDC, 0xE7, 0xF2, 0xAF, 0xDD, 0x59, 0xF9, 0xBD, 0x38, 0x8D, - 0x66, 0xCD, 0x56, 0x99, 0xE9, 0xF2, 0xBD, 0x57, 0x77, 0xE7, 0x3F, 0x95, 0x65, 0x29, 0x1D, 0x94, - 0xE2, 0x32, 0x79, 0x36, 0xD4, 0xFE, 0x19, 0xD3, 0x06, 0xB5, 0xE2, 0x2B, 0x2B, 0x56, 0xFF, 0x00, - 0x96, 0xF3, 0x22, 0xB7, 0xB2, 0xE7, 0x9F, 0xC8, 0x66, 0xA9, 0xCA, 0xE5, 0x8E, 0x3D, 0x2B, 0xB3, - 0xF8, 0x21, 0xA1, 0xFD, 0xB7, 0x5F, 0x9B, 0x50, 0x75, 0xF9, 0x2C, 0x93, 0x6A, 0x1F, 0xF6, 0x9F, - 0x23, 0xF4, 0x5D, 0xDF, 0x98, 0xA2, 0x9C, 0x5C, 0xE6, 0x97, 0x73, 0x4C, 0x5D, 0x65, 0x43, 0x0F, - 0x3A, 0xBE, 0x5F, 0x8F, 0x4F, 0xC4, 0xC1, 0xFF, 0x00, 0x82, 0x81, 0xF8, 0x9D, 0x34, 0xBF, 0x82, - 0x70, 0xD8, 0x09, 0x19, 0x67, 0xD5, 0xF5, 0x18, 0xD7, 0x67, 0xF7, 0xA3, 0x8D, 0x5D, 0xD8, 0xFE, - 0x0D, 0xE5, 0x7E, 0x75, 0xF1, 0x30, 0x38, 0x26, 0xBE, 0x87, 0xFF, 0x00, 0x82, 0x87, 0xF8, 0xE4, - 0x6B, 0x9F, 0x12, 0x74, 0xCF, 0x0F, 0xC5, 0x23, 0x34, 0x3A, 0x0D, 0xA7, 0x99, 0x2A, 0x9F, 0xE1, - 0x9E, 0x7C, 0x39, 0x1F, 0xF7, 0xED, 0x62, 0xFC, 0x73, 0x5F, 0x3C, 0x02, 0x46, 0x2B, 0xF9, 0x37, - 0xC5, 0x0C, 0xD2, 0x38, 0xDE, 0x21, 0xAC, 0xE0, 0xEF, 0x18, 0x5A, 0x2B, 0xFE, 0xDD, 0xDF, 0xF1, - 0xB9, 0xFA, 0xF7, 0x87, 0x99, 0x7B, 0xC2, 0x64, 0x54, 0x63, 0x25, 0x67, 0x3B, 0xC9, 0xFF, 0x00, - 0xDB, 0xCF, 0x4F, 0xC2, 0xC3, 0x28, 0xA2, 0x8A, 0xFC, 0xF4, 0xFB, 0x60, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, - 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x00, 0xA2, 0x8A, 0x28, 0x02, - 0xE6, 0x9F, 0x7D, 0x36, 0x9D, 0x79, 0x0C, 0xF0, 0xB3, 0xC7, 0x2C, 0x2E, 0x1D, 0x1D, 0x5B, 0x6B, - 0x29, 0x07, 0x20, 0x83, 0xD8, 0x83, 0xDE, 0xBF, 0x4B, 0xFF, 0x00, 0x67, 0x6F, 0x8D, 0x0D, 0xE2, - 0x8F, 0x0E, 0x78, 0x6F, 0xC6, 0x76, 0x7E, 0x5B, 0x5C, 0x32, 0x87, 0xB9, 0x85, 0x08, 0x50, 0x25, - 0x5C, 0xA4, 0xF1, 0x60, 0x7D, 0xD0, 0x4E, 0xFC, 0x03, 0xCE, 0xD7, 0x53, 0xDC, 0x57, 0xE6, 0x36, - 0x77, 0x37, 0x5A, 0xFA, 0x0B, 0xF6, 0x13, 0xF8, 0xDC, 0xBE, 0x09, 0xF1, 0xA4, 0x9E, 0x17, 0xD4, - 0x66, 0xDB, 0xA5, 0xF8, 0x81, 0xD4, 0x5B, 0xB3, 0x1F, 0x96, 0xDE, 0xEF, 0xA2, 0x1F, 0x61, 0x20, - 0xF9, 0x0F, 0x1F, 0x7B, 0xCB, 0x24, 0x80, 0xA6, 0xBF, 0x4F, 0xF0, 0xBF, 0x89, 0x21, 0x96, 0xE6, - 0x6F, 0x0B, 0x88, 0x76, 0xA5, 0x59, 0x72, 0xBE, 0xC9, 0xF4, 0x7F, 0x8B, 0x5F, 0x33, 0xF3, 0xFF, - 0x00, 0x11, 0x32, 0x19, 0xE3, 0xF2, 0xEF, 0xAC, 0x61, 0xD7, 0xEF, 0x68, 0xBE, 0x65, 0xE6, 0xBA, - 0xAF, 0xD7, 0xE4, 0x7D, 0xC9, 0xFB, 0x67, 0xE8, 0xB0, 0xCD, 0xE2, 0x7D, 0x17, 0xC5, 0x16, 0x27, - 0xCC, 0xB1, 0xF1, 0x15, 0x88, 0x5F, 0x30, 0x0E, 0xB2, 0x47, 0x8C, 0x13, 0xE8, 0x4C, 0x6E, 0x83, - 0x07, 0xFB, 0x87, 0xD2, 0xBC, 0x54, 0x4F, 0xC7, 0x5A, 0xF6, 0xA9, 0xA6, 0x3E, 0x3B, 0xF8, 0x5D, - 0xA8, 0x78, 0x46, 0x7F, 0x9A, 0xE6, 0x27, 0xFE, 0xD0, 0xD1, 0x49, 0xEA, 0x2E, 0x13, 0x3B, 0xA0, - 0xFF, 0x00, 0xB6, 0xAA, 0x5D, 0x54, 0x7F, 0x7D, 0x87, 0x5C, 0xF1, 0xE1, 0x69, 0x2E, 0xEE, 0x41, - 0xAF, 0xEA, 0x2C, 0x1D, 0x39, 0x51, 0x83, 0xA1, 0x2F, 0xB3, 0xB7, 0x9A, 0x7B, 0x1F, 0x88, 0xFB, - 0x68, 0x62, 0x52, 0xAD, 0x1E, 0xBB, 0xF9, 0x3E, 0xA5, 0xC1, 0x2E, 0x7B, 0xD0, 0x65, 0xC7, 0x7A, - 0xAB, 0xE6, 0x37, 0xAD, 0x1E, 0x63, 0x7A, 0xD7, 0x59, 0x9F, 0x21, 0x68, 0xCD, 0xC7, 0x5A, 0x8D, - 0xA7, 0xAA, 0xFE, 0x66, 0x7B, 0xD3, 0x5E, 0x5D, 0x87, 0xEB, 0x46, 0x85, 0x46, 0x9F, 0x43, 0xFA, - 0x48, 0xFF, 0x00, 0x82, 0x0A, 0xB6, 0xEF, 0xF8, 0x25, 0x17, 0xC2, 0x93, 0xFE, 0xCE, 0xAB, 0xFF, - 0x00, 0xA7, 0x6B, 0xDA, 0xFB, 0x0A, 0xBE, 0x3E, 0xFF, 0x00, 0x82, 0x0D, 0xDB, 0x3D, 0x9F, 0xFC, - 0x12, 0x8B, 0xE1, 0x32, 0xC8, 0xBB, 0x59, 0xA1, 0xD4, 0xA4, 0x19, 0xEE, 0x1B, 0x54, 0xBC, 0x60, - 0x7F, 0x10, 0x41, 0xAF, 0xAF, 0xC7, 0x2B, 0x5F, 0xC3, 0xBC, 0x4C, 0xFF, 0x00, 0xE1, 0x63, 0x17, - 0xFF, 0x00, 0x5F, 0x27, 0xFF, 0x00, 0xA5, 0x33, 0xFA, 0x73, 0x24, 0x56, 0xCB, 0xE8, 0x2F, 0xEE, - 0x47, 0xF2, 0x47, 0x96, 0xFE, 0xDC, 0x3F, 0xF2, 0x66, 0x3F, 0x16, 0xBF, 0xEC, 0x4E, 0xD6, 0x3F, - 0xF4, 0x8A, 0x6A, 0xFE, 0x4F, 0x44, 0xD8, 0x81, 0x7E, 0x82, 0xBF, 0xAC, 0x4F, 0xDB, 0x42, 0xCA, - 0x4B, 0xEF, 0xD9, 0x0B, 0xE2, 0xA5, 0xBC, 0x6B, 0xBA, 0x4B, 0x8F, 0x08, 0x6A, 0xD1, 0xAA, 0x8E, - 0xE4, 0xD9, 0x4C, 0x00, 0xAF, 0xE4, 0xCE, 0x29, 0x7F, 0x72, 0xA7, 0x76, 0x37, 0x28, 0x20, 0x57, - 0xED, 0x1E, 0x08, 0xBF, 0xDC, 0xE2, 0xBD, 0x63, 0xF9, 0x33, 0xF3, 0xDF, 0x11, 0x23, 0x7A, 0xB4, - 0x7D, 0x18, 0xE7, 0x7A, 0x82, 0x47, 0xC9, 0xA2, 0x59, 0x33, 0x50, 0x48, 0xFB, 0xCD, 0x7E, 0xE6, - 0xDD, 0xCF, 0xCE, 0xE1, 0x00, 0x91, 0xF7, 0x1F, 0x6A, 0xAD, 0x3C, 0xB9, 0xA7, 0xCA, 0xF9, 0x1F, - 0xEC, 0xD5, 0x79, 0x5C, 0x03, 0x9F, 0xCA, 0xA2, 0x52, 0x5B, 0x1D, 0x74, 0xE2, 0x18, 0x69, 0x4E, - 0xC5, 0x0C, 0xCC, 0xC7, 0x18, 0x03, 0x24, 0x9F, 0x4A, 0xF6, 0x3D, 0x25, 0x2C, 0xFE, 0x11, 0xFC, - 0x36, 0x9A, 0xF3, 0x50, 0x61, 0x1C, 0x3A, 0x75, 0xBB, 0xDF, 0x5F, 0x30, 0x61, 0x92, 0xC0, 0x64, - 0xA8, 0x3D, 0xCF, 0x0A, 0x8B, 0xEA, 0x71, 0xDC, 0xD7, 0x2D, 0xF0, 0x67, 0xC0, 0xED, 0x79, 0x76, - 0xBA, 0xBD, 0xC0, 0xFD, 0xCC, 0x04, 0xFD, 0x98, 0x1F, 0xE3, 0x71, 0xFC, 0x7F, 0x45, 0xED, 0xFE, - 0xD7, 0xFB, 0xB5, 0xE5, 0x9F, 0xB7, 0xE7, 0xC7, 0x15, 0x71, 0x0F, 0x81, 0xF4, 0xD9, 0x81, 0x2A, - 0x56, 0xE3, 0x57, 0x65, 0x27, 0x93, 0xC1, 0x8E, 0x1C, 0xFB, 0x7F, 0xAC, 0x61, 0xC8, 0xC9, 0x8F, - 0xA1, 0x52, 0x2B, 0xC3, 0xE2, 0x6C, 0xFA, 0x96, 0x4B, 0x95, 0xD4, 0xC7, 0xD4, 0x7E, 0xFD, 0xAD, - 0x05, 0xDE, 0x4F, 0x6F, 0xF3, 0xF4, 0x32, 0xC3, 0x65, 0xF2, 0xCE, 0x73, 0x3A, 0x79, 0x5D, 0x2F, - 0x82, 0x2F, 0x9A, 0x6F, 0xB2, 0x5D, 0x3F, 0x4F, 0x56, 0x8F, 0x9C, 0xBC, 0x7D, 0xE2, 0xFB, 0xAF, - 0x88, 0x1E, 0x32, 0xD4, 0xB5, 0xAB, 0xCD, 0xBF, 0x69, 0xD4, 0xEE, 0x1E, 0xE1, 0xC0, 0xFB, 0xAA, - 0x58, 0x93, 0x81, 0xE8, 0xA3, 0xA0, 0x1D, 0x80, 0x02, 0xB1, 0x98, 0xB1, 0x38, 0xE3, 0xA6, 0x32, - 0x3D, 0xA9, 0xD6, 0xEE, 0xA8, 0x92, 0x6E, 0x52, 0xC5, 0x97, 0x08, 0x77, 0x63, 0x69, 0xC8, 0xE7, - 0xDF, 0x8C, 0x8C, 0x7B, 0xD4, 0x15, 0xFC, 0x5B, 0x5A, 0xB4, 0xEA, 0xCD, 0xD4, 0x9B, 0xBB, 0x6E, - 0xEF, 0xD5, 0xEA, 0x7F, 0x49, 0x53, 0xA7, 0x18, 0x45, 0x42, 0x0A, 0xC9, 0x69, 0xF7, 0x05, 0x14, - 0x51, 0x59, 0x94, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, - 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, - 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x38, 0x6E, 0x22, 0x9C, 0xAE, 0xC8, 0xC1, 0x87, 0x50, - 0x78, 0x3E, 0x95, 0x1D, 0x15, 0x49, 0xD8, 0x0F, 0xBA, 0x3F, 0x64, 0x4F, 0xDA, 0x5A, 0x3F, 0x8B, - 0x5E, 0x1F, 0x8F, 0x45, 0xD5, 0xAE, 0x76, 0xF8, 0xA3, 0x4B, 0x88, 0x65, 0xDD, 0xBE, 0x6D, 0x4E, - 0x25, 0xE3, 0xCD, 0x07, 0xBC, 0x8A, 0x31, 0xBC, 0x1E, 0x4F, 0xDF, 0xE7, 0xE7, 0xD9, 0xD7, 0x7C, - 0x40, 0xF0, 0xA4, 0xB6, 0x97, 0x73, 0x6A, 0x10, 0xEE, 0x92, 0x0B, 0x87, 0x32, 0x4C, 0x00, 0xE6, - 0x36, 0x27, 0x24, 0xFF, 0x00, 0xBA, 0x4F, 0xE5, 0xF9, 0x57, 0xE7, 0xDF, 0x86, 0xFC, 0x43, 0x79, - 0xE1, 0x1D, 0x6A, 0xD7, 0x52, 0xD3, 0xEE, 0x24, 0xB5, 0xBE, 0xB2, 0x95, 0x65, 0x82, 0x64, 0x38, - 0x68, 0xDD, 0x4E, 0x41, 0x15, 0xF7, 0x17, 0xEC, 0xD3, 0xFB, 0x54, 0x69, 0x7F, 0x1D, 0x2C, 0x06, - 0x97, 0x7E, 0xB0, 0xE9, 0xDE, 0x28, 0x85, 0x3E, 0x7B, 0x6C, 0xE2, 0x3D, 0x41, 0x42, 0xFC, 0xD2, - 0x43, 0xFE, 0xD7, 0x52, 0xD1, 0xF5, 0x03, 0x91, 0x95, 0x0D, 0xB3, 0xFA, 0x5B, 0xC3, 0x5F, 0x10, - 0xA9, 0x63, 0x69, 0x47, 0x2A, 0xCC, 0xE7, 0x6A, 0xAB, 0x48, 0x49, 0xFD, 0xAE, 0xC9, 0xBE, 0xFF, - 0x00, 0x9F, 0xA9, 0xF8, 0x3F, 0x1B, 0x70, 0x6D, 0x6C, 0x05, 0x79, 0x66, 0xB9, 0x64, 0x79, 0xA9, - 0x4B, 0x59, 0xC1, 0x74, 0xF3, 0x4B, 0xB7, 0xE5, 0xE8, 0x56, 0xF3, 0xBF, 0xDA, 0x1F, 0x95, 0x2F, - 0x9B, 0xFE, 0xD7, 0xE9, 0x5D, 0x7F, 0x8B, 0x3E, 0x1B, 0x6F, 0xDD, 0x71, 0xA5, 0xF0, 0x7A, 0xB5, - 0xBE, 0x71, 0xFF, 0x00, 0x7C, 0x1F, 0xE8, 0x7F, 0x0E, 0xC2, 0xB8, 0x89, 0xD2, 0x4B, 0x59, 0x9A, - 0x39, 0x15, 0x92, 0x44, 0x38, 0x65, 0x6C, 0xAB, 0x29, 0xF7, 0x15, 0xFB, 0x14, 0xA2, 0xE3, 0xB9, - 0xF1, 0x78, 0x5A, 0xB4, 0xB1, 0x11, 0xE6, 0xA6, 0xFE, 0x5D, 0x51, 0x3F, 0x99, 0x91, 0xF7, 0xB1, - 0x8A, 0xFA, 0x1F, 0xF6, 0x6C, 0xFF, 0x00, 0x82, 0x53, 0x7C, 0x7C, 0xFD, 0xAB, 0xAE, 0x34, 0x79, - 0x3C, 0x2D, 0xF0, 0xEF, 0x59, 0xB7, 0xD1, 0x35, 0xC4, 0x8A, 0x7B, 0x7D, 0x7F, 0x58, 0x4F, 0xB0, - 0x69, 0x4B, 0x6D, 0x26, 0x0A, 0xDC, 0xF9, 0xB2, 0x7C, 0xD2, 0xC5, 0xB4, 0x86, 0xFD, 0xCA, 0xC8, - 0xEC, 0x3E, 0xEA, 0xB5, 0x7C, 0xDC, 0x64, 0xC0, 0xFB, 0xC2, 0xBE, 0x84, 0xF8, 0x73, 0xFF, 0x00, - 0x05, 0x63, 0xFD, 0xA3, 0x3E, 0x12, 0xF8, 0x17, 0x49, 0xF0, 0xCF, 0x86, 0xFE, 0x2B, 0x6B, 0xDA, - 0x4E, 0x81, 0xA0, 0xD9, 0xC7, 0x61, 0xA7, 0xD9, 0xC5, 0x69, 0x66, 0xD1, 0xDB, 0x41, 0x1A, 0x85, - 0x48, 0xC1, 0x68, 0x4B, 0x10, 0x14, 0x01, 0xC9, 0x26, 0xBC, 0x3C, 0xED, 0x66, 0x92, 0xA1, 0x6C, - 0xAD, 0xC1, 0x4B, 0xAB, 0x9D, 0xEC, 0x97, 0x74, 0x91, 0xEE, 0x65, 0x90, 0xC1, 0xAA, 0x97, 0xC6, - 0x73, 0x72, 0xFF, 0x00, 0x77, 0xFE, 0x09, 0xFD, 0x29, 0x7E, 0xCB, 0x7F, 0x01, 0x6C, 0x3F, 0x65, - 0xCF, 0xD9, 0xD7, 0xC1, 0x3F, 0x0F, 0x74, 0xD9, 0x9E, 0xF2, 0xD7, 0xC1, 0xFA, 0x3D, 0xB6, 0x96, - 0x2E, 0x99, 0x3C, 0xB6, 0xBC, 0x78, 0xD0, 0x09, 0x27, 0x65, 0xC9, 0xDA, 0xD2, 0x3E, 0xE7, 0x2A, - 0x09, 0x00, 0xB9, 0x03, 0x8A, 0xF4, 0x20, 0xDC, 0xD7, 0xF3, 0x08, 0xDF, 0xF0, 0x5A, 0xFF, 0x00, - 0xDA, 0xA0, 0x7F, 0xCD, 0x68, 0xF1, 0x1F, 0xFE, 0x01, 0x58, 0xFF, 0x00, 0xF1, 0x8A, 0x63, 0x7F, - 0xC1, 0x6C, 0x3F, 0x6A, 0x81, 0xFF, 0x00, 0x35, 0xA7, 0xC4, 0x7F, 0xF8, 0x09, 0x63, 0xFF, 0x00, - 0xC6, 0x2B, 0xF0, 0x2A, 0xBE, 0x0F, 0xE7, 0x15, 0x66, 0xEA, 0xD4, 0xAD, 0x07, 0x29, 0x3B, 0xB7, - 0x79, 0x6A, 0xDE, 0xFD, 0x0F, 0xD4, 0xA9, 0x71, 0xDE, 0x02, 0x11, 0x50, 0x8C, 0x25, 0x64, 0xAD, - 0xB2, 0xFF, 0x00, 0x33, 0xFA, 0x73, 0xD5, 0xF4, 0xCB, 0x6D, 0x73, 0x4A, 0xBA, 0xB3, 0xBA, 0x89, - 0x27, 0xB5, 0xBC, 0x89, 0xA0, 0x9A, 0x27, 0x19, 0x59, 0x11, 0x81, 0x56, 0x53, 0xEC, 0x41, 0x22, - 0xBF, 0x9A, 0xBF, 0xDA, 0xFF, 0x00, 0xFE, 0x08, 0x7F, 0xF1, 0xEB, 0xF6, 0x5C, 0xF8, 0x8B, 0xAB, - 0x58, 0xE9, 0x3E, 0x05, 0xD7, 0xBC, 0x79, 0xE0, 0xDB, 0x7B, 0x89, 0x3F, 0xB2, 0x75, 0xDD, 0x0A, - 0x1F, 0xB7, 0x9B, 0xAB, 0x51, 0xCA, 0x34, 0xF0, 0x47, 0x99, 0x62, 0x95, 0x50, 0x80, 0xE1, 0x90, - 0x26, 0xF0, 0xDB, 0x19, 0xD7, 0x0C, 0x79, 0xB6, 0xFF, 0x00, 0x82, 0xD8, 0xFE, 0xD5, 0x4B, 0xFF, - 0x00, 0x35, 0xA7, 0xC4, 0xBF, 0xF8, 0x09, 0x63, 0xFF, 0x00, 0xC6, 0x2A, 0xBE, 0xA1, 0xFF, 0x00, - 0x05, 0xA4, 0xFD, 0xA9, 0xB5, 0x1B, 0x39, 0xAD, 0xEE, 0x3E, 0x32, 0xF8, 0x8E, 0x48, 0x6E, 0x23, - 0x68, 0xE4, 0x4F, 0xB1, 0xD9, 0x7C, 0xCA, 0xC3, 0x04, 0x7F, 0xA8, 0xEE, 0x0D, 0x7D, 0x4F, 0x09, - 0xF0, 0x4F, 0x11, 0x64, 0x35, 0xA5, 0x3C, 0x35, 0x5A, 0x52, 0x8C, 0xED, 0xCC, 0x9F, 0x37, 0x4D, - 0x9A, 0xB2, 0xDD, 0x5D, 0x9E, 0x56, 0x75, 0x9F, 0x65, 0x99, 0x9C, 0x23, 0x1A, 0x91, 0x9A, 0x71, - 0xD9, 0xAB, 0x75, 0x3E, 0x61, 0x33, 0xF9, 0xAA, 0x19, 0x5B, 0xE5, 0x23, 0x20, 0x8A, 0x86, 0x69, - 0x70, 0x29, 0x37, 0xED, 0x45, 0x55, 0xE0, 0x28, 0xC0, 0x14, 0xC6, 0x72, 0x5B, 0x6E, 0x37, 0x31, - 0x38, 0x0A, 0x2B, 0xF6, 0x8E, 0x66, 0xAD, 0x73, 0xE1, 0x23, 0x4C, 0x6B, 0x49, 0xC7, 0x26, 0xBA, - 0x6F, 0x87, 0x3F, 0x0E, 0xE5, 0xF1, 0x7D, 0xD7, 0x9F, 0x71, 0xBA, 0x3D, 0x3E, 0x33, 0x87, 0x71, - 0xC1, 0x90, 0x8F, 0xE0, 0x5F, 0xEA, 0x7B, 0x7D, 0x71, 0x5A, 0x5E, 0x07, 0xF8, 0x35, 0x3E, 0xA7, - 0x22, 0x5D, 0x6B, 0x0A, 0xF6, 0xF6, 0xEB, 0xCA, 0xDB, 0x67, 0x12, 0x4B, 0xFE, 0xF7, 0x74, 0x1F, - 0xF8, 0xF7, 0xD3, 0xAD, 0x5F, 0xF8, 0xED, 0xFB, 0x41, 0x68, 0x7F, 0xB3, 0xD7, 0x86, 0x92, 0x39, - 0x12, 0x1B, 0x8D, 0x52, 0x58, 0xBF, 0xD0, 0x34, 0xC8, 0xDB, 0x66, 0xE5, 0xE8, 0x1D, 0xF1, 0xF7, - 0x22, 0x1C, 0xFB, 0xB1, 0x04, 0x2F, 0x76, 0x5E, 0x7C, 0x6E, 0x2F, 0x0D, 0x97, 0xE1, 0xA5, 0x8D, - 0xC7, 0xC9, 0x42, 0x11, 0xEF, 0xD7, 0xC9, 0x2E, 0xAD, 0xF4, 0x38, 0x25, 0x8A, 0xAF, 0x89, 0xAE, - 0xB0, 0x19, 0x64, 0x79, 0xEA, 0x4B, 0xB6, 0xCB, 0xCE, 0xFB, 0x69, 0xD5, 0xEC, 0x88, 0x3F, 0x68, - 0x9F, 0x8F, 0x3A, 0x7F, 0xEC, 0xFD, 0xE0, 0x75, 0x5B, 0x7F, 0x25, 0xB5, 0xDB, 0xB8, 0x8C, 0x7A, - 0x5D, 0x92, 0xE0, 0xF9, 0x40, 0x0C, 0x79, 0xCE, 0xBD, 0xA3, 0x4E, 0xC0, 0xFD, 0xF6, 0x1B, 0x46, - 0x40, 0x72, 0xBF, 0x06, 0x6A, 0xDA, 0x9D, 0xC6, 0xBB, 0xA8, 0xDC, 0x5E, 0xDD, 0x4D, 0x2D, 0xC5, - 0xCD, 0xD4, 0x8D, 0x34, 0xF3, 0x48, 0xC5, 0x9E, 0x47, 0x63, 0x92, 0xCC, 0x4F, 0x24, 0x92, 0x72, - 0x4F, 0x72, 0x6B, 0x47, 0xE2, 0x07, 0x8F, 0xF5, 0x6F, 0x89, 0x9E, 0x29, 0xB8, 0xD6, 0x75, 0x8B, - 0xA6, 0xBC, 0xBD, 0xBB, 0x3C, 0xB6, 0x30, 0xA8, 0xA3, 0xA2, 0x28, 0xE8, 0xAA, 0xA3, 0x80, 0x07, - 0x00, 0x56, 0x09, 0xE7, 0xA5, 0x7F, 0x26, 0xF1, 0xCF, 0x18, 0xD6, 0xCF, 0xF1, 0xBE, 0xD3, 0xE1, - 0xA5, 0x1B, 0xA8, 0x47, 0xB2, 0xEE, 0xFC, 0xD9, 0xFB, 0x97, 0x08, 0xF0, 0xB5, 0x2C, 0x97, 0x09, - 0xEC, 0xD7, 0xBD, 0x56, 0x5A, 0xCE, 0x5D, 0xDF, 0x65, 0xE4, 0xBA, 0x7D, 0xE3, 0x58, 0xF3, 0xFF, - 0x00, 0xD6, 0xA6, 0xD1, 0x45, 0x7C, 0x39, 0xF5, 0x61, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, - 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, - 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, - 0x01, 0x45, 0x14, 0x50, 0x03, 0xB6, 0x73, 0x56, 0xAC, 0x6F, 0xE6, 0xD3, 0xEE, 0xA3, 0x9E, 0x09, - 0x9A, 0x19, 0xA1, 0x60, 0xC9, 0x22, 0x31, 0x0C, 0x84, 0x1C, 0x82, 0x08, 0xE4, 0x10, 0x79, 0xE2, - 0xA9, 0xD1, 0x55, 0x19, 0x38, 0xBE, 0x65, 0xB8, 0x3D, 0x77, 0x3E, 0xAA, 0xF8, 0x0B, 0xFB, 0x7B, - 0x2C, 0x51, 0x43, 0xA4, 0xF8, 0xE7, 0xCC, 0x9B, 0x6E, 0x12, 0x3D, 0x66, 0x24, 0xDC, 0xE0, 0x7F, - 0xD3, 0x78, 0xC0, 0xCB, 0xE0, 0x7F, 0x1A, 0xFC, 0xDC, 0x72, 0xAE, 0x49, 0x35, 0xF4, 0x59, 0xB2, - 0xD1, 0x7E, 0x23, 0xE8, 0x76, 0xFA, 0x84, 0x32, 0x5B, 0xEA, 0x16, 0x77, 0x2B, 0xFE, 0x8F, 0x7B, - 0x6C, 0xE1, 0x83, 0x63, 0x82, 0x03, 0x8F, 0x43, 0x90, 0x54, 0xE7, 0x07, 0x20, 0x80, 0x6B, 0xF3, - 0x38, 0xF4, 0xAE, 0x8F, 0xC0, 0x5F, 0x14, 0xBC, 0x41, 0xF0, 0xBB, 0x52, 0x37, 0x7A, 0x0E, 0xAD, - 0x77, 0xA6, 0xC8, 0xC4, 0x6F, 0x11, 0xB6, 0x63, 0x97, 0x1D, 0x03, 0xA1, 0xCA, 0xB8, 0xF6, 0x60, - 0x45, 0x7E, 0xC1, 0xC2, 0xBE, 0x2E, 0x63, 0x70, 0x10, 0x58, 0x5C, 0xCA, 0x2E, 0xB5, 0x3E, 0xFF, - 0x00, 0x69, 0x2F, 0x5E, 0xBF, 0x3F, 0xBC, 0xFC, 0xC7, 0x88, 0xBC, 0x33, 0xC2, 0xE3, 0x26, 0xF1, - 0x39, 0x74, 0xBD, 0x8D, 0x5F, 0x2F, 0x85, 0xFC, 0x96, 0xDF, 0x2D, 0x3C, 0x8F, 0xB8, 0xF5, 0x9F, - 0x84, 0x57, 0x96, 0xA5, 0x9A, 0xC6, 0x68, 0xEE, 0xD3, 0xB2, 0x39, 0xF2, 0xE4, 0x1F, 0xFB, 0x29, - 0xFC, 0xC5, 0x73, 0x1A, 0xAE, 0x89, 0x7F, 0xA3, 0x1F, 0xF4, 0xAB, 0x3B, 0x88, 0x57, 0xFB, 0xCC, - 0x84, 0x29, 0xFA, 0x1E, 0x87, 0xF0, 0x35, 0xC0, 0x7C, 0x3D, 0xFF, 0x00, 0x82, 0x8B, 0x5C, 0x42, - 0xB1, 0xDB, 0xF8, 0xA3, 0x43, 0x8E, 0xEB, 0x6E, 0x03, 0x5D, 0xE9, 0xEF, 0xE5, 0x3E, 0x31, 0xC9, - 0x68, 0xDB, 0x2A, 0xCC, 0x7F, 0xD9, 0x64, 0x1E, 0xD5, 0xEB, 0xFE, 0x14, 0xFD, 0xB0, 0xBE, 0x1E, - 0x78, 0xA5, 0x17, 0x66, 0xBE, 0x34, 0xD9, 0x64, 0x38, 0xF2, 0x75, 0x08, 0x5E, 0x06, 0xFC, 0x5C, - 0x06, 0x8F, 0x8E, 0x3F, 0x8F, 0xBD, 0x7E, 0xD5, 0x96, 0x71, 0xE7, 0x0F, 0x66, 0x11, 0x4E, 0x96, - 0x21, 0x42, 0x5D, 0xA7, 0xEE, 0xDB, 0xEF, 0xD3, 0xEE, 0x6C, 0xFC, 0xDF, 0x19, 0x90, 0xF1, 0x0E, - 0x5E, 0xED, 0x5F, 0x0F, 0xED, 0x17, 0x78, 0x6B, 0xF9, 0x7E, 0xA9, 0x1C, 0x81, 0x9B, 0x7F, 0x45, - 0x26, 0xA3, 0x67, 0x23, 0xAE, 0x7F, 0x0A, 0xF5, 0xDB, 0x7F, 0x10, 0xF8, 0x4F, 0xC4, 0x9C, 0xDB, - 0xEA, 0x3E, 0x17, 0xD4, 0x0B, 0x67, 0x26, 0x3B, 0xAB, 0x79, 0xB1, 0xC6, 0x4F, 0x42, 0x71, 0xC7, - 0x35, 0x3C, 0x9E, 0x18, 0xD0, 0x56, 0x7D, 0x8D, 0x67, 0xA5, 0xAC, 0x99, 0xC6, 0xD2, 0x10, 0x37, - 0xE5, 0x5F, 0x4F, 0x4E, 0xA6, 0x1E, 0x6B, 0x9A, 0x95, 0x58, 0xC9, 0x79, 0x34, 0x79, 0x32, 0xCD, - 0x65, 0x49, 0xF2, 0xD6, 0xA3, 0x38, 0xBF, 0x4F, 0xF3, 0xB1, 0xE2, 0xF2, 0x4B, 0xB7, 0xB1, 0x5F, - 0xC2, 0xA5, 0xB0, 0xD2, 0x2F, 0x35, 0xA6, 0xDB, 0x6B, 0x6B, 0x71, 0x71, 0xCF, 0xFC, 0xB3, 0x8C, - 0xB0, 0x1F, 0x5C, 0x74, 0xFC, 0x6B, 0xD7, 0xEE, 0xAF, 0x3C, 0x2F, 0xE1, 0xB9, 0x1B, 0xED, 0x17, - 0x1E, 0x1B, 0xD3, 0xCA, 0x1C, 0x31, 0x9E, 0x68, 0x20, 0xDA, 0x72, 0x47, 0x25, 0x88, 0xC7, 0x20, - 0x8E, 0x7B, 0x8A, 0xE6, 0x7C, 0x4F, 0xFB, 0x57, 0x7C, 0x3D, 0xF0, 0x9A, 0xB4, 0x73, 0x78, 0x96, - 0xCE, 0xEA, 0x48, 0x87, 0x10, 0xD9, 0xA3, 0xDC, 0x97, 0xF6, 0x0C, 0x80, 0xC7, 0xED, 0xCB, 0x0E, - 0xBF, 0x5A, 0xE4, 0xC5, 0x66, 0x99, 0x76, 0x19, 0x73, 0x62, 0xB1, 0x31, 0x8A, 0xF3, 0x92, 0xFF, - 0x00, 0x33, 0xAB, 0x0F, 0x8A, 0xC7, 0x62, 0x1D, 0xB0, 0x98, 0x49, 0xCB, 0xCE, 0xCF, 0xFC, 0xBF, - 0x53, 0x37, 0x43, 0xF8, 0x2B, 0xA9, 0x5F, 0x30, 0x6B, 0xC7, 0x86, 0xC6, 0x3E, 0xFC, 0xF9, 0xB2, - 0x1F, 0xC1, 0x4E, 0x3F, 0x33, 0xF8, 0x57, 0x71, 0xA2, 0xF8, 0x2B, 0x47, 0xF0, 0x1D, 0xAC, 0x97, - 0x84, 0x45, 0x17, 0xD9, 0xD0, 0xC9, 0x35, 0xED, 0xDC, 0x8A, 0xA2, 0x25, 0xEE, 0xC5, 0x8E, 0x15, - 0x07, 0xBF, 0x1E, 0xE4, 0xD7, 0xCF, 0xFE, 0x3F, 0xFF, 0x00, 0x82, 0x8B, 0xDB, 0x42, 0xB2, 0x41, - 0xE1, 0x7D, 0x05, 0xA4, 0x6F, 0xE1, 0xBA, 0xD5, 0x1F, 0x00, 0x7F, 0xDB, 0x18, 0xCF, 0x5F, 0x43, - 0xE6, 0x63, 0xD4, 0x1A, 0xF0, 0x5F, 0x89, 0x3F, 0x1B, 0xBC, 0x55, 0xF1, 0x6E, 0x75, 0x6D, 0x7B, - 0x56, 0xBA, 0xBC, 0x86, 0x36, 0xDE, 0x96, 0xEB, 0x88, 0xED, 0xE1, 0x3D, 0x32, 0xB1, 0xA8, 0x08, - 0x0E, 0x08, 0x19, 0xC6, 0x4F, 0x19, 0x26, 0xBF, 0x3D, 0xCE, 0xBC, 0x5C, 0xC9, 0xB0, 0x31, 0x71, - 0xCB, 0x93, 0xAD, 0x3E, 0x8F, 0x68, 0xAF, 0x9B, 0xD5, 0xFC, 0x95, 0xBC, 0xCF, 0xA7, 0xC0, 0x70, - 0x0E, 0x79, 0x98, 0xBB, 0xE6, 0x12, 0x54, 0x69, 0xF5, 0x4B, 0x59, 0x35, 0xF2, 0xFD, 0x5F, 0xC8, - 0xFA, 0x33, 0xE3, 0xAF, 0xED, 0xE3, 0x61, 0xE1, 0xF1, 0x36, 0x9B, 0xE0, 0xBF, 0x2F, 0x51, 0xBE, - 0xE5, 0x5F, 0x52, 0x96, 0x3F, 0xF4, 0x78, 0x0F, 0xFD, 0x33, 0x46, 0x1F, 0xBC, 0x60, 0x7F, 0x89, - 0x86, 0xCE, 0x3A, 0x38, 0x39, 0x1F, 0x28, 0xEB, 0xBA, 0xFD, 0xF7, 0x8A, 0xB5, 0x99, 0xEF, 0xF5, - 0x0B, 0xAB, 0x8B, 0xCB, 0xBB, 0xA6, 0xDF, 0x2C, 0xF3, 0xB9, 0x91, 0xDC, 0xFA, 0x96, 0x3C, 0x9E, - 0x9F, 0xA5, 0x66, 0x81, 0x86, 0xF5, 0xFA, 0x50, 0xCD, 0xBB, 0xFA, 0x0F, 0x4A, 0xFE, 0x7F, 0xE2, - 0x4E, 0x2C, 0xCC, 0x73, 0xBA, 0xDE, 0xD7, 0x1B, 0x3D, 0x16, 0xD1, 0x5A, 0x45, 0x7A, 0x2F, 0xD7, - 0x73, 0xF5, 0xDC, 0x87, 0x86, 0xF0, 0x19, 0x3D, 0x1F, 0x65, 0x83, 0x86, 0xAF, 0x79, 0x3D, 0x5B, - 0xF5, 0x7F, 0xA2, 0xB2, 0x24, 0x27, 0x00, 0x01, 0xF8, 0x9A, 0x61, 0x66, 0xDC, 0x09, 0xA6, 0x67, - 0x34, 0x57, 0xCD, 0x5C, 0xF7, 0x82, 0x8A, 0x28, 0xA4, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, - 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, - 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, - 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x03, 0xBE, 0xEB, 0x50, 0x5F, 0xF0, 0xA6, - 0xD1, 0x4E, 0xE0, 0x38, 0x9E, 0x7D, 0xAA, 0x48, 0xA5, 0x78, 0x81, 0x55, 0x6C, 0x06, 0xEA, 0x3B, - 0x1F, 0xAD, 0x43, 0x45, 0x17, 0x02, 0x6F, 0xB4, 0x3F, 0x62, 0x7B, 0xF7, 0xA7, 0x19, 0xF2, 0x7F, - 0x8C, 0xFD, 0x5B, 0xFF, 0x00, 0xAD, 0x55, 0xE8, 0xAA, 0x55, 0x24, 0xB4, 0x4C, 0x2C, 0xAF, 0x72, - 0x4F, 0x39, 0x89, 0xFB, 0xCF, 0xF9, 0xD1, 0x91, 0xE6, 0x0C, 0xEE, 0xDB, 0xF5, 0xE6, 0xA3, 0xA2, - 0x93, 0x93, 0x7B, 0x81, 0x21, 0x6D, 0xCD, 0xDA, 0x87, 0x95, 0x9B, 0xD7, 0x81, 0x81, 0xEC, 0x2A, - 0x3A, 0x29, 0x5C, 0x02, 0x8A, 0x28, 0xA4, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, - 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, - 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, - 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, - 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, - 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, - 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x01, - 0x45, 0x14, 0x50, 0x07, 0xFF, 0xD9, -} ; diff --git a/lib/M5ez/examples/M5ez-demo/raw_githubusercontent_com.h b/lib/M5ez/examples/M5ez-demo/raw_githubusercontent_com.h deleted file mode 100644 index b7e9cbf..0000000 --- a/lib/M5ez/examples/M5ez-demo/raw_githubusercontent_com.h +++ /dev/null @@ -1,38 +0,0 @@ -// This is the root certificate include file for raw.githubusercontent.com -// as obtained by the get_cert script on: Wed Aug 15 20:22:14 CEST 2018 -// -// -// Certificate info: -// issuer= /C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV Root CA -// notAfter=Oct 22 12:00:00 2028 GMT -// - -const char* root_cert = \ - "-----BEGIN CERTIFICATE-----\n" \ - "MIIEsTCCA5mgAwIBAgIQBOHnpNxc8vNtwCtCuF0VnzANBgkqhkiG9w0BAQsFADBs\n" \ - "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" \ - "d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j\n" \ - "ZSBFViBSb290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowcDEL\n" \ - "MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3\n" \ - "LmRpZ2ljZXJ0LmNvbTEvMC0GA1UEAxMmRGlnaUNlcnQgU0hBMiBIaWdoIEFzc3Vy\n" \ - "YW5jZSBTZXJ2ZXIgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2\n" \ - "4C/CJAbIbQRf1+8KZAayfSImZRauQkCbztyfn3YHPsMwVYcZuU+UDlqUH1VWtMIC\n" \ - "Kq/QmO4LQNfE0DtyyBSe75CxEamu0si4QzrZCwvV1ZX1QK/IHe1NnF9Xt4ZQaJn1\n" \ - "itrSxwUfqJfJ3KSxgoQtxq2lnMcZgqaFD15EWCo3j/018QsIJzJa9buLnqS9UdAn\n" \ - "4t07QjOjBSjEuyjMmqwrIw14xnvmXnG3Sj4I+4G3FhahnSMSTeXXkgisdaScus0X\n" \ - "sh5ENWV/UyU50RwKmmMbGZJ0aAo3wsJSSMs5WqK24V3B3aAguCGikyZvFEohQcft\n" \ - "bZvySC/zA/WiaJJTL17jAgMBAAGjggFJMIIBRTASBgNVHRMBAf8ECDAGAQH/AgEA\n" \ - "MA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw\n" \ - "NAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy\n" \ - "dC5jb20wSwYDVR0fBEQwQjBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNlcnQuY29t\n" \ - "L0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDA9BgNVHSAENjA0MDIG\n" \ - "BFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQ\n" \ - "UzAdBgNVHQ4EFgQUUWj/kK8CB3U8zNllZGKiErhZcjswHwYDVR0jBBgwFoAUsT7D\n" \ - "aQP4v0cB1JgmGggC72NkK8MwDQYJKoZIhvcNAQELBQADggEBABiKlYkD5m3fXPwd\n" \ - "aOpKj4PWUS+Na0QWnqxj9dJubISZi6qBcYRb7TROsLd5kinMLYBq8I4g4Xmk/gNH\n" \ - "E+r1hspZcX30BJZr01lYPf7TMSVcGDiEo+afgv2MW5gxTs14nhr9hctJqvIni5ly\n" \ - "/D6q1UEL2tU2ob8cbkdJf17ZSHwD2f2LSaCYJkJA69aSEaRkCldUxPUd1gJea6zu\n" \ - "xICaEnL6VpPX/78whQYwvwt/Tv9XBZ0k7YXDK/umdaisLRbvfXknsuvCnQsH6qqF\n" \ - "0wGjIChBWUMo0oHjqvbsezt3tkBigAVBRQHvFwY+3sAzm2fTYS5yh+Rp/BIAV0Ae\n" \ - "cPUeybQ=\n" \ - "-----END CERTIFICATE-----\n"; diff --git a/lib/M5ez/examples/M5ez-demo/z-sysInfo.ino b/lib/M5ez/examples/M5ez-demo/z-sysInfo.ino deleted file mode 100644 index 3717724..0000000 --- a/lib/M5ez/examples/M5ez-demo/z-sysInfo.ino +++ /dev/null @@ -1,82 +0,0 @@ -/* - * - * This is a "z-sketch". It means you can run this sketch on its own, or use it as a sub-sketch of some bigger program - * See the M5ez user manual under z-sketches at https://github.com/ropg/M5ez - * - */ - -#ifndef MAIN_DECLARED - -#include -#include - -void setup() { - ez.begin(); - sysInfo(); -} - -void loop() { - -} - -String exit_button = ""; - -#else - -String exit_button = "Exit"; - -#endif // #ifndef MAIN_DECLARED - - - -void sysInfo() { - sysInfoPage1(); - while(true) { - String btn = ez.buttons.poll(); - if (btn == "up") sysInfoPage1(); - if (btn == "down") sysInfoPage2(); - if (btn == "Exit") break; - } -} - -#include - -void sysInfoPage1() { - const byte tab = 120; - ez.screen.clear(); - ez.header.show("System Info (1/2)"); - ez.buttons.show("#" + exit_button + "#down"); - ez.canvas.font(&FreeSans9pt7b); - ez.canvas.lmargin(10); - ez.canvas.println(""); - ez.canvas.print("CPU freq:"); ez.canvas.x(tab); ez.canvas.println(String(ESP.getCpuFreqMHz()) + " MHz"); - ez.canvas.print("CPU cores:"); ez.canvas.x(tab); ez.canvas.println("2"); // :) - ez.canvas.print("Chip rev.:"); ez.canvas.x(tab); ez.canvas.println(String(ESP.getChipRevision())); - ez.canvas.print("Flash speed:"); ez.canvas.x(tab); ez.canvas.println(String(ESP.getFlashChipSpeed() / 1000000) + " MHz"); - ez.canvas.print("Flash size:"); ez.canvas.x(tab); ez.canvas.println(String(ESP.getFlashChipSize() / 1000000) + " MB"); - ez.canvas.print("ESP SDK:"); ez.canvas.x(tab); ez.canvas.println(String(ESP.getSdkVersion())); - ez.canvas.print("M5ez:"); ez.canvas.x(tab); ez.canvas.println(String(ez.version())); -} - -void sysInfoPage2() { - const String SD_Type[5] = {"NONE", "MMC", "SD", "SDHC", "UNKNOWN"}; - const byte tab = 140; - ez.screen.clear(); - ez.header.show("System Info (2/2)"); - ez.buttons.show("up#" + exit_button + "#"); - ez.canvas.font(&FreeSans9pt7b); - ez.canvas.lmargin(10); - ez.canvas.println(""); - ez.canvas.print("Free RAM:"); ez.canvas.x(tab); ez.canvas.println(String((long)ESP.getFreeHeap()) + " bytes"); - ez.canvas.print("Min. free seen:"); ez.canvas.x(tab); ez.canvas.println(String((long)esp_get_minimum_free_heap_size()) + " bytes"); - const int sd_type = SD.cardType(); - - SPIFFS.begin(); - ez.canvas.print("SPIFFS size:"); ez.canvas.x(tab); ez.canvas.println(String((long)SPIFFS.totalBytes()) + " bytes"); - ez.canvas.print("SPIFFS used:"); ez.canvas.x(tab); ez.canvas.println(String((long)SPIFFS.usedBytes()) + " bytes"); - ez.canvas.print("SD type:"); ez.canvas.x(tab); ez.canvas.println(SD_Type[sd_type]); - if (sd_type != 0) { - ez.canvas.print("SD size:"); ez.canvas.x(tab); ez.canvas.println(String((long)SD.cardSize() / 1000000) + " MB"); - ez.canvas.print("SD used:"); ez.canvas.x(tab); ez.canvas.println(String((long)SD.usedBytes() / 1000000) + " MB"); - } -} diff --git a/lib/M5ez/examples/OTA_https/OTA_https.ino b/lib/M5ez/examples/OTA_https/OTA_https.ino deleted file mode 100644 index 2d5009a..0000000 --- a/lib/M5ez/examples/OTA_https/OTA_https.ino +++ /dev/null @@ -1,26 +0,0 @@ -#include -#include - -void setup() { - ez.begin(); - ezMenu OTA_menu ("OTA via https demo"); - OTA_menu.addItem("Settings", ez.settings.menu); - OTA_menu.addItem("Get M5ez demo", OTA_demo); - OTA_menu.run(); -} - -void loop() { -} - -void OTA_demo() { - if (ez.msgBox("Get M5ez demo program", "This will download and replace the current sketch with the M5ez demo program.", "Cancel#OK#") == "OK") { - ezProgressBar progress_bar("OTA update in progress", "Downloading ...", "Abort"); - #include "raw_githubusercontent_com.h" // the root certificate is now in const char * root_cert - if (ez.wifi.update("https://raw.githubusercontent.com/ropg/M5ez/master/compiled_binaries/M5ez-demo.bin", root_cert, &progress_bar)) { - ez.msgBox("Over The Air updater", "OTA download successful. Reboot to new firmware", "Reboot"); - ESP.restart(); - } else { - ez.msgBox("OTA error", ez.wifi.updateError(), "OK"); - } - } -} diff --git a/lib/M5ez/examples/OTA_https/README.md b/lib/M5ez/examples/OTA_https/README.md deleted file mode 100644 index 4b85368..0000000 --- a/lib/M5ez/examples/OTA_https/README.md +++ /dev/null @@ -1,129 +0,0 @@ -## Over The Air (OTA) firmware updates via https - -OTA stands for Over The Air, and in the context of the M5Stack usually means we're upgrading compiled firmware via the WiFi network, without hooking the USB port to a computer and running the Arduino IDE. The ez.wifi.update routine that we will be using to update the firmware uses https to transport the firmware, because code that is going to be running on all sorts of devices should be encrypted. - -![](../../images/ezProgressBar.png) - -### Using the demo - -Load this program in your Arduino IDE and load it to your M5Stack. The menu will allow you to get the larger M5ez-demo program from the `/compiled_binaries` directory on the M5ez Github repository and replaces this `OTA_http` demo sketch with it. There's a menu item that points to M5ez's built-in WiFi menu in case you haven't set up any autoconnecting networks with M5ez before. - -(As it happens the M5ez-demo program allows you to load a compiled version of this demo program again, so you can Over-The-Air update back and forth. Not because that's a useful things to do, but it does serve to demonstrate the mechanism.) - -### Using `ez.wifi.update` in your own code - -The steps below may seem a bit complicated. Most of that is due to the need to include a root certyificate for the server we will be connecting to. This is to prevent evildoers on the internet from swapping their malicious firmware for yours, somewhere along the way. And that was the whole point of using https over some unencrypted method in the first place. - -In case you want to use it from your own code, follow the follwing steps: - -* Export your firmare binary by selecting "Sketch / Export compiled binary in the Arduino IDE. The binary will be in the sketch directory once compilation is done. - -* Put it on the web at some server that supports https. Someplace on Github works well. - -* Let's assume we want our code to get the `M5ez-demo.bin` image from the `/compiled_binaries` directory of this repository. We then [browse](https://github.com/ropg/M5ez/blob/master/compiled_binaries/M5ez-demo.bin) to that, and get a download link called "[view raw](https://github.com/ropg/M5ez/blob/master/compiled_binaries/M5ez-demo.bin?raw=true)" which, if we right-click and hit "save link as" points to `https://github.com/ropg/M5ez/blob/master/compiled_binaries/M5ez-demo.bin?raw=true`. - -* Download the `get_cert` script from the `/tools` directory on this repository to a MacOS or Linux computer, or a linux-like shell on your windows machine. - -* Make the script executable with `chmod a+x get_cert`. - -* Make sure you have the `opensll` command-line utility, or download and install it if you don't. - -* Run `./get_cert https://github.com/ropg/M5ez/blob/master/compiled_binaries/M5ez-demo.bin?raw=true`, or in other words, supply the URL that you want to download from. - -``` -your_prompt$ ./get_cert https://github.com/ropg/M5ez/blob/master/compiled_binaries/M5ez-demo.bin?raw=true -The effective download URL (after resolving forwards) is: - https://raw.githubusercontent.com/ropg/M5ez/master/compiled_binaries/M5ez-demo.bin - -The root certificate include file is saved as: - raw_githubusercontent_com.h -``` - -* As you can see the script tells you two things. First it will tell you what the "real" URL is. That is: what site and path does the first URL evetually redirect to. That is the URL you will want to use in your code, and that is the domain you want the root certificate for. - -* It will also have saved a `.h` include file named after the domain (with dots replaced by underscores) that holds the root certificate in a format ready to be used by our code. Including that anywhere in your code will be replaced by a definition of a `const char *` variable called `root_cert` that can be passed to `ez.wifi.update`. Here's what that file looks like: - -``` -your_prompt$ cat raw_githubusercontent_com.h -// This is the root certificate include file for raw.githubusercontent.com -// as obtained by the get_cert script on: Wed Aug 15 21:34:20 CEST 2018 -// -// -// Certificate info: -// issuer= /C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV Root CA -// notAfter=Oct 22 12:00:00 2028 GMT -// - -const char* root_cert = \ - "-----BEGIN CERTIFICATE-----\n" \ - "MIIEsTCCA5mgAwIBAgIQBOHnpNxc8vNtwCtCuF0VnzANBgkqhkiG9w0BAQsFADBs\n" \ - "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" \ - "d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j\n" \ - "ZSBFViBSb290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowcDEL\n" \ - "MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3\n" \ - "LmRpZ2ljZXJ0LmNvbTEvMC0GA1UEAxMmRGlnaUNlcnQgU0hBMiBIaWdoIEFzc3Vy\n" \ - "YW5jZSBTZXJ2ZXIgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2\n" \ - "4C/CJAbIbQRf1+8KZAayfSImZRauQkCbztyfn3YHPsMwVYcZuU+UDlqUH1VWtMIC\n" \ - "Kq/QmO4LQNfE0DtyyBSe75CxEamu0si4QzrZCwvV1ZX1QK/IHe1NnF9Xt4ZQaJn1\n" \ - "itrSxwUfqJfJ3KSxgoQtxq2lnMcZgqaFD15EWCo3j/018QsIJzJa9buLnqS9UdAn\n" \ - "4t07QjOjBSjEuyjMmqwrIw14xnvmXnG3Sj4I+4G3FhahnSMSTeXXkgisdaScus0X\n" \ - "sh5ENWV/UyU50RwKmmMbGZJ0aAo3wsJSSMs5WqK24V3B3aAguCGikyZvFEohQcft\n" \ - "bZvySC/zA/WiaJJTL17jAgMBAAGjggFJMIIBRTASBgNVHRMBAf8ECDAGAQH/AgEA\n" \ - "MA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw\n" \ - "NAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy\n" \ - "dC5jb20wSwYDVR0fBEQwQjBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNlcnQuY29t\n" \ - "L0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDA9BgNVHSAENjA0MDIG\n" \ - "BFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQ\n" \ - "UzAdBgNVHQ4EFgQUUWj/kK8CB3U8zNllZGKiErhZcjswHwYDVR0jBBgwFoAUsT7D\n" \ - "aQP4v0cB1JgmGggC72NkK8MwDQYJKoZIhvcNAQELBQADggEBABiKlYkD5m3fXPwd\n" \ - "aOpKj4PWUS+Na0QWnqxj9dJubISZi6qBcYRb7TROsLd5kinMLYBq8I4g4Xmk/gNH\n" \ - "E+r1hspZcX30BJZr01lYPf7TMSVcGDiEo+afgv2MW5gxTs14nhr9hctJqvIni5ly\n" \ - "/D6q1UEL2tU2ob8cbkdJf17ZSHwD2f2LSaCYJkJA69aSEaRkCldUxPUd1gJea6zu\n" \ - "xICaEnL6VpPX/78whQYwvwt/Tv9XBZ0k7YXDK/umdaisLRbvfXknsuvCnQsH6qqF\n" \ - "0wGjIChBWUMo0oHjqvbsezt3tkBigAVBRQHvFwY+3sAzm2fTYS5yh+Rp/BIAV0Ae\n" \ - "cPUeybQ=\n" \ - "-----END CERTIFICATE-----\n"; -``` - -* Place this certificate include file in the directory of your sketch. In your code, you can now use something like below to do the actual upgrade. - -``` -ezProgressBar pb("OTA update in progress", "Downloading ...", "Abort"); -String url = "https://raw.githubusercontent.com/ropg/M5ez/master/compiled_binaries/M5ez-demo.bin"; -#include "raw_githubusercontent_com.h" -if (ez.wifi.update(url, root_cert, &pb)) { - ez.msgBox("Over The Air updater", "OTA download successful. Reboot to new firmware", "Reboot"); - ESP.restart(); -} else { - ez.msgBox("OTA error", ez.wifi.updateError(), "OK"); -} -``` - -* And presto: you have Over The Air updates using https. - -### Finer points - -* The above example is actually from the `OTA_https` demo. Naturally you would replace the certificate and URL with your own. - -* Over the Air updates only work if your partition table allows for two concurrent firmwares to be present. This means that if you do not plan to use OTA, you can have twice as much space for your programs. (Tools / Partition Scheme / No OTA in the Arduino IDE.) - -* As you can see, the `ez.wifi.update` function takes three arguments: the first one is the https url for the firmware, the second one is the root certificate and the (optional) third one is a pointer to the ezProgressBar instance that will show progress for this download. (Don't forget the ampersand - `&` - in front.) - -* Should `ez.wifi.update` return `false`: the human-readable error is provided when you call `ez.wifi.updateError()`. - -* If your download is much more sneaky and silent, you can also just do something like: - -``` -#include "updates_com.h" -if (ez.wifi.update("https://updates.com/path/firmware.bin", root_cert)) { - ESP.restart; -} -``` - -* For simplicity's sake, this ignores the obvious need to figure out whether there actually are any new updates that the user hasn't installed yet. This will just get the file, put it in flash and switch to it. - -* If the certificate on the server is signed by a different root certificate - which may happen before the cert expiration date - updates will not work anymore. If you plan to deploy lots of IoT appliances using this update method it is probably best to control your own server and have a root certificate on it that expires sometime well after you will have sold your startup. - -* `ez.wifi.update` cannot be used to pass basic auth credentials with the user:pass@host notation. - -* Various other limitations of the WifiClientSecure class apply: this is IoT land... \ No newline at end of file diff --git a/lib/M5ez/examples/OTA_https/raw_githubusercontent_com.h b/lib/M5ez/examples/OTA_https/raw_githubusercontent_com.h deleted file mode 100644 index b7e9cbf..0000000 --- a/lib/M5ez/examples/OTA_https/raw_githubusercontent_com.h +++ /dev/null @@ -1,38 +0,0 @@ -// This is the root certificate include file for raw.githubusercontent.com -// as obtained by the get_cert script on: Wed Aug 15 20:22:14 CEST 2018 -// -// -// Certificate info: -// issuer= /C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV Root CA -// notAfter=Oct 22 12:00:00 2028 GMT -// - -const char* root_cert = \ - "-----BEGIN CERTIFICATE-----\n" \ - "MIIEsTCCA5mgAwIBAgIQBOHnpNxc8vNtwCtCuF0VnzANBgkqhkiG9w0BAQsFADBs\n" \ - "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" \ - "d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j\n" \ - "ZSBFViBSb290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowcDEL\n" \ - "MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3\n" \ - "LmRpZ2ljZXJ0LmNvbTEvMC0GA1UEAxMmRGlnaUNlcnQgU0hBMiBIaWdoIEFzc3Vy\n" \ - "YW5jZSBTZXJ2ZXIgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2\n" \ - "4C/CJAbIbQRf1+8KZAayfSImZRauQkCbztyfn3YHPsMwVYcZuU+UDlqUH1VWtMIC\n" \ - "Kq/QmO4LQNfE0DtyyBSe75CxEamu0si4QzrZCwvV1ZX1QK/IHe1NnF9Xt4ZQaJn1\n" \ - "itrSxwUfqJfJ3KSxgoQtxq2lnMcZgqaFD15EWCo3j/018QsIJzJa9buLnqS9UdAn\n" \ - "4t07QjOjBSjEuyjMmqwrIw14xnvmXnG3Sj4I+4G3FhahnSMSTeXXkgisdaScus0X\n" \ - "sh5ENWV/UyU50RwKmmMbGZJ0aAo3wsJSSMs5WqK24V3B3aAguCGikyZvFEohQcft\n" \ - "bZvySC/zA/WiaJJTL17jAgMBAAGjggFJMIIBRTASBgNVHRMBAf8ECDAGAQH/AgEA\n" \ - "MA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw\n" \ - "NAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy\n" \ - "dC5jb20wSwYDVR0fBEQwQjBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNlcnQuY29t\n" \ - "L0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDA9BgNVHSAENjA0MDIG\n" \ - "BFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQ\n" \ - "UzAdBgNVHQ4EFgQUUWj/kK8CB3U8zNllZGKiErhZcjswHwYDVR0jBBgwFoAUsT7D\n" \ - "aQP4v0cB1JgmGggC72NkK8MwDQYJKoZIhvcNAQELBQADggEBABiKlYkD5m3fXPwd\n" \ - "aOpKj4PWUS+Na0QWnqxj9dJubISZi6qBcYRb7TROsLd5kinMLYBq8I4g4Xmk/gNH\n" \ - "E+r1hspZcX30BJZr01lYPf7TMSVcGDiEo+afgv2MW5gxTs14nhr9hctJqvIni5ly\n" \ - "/D6q1UEL2tU2ob8cbkdJf17ZSHwD2f2LSaCYJkJA69aSEaRkCldUxPUd1gJea6zu\n" \ - "xICaEnL6VpPX/78whQYwvwt/Tv9XBZ0k7YXDK/umdaisLRbvfXknsuvCnQsH6qqF\n" \ - "0wGjIChBWUMo0oHjqvbsezt3tkBigAVBRQHvFwY+3sAzm2fTYS5yh+Rp/BIAV0Ae\n" \ - "cPUeybQ=\n" \ - "-----END CERTIFICATE-----\n"; diff --git a/lib/M5ez/examples/SortedMenus/SortedMenus.ino b/lib/M5ez/examples/SortedMenus/SortedMenus.ino deleted file mode 100755 index 2445bbf..0000000 --- a/lib/M5ez/examples/SortedMenus/SortedMenus.ino +++ /dev/null @@ -1,50 +0,0 @@ -#include - -// A sorting method takes the form: -// bool func(const char* s1, const char* s2) { return s1 comparedTo s2; } -// Built-in sorting methos include: -// For sorting by Names as quickly as possible -// ezMenu::sort_asc_name_cs Sort ascending by menu name, case sensitive -// ezMenu::sort_asc_name_ci Sort ascending by menu name, case insensitive -// ezMenu::sort_dsc_name_cs Sort descending by menu name, case sensitive -// ezMenu::sort_dsc_name_ci Sort descending by menu name, case insensitive - -// For sorting by Caption if there is one, falling back to sorting by Name if no Caption is provided (all purpose) -// ezMenu::sort_asc_caption_cs Sort ascending by menu caption or name if no caption, case sensitive -// ezMenu::sort_asc_caption_ci Sort ascending by menu caption or name if no caption, incase sensitive -// ezMenu::sort_dsc_caption_cs Sort descending by menu caption or name if no caption, case sensitive -// ezMenu::sort_dsc_caption_ci Sort descending by menu caption or name if no caption, case insensitive - - -void setup() { - ez.begin(); -} - - -void loop() { - ezMenu menu("Menu Sorting Demo"); - menu.txtSmall(); - // You can simply call setSortFunction once here, and the menu will always stay sorted. - // menu.setSortFunction(yourSortingFunction); - menu.buttons("asc # Asc # dsc # Dsc # Unsorted #"); - menu.addItem("Just a few"); - menu.addItem("words of"); - menu.addItem("random text in"); - menu.addItem("No Particular Order."); - menu.addItem("Press buttons"); - menu.addItem("to re-sort"); - menu.addItem("NAME IS IGNORED! Value is sorted. | as you like."); - - // Or, you can call setSortFunction any time to re-sort the menu. - // Once set, additional insertions are made in sorted order as well. - // No need to call setSortFunction again unless you want to change to a different order. - while(true) { - menu.runOnce(); - String result = menu.pickButton(); - if (result == "asc") menu.setSortFunction(ezMenu::sort_asc_caption_ci); - else if(result == "Asc") menu.setSortFunction(ezMenu::sort_asc_caption_cs); - else if(result == "dsc") menu.setSortFunction(ezMenu::sort_dsc_caption_ci); - else if(result == "Dsc") menu.setSortFunction(ezMenu::sort_dsc_caption_cs); - else if(result == "Unsorted") menu.setSortFunction(nullptr); - } -} diff --git a/lib/M5ez/examples/z-ezTimeLog/z-ezTimeLog.ino b/lib/M5ez/examples/z-ezTimeLog/z-ezTimeLog.ino deleted file mode 100644 index 066aa6e..0000000 --- a/lib/M5ez/examples/z-ezTimeLog/z-ezTimeLog.ino +++ /dev/null @@ -1,49 +0,0 @@ -/* - * - * This is a "z-sketch". It means you can run this sketch on its own, or use it as a sub-sketch of some bigger program - * See the M5ez user manual under z-sketches at https://github.com/ropg/M5ez - * - * This program sets the ezTime NTP interval to 20 seconds (way too often, use only for demonstration purposes) and - * then show the debug log of ezTime so you can see it update its clock. The output will scroll when it reaches the - * bottom of the screen. - * - */ - - -#ifndef MAIN_DECLARED - -#include -#include -#include - -void setup() { - ez.begin(); - ezTimeLog(); - - while(true) { - ez.yield(); - } -} - -void loop() { - -} - -#endif // #ifndef MAIN_DECLARED - - - -void ezTimeLog() { - - ez.header.show("ezTime log"); - ez.canvas.scroll(true); - ezt::setDebug(INFO, ez.canvas); - ezt::setInterval(20); - - #ifdef MAIN_DECLARED - ez.buttons.wait("Abort"); - ezt::setDebug(NONE, Serial); - ezt::setInterval(600); - #endif - -} diff --git a/lib/M5ez/examples/z-sysInfo/z-sysInfo.ino b/lib/M5ez/examples/z-sysInfo/z-sysInfo.ino deleted file mode 100644 index 3717724..0000000 --- a/lib/M5ez/examples/z-sysInfo/z-sysInfo.ino +++ /dev/null @@ -1,82 +0,0 @@ -/* - * - * This is a "z-sketch". It means you can run this sketch on its own, or use it as a sub-sketch of some bigger program - * See the M5ez user manual under z-sketches at https://github.com/ropg/M5ez - * - */ - -#ifndef MAIN_DECLARED - -#include -#include - -void setup() { - ez.begin(); - sysInfo(); -} - -void loop() { - -} - -String exit_button = ""; - -#else - -String exit_button = "Exit"; - -#endif // #ifndef MAIN_DECLARED - - - -void sysInfo() { - sysInfoPage1(); - while(true) { - String btn = ez.buttons.poll(); - if (btn == "up") sysInfoPage1(); - if (btn == "down") sysInfoPage2(); - if (btn == "Exit") break; - } -} - -#include - -void sysInfoPage1() { - const byte tab = 120; - ez.screen.clear(); - ez.header.show("System Info (1/2)"); - ez.buttons.show("#" + exit_button + "#down"); - ez.canvas.font(&FreeSans9pt7b); - ez.canvas.lmargin(10); - ez.canvas.println(""); - ez.canvas.print("CPU freq:"); ez.canvas.x(tab); ez.canvas.println(String(ESP.getCpuFreqMHz()) + " MHz"); - ez.canvas.print("CPU cores:"); ez.canvas.x(tab); ez.canvas.println("2"); // :) - ez.canvas.print("Chip rev.:"); ez.canvas.x(tab); ez.canvas.println(String(ESP.getChipRevision())); - ez.canvas.print("Flash speed:"); ez.canvas.x(tab); ez.canvas.println(String(ESP.getFlashChipSpeed() / 1000000) + " MHz"); - ez.canvas.print("Flash size:"); ez.canvas.x(tab); ez.canvas.println(String(ESP.getFlashChipSize() / 1000000) + " MB"); - ez.canvas.print("ESP SDK:"); ez.canvas.x(tab); ez.canvas.println(String(ESP.getSdkVersion())); - ez.canvas.print("M5ez:"); ez.canvas.x(tab); ez.canvas.println(String(ez.version())); -} - -void sysInfoPage2() { - const String SD_Type[5] = {"NONE", "MMC", "SD", "SDHC", "UNKNOWN"}; - const byte tab = 140; - ez.screen.clear(); - ez.header.show("System Info (2/2)"); - ez.buttons.show("up#" + exit_button + "#"); - ez.canvas.font(&FreeSans9pt7b); - ez.canvas.lmargin(10); - ez.canvas.println(""); - ez.canvas.print("Free RAM:"); ez.canvas.x(tab); ez.canvas.println(String((long)ESP.getFreeHeap()) + " bytes"); - ez.canvas.print("Min. free seen:"); ez.canvas.x(tab); ez.canvas.println(String((long)esp_get_minimum_free_heap_size()) + " bytes"); - const int sd_type = SD.cardType(); - - SPIFFS.begin(); - ez.canvas.print("SPIFFS size:"); ez.canvas.x(tab); ez.canvas.println(String((long)SPIFFS.totalBytes()) + " bytes"); - ez.canvas.print("SPIFFS used:"); ez.canvas.x(tab); ez.canvas.println(String((long)SPIFFS.usedBytes()) + " bytes"); - ez.canvas.print("SD type:"); ez.canvas.x(tab); ez.canvas.println(SD_Type[sd_type]); - if (sd_type != 0) { - ez.canvas.print("SD size:"); ez.canvas.x(tab); ez.canvas.println(String((long)SD.cardSize() / 1000000) + " MB"); - ez.canvas.print("SD used:"); ez.canvas.x(tab); ez.canvas.println(String((long)SD.usedBytes() / 1000000) + " MB"); - } -} diff --git a/lib/M5ez/images/FACES.png b/lib/M5ez/images/FACES.png deleted file mode 100644 index ead00ee..0000000 Binary files a/lib/M5ez/images/FACES.png and /dev/null differ diff --git a/lib/M5ez/images/M5ez.png b/lib/M5ez/images/M5ez.png deleted file mode 100644 index e55f6bb..0000000 Binary files a/lib/M5ez/images/M5ez.png and /dev/null differ diff --git a/lib/M5ez/images/buttons1.png b/lib/M5ez/images/buttons1.png deleted file mode 100644 index 04b1e81..0000000 Binary files a/lib/M5ez/images/buttons1.png and /dev/null differ diff --git a/lib/M5ez/images/buttons2.png b/lib/M5ez/images/buttons2.png deleted file mode 100644 index eb0c904..0000000 Binary files a/lib/M5ez/images/buttons2.png and /dev/null differ diff --git a/lib/M5ez/images/ezMenu1.png b/lib/M5ez/images/ezMenu1.png deleted file mode 100644 index 73ba8ed..0000000 Binary files a/lib/M5ez/images/ezMenu1.png and /dev/null differ diff --git a/lib/M5ez/images/ezMenu2.png b/lib/M5ez/images/ezMenu2.png deleted file mode 100644 index a42c9ab..0000000 Binary files a/lib/M5ez/images/ezMenu2.png and /dev/null differ diff --git a/lib/M5ez/images/ezMenu3.png b/lib/M5ez/images/ezMenu3.png deleted file mode 100644 index 941f4d1..0000000 Binary files a/lib/M5ez/images/ezMenu3.png and /dev/null differ diff --git a/lib/M5ez/images/ezProgressBar.png b/lib/M5ez/images/ezProgressBar.png deleted file mode 100644 index 1c37f09..0000000 Binary files a/lib/M5ez/images/ezProgressBar.png and /dev/null differ diff --git a/lib/M5ez/images/msgBox1.png b/lib/M5ez/images/msgBox1.png deleted file mode 100644 index 06627af..0000000 Binary files a/lib/M5ez/images/msgBox1.png and /dev/null differ diff --git a/lib/M5ez/images/msgBox2.png b/lib/M5ez/images/msgBox2.png deleted file mode 100644 index 18beab1..0000000 Binary files a/lib/M5ez/images/msgBox2.png and /dev/null differ diff --git a/lib/M5ez/images/textBox.png b/lib/M5ez/images/textBox.png deleted file mode 100644 index 83e1f50..0000000 Binary files a/lib/M5ez/images/textBox.png and /dev/null differ diff --git a/lib/M5ez/images/textInput.png b/lib/M5ez/images/textInput.png deleted file mode 100644 index 72d0a24..0000000 Binary files a/lib/M5ez/images/textInput.png and /dev/null differ diff --git a/lib/M5ez/images/theme-dark.jpg b/lib/M5ez/images/theme-dark.jpg deleted file mode 100644 index 5fb13f9..0000000 Binary files a/lib/M5ez/images/theme-dark.jpg and /dev/null differ diff --git a/lib/M5ez/images/theme-default.jpg b/lib/M5ez/images/theme-default.jpg deleted file mode 100644 index 8cc52bd..0000000 Binary files a/lib/M5ez/images/theme-default.jpg and /dev/null differ diff --git a/lib/M5ez/images/truetype2gfx-screenshot.png b/lib/M5ez/images/truetype2gfx-screenshot.png deleted file mode 100644 index 340517a..0000000 Binary files a/lib/M5ez/images/truetype2gfx-screenshot.png and /dev/null differ diff --git a/lib/M5ez/images/wifi1.png b/lib/M5ez/images/wifi1.png deleted file mode 100644 index 871931d..0000000 Binary files a/lib/M5ez/images/wifi1.png and /dev/null differ diff --git a/lib/M5ez/images/wifi2.png b/lib/M5ez/images/wifi2.png deleted file mode 100644 index 69a4fb8..0000000 Binary files a/lib/M5ez/images/wifi2.png and /dev/null differ diff --git a/lib/M5ez/images/wifi3.png b/lib/M5ez/images/wifi3.png deleted file mode 100644 index bc7c231..0000000 Binary files a/lib/M5ez/images/wifi3.png and /dev/null differ diff --git a/lib/M5ez/images/wifi4.png b/lib/M5ez/images/wifi4.png deleted file mode 100644 index 9ba4e60..0000000 Binary files a/lib/M5ez/images/wifi4.png and /dev/null differ diff --git a/lib/M5ez/keywords.txt b/lib/M5ez/keywords.txt deleted file mode 100644 index 43ca05e..0000000 --- a/lib/M5ez/keywords.txt +++ /dev/null @@ -1,118 +0,0 @@ -ez KEYWORD3 -begin KEYWORD2 - -screen KEYWORD3 -clear KEYWORD2 -background KEYWORD2 - -header KEYWORD3 -show KEYWORD2 -shown KEYWORD2 -title KEYWORD2 -insert KEYWORD2 -remove KEYWORD2 -position KEYWORD2 -draw KEYWORD2 - -canvas KEYWORD3 -reset KEYWORD2 -top KEYWORD2 -bottom KEYWORD2 -left KEYWORD2 -right KEYWORD2 -height KEYWORD2 -width KEYWORD2 -print KEYWORD2 -println KEYWORD2 -printf KEYWORD2 -scroll KEYWORD2 -wrap KEYWORD2 -lmargin KEYWORD2 -font KEYWORD2 -color KEYWORD2 -x KEYWORD2 -y KEYWORD2 -pos KEYWORD2 - -buttons KEYWORD3 -poll KEYWORD2 -wait KEYWORD2 - -yield KEYWORD2 - -addEvent KEYWORD2 -removeEvent KEYWORD2 - - -msgBox KEYWORD2 - -textInput KEYWORD2 - -textBox KEYWORD2 - -faces KEYWORD3 -on KEYWORD2 - -# m5.lcd wrappers that make fonts easier -setFont KEYWORD2 -fontHeight KEYWORD2 - -settings KEYWORD3 -menu KEYWORD2 -add KEYWORD2 -remove KEYWORD2 -ReadFlash KEYWORD2 -WriteFlash KEYWORD2 -indexForSSID KEYWORD2 -update KEYWORD2 -updateError KEYWORD2 - -clock KEYWORD3 - -backlight KEYWORD3 - -menuObj KEYWORD2 - - -# Generic String object helper functions -rightOf KEYWORD2 -leftOf KEYWORD2 -chopString KEYWORD2 -charsFit KEYWORD2 -clipString KEYWORD2 -isBackExitOrDone KEYWORD2 - - -# ezProgressBar -ezProgressBar KEYWORD3 -value KEYWORD2 - -# ezMenu -ezMenu KEYWORD3 -addItem KEYWORD2 -deleteItem KEYWORD2 -setCaption KEYWORD2 -getItemNum KEYWORD2 -buttons KEYWORD2 -upOnFirst KEYWORD2 -leftOnFirst KEYWORD2 -downOnLast KEYWORD2 -rightOnLast KEYWORD2 -pick KEYWORD2 -pickName KEYWORD2 -pickCaption KEYWORD2 -run KEYWORD2 -runOnce KEYWORD2 -txtBig KEYWORD2 -txtSmall KEYWORD2 -txtFont KEYWORD2 -imgBackground KEYWORD2 -imgFromTop KEYWORD2 -imgCaptionFont KEYWORD2 -imgCaptionLocation KEYWORD2 -imgCaptionColor KEYWORD2 -imgCaptionMargins KEYWORD2 - -# because it was forgotten in the M5Stack keywords.txt -# (They only have it as "Lcd", but they made the "lcd" alias.) -lcd KEYWORD2 diff --git a/lib/M5ez/library.json b/lib/M5ez/library.json deleted file mode 100644 index 1ff9100..0000000 --- a/lib/M5ez/library.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "M5ez", - "description": "Complete interface builder for the M5Stack, an ESP32 based mini tinker-computer", - "keywords": "m5ez, m5stack, esp32", - "authors": { - "name": "Rop Gonggrijp", - "url": "https://github.com/ropg", - "maintainer": true - }, - "repository": { - "type": "git", - "url": "https://github.com/ropg/M5ez" - }, - "dependencies": { - "name": "M5Unified" - }, - "version": "2.1.2", - "framework": "arduino", - "platforms": "espressif32", - "build": { - "libArchive": false - } -} diff --git a/lib/M5ez/library.properties b/lib/M5ez/library.properties deleted file mode 100644 index 4dddef8..0000000 --- a/lib/M5ez/library.properties +++ /dev/null @@ -1,11 +0,0 @@ -name=M5ez -version=2.1.2 -author=Rop Gonggrijp -maintainer=Rop Gonggrijp -sentence=Complete interface builder for the M5Stack, an ESP32 based mini tinker-computer -paragraph=See more on https://github.com/ropg/M5ez -category=Display -url=https://github.com/ropg/M5ez -architectures=esp32 -includes=M5ez.h -depends=ezTime, M5Stack diff --git a/lib/M5ez/src/M5ez.cpp b/lib/M5ez/src/M5ez.cpp deleted file mode 100644 index 2ba42d0..0000000 --- a/lib/M5ez/src/M5ez.cpp +++ /dev/null @@ -1,1792 +0,0 @@ -#include - -#include - -#include -#include - -const char *M5EZ_TAG = "M5ez"; - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// T H E M E -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ezTheme::update(void) { - const GFXfont *font; - uint8_t radius; - - switch (M5.getBoard()) { - case m5::board_t::board_M5StickC: - font = hzk16; - ez.theme->header_height = 12; - ez.theme->button_height = 11; - radius = 3; - break; - case m5::board_t::board_M5StickCPlus: - case m5::board_t::board_M5StickCPlus2: - font = sans16; - ez.theme->header_height = 23; - ez.theme->button_height = 19; - radius = 8; - break; - case m5::board_t::board_M5StackCore2: - case m5::board_t::board_M5Stack: - default: - font = (&FreeMono12pt7b); - ez.theme->header_height = 23; - ez.theme->button_height = 24; - radius = 8; - } - - ez.theme->header_font = font; - ez.theme->print_font = font; - ez.theme->button_font = font; - ez.theme->menu_big_font = font; - ez.theme->menu_small_font = font; - ez.theme->msg_font = font = font; - - ez.theme->button_radius = radius; - ez.theme->menu_item_radius = radius; -} - -void ezTheme::begin() { - if (!ez.themes.size()) { - ezTheme defaultTheme; - defaultTheme.add(); - } - ez.theme = &ez.themes[0]; - update(); - Preferences prefs; - prefs.begin("M5ez", true); // read-only - select(prefs.getString("theme", "").c_str()); - prefs.end(); -} - -void ezTheme::add() { - ez.themes.push_back(*this); -} - -bool ezTheme::select(std::string name) { - for (uint8_t n = 0; n < ez.themes.size(); n++) { - if (ez.themes[n].name == name) { - ez.theme = &ez.themes[n]; - update(); - return true; - } - } - - return false; -} - -void ezTheme::menu() { - std::string orig_name = ez.theme->name; - ezMenu thememenu("Theme chooser"); - thememenu.txtSmall(); - thememenu.buttons({"OK", "down"}); - thememenu.downOnLast("first"); - - for (uint8_t n = 0; n < ez.themes.size(); n++) { - thememenu.addItem(ez.themes[n].name); - } - thememenu.addItem("Back"); - while (thememenu.runOnce()) { - if (thememenu.pickName() == "Back") - break; - if (thememenu.pick()) { - ez.theme->select(thememenu.pickName()); - } - } - if (ez.theme->name != orig_name) { - Preferences prefs; - prefs.begin("M5ez"); - prefs.putString("theme", ez.theme->name.c_str()); - prefs.end(); - } - return; -} - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// S C R E E N -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -uint16_t ezScreen::_background; - -void ezScreen::begin() { - _background = ez.theme->background; - ez.header.begin(); - ez.canvas.begin(); - ez.buttons.begin(); -} - -uint16_t ezScreen::background() { - return _background; -} - -void ezScreen::clear() { - clear(ez.theme->background); -} - -void ezScreen::clear(uint16_t color) { - _background = color; - ez.header.clear(false); - ez.buttons.clear(false); - ez.canvas.reset(); - M5.Lcd.fillRect(0, 0, ez.canvas.tft.width, ez.canvas.tft.height, color); -} - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// H E A D E R -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -std::vector ezHeader::_widgets; -bool ezHeader::_shown; -std::string ezHeader::_title; - -void ezHeader::begin() { - _widgets.clear(); - insert(0, "title", 0, _drawTitle, true); - _shown = false; -} - -void ezHeader::_recalculate() { - bool we_have_leftover = false; - uint16_t x = 0; - for (uint8_t n = 0; n < _widgets.size(); n++) { // start from left, set x values - _widgets[n].x = x; - if (_widgets[n].leftover) { // until "leftover" widget (usually "title") - we_have_leftover = true; - break; - } - x += _widgets[n].w; - } - if (we_have_leftover) { // Then start from right setting x values - x = ez.canvas.tft.width; - for (int8_t n = _widgets.size() - 1; n >= 0; n--) { - if (_widgets[n].leftover) { // and set width of leftover widget to remainder - _widgets[n].w = x - _widgets[n].x; - break; - } - x -= _widgets[n].w; - _widgets[n].x = x; - } - } - if (_shown) - show(); -} - -void ezHeader::insert(uint8_t position, - std::string name, - uint16_t width, - void (*function)(uint16_t x, uint16_t w), - bool leftover /* = false */) { - for (uint8_t n = 0; n < _widgets.size(); n++) { - if (_widgets[n].leftover) - leftover = false; // ignore leftover if there already is one - if (_widgets[n].name == name) - return; // fail silently if trying to create two widgets with same name - } - if (position > _widgets.size()) - position = _widgets.size(); // interpret anything over current number of widgets as RIGHTMOST - header_widget_t wdgt; - wdgt.name = name; - wdgt.leftover = leftover; - wdgt.x = 0; - wdgt.w = width; - wdgt.function = function; - _widgets.insert(_widgets.begin() + position, wdgt); - _recalculate(); -} - -void ezHeader::remove(std::string name) { - for (uint8_t n = 0; n < _widgets.size(); n++) { - if (_widgets[n].name == name) { - _widgets.erase(_widgets.begin() + n); - _recalculate(); - } - } -} - -uint8_t ezHeader::position(std::string name) { - for (uint8_t n = 0; n < _widgets.size(); n++) { - if (_widgets[n].name == name) - return n; - } - return 0; -} - -void ezHeader::show(std::string t /* = "" */) { - _shown = true; - if (t != "") - _title = t; // only change title if provided - M5.Lcd.fillRect(0, 0, ez.canvas.tft.width, ez.theme->header_height, - ez.theme->header_bgcolor); // Clear header area - for (uint8_t n = 0; n < _widgets.size(); n++) { - (_widgets[n].function)(_widgets[n].x, _widgets[n].w); // Tell all header widgets to draw - } - ez.canvas.top(ez.theme->header_height); -} - -void ezHeader::draw(std::string name) { - if (!_shown) - return; - for (uint8_t n = 0; n < _widgets.size(); n++) { - if (_widgets[n].name == name) { - (_widgets[n].function)(_widgets[n].x, _widgets[n].w); - } - } -} - -void ezHeader::clear(bool wipe /* = true */) { - if (wipe) - M5.Lcd.fillRect(0, 0, ez.canvas.tft.width, ez.theme->header_height, ez.theme->background); - _shown = false; - ez.canvas.top(0); -} - -bool ezHeader::shown() { - return _shown; -} - -std::string ezHeader::title() { - return _title; -} - -void ezHeader::title(std::string t) { - _title = t; - if (_shown) - draw("title"); -} - -void ezHeader::_drawTitle(uint16_t x, uint16_t w) { - M5.Lcd.fillRect(x, 0, w, ez.theme->header_height, ez.theme->header_bgcolor); - M5.Lcd.setTextDatum(TL_DATUM); - M5.Lcd.setTextColor(ez.theme->header_fgcolor); - ez.setFont(ez.theme->header_font); - M5.Lcd.drawString(ez.clipString(_title, w - ez.theme->header_hmargin).c_str(), - x + ez.theme->header_hmargin, ez.theme->header_tmargin); -} - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// C A N V A S -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -ezTFT ezCanvas::tft; -uint8_t ezCanvas::_y, ezCanvas::_top, ezCanvas::_bottom; -uint16_t ezCanvas::_x, ezCanvas::_left, ezCanvas::_right, ezCanvas::_lmargin; -const GFXfont *ezCanvas::_font; -uint16_t ezCanvas::_color; -bool ezCanvas::_wrap, ezCanvas::_scroll; -std::vector ezCanvas::_printed; -uint32_t ezCanvas::_next_scroll; - -void ezCanvas::begin() { - switch (M5.getBoard()) { - case m5::board_t::board_M5StickC: - tft.width = 160; - tft.height = 80; - M5.Lcd.setRotation(3); - break; - case m5::board_t::board_M5StickCPlus: - case m5::board_t::board_M5StickCPlus2: - tft.width = 240; - tft.height = 135; - M5.Lcd.setRotation(3); - break; - case m5::board_t::board_M5StackCore2: - case m5::board_t::board_M5Stack: - default: - tft.width = 320; - tft.height = 240; - } - - _left = 0; - _right = ez.canvas.tft.width - 1; - _top = 0; - _bottom = ez.canvas.tft.height - 1; - ez.addEvent(ez.canvas.loop); - reset(); -} - -void ezCanvas::reset() { - _wrap = true; - _scroll = false; - _font = ez.theme->print_font; - _color = ez.theme->print_color; - _lmargin = 0; - _next_scroll = 0; - clear(); -} - -void ezCanvas::clear() { - M5.Lcd.fillRect(left(), top(), width(), height(), ez.screen.background()); - _x = _lmargin; - _y = 0; - _printed.clear(); -} - -uint8_t ezCanvas::top() { - return _top; -} - -uint8_t ezCanvas::bottom() { - return _bottom; -} - -uint16_t ezCanvas::left() { - return _left; -} - -uint16_t ezCanvas::right() { - return _right; -} - -uint8_t ezCanvas::height() { - return _bottom - _top + 1; -} - -uint16_t ezCanvas::width() { - return _right - _left + 1; -} - -bool ezCanvas::scroll() { - return _scroll; -} - -void ezCanvas::scroll(bool s) { - _scroll = s; -} - -bool ezCanvas::wrap() { - return _wrap; -} - -void ezCanvas::wrap(bool w) { - _wrap = w; -} - -uint16_t ezCanvas::lmargin() { - return _lmargin; -} - -void ezCanvas::lmargin(uint16_t newmargin) { - if (_x < newmargin) - _x = newmargin; - _lmargin = newmargin; -} - -void ezCanvas::font(const GFXfont *font) { - _font = font; -} - -const GFXfont *ezCanvas::font() { - return _font; -} - -void ezCanvas::color(uint16_t color) { - _color = color; -} - -uint16_t ezCanvas::color() { - return _color; -} - -void ezCanvas::pos(uint16_t x, uint8_t y) { - _x = x; - _y = y; -} - -uint16_t ezCanvas::x() { - return _x; -} - -void ezCanvas::x(uint16_t x) { - _x = x; -} - -uint8_t ezCanvas::y() { - return _y; -} - -void ezCanvas::y(uint8_t y) { - _y = y; -} - -void ezCanvas::top(uint8_t newtop) { - if (_y < newtop) - _y = newtop; - _top = newtop; -} - -void ezCanvas::bottom(uint8_t newbottom) { - _bottom = newbottom; -} - -size_t ezCanvas::write(uint8_t c) { - _print(std::string(c, 1)); - return 1; -} - -size_t ezCanvas::write(const char *str) { - std::string tmp = std::string(str); - _print(tmp); - return tmp.size(); -} - -size_t ezCanvas::write(const uint8_t *buffer, size_t size) { - std::string tmp = std::string((const char *)buffer, size); - _print(tmp); - return size; -} - -uint16_t ezCanvas::loop(void *context) { - if (_next_scroll && millis() >= _next_scroll) { - ez.setFont(_font); - uint8_t h = ez.fontHeight(); - uint8_t scroll_by = _y - _bottom; - if (_x > _lmargin) - scroll_by += h; - const GFXfont *hold_font = _font; - const uint16_t hold_color = _color; - for (uint16_t n = 0; n < _printed.size(); n++) { - _printed[n].y -= scroll_by; - } - M5.Lcd.fillRect(left(), top(), width(), height(), ez.screen.background()); - // M5.Lcd.fillRect(0, 0, 320, 240, ez.screen.background()); - for (uint16_t n = 0; n < _printed.size(); n++) { - if (_printed[n].y >= _top) { - if (_printed[n].font != _font) - ez.setFont(_printed[n].font); - if (_printed[n].color != _color) - M5.Lcd.setTextColor(_printed[n].color); - M5.Lcd.drawString(_printed[n].text.c_str(), _printed[n].x, _printed[n].y); - } - } - _y -= scroll_by; - _font = hold_font; - _color = hold_color; - _next_scroll = 0; - - std::vector clean_copy; - for (uint16_t n = 0; n < _printed.size(); n++) { - if (_printed[n].y >= 0) - clean_copy.push_back(_printed[n]); - } - _printed = clean_copy; - ESP_LOGD(M5EZ_TAG, "%d", ESP.getFreeHeap()); - } - return 10; -} - -void ezCanvas::_print(std::string text) { - ez.setFont(_font); - M5.Lcd.setTextDatum(TL_DATUM); - M5.Lcd.setTextColor(_color, ez.theme->background); - uint8_t h = ez.fontHeight(); - if (_y + h > _bottom) { - if (!_scroll) - return; - if (!_next_scroll) - _next_scroll = millis() + 200; - } - auto crlf = text.find("\r\n"); - std::string remainder = ""; - if (crlf != std::string::npos) { - remainder = text.substr(crlf + 2); - text = text.substr(0, crlf); - } - if (_x + M5.Lcd.textWidth(text.c_str()) <= _right) { - if (text != "") - _putString(text); - } else { - for (uint16_t n = 0; n < text.length(); n++) { - if (_x + M5.Lcd.textWidth(text.substr(0, n + 1).c_str()) > _right) { - if (n) { - _putString(text.substr(0, n)); - } - if (_wrap) { - _x = _lmargin; - _y += h; - _print(text.substr(n)); - } - break; - } - } - } - if (crlf != -1) { - _x = _lmargin; - _y += h; - if (remainder != "") - _print(remainder); - } -} - -void ezCanvas::_putString(std::string text) { - ez.setFont(_font); - uint8_t h = ez.fontHeight(); - if (_scroll) { - print_t p; - p.font = _font; - p.color = _color; - p.x = _x; - p.y = _y; - p.text = text; - _printed.push_back(p); - } - if (_y + h > _bottom) { - _x += M5.Lcd.textWidth(text.c_str()); - } else { - _x += M5.Lcd.drawString(text.c_str(), _x, _y); - } -} - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// B U T T O N S -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -std::string ezButtons::_btn_a; -std::string ezButtons::_btn_b; -std::string ezButtons::_btn_c; -bool ezButtons::_key_release_wait; -bool ezButtons::_lower_button_row, ezButtons::_upper_button_row; - -void ezButtons::begin() { - clear(false); -} - -void ezButtons::show(std::vector buttons) { - switch (buttons.size()) { - case 1: - _drawButtons("", buttons[0], ""); - break; - case 2: - _drawButtons(buttons[0], buttons[1], ""); - break; - case 3: - _drawButtons(buttons[0], buttons[1], buttons[2]); - break; - } -} - -std::vector ezButtons::get() { - return {_btn_a, _btn_b, _btn_c}; -} - -void ezButtons::clear(bool wipe /* = true */) { - if (wipe && (_lower_button_row || _upper_button_row)) { - M5.Lcd.fillRect(0, ez.canvas.bottom() + 1, ez.canvas.tft.height - ez.canvas.bottom() - 1, - ez.canvas.tft.width, ez.screen.background()); - } - _btn_a = _btn_b = _btn_c = ""; - _lower_button_row = false; - _upper_button_row = false; - ez.canvas.bottom(ez.canvas.tft.height - 1); -} - -void ezButtons::_drawButtons(std::string btn_a, std::string btn_b, std::string btn_c) { - int16_t btnwidth = int16_t((ez.canvas.tft.width - 4 * ez.theme->button_gap) / 3); - - // See if any buttons are used on the bottom row - if (btn_a != "" || btn_b != "" || btn_c != "") { - if (!_lower_button_row) { - // If the lower button row wasn't there before, clear the area first - M5.Lcd.fillRect(0, ez.canvas.tft.height - ez.theme->button_height - ez.theme->button_gap, - ez.canvas.tft.width, ez.theme->button_height + ez.theme->button_gap, - ez.screen.background()); - } - // Then draw the three buttons there. (drawButton erases single buttons if unused.) - if (_btn_a != btn_a) { - _drawButton(1, btn_a, ez.theme->button_gap, btnwidth); - _btn_a = btn_a; - } - if (_btn_b != btn_b) { - _drawButton(1, btn_b, btnwidth + 2 * ez.theme->button_gap, btnwidth); - _btn_b = btn_b; - } - if (_btn_c != btn_c) { - _drawButton(1, btn_c, 2 * btnwidth + 3 * ez.theme->button_gap, btnwidth); - _btn_c = btn_c; - } - _lower_button_row = true; - } else { - if (_lower_button_row) { - // If there was a lower button row before and it's now gone, clear the area - M5.Lcd.fillRect(0, ez.canvas.tft.height - ez.theme->button_height - ez.theme->button_gap, - ez.canvas.tft.width, ez.theme->button_height + ez.theme->button_gap, - ez.screen.background()); - _btn_a = _btn_b = _btn_c = ""; - _lower_button_row = false; - } - } - - uint8_t button_rows = _upper_button_row ? 2 : (_lower_button_row ? 1 : 0); - ez.canvas.bottom(ez.canvas.tft.height - - (button_rows * (ez.theme->button_height + ez.theme->button_gap))); -} - -void ezButtons::_drawButton(int16_t row, std::string text, int16_t x, int16_t w) { - // row = 1 for lower and 2 for upper row - int16_t y, bg_color; - if (row == 1) { - y = ez.canvas.tft.height - ez.theme->button_height; - bg_color = ez.theme->button_bgcolor_b; - } else { - y = ez.canvas.tft.height - 2 * ez.theme->button_height - ez.theme->button_gap; - bg_color = ez.theme->button_bgcolor_t; - } - if (text != "") { - ez.setFont(ez.theme->button_font); - M5.Lcd.fillRoundRect(x, y, w, ez.theme->button_height, ez.theme->button_radius, bg_color); - _drawButtonString(text, x + int16_t(w / 2), y + ez.theme->button_tmargin, - ez.theme->button_fgcolor, TC_DATUM); - } else { - M5.Lcd.fillRect(x, y, w, ez.theme->button_height, ez.screen.background()); - } -} - -void ezButtons::_drawButtonString(std::string text, - int16_t x, - int16_t y, - uint16_t color, - int16_t datum) { - if (text == "~") - return; - if (text == "up" || text == "down" || text == "left" || text == "right") { - y += 2; - int16_t w = M5.Lcd.textWidth("A") * 1.2; - int16_t h = ez.fontHeight() * 0.6; - if (datum == TR_DATUM) - x = x - w; - if (datum == TC_DATUM) - x = x - w / 2; - if (text == "up") - M5.Lcd.fillTriangle(x, y + h, x + w, y + h, x + w / 2, y, color); - if (text == "down") - M5.Lcd.fillTriangle(x, y, x + w, y, x + w / 2, y + h, color); - if (text == "right") - M5.Lcd.fillTriangle(x, y, x, y + h, x + w, y + h / 2, color); - if (text == "left") - M5.Lcd.fillTriangle(x + w, y, x + w, y + h, x, y + h / 2, color); - } else { - M5.Lcd.setTextColor(color); - M5.Lcd.setTextDatum(datum); - M5.Lcd.drawString(text.c_str(), x, y + 1); - } -} - -void ezButtons::releaseWait() { - _key_release_wait = true; -} - -std::string ezButtons::poll() { - std::string keystr = ""; - - ez.yield(); - - if (!_key_release_wait) { - if (_btn_a != "" && M5.BtnA.wasReleased()) { - keystr = _btn_a; - } - - if (_btn_b != "" && M5.BtnB.wasReleased()) { - keystr = _btn_b; - } - } - - if (M5.BtnA.isReleased() && M5.BtnB.isReleased()) { - _key_release_wait = false; - } - - if (keystr == "~") - keystr = ""; -#ifdef M5EZ_BACKLIGHT - if (keystr != "") - ez.backlight.activity(); -#endif - return keystr; -} - -const std::string ezButtons::wait() { - std::string keystr = ""; - while (keystr == "") { - keystr = ez.buttons.poll(); - } - return keystr; -} - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// S E T T I N G S -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -ezMenu ezSettings::menuObj("Settings menu"); - -void ezSettings::begin() { - menuObj.txtSmall(); - menuObj.buttons({"up", "select", "down"}); -#ifdef M5EZ_BATTERY - ez.battery.begin(); -#endif -#ifdef M5EZ_BACKLIGHT - ez.backlight.begin(); -#endif - if (ez.themes.size() > 1) { - ez.settings.menuObj.addItem("Theme chooser", "", ez.theme->menu); - } - ez.settings.menuObj.addItem("Factory defaults", "", ez.settings.defaults); -} - -void ezSettings::menu() { - menuObj.run(); -} - -void ezSettings::defaults() { - if (ez.msgBox("Reset to defaults?", - {"Are you sure you want to reset all settings to factory defaults?"}, - {"yes", "", "no"}) - == "yes") { - Preferences prefs; - prefs.begin("M5ez"); - prefs.clear(); - prefs.end(); - ESP.restart(); - } -} - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// B A C K L I G H T -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -#ifdef M5EZ_BACKLIGHT -uint8_t ezBacklight::_brightness; -uint8_t ezBacklight::_inactivity; -uint32_t ezBacklight::_last_activity; -uint8_t ezBacklight::_MinimumBrightness; -const uint8_t ezBacklight::_MaxSteps; -bool ezBacklight::_backlight_off = false; - -void ezBacklight::begin() { - ez.addEvent(ez.backlight.loop); - ez.settings.menuObj.addItem("Backlight settings", "", ez.backlight.menu); - Preferences prefs; - prefs.begin("M5ez", true); // read-only - _brightness = prefs.getUChar("brightness", 128); - switch (M5.getBoard()) { - case m5::board_t::board_M5StickCPlus2: - case m5::board_t::board_M5StackCore2: - _MinimumBrightness = 16; - break; - case m5::board_t::board_M5StickCPlus: - case m5::board_t::board_M5StickC: - _MinimumBrightness = 48; - break; - default: - _MinimumBrightness = 64; - } - if (_brightness < _MinimumBrightness) { - _brightness = 100; - } - _inactivity = prefs.getUChar("inactivity", 1); - prefs.end(); - M5.Display.setBrightness(_brightness); -} - -void ezBacklight::menu() { - uint8_t start_brightness = _brightness; - uint8_t start_inactivity = _inactivity; - ezMenu blmenu("Backlight settings"); - blmenu.txtSmall(); - blmenu.buttons({"OK", "down"}); - blmenu.addItem("Backlight brightness"); - blmenu.addItem("Inactivity timeout"); - blmenu.addItem("Back"); - blmenu.downOnLast("first"); - uint8_t step = (_brightness - _MinimumBrightness) * _MaxSteps - / (256 - _MinimumBrightness); // Calculate step from brightness - while (true) { - switch (blmenu.runOnce()) { - case 1: { - ezProgressBar bl("Backlight brightness", {"Set brightness"}, {"Adjust", "Back"}); - while (true) { - std::string b = ez.buttons.poll(); - if (b == "Adjust") { - if (_brightness >= _MinimumBrightness && step < _MaxSteps - 1) { - step++; - _brightness = - _MinimumBrightness + (step * (255 - _MinimumBrightness) / (_MaxSteps - 1)); - } else { - step = 0; - _brightness = _MinimumBrightness; - } - } - float p = ((float)step / (_MaxSteps - 1)) * 100.0f; - bl.value(p); - M5.Display.setBrightness(_brightness); - if (b == "Back") - break; - } - } break; - case 2: { - const std::vector text = { - "Backlight will not turn off", - "Backlight will turn off after 30 seconds of inactivity", - "Backlight will turn off after a minute of inactivity", - }; - while (true) { - ez.msgBox("Inactivity timeout", {text[_inactivity]}, {"Adjust", "Back"}, false); - std::string b = ez.buttons.wait(); - if (b == "Adjust") - _inactivity++; - if (_inactivity > 2) - _inactivity = 0; - if (b == "Back") - break; - } - } break; - case 0: - if (_brightness != start_brightness || _inactivity != start_inactivity) { - Preferences prefs; - prefs.begin("M5ez"); - prefs.putUChar("brightness", _brightness); - prefs.putUChar("inactivity", _inactivity); - prefs.end(); - } - return; - // - } - } -} - -void ezBacklight::inactivity(uint8_t half_minutes) { - if (half_minutes == USER_SET) { - Preferences prefs; - prefs.begin("M5ez", true); - _inactivity = prefs.getUShort("inactivity", 0); - prefs.end(); - } else { - _inactivity = half_minutes; - } -} - -void ezBacklight::activity() { - _last_activity = millis(); -} - -void changeCpuPower(bool reduce) { - switch (M5.getBoard()) { - case m5::board_t::board_M5StickC: - case m5::board_t::board_M5StickCPlus: - // do nothing, lightSleep() works with backlight control - break; - default: - // Backlight is PWM controlled, lightSleep() stops PWM - if (reduce) { - setCpuFrequencyMhz(10); - } else { - setCpuFrequencyMhz(80); - } - } -} - -uint16_t ezBacklight::loop(void *context) { - if (!_backlight_off && _inactivity) { - if (millis() > _last_activity + 30000 * _inactivity) { - _backlight_off = true; - M5.Display.setBrightness(_MinimumBrightness); - changeCpuPower(true); - while (true) { - if (M5.BtnA.wasClicked() || M5.BtnB.wasClicked()) - break; - ez.yield(); - switch (M5.getBoard()) { - case m5::board_t::board_M5StickC: - case m5::board_t::board_M5StickCPlus: - M5.Power.lightSleep(100000); - break; - default: - // PWM inactive during lightSleep, thus display backlight not controlled - delay(100); - } - } - ez.buttons.releaseWait(); // Make sure the key pressed to wake display gets ignored - changeCpuPower(false); - M5.Display.setBrightness(_brightness); - activity(); - _backlight_off = false; - } - } - return 1000; -} -#endif - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// B A T T E R Y -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -#ifdef M5EZ_BATTERY -bool ezBattery::_on = false; - -void ezBattery::begin() { - _on = true; - if (_on) { - _refresh(); - } -} - -uint16_t ezBattery::loop(void *context) { - if (!_on) - return 0; - ez.header.draw("battery"); - return 5000; -} - -// Transform the M5Stack built in battery level into an internal format. -// From [100, 75, 50, 25, 0] to [4, 3, 2, 1, 0] -uint8_t ezBattery::getTransformedBatteryLevel() { - int32_t level = M5.Power.getBatteryLevel(); - - return map(level, 0, 85, 0, 4); -} - -// Return the theme based battery bar color according to its level -uint16_t ezBattery::getBatteryBarColor(uint8_t batteryLevel) { - switch (batteryLevel) { - case 0: - return ez.theme->battery_0_fgcolor; - case 1: - return ez.theme->battery_25_fgcolor; - case 2: - return ez.theme->battery_50_fgcolor; - case 3: - return ez.theme->battery_75_fgcolor; - case 4: - return ez.theme->battery_100_fgcolor; - default: - return ez.theme->header_fgcolor; - } -} - -void ezBattery::_refresh() { - if (_on) { - ez.header.insert(RIGHTMOST, "battery", - ez.theme->battery_bar_width + 2 * ez.theme->header_hmargin, - ez.battery._drawWidget); - ez.addEvent(ez.battery.loop); - } else { - ez.header.remove("battery"); - ez.removeEvent(ez.battery.loop); - } -} - -void ezBattery::_drawWidget(uint16_t x, uint16_t w) { - uint8_t currentBatteryLevel = getTransformedBatteryLevel(); - uint16_t left_offset = x + ez.theme->header_hmargin; - uint8_t top = (ez.theme->header_height / 10) + 1; - uint8_t height = ez.theme->header_height * 0.8; - M5.Lcd.fillRoundRect(left_offset, top, ez.theme->battery_bar_width, height, - ez.theme->battery_bar_gap, ez.theme->header_bgcolor); - M5.Lcd.drawRoundRect(left_offset, top, ez.theme->battery_bar_width, height, - ez.theme->battery_bar_gap, ez.theme->header_fgcolor); - uint8_t bar_width = (ez.theme->battery_bar_width - ez.theme->battery_bar_gap * 5) / 4.0; - uint8_t bar_height = height - ez.theme->battery_bar_gap * 2; - left_offset += ez.theme->battery_bar_gap; - for (uint8_t n = 0; n < currentBatteryLevel; n++) { - M5.Lcd.fillRect(left_offset + n * (bar_width + ez.theme->battery_bar_gap), - top + ez.theme->battery_bar_gap, bar_width, bar_height, - getBatteryBarColor(currentBatteryLevel)); - } -} - -#endif - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// E Z -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -std::vector M5ez::themes; -ezTheme *M5ez::theme = NULL; -ezScreen M5ez::screen; -constexpr ezScreen &M5ez::s; -ezHeader M5ez::header; -constexpr ezHeader &M5ez::h; -ezCanvas M5ez::canvas; -constexpr ezCanvas &M5ez::c; -ezButtons M5ez::buttons; -constexpr ezButtons &M5ez::b; -ezSettings M5ez::settings; -#ifdef M5EZ_BATTERY -ezBattery M5ez::battery; -#endif -#ifdef M5EZ_BACKLIGHT -ezBacklight M5ez::backlight; -#endif -std::vector M5ez::_events; - -void M5ez::begin() { - auto cfg = M5.config(); - cfg.internal_imu = false; - cfg.internal_spk = false; - cfg.internal_mic = false; - M5.begin(cfg); - ezTheme::begin(); - ez.screen.begin(); - ez.settings.begin(); -} - -void M5ez::yield(bool events) { - vTaskDelay(1); // allow lower priority tasks to run - ::yield(); // execute the Arduino yield in the root namespace - M5.update(); - if (!events) { - return; - } - - for (uint8_t n = 0; n < _events.size(); n++) { - if (millis() > _events[n].when) { - uint16_t r = (_events[n].function)(_events[n].context); - if (r) { - _events[n].when = millis() + r - 1; - } else { - _events.erase(_events.begin() + n); - break; // make sure we don't go beyond _events.size() after deletion - } - } - } -} - -void M5ez::addEvent(uint16_t (*function)(void *context), void *context, uint32_t when /* = 1 */) { - event_t n; - n.function = function; - n.context = context; - n.when = millis() + when - 1; - _events.push_back(n); -} - -void M5ez::removeEvent(uint16_t (*function)(void *context)) { - uint8_t n = 0; - while (n < _events.size()) { - if (_events[n].function == function) { - _events.erase(_events.begin() + n); - } - n++; - } -} - -bool M5ez::_redraw; - -void M5ez::redraw() { - _redraw = true; -} - -// ez.msgBox - -std::string M5ez::msgBox(std::string header, - std::vector msg, - std::vector buttons /* = "OK" */, - const bool blocking /* = true */, - const GFXfont *font /* = NULL */, - uint16_t color /* = NO_COLOR */, - const bool clear /* = true */) { - if (ez.header.title() != header) { - ez.screen.clear(); - if (header != "") - ez.header.show(header); - } else { - if (clear) { - ez.canvas.clear(); - } - } - if (!font) - font = ez.theme->msg_font; - if (color == NO_COLOR) - color = ez.theme->msg_color; - ez.buttons.show(buttons); - std::vector lines; - M5.Lcd.setTextDatum(CC_DATUM); - M5.Lcd.setTextColor(color); - ez.setFont(font); - _fitLines(msg, ez.canvas.width() - 2 * ez.theme->msg_hmargin, ez.canvas.width() / 3, lines); - int16_t font_h = ez.fontHeight(); - for (int8_t n = 0; n < lines.size(); n++) { - int16_t y = - ez.canvas.top() + ez.canvas.height() / 2 - ((lines.size() - 1) * font_h / 2) + n * font_h; - if (!clear) { - M5.Lcd.fillRect(0, y - font_h / 2, ez.canvas.tft.width, font_h, ez.theme->background); - } - M5.Lcd.drawString(lines[n].line.c_str(), ez.canvas.tft.width / 2, y); - } - if (buttons.size() != 0 && blocking) { - std::string ret = ez.buttons.wait(); - ez.screen.clear(); - return ret; - } else { - return ""; - } -} - -std::size_t M5ez::_findBreak(std::string text, const std::size_t pos, uint16_t max_width) { - uint16_t start = pos; - std::size_t breakpoint = std::string::npos; - while (true) { - std::size_t testpoint = text.find(' ', start); - if (testpoint == std::string::npos) { - breakpoint = testpoint; - break; - } - uint16_t width = M5.Lcd.textWidth(text.substr(pos, testpoint - pos).c_str()); - if (width > max_width) { - break; - } - - breakpoint = testpoint; - start = testpoint + 1; - } - - return breakpoint; -} - -void M5ez::_fitLines(std::vector text, - uint16_t max_width, - uint16_t min_width, - std::vector &lines) { - for (int16_t n = 0; n < text.size(); n++) { - // break each line on spaces into text width - uint16_t pos = 0; - std::size_t breakpoint; - do { - breakpoint = _findBreak(text[n], pos, max_width - 10); - line_t line = {n, text[n].substr(pos, breakpoint - pos)}; - lines.push_back(line); - pos = breakpoint + 1; - } while (breakpoint != std::string::npos); - } -} - -std::string M5ez::clipString(std::string input, int16_t cutoff, bool dots /* = true */) { - if (M5.Lcd.textWidth(input.c_str()) <= cutoff) { - return input; - } else { - for (int16_t n = input.length(); n >= 0; n--) { - std::string toMeasure = input.substr(0, n); - if (dots) - toMeasure = toMeasure + ".."; - if (M5.Lcd.textWidth(toMeasure.c_str()) <= cutoff) - return toMeasure; - } - return ""; - } -} - -bool M5ez::isBackExitOrDone(std::string str) { - if (str == "back" || str == "exit" || str == "done" || str == "Back" || str == "Exit" - || str == "Done") - return true; - return false; -} - -// Font related M5.Lcd wrappers - -void M5ez::setFont(const GFXfont *font) { - long ptrAsInt = (long)font; - int16_t size = 1; - if (ptrAsInt <= 16) { - if (ptrAsInt > 8) { - ptrAsInt -= 8; - size++; - } - M5.Lcd.setTextFont(ptrAsInt); - } else { - M5.Lcd.setFont(font); - } - M5.Lcd.setTextSize(size); -} - -int16_t M5ez::fontHeight() { - switch (M5.getBoard()) { - case m5::board_t::board_M5StickC: - return 11; - default: - return M5.Lcd.fontHeight(M5.Lcd.getFont()); - } -} - -std::string M5ez::version() { - return std::string(M5EZ_VERSION); -} - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// M E N U -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -ezMenu::ezMenu(std::string hdr /* = "" */) { - _img_background = NO_COLOR; - _offset = 0; - _selected = -1; - _header = hdr; - _buttons = {}; - _font = NULL; - _img_from_top = 0; - _img_caption_location = TC_DATUM; - _img_caption_font = &FreeSansBold12pt7b; - _img_caption_color = TFT_RED; - _img_caption_hmargin = 10; - _img_caption_vmargin = 10; - _sortFunction = NULL; -} - -void ezMenu::txtBig() { - _font = ez.theme->menu_big_font; -} - -void ezMenu::txtSmall() { - _font = ez.theme->menu_small_font; -} - -void ezMenu::txtFont(const GFXfont *font) { - _font = font; -} - -bool ezMenu::addItem(std::string name, - std::string caption, - void (*simpleFunction)() /* = NULL */, - void *context /* = NULL */, - bool (*advancedFunction)(ezMenu *callingMenu, void *context) /* = NULL */, - void (*drawFunction)(ezMenu *callingMenu, - int16_t x, - int16_t y, - int16_t w, - int16_t h) /* = NULL */) { - MenuItem_t new_item = {name, caption, context, simpleFunction, advancedFunction, drawFunction}; - if (_selected == -1) - _selected = _items.size(); - _items.push_back(new_item); - _sortItems(); - M5ez::_redraw = true; - return true; -} - -bool ezMenu::deleteItem(int16_t index) { - if (index < 1 || index > _items.size()) - return false; - index--; // internally we work with zero-referenced items - _items.erase(_items.begin() + index); - if (_selected >= _items.size()) - _selected = _items.size() - 1; - _fixOffset(); - M5ez::_redraw = true; - return true; -} - -bool ezMenu::deleteItem(std::string name) { - return deleteItem(getItemNum(name)); -} - -bool ezMenu::setCaption(std::string name, std::string caption) { - for (int n = 0; n < _items.size(); n++) { - if (_items[n].name == name) { - _items[n].caption = caption; - M5ez::_redraw = true; - return true; - } - } - - return false; -} - -int16_t ezMenu::countItems(void) { - return _items.size(); -} - -int16_t ezMenu::getItemNum(std::string name) { - for (int16_t n = 0; n < _items.size(); n++) { - if (_items[n].name == name) - return n + 1; - } - return 0; -} - -void ezMenu::setSortFunction(bool (*sortFunction)(const char *s1, const char *s2)) { - _sortFunction = sortFunction; - _sortItems(); // In case the menu is already populated -} - -void ezMenu::buttons(std::vector bttns) { - _buttons = bttns; -} - -void ezMenu::upOnFirst(std::string name) { - _up_on_first = name; -} - -void ezMenu::leftOnFirst(std::string name) { - _up_on_first = name; -} - -void ezMenu::downOnLast(std::string name) { - _down_on_last = name; -} - -void ezMenu::rightOnLast(std::string name) { - _down_on_last = name; -} - -void ezMenu::run() { - while (runOnce()) { - } -} - -int16_t ezMenu::runOnce(bool dynamic) { - if (_items.size() == 0) - return 0; - if (_selected == -1) - _selected = 0; - if (!_font) - _font = ez.theme->menu_big_font; // Cannot be in constructor: ez.theme not there yet - - int16_t r; - - do { - r = _runTextOnce(dynamic); - } while (r < 0); - - return r; -} - -int16_t ezMenu::_runTextOnce(bool dynamic) { - if (_buttons.size() == 0) - _buttons = {"up", "select", "down"}; - ez.screen.clear(); - if (_header != "") - ez.header.show(_header); - ez.setFont(_font); - _per_item_h = ez.fontHeight(); - ez.buttons.show( - _buttons); // we need to draw the buttons here to make sure ez.canvas.height() is correct - _items_per_screen = (ez.canvas.height() - 5) / _per_item_h; - _drawItems(); - while (true) { - int16_t old_selected = _selected; - int16_t old_offset = _offset; - std::vector tmp_buttons = _buttons; - if (_selected <= 0) - _replace(tmp_buttons, "up", _up_on_first); - if (_selected >= _items.size() - 1) - _replace(tmp_buttons, "down", _down_on_last); - ez.buttons.show(tmp_buttons); - std::string name = _items[_selected].name; - std::string pressed; - while (true) { - pressed = ez.buttons.poll(); - if (M5ez::_redraw) { - _drawItems(); - if (dynamic) { - return -1; - } - } - if (pressed != "") - break; - } - if (pressed == "up") { - _selected--; - _fixOffset(); - } else if (pressed == "down") { - _selected++; - _fixOffset(); - } else if (pressed == "first") { - _selected = 0; - _offset = 0; - } else if (pressed == "last") { - _selected = _items.size() - 1; - _offset = 0; - _fixOffset(); - } else if ((ez.isBackExitOrDone(name) && !_items[_selected].advancedFunction) - || ez.isBackExitOrDone(pressed)) { - _pick_button = pressed; - _selected = -1; - ez.screen.clear(); - return 0; - } else { - // Some other key must have been pressed. We're done here! - ez.screen.clear(); - _pick_button = pressed; - if (_items[_selected].simpleFunction) { - (_items[_selected].simpleFunction)(); - } - if (_items[_selected].advancedFunction) { - if (!(_items[_selected].advancedFunction)(this, _items[_selected].context)) - return 0; - } - return _selected - + 1; // We return items starting at one, but work starting at zero internally - } - - // Flicker prevention, only redraw whole menu if scrolled - if (_offset == old_offset) { - int16_t top_item_h = - ez.canvas.top() - + (ez.canvas.height() % _per_item_h) - / 2; // remainder of screen left over by last item not fitting split to center menu - if (_items[old_selected].drawFunction) { - ez.setFont(_font); - (_items[old_selected].drawFunction)( - this, ez.theme->menu_lmargin, top_item_h + (old_selected - _offset) * _per_item_h, - ez.canvas.tft.width - ez.theme->menu_lmargin - ez.theme->menu_rmargin, _per_item_h); - } else { - MenuItem_t *item = &_items[old_selected]; - std::string text = item->caption != "" ? item->caption : item->name; - _drawItem(old_selected - _offset, text, false); - }; - if (_items[_selected].drawFunction) { - ez.setFont(_font); - (_items[_selected].drawFunction)( - this, ez.theme->menu_lmargin, top_item_h + (_selected - _offset) * _per_item_h, - ez.canvas.tft.width - ez.theme->menu_lmargin - ez.theme->menu_rmargin, _per_item_h); - } else { - MenuItem_t *item = &_items[_selected]; - std::string text = item->caption != "" ? item->caption : item->name; - _drawItem(_selected - _offset, text, true); - }; - } else { - ez.canvas.clear(); - _drawItems(); - } - } -} - -void ezMenu::_drawItems() { - for (int16_t n = 0; n < _items_per_screen; n++) { - int16_t item_ref = _offset + n; - if (item_ref < _items.size()) { - if (_items[item_ref].drawFunction) { - int16_t top_item_h = - ez.canvas.top() - + (ez.canvas.height() % _per_item_h) / 2; // remainder of screen left over by last item - // not fitting split to center menu - ez.setFont(_font); - (_items[item_ref].drawFunction)( - this, ez.theme->menu_lmargin, top_item_h + n * _per_item_h, - ez.canvas.tft.width - ez.theme->menu_lmargin - ez.theme->menu_rmargin, _per_item_h); - } else { - MenuItem_t *item = &_items[item_ref]; - std::string text = item->caption != "" ? item->caption : item->name; - _drawItem(n, text, (item_ref == _selected)); - } - } - } - _Arrows(); - M5ez::_redraw = false; -} - -void ezMenu::_drawItem(int16_t n, std::string text, bool selected) { - uint16_t fill_color; - ez.setFont(_font); - int16_t top_item_h = - ez.canvas.top() - + (ez.canvas.height() % _per_item_h) - / 2; // remainder of screen left over by last item not fitting split to center menu - int16_t menu_text_y = - top_item_h + (n * _per_item_h) + (_per_item_h / 2) + (_per_item_h % 2 ? 1 : 0); - M5.Lcd.setTextDatum(CL_DATUM); - if (selected) { - fill_color = ez.theme->menu_sel_bgcolor; - M5.Lcd.setTextColor(ez.theme->menu_sel_fgcolor); - } else { - fill_color = ez.screen.background(); - M5.Lcd.setTextColor(ez.theme->menu_item_color); - } - text = ez.clipString(text, ez.canvas.tft.width - ez.theme->menu_lmargin - - 2 * ez.theme->menu_item_hmargin - ez.theme->menu_rmargin); - M5.Lcd.fillRoundRect(ez.theme->menu_lmargin, top_item_h + n * _per_item_h, - ez.canvas.tft.width - ez.theme->menu_lmargin - ez.theme->menu_rmargin, - _per_item_h, ez.theme->menu_item_radius, fill_color); - auto tabpos = text.find("\t"); - M5.Lcd.drawString(text.substr(0, tabpos).c_str(), - ez.theme->menu_lmargin + ez.theme->menu_item_hmargin, menu_text_y); - if (tabpos != std::string::npos) { - M5.Lcd.setTextDatum(CR_DATUM); - M5.Lcd.drawString(text.substr(tabpos, std::string::npos).c_str(), - ez.canvas.tft.width - ez.theme->menu_rmargin - ez.theme->menu_item_hmargin, - menu_text_y); - } -} - -void ezMenu::imgBackground(uint16_t color) { - _img_background = color; -} - -void ezMenu::imgFromTop(int16_t offset) { - _img_from_top = offset; -} - -void ezMenu::imgCaptionFont(const GFXfont *font) { - _img_caption_font = font; -} - -void ezMenu::imgCaptionLocation(uint8_t datum) { - _img_caption_location = datum; -} - -void ezMenu::imgCaptionColor(uint16_t color) { - _img_caption_color = color; -} - -void ezMenu::imgCaptionMargins(int16_t hmargin, int16_t vmargin) { - _img_caption_hmargin = hmargin; - _img_caption_vmargin = vmargin; -} - -void ezMenu::imgCaptionMargins(int16_t margin) { - _img_caption_hmargin = margin; - _img_caption_vmargin = margin; -} - -void ezMenu::_drawCaption() { - int16_t x, y; - std::string caption = _items[_selected].caption; - if (_img_caption_font == NULL || caption == "") - return; - ez.setFont(_img_caption_font); - M5.Lcd.setTextColor(_img_caption_color); - M5.Lcd.setTextDatum(_img_caption_location); - // Set X and Y for printing caption seperately, less code duplication - switch (_img_caption_location) { - case TL_DATUM: - case ML_DATUM: - case BL_DATUM: - x = ez.canvas.left() + _img_caption_hmargin; - break; - case TC_DATUM: - case MC_DATUM: - case BC_DATUM: - x = ez.canvas.left() + ez.canvas.width() / 2; - break; - case TR_DATUM: - case MR_DATUM: - case BR_DATUM: - default: - x = ez.canvas.right() - _img_caption_hmargin; - break; - return; - // - } - switch (_img_caption_location) { - case TL_DATUM: - case TC_DATUM: - case TR_DATUM: - y = ez.canvas.top() + _img_caption_vmargin; - break; - case ML_DATUM: - case MC_DATUM: - case MR_DATUM: - y = ez.canvas.top() + ez.canvas.height() / 2; - break; - case BL_DATUM: - case BC_DATUM: - case BR_DATUM: - default: - y = ez.canvas.bottom() - _img_caption_vmargin; - break; - // - } - M5.Lcd.drawString(caption.c_str(), x, y); -} - -void ezMenu::_fixOffset() { - if (_selected != -1) { - if (_selected >= _offset + _items_per_screen) - _offset = _selected - _items_per_screen + 1; - if (_selected < _offset) - _offset = _selected; - - // If there's stuff above the current screen and screen is not full (can only happen after - // deleteItem) - if (_offset > 0 && _offset + _items_per_screen > _items.size()) { - _offset = _items.size() - _items_per_screen; - } - - } else { - _offset = 0; - } -} - -int16_t ezMenu::pick() { - return _selected + 1; -} - -std::string ezMenu::pickName() { - return _items[_selected].name; -} - -void ezMenu::_Arrows() { - uint16_t fill_color; - - int16_t top = ez.canvas.top(); - int16_t height = ez.canvas.height(); - - // Up arrow - if (_offset > 0) { - fill_color = ez.theme->menu_item_color; - } else { - fill_color = ez.screen.background(); - } - static uint8_t l = ez.theme->menu_arrows_lmargin; - M5.Lcd.fillTriangle(l, top + 17, l + 6, top + 17, l + 3, top + 5, fill_color); - - // Down arrow - if (_items.size() > _offset + _items_per_screen) { - fill_color = ez.theme->menu_item_color; - } else { - fill_color = ez.screen.background(); - } - M5.Lcd.fillTriangle(l, top + height - 17, l + 6, top + height - 17, l + 3, top + height - 5, - fill_color); -} - -bool ezMenu::_sortWrapper(MenuItem_t &item1, MenuItem_t &item2) { - // Be sure to set _sortFunction before calling _sortWrapper(), as _sortItems ensures. - return _sortFunction(item1.name.c_str(), item2.name.c_str()); -} - -void ezMenu::_sortItems() { - if (_sortFunction && _items.size() > 1) { - using namespace std::placeholders; // For _1, _2 - sort(_items.begin(), _items.end(), std::bind(&ezMenu::_sortWrapper, this, _1, _2)); - M5ez::_redraw = true; - } -} - -void ezMenu::_replace(std::vector &list, std::string name, std::string replacement) { - for (int i = 0; i < list.size(); i++) { - if (list[i] == name) { - list[i] = replacement; - return; - } - } -} - -// For sorting by Names as quickly as possible -bool ezMenu::sort_asc_name_cs(const char *s1, const char *s2) { - return 0 > strcmp(s1, s2); -} -bool ezMenu::sort_asc_name_ci(const char *s1, const char *s2) { - return 0 > strcasecmp(s1, s2); -} -bool ezMenu::sort_dsc_name_cs(const char *s1, const char *s2) { - return 0 < strcmp(s1, s2); -} -bool ezMenu::sort_dsc_name_ci(const char *s1, const char *s2) { - return 0 < strcasecmp(s1, s2); -} - -// For sorting by Caption if there is one, falling back to sorting by Name if no Caption is provided -// (all purpose) -const char *captionSortHelper(const char *nameAndCaption) { - char *sub = strchr(nameAndCaption, '|'); // Find the separator - if (nullptr == sub) - return nameAndCaption; // If none, return the entire string - sub++; // move past the separator - while (isspace(sub[0])) - sub++; // trim whitespace - return sub; -} -bool ezMenu::sort_asc_caption_cs(const char *s1, const char *s2) { - return 0 > strcmp(captionSortHelper(s1), captionSortHelper(s2)); -} -bool ezMenu::sort_asc_caption_ci(const char *s1, const char *s2) { - return 0 > strcasecmp(captionSortHelper(s1), captionSortHelper(s2)); -} -bool ezMenu::sort_dsc_caption_cs(const char *s1, const char *s2) { - return 0 < strcmp(captionSortHelper(s1), captionSortHelper(s2)); -} -bool ezMenu::sort_dsc_caption_ci(const char *s1, const char *s2) { - return 0 < strcasecmp(captionSortHelper(s1), captionSortHelper(s2)); -} - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// P R O G R E S S B A R -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -ezProgressBar::ezProgressBar(const std::string header /* = "" */, - std::vector msg /* = "" */, - std::vector buttons /* = "" */, - const GFXfont *font /* = NULL */, - uint16_t color /* = NO_COLOR */, - uint16_t bar_color /* = NO_COLOR */) { - if (!font) - font = ez.theme->msg_font; - if (color == NO_COLOR) - color = ez.theme->msg_color; - if (bar_color == NO_COLOR) - bar_color = ez.theme->progressbar_color; - _bar_color = bar_color; - ez.screen.clear(); - M5.Lcd.fillRect(0, 0, ez.canvas.tft.width, ez.canvas.tft.height, ez.screen.background()); - - if (header != "") - ez.header.show(header); - ez.buttons.show(buttons); - std::vector lines; - M5.Lcd.setTextDatum(CC_DATUM); - M5.Lcd.setTextColor(color); - ez.setFont(font); - ez._fitLines(msg, ez.canvas.width() - 2 * ez.theme->msg_hmargin, ez.canvas.width() / 3, lines); - uint8_t font_h = ez.fontHeight(); - uint8_t num_lines = lines.size() + 2; - for (uint8_t n = 0; n < lines.size(); n++) { - int16_t y = - ez.canvas.top() + ez.canvas.height() / 2 - ((num_lines - 1) * font_h / 2) + n * font_h; - M5.Lcd.drawString(lines[n].line.c_str(), ez.canvas.tft.width / 2, y); - } - _bar_y = ez.canvas.top() + ez.canvas.height() / 2 + ((num_lines - 1) * font_h / 2) - - ez.theme->progressbar_width / 2; - for (uint8_t n = 0; n < ez.theme->progressbar_line_width; n++) { - M5.Lcd.drawRect(ez.canvas.left() + ez.theme->msg_hmargin + n, _bar_y + n, - ez.canvas.width() - 2 * ez.theme->msg_hmargin - 2 * n, - ez.theme->progressbar_width - 2 * n, bar_color); - } -} - -void ezProgressBar::value(float val) { - uint16_t left = ez.canvas.left() + ez.theme->msg_hmargin + ez.theme->progressbar_line_width; - uint16_t width = (int16_t)(ez.canvas.width() - 2 * ez.theme->msg_hmargin - - 2 * ez.theme->progressbar_line_width); - M5.Lcd.fillRect(left, _bar_y + ez.theme->progressbar_line_width, width * val / 100, - ez.theme->progressbar_width - 2 * ez.theme->progressbar_line_width, _bar_color); - M5.Lcd.fillRect(left + (width * val / 100), _bar_y + ez.theme->progressbar_line_width, - width - (width * val / 100), - ez.theme->progressbar_width - 2 * ez.theme->progressbar_line_width, - ez.screen.background()); -} - -M5ez ez; diff --git a/lib/M5ez/src/M5ez.h b/lib/M5ez/src/M5ez.h deleted file mode 100644 index fb7244d..0000000 --- a/lib/M5ez/src/M5ez.h +++ /dev/null @@ -1,544 +0,0 @@ -#ifndef _M5EZ_H_ -#define _M5EZ_H_ - -#define M5EZ_VERSION "2.1.2" - -// Turn this off if you don't have a battery attached -#define M5EZ_BATTERY - -// Determines whether the backlight is settable -#define M5EZ_BACKLIGHT - -#include -#include - -// Special fake font pointers to access the older non FreeFonts in a unified way. -// Only valid if passed to ez.setFont -// (Note that these need to be specified without the & in front, unlike the FreeFonts) -#define hzk16 (GFXfont *)1 -#define sans16 (GFXfont *)2 -#define sans26 (GFXfont *)4 -#define numonly48 (GFXfont *)6 -#define numonly7seg48 (GFXfont *)7 -#define numonly75 (GFXfont *)8 -// The following fonts are just scaled up from previous ones (textSize 2) -// But they might still be useful. -#define mono12x16 (GFXfont *)9 -#define sans32 (GFXfont *)10 -#define sans52 (GFXfont *)12 -#define numonly96 (GFXfont *)14 -#define numonly7seg96 (GFXfont *)15 -#define numonly150 (GFXfont *)16 - -#define NO_COLOR TFT_TRANSPARENT - -struct line_t { - int16_t position; - std::string line; -}; - -extern const char *M5EZ_TAG; - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// T H E M E -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -class ezTheme { - public: - static void begin(); - void add(); - static bool select(std::string name); - static void menu(); - - std::string name = "Default"; // Change this when making theme - uint16_t background = 0xEF7D; - uint16_t foreground = TFT_BLACK; - uint8_t header_height; - const GFXfont *header_font; - uint8_t header_hmargin = 5; - uint8_t header_tmargin = 3; - uint16_t header_bgcolor = TFT_BLUE; - uint16_t header_fgcolor = TFT_WHITE; - - const GFXfont *print_font; - uint16_t print_color = foreground; - - uint8_t button_height; - const GFXfont *button_font; - uint8_t button_tmargin = 1; - uint8_t button_hmargin = 5; - uint8_t button_gap = 3; - uint8_t button_radius; - uint16_t button_bgcolor_b = TFT_BLUE; - uint16_t button_bgcolor_t = TFT_PURPLE; - uint16_t button_fgcolor = TFT_WHITE; - uint16_t button_longcolor = TFT_CYAN; - - uint8_t menu_lmargin = 15; - uint8_t menu_rmargin = 10; - uint8_t menu_arrows_lmargin = 4; - uint16_t menu_item_color = foreground; - uint16_t menu_sel_bgcolor = foreground; - uint16_t menu_sel_fgcolor = background; - const GFXfont *menu_big_font; - const GFXfont *menu_small_font; - uint8_t menu_item_hmargin = 10; - uint8_t menu_item_radius; - - const GFXfont *msg_font; - uint16_t msg_color = foreground; - uint8_t msg_hmargin = 20; - - uint8_t progressbar_line_width = 4; - uint8_t progressbar_width = 25; - uint16_t progressbar_color = foreground; - - uint16_t signal_interval = 2000; - uint8_t signal_bar_width = 4; - uint8_t signal_bar_gap = 2; - - uint8_t battery_bar_width = 26; - uint8_t battery_bar_gap = 2; - uint16_t battery_0_fgcolor = TFT_RED; - uint16_t battery_25_fgcolor = TFT_ORANGE; - uint16_t battery_50_fgcolor = TFT_YELLOW; - uint16_t battery_75_fgcolor = TFT_GREENYELLOW; - uint16_t battery_100_fgcolor = TFT_GREEN; - - private: - static void update(void); -}; - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// S C R E E N -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -class ezScreen { - public: - static void begin(); - static void clear(); - static void clear(uint16_t color); - static uint16_t background(); - - private: - static uint16_t _background; - // -}; - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// H E A D E R -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -struct header_widget_t { - std::string name; - bool leftover; // widget gets all leftover width. Used by title, only one widget can have this - // property - uint16_t x; - uint16_t w; - void (*function)(uint16_t x, uint16_t w); -}; -#define LEFTMOST 0 -#define RIGHTMOST 255 - -class ezHeader { - private: - static std::vector _widgets; - static bool _shown; - static std::string _title; - static void _drawTitle(uint16_t x, uint16_t w); - static void _recalculate(); - - public: - static void begin(); - static void show(std::string t = ""); - static bool shown(); - static void clear(bool wipe = true); - static void insert(uint8_t position, - std::string name, - uint16_t width, - void (*function)(uint16_t x, uint16_t w), - bool leftover = false); - static void remove(std::string name); - static uint8_t position(std::string name); - static void draw(std::string name = ""); - static std::string title(); - static void title(std::string title); - // -}; - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// C A N V A S -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -struct print_t { - const GFXfont *font; - uint16_t color; - uint16_t x; - int16_t y; - std::string text; -}; - -class ezTFT { - public: - int32_t width; - int32_t height; -}; - -class ezCanvas: public Print { - friend class ezHeader; - friend class ezButtons; - friend class ezProgressBar; - friend class ezMenu; - friend class ezScreen; - friend class M5ez; - - public: - static void begin(); - static void reset(); - static void clear(); - static uint8_t top(); - static uint8_t bottom(); - static uint16_t left(); - static uint16_t right(); - static uint8_t height(); - static uint16_t width(); - static bool scroll(); - static void scroll(bool s); - static bool wrap(); - static void wrap(bool w); - static uint16_t lmargin(); - static void lmargin(uint16_t newmargin); - static void font(const GFXfont *font); - static const GFXfont *font(); - static void color(uint16_t color); - static uint16_t color(); - static void pos(uint16_t x, uint8_t y); - static uint16_t x(); - static void x(uint16_t newx); - static uint8_t y(); - static void y(uint8_t newy); - virtual size_t write( - uint8_t c); // These three are used to inherit print and println from Print class - virtual size_t write(const char *str); - virtual size_t write(const uint8_t *buffer, size_t size); - static uint16_t loop(void *context); - - private: - static ezTFT tft; - static std::vector _printed; - static uint32_t _next_scroll; - static void top(uint8_t newtop); - static void bottom(uint8_t newbottom); - static uint8_t _y, _top, _bottom; - static uint16_t _x, _left, _right, _lmargin; - static bool _wrap, _scroll; - static const GFXfont *_font; - static uint16_t _color; - static void _print(std::string text); - static void _putString(std::string text); - // -}; - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// B U T T O N S -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -class ezButtons { - public: - static void begin(); - static void show(std::vector buttons); - static std::vector get(); - static void clear(bool wipe = true); - static void releaseWait(); - static std::string poll(); - static const std::string wait(); - - private: - static std::string _btn_a; - static std::string _btn_b; - static std::string _btn_c; - static bool _key_release_wait; - static bool _lower_button_row, _upper_button_row; - static void _drawButtons(std::string btn_a, std::string btn_b, std::string btn_c); - static void _drawButton(int16_t row, std::string text, int16_t x, int16_t w); - static void _drawButtonString(std::string text, - int16_t x, - int16_t y, - uint16_t color, - int16_t datum); -}; - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// M E N U -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -class ezMenu { - public: - ezMenu(std::string hdr = ""); - bool addItem( - std::string name, - std::string caption = "", - void (*simpleFunction)() = NULL, - void *context = nullptr, - bool (*advancedFunction)(ezMenu *callingMenu, void *context) = NULL, - void (*drawFunction)(ezMenu *callingMenu, int16_t x, int16_t y, int16_t w, int16_t h) = NULL); - bool deleteItem(int16_t index); - bool deleteItem(std::string name); - bool setCaption(std::string name, std::string caption); - int16_t countItems(void); - void setSortFunction(bool (*sortFunction)(const char *s1, const char *s2)); - void buttons(std::vector bttns); - void upOnFirst(std::string name); - void leftOnFirst(std::string name); - void downOnLast(std::string name); - void rightOnLast(std::string name); - int16_t getItemNum(std::string name); - int16_t pick(); - std::string pickName(); - void run(); - int16_t runOnce(bool dynamic = false); - void txtBig(); - void txtSmall(); - void txtFont(const GFXfont *font); - void imgBackground(uint16_t color); - void imgFromTop(int16_t offset); - void imgCaptionFont(const GFXfont *font); - void imgCaptionLocation(uint8_t datum); - void imgCaptionColor(uint16_t color); - void imgCaptionMargins(int16_t hmargin, int16_t vmargin); - void imgCaptionMargins(int16_t margin); - - static bool sort_asc_name_cs(const char *s1, const char *s2); - static bool sort_asc_name_ci(const char *s1, const char *s2); - static bool sort_dsc_name_cs(const char *s1, const char *s2); - static bool sort_dsc_name_ci(const char *s1, const char *s2); - static bool sort_asc_caption_cs(const char *s1, const char *s2); - static bool sort_asc_caption_ci(const char *s1, const char *s2); - static bool sort_dsc_caption_cs(const char *s1, const char *s2); - static bool sort_dsc_caption_ci(const char *s1, const char *s2); - - private: - struct MenuItem_t { - std::string name; - std::string caption; - void *context; - void (*simpleFunction)(); - bool (*advancedFunction)(ezMenu *callingMenu, void *context); - void (*drawFunction)(ezMenu *callingMenu, int16_t x, int16_t y, int16_t w, int16_t h); - }; - std::vector _items; - int16_t _selected, _offset; - bool _redraw; - std::string _header, _pick_button; - std::vector _buttons; - std::string _up_on_first, _down_on_last; - int16_t _per_item_h, _vmargin; - int16_t _items_per_screen; - uint16_t _old_background; - void _drawCaption(); - const GFXfont *_font; - int16_t _runTextOnce(bool dynamic = false); - void _fixOffset(); - void _drawItems(); - void _drawItem(int16_t n, std::string text, bool selected); - void _Arrows(); - int16_t _img_from_top; - uint8_t _img_caption_location; - uint16_t _img_caption_color; - uint16_t _img_background; - const GFXfont *_img_caption_font; - int16_t _img_caption_hmargin, _img_caption_vmargin; - bool (*_sortFunction)(const char *s1, const char *s2); - void _sortItems(); - bool _sortWrapper(MenuItem_t &item1, MenuItem_t &item2); - void _replace(std::vector &list, std::string name, std::string replacement); - // -}; - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// P R O G R E S S B A R -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -class ezProgressBar { - public: - ezProgressBar(const std::string header = "", - std::vector msg = {""}, - std::vector buttons = {""}, - const GFXfont *font = NULL, - uint16_t color = NO_COLOR, - uint16_t bar_color = NO_COLOR); - void value(float val); - - private: - int16_t _bar_y; - uint16_t _bar_color; -}; - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// S E T T I N G S -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -class ezSettings { - public: - static void begin(); - static void menu(); - static void defaults(); - static ezMenu menuObj; - // -}; - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// B A C K L I G H T -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -#ifdef M5EZ_BACKLIGHT -#define NEVER 0 -#define USER_SET 255 -class ezBacklight { - public: - static void begin(); - static void menu(); - static void inactivity(uint8_t half_minutes); - static void activity(); - static uint16_t loop(void *context); - - private: - static uint8_t _brightness; - static uint8_t _inactivity; - static uint32_t _last_activity; - static uint8_t _MinimumBrightness; - static const uint8_t _MaxSteps = 8; - static bool _backlight_off; - // -}; -#endif - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// B A T T E R Y -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -#ifdef M5EZ_BATTERY - -class ezBattery { - public: - static void begin(); - static uint16_t loop(void *context); - static uint8_t getTransformedBatteryLevel(); - static uint16_t getBatteryBarColor(uint8_t batteryLevel); - - private: - static bool _on; - static void _refresh(); - static void _drawWidget(uint16_t x, uint16_t w); -}; - -#endif - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// E Z -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -struct event_t { - uint16_t (*function)(void *); - void *context; - uint32_t when; -}; - -class M5ez { - friend class ezProgressBar; - friend class ezHeader; // TMP? - friend class ezMenu; // For _redraw - - public: - static std::vector themes; - static ezTheme *theme; - static ezScreen screen; - static constexpr ezScreen &s = screen; - static ezHeader header; - static constexpr ezHeader &h = header; - static ezCanvas canvas; - static constexpr ezCanvas &c = canvas; - static ezButtons buttons; - static constexpr ezButtons &b = buttons; - static ezSettings settings; -#ifdef M5EZ_BATTERY - static ezBattery battery; -#endif -#ifdef M5EZ_BACKLIGHT - static ezBacklight backlight; -#endif - - static void begin(); - - static void yield(bool events = true); - - static void addEvent(uint16_t (*function)(void *), void *context = nullptr, uint32_t when = 1); - static void removeEvent(uint16_t (*function)(void *context)); - static void redraw(); - - // ez.msgBox - static std::string msgBox(std::string header, - std::vector msg, - std::vector buttons = {"OK"}, - const bool blocking = true, - const GFXfont *font = NULL, - uint16_t color = NO_COLOR, - const bool clear = true); - - static std::string clipString(std::string input, int16_t cutoff, bool dots = true); - static bool isBackExitOrDone(std::string str); - - // m5.lcd wrappers that make fonts easier - static void setFont(const GFXfont *font); - static int16_t fontHeight(); - - static std::string version(); - - private: - static std::vector _events; - static bool _redraw; - - static void _fitLines(std::vector text, - uint16_t max_width, - uint16_t min_width, - std::vector &lines); - - static std::size_t _findBreak(std::string text, std::size_t pos, uint16_t max_width); -}; - -extern M5ez ez; - -#endif //_M5EZ_H_ diff --git a/lib/M5ez/src/themes/dark.h b/lib/M5ez/src/themes/dark.h deleted file mode 100644 index 86febbc..0000000 --- a/lib/M5ez/src/themes/dark.h +++ /dev/null @@ -1,19 +0,0 @@ -// This is the dark theme. See how little you need to change for a different look? - -{ - ezTheme theme; - - theme.name = "Dark"; // Change this when making theme - theme.background = TFT_BLACK; - theme.foreground = TFT_WHITE; - theme.header_bgcolor = TFT_DARKGREY; - theme.header_fgcolor = TFT_WHITE; - theme.print_color = theme.foreground; - theme.menu_item_color = theme.foreground; - theme.menu_sel_bgcolor = theme.foreground; - theme.menu_sel_fgcolor = theme.background; - theme.msg_color = theme.foreground; - theme.progressbar_color = theme.foreground; - - theme.add(); -} diff --git a/lib/M5ez/src/themes/default.h b/lib/M5ez/src/themes/default.h deleted file mode 100644 index 79af75b..0000000 --- a/lib/M5ez/src/themes/default.h +++ /dev/null @@ -1,64 +0,0 @@ -// This is the default theme. The values you see are all commented out because they are the -// defaults so you do not need to specify them. Take a look at the dark.h theme file to see how -// another theme works. -// -// (You only need to #include this file if you offer more than one theme and want the theme chooser -// in the settings to also offer the default theme. The original active theme at startup is the -// last one you included until the user has saved one in the settings.) - -{ - ezTheme theme; - - // theme.name = "Default"; // Change - // this when making theme theme.background = 0xEF7D; theme.foreground = TFT_BLACK; - // theme.header_height = 23; theme.GFXfont* header_font = &FreeSansBold9pt7b; theme.header_hmargin - // = 5; theme.header_tmargin = 3; theme.header_bgcolor = TFT_BLUE; theme.header_fgcolor = - // TFT_WHITE; theme.GFXfont* print_font = mono6x8; theme.print_color = theme.foreground; - // theme.GFXfont* clock_font = mono12x16; - // theme.longpress_time = 250; //milliseconds theme.button_height = 19; theme.GFXfont* button_font - // = &FreeSans9pt7b; theme.button_tmargin = 1; theme.button_hmargin = 5; theme.button_gap = 3; - // theme.button_radius = 8; - // theme.button_bgcolor_b = TFT_BLUE; - // theme.button_bgcolor_t = TFT_PURPLE; - // theme.button_fgcolor = TFT_WHITE; - // theme.button_longcolor = TFT_CYAN; - // theme.input_top = 50; // - // pixels below ez.canvas.top() theme.input_hmargin = 10; - // // The distance between text box and edge of screen theme.input_vmargin = 10; - // // Vertical margin _inside_ the text box theme.GFXfont* input_font = &FreeMonoBold12pt7b; - // theme.GFXfont* input_keylock_font = &FreeSansBold9pt7b; theme.input_bgcolor = TFT_BLACK; - // theme.input_fgcolor = TFT_GREEN; - // theme.input_cursor_blink = 500; // milliseconds - // theme.input_faces_btns = 18; - // theme.GFXfont* tb_font = &FreeSans9pt7b; - // theme.tb_color = theme.foreground; - // theme.tb_hmargin = 5; - // theme.menu_lmargin = 20; - // theme.menu_rmargin = 10; - // theme.menu_arrows_lmargin = 5; - // theme.menu_item_color = theme.foreground; - // theme.menu_sel_bgcolor = theme.foreground; - // theme.menu_sel_fgcolor = theme.background; - // theme.GFXfont* menu_big_font = &FreeSans12pt7b; - // theme.GFXfont* menu_small_font = &FreeSans9pt7b; - // theme.menu_item_hmargin = 10; - // theme.menu_item_radius = 8; - // theme.GFXfont* msg_font = &FreeSans12pt7b; - // theme.msg_color = theme.foreground; - // theme.msg_hmargin = 20; - // theme.progressbar_line_width = 4; - // theme.progressbar_width = 25; - // theme.progressbar_color = theme.foreground; - // theme.signal_interval = 2000; - // theme.signal_bar_width = 4; - // theme.signal_bar_gap = 2; - // theme.battery_bar_width = 26; - // theme.battery_bar_gap = 2; - // theme.battery_0_fgcolor = TFT_RED; - // theme.battery_25_fgcolor = TFT_ORANGE; - // theme.battery_50_fgcolor = TFT_YELLOW; - // theme.battery_75_fgcolor = TFT_GREENYELLOW; - // theme.battery_100_fgcolor = TFT_GREEN; - - theme.add(); -} \ No newline at end of file diff --git a/lib/M5ez/src/themes/mono_furble.h b/lib/M5ez/src/themes/mono_furble.h deleted file mode 100644 index d26adf1..0000000 --- a/lib/M5ez/src/themes/mono_furble.h +++ /dev/null @@ -1,28 +0,0 @@ -// This is the dark theme. See how little you need to change for a different look? - -#define TFT_FURBLE (0xFB60) // color565(255,111,0) -{ - ezTheme theme; - - theme.name = "Mono Furble"; // Change this when making theme - theme.background = TFT_BLACK; - theme.foreground = TFT_WHITE; - theme.header_bgcolor = TFT_BLACK; - theme.header_fgcolor = TFT_FURBLE; - theme.print_color = theme.foreground; - theme.button_bgcolor_b = TFT_WHITE; - theme.button_bgcolor_t = TFT_WHITE; - theme.button_fgcolor = TFT_BLACK; - theme.menu_item_color = theme.foreground; - theme.menu_sel_bgcolor = theme.foreground; - theme.menu_sel_fgcolor = theme.background; - theme.msg_color = theme.foreground; - theme.progressbar_color = theme.foreground; - theme.battery_0_fgcolor = TFT_FURBLE; - theme.battery_25_fgcolor = TFT_FURBLE; - theme.battery_50_fgcolor = TFT_FURBLE; - theme.battery_75_fgcolor = TFT_WHITE; - theme.battery_100_fgcolor = TFT_WHITE; - - theme.add(); -} diff --git a/lib/M5ez/tech_notes/converting_from_M5ez_1.x.md b/lib/M5ez/tech_notes/converting_from_M5ez_1.x.md deleted file mode 100644 index 5b60cc4..0000000 --- a/lib/M5ez/tech_notes/converting_from_M5ez_1.x.md +++ /dev/null @@ -1,45 +0,0 @@ -## Converting from M5ez 1.x - -This section deals with conversion of code that was written for the 1.x versions of M5ez. Since it's now 2020 and the last 1.x was released in September of 2018, this section has been moved from the user manual to a tech note. This document is of no use if you've never used M5ez 1.x. - -### What's new in M5ez 2.x ? - -> *Kept here just to show what wasn't there in 1.x* - -* Many user-selectable settings, including backlight, clock/timezone, FACES keyboard, etc. -* Theme values can be changed at runtime, multiple thmes can be loaded; a theme chooser then shows up in the settings -* Header can have user-defined widgets (internally used for clock and wifi signal display) -* The canvas now inherits from the Print class, so everything print and println could do, you can now also do on the canvas. -* The canvas scrolls (if you want it to) -* you can create your own events that get ran in the M5ez button wait loops -* Many bugs got fixed -* Code is a lot cleaner now, easier to add things without the sometimes ad-hoc and organically grown constructs of M5ez 1.x - -However, all of this comes with some changes to the names of common functions in M5ez. Generally things are more consistent now, but that does mean they are different: your M5ez 1.x code will not run without modifications. I am not planning an M5ez 3.0 at this time, so expect no further modifications to be needed to existing code for a while. - -### Converting code from M5ez 1.x to 2.x - -To convert code form 1.x to 2.x, first remove the `m5.begin()` at the start of your code, and replace it by `ez.begin()`. Then it is recommended you replace the following functions: - -| M5ez 1.x | | M5ez 2.x | -|:---------|:--:|:---------| -| `ez.clearScreen()` | **—>** | `ez.screen.clear()` -| `ez.background()` | **—>** | `ez.screen.background()` -| `ez.drawHeader(` | **—>** | `ez.header.show(` -| `ez.removeHeader()` | **—>** | `ez.header.hide()` -| `ez.clearCanvas()` | **—>** | `ez.canvas.clear()` -| `ez.canvasTop()` | **—>** | `ez.canvas.top()` -| `ez.canvasBottom()` | **—>** | `ez.canvas.bottom()` -| `ez.canvasLeft()` | **—>** | `ez.canvas.left()` -| `ez.canvasRight()` | **—>** | `ez.canvas.right()` -| `ez.canvasWidth()` | **—>** | `ez.canvas.width()` -| `ez.canvasHeight()` | **—>** | `ez.canvas.height()` -| `ez.print(` | **—>** | `ez.canvas.print(` -| `ez.println(` | **—>** | `ez.canvas.println(` -| `ez.drawButtons(` | **—>** | `ez.buttons.show(` -| `ez.getButtons(` | **—>** | `ez.buttons.poll(` -| `ez.waitForButtons(` | **—>** | `ez.buttons.wait(` - -Then, check all your print functions: they no longer support the setting of font, location or color in the statement itself: these need to be done with `ez.canvas.font`, `ez.canvas.color` etc. (Print is much nicer now because the canvas object inherited from the Print class, and because the canvas can scroll now.) - -Then simply fix any remaining problems by first fixing any compiler complaints with this document in hand, and then walk through all of your program's functionality to see if everything works as expected. \ No newline at end of file diff --git a/lib/M5ez/tech_notes/obscure_errors_and_resolutions.md b/lib/M5ez/tech_notes/obscure_errors_and_resolutions.md deleted file mode 100644 index 37647ff..0000000 --- a/lib/M5ez/tech_notes/obscure_errors_and_resolutions.md +++ /dev/null @@ -1,12 +0,0 @@ -### WPS error - -By default, M5ez supports "WPS pushbutton" and "WPS pincode" as ways to connect to Wifi. If you run into an error saying ... - -``` -Documents\Arduino\libraries\M5ez-master\src\M5ez.cpp:1319:19: -error: 'struct esp_wps_config_t' has no member named 'factory_info' -``` - -... it probably means your ESP32 library is out of date. Espressif (the ESP32 people) changed how WPS is done in late June 2018. If this happens to you and you would like to have WPS, simply follow the instructions [here](https://github.com/espressif/arduino-esp32/blob/master/docs/arduino-ide/boards_manager.md) to make sure you have the latest ESP32 Arduino support and the problem should go away. - -> *Alternatively, if you do not plan to use WPS, you can also comment out `#define M5EZ_WPS` in the `M5ez.h` file in the library's directory.** \ No newline at end of file diff --git a/lib/M5ez/tech_notes/settings_persistence.md b/lib/M5ez/tech_notes/settings_persistence.md deleted file mode 100644 index 6607624..0000000 --- a/lib/M5ez/tech_notes/settings_persistence.md +++ /dev/null @@ -1,93 +0,0 @@ -# M5ez Settings Persistence - -M5ez provides UI for managing a number of settings which are persisted in NVS (Non-Volatile Storage) memory, and are accessible from other programs. -All settings share a single NVS namespace: - -> NVS NameSpace: "M5ez" - -## Wifi Settings - -The WiFi settings include Autoconnect, SSIDs, and passwords. SSIDs and passwords are grouped in pairs, but stored as individual strings. -The association between SSID and password is established by a numeric suffix, starting at one and incrementing for each pair. - -| Key Name | Type | Purpose | -| :------------- | :------ | :---------------------------------------------- | -| autoconnect_on | boolean | True if ezWiFi should connect to strongest SSID | -| SSID1 | String | Name of first stored network (if any) | -| key1 | String | Password of first stored network (if any) | -| ... | -| SSIDn | String | Name of last stored network | -| keyn | String | Password of last stored network | - -## Battery Settings - -The Battery settings contains only a single flag which indicates whether or not the battery icon is displayed in the header. - -| Key Name | Type | Purpose | -| :-------------- | :------ | :------------------------------------------------ | -| battery_icon_on | boolean | True if battery icon is to be displayed in header | - -## Clock Settings - -The Clock settings include 4 items. The timezone names are described [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). - -| Key Name | Type | Purpose | -| :-------------- | :------ | :------------------------------------------------- | -| clock_on | boolean | True if time is to be displayed in header | -| timezone | String | Name or description of selected timezone | -| clock12 | boolean | True if time should be displayed in 12 hr format | -| ampm | boolean | True if am/pm should be displayed after 12 hr time | - -## Backlight Settings - -The Backlight settings are stored as two unsigned characters (0 - 255) representing brightness and inactivity timeout. - -| Key Name | Type | Purpose | -| :-------------- | :-------- | :--------------------------------------------------------- | -| brightness | uint8_t | Lcd brightness. Default is 128. | -| inactivity | uint8_t | Time until Lcd is blanked. 0 = never, else n * 30 seconds. | - -## FACES Settings - -The Faces settings group currently contains a single member. - -| Key Name | Type | Purpose | -| :-------------- | :------ | :--------------------------------------------------------- | -| faces_on | boolean | True if the keyboard Face should be used for text input. | - -## Theme Settings - -The Theme settings group also contains a single member. - -| Key Name | Type | Purpose | -| :-------------- | :------ | :--------------------------------------------------------- | -| theme | String | Name of the selected theme (if any.) | - ---- - -## Accessing Settings - -Using the Arduino Framework, access NVS settings using the Preferences library. - -Reading preferences: -``` -#include -Preferences prefs; -... - // Read preferences - prefs.begin("M5ez", true); // Namespace = "M5ez", true means ReadOnly. No reason to modify settings when we're reading them. - String theme = prefs.getString("theme", "Default"); // Type = String, Key name = "theme", default value if missing = "Default" (can be omitted) - uint8_t brightness = prefs.getUChar("brightness", 128); // Type = uint8_t (UChar), Key name = "brightness", default value if missing = 128 (can be omitted) - prefs.end(); // Always close preferences promptly, open it only when needed. -``` - -Writing preferences. Be certain to write using the correct data type or an error will result. -``` -#include -Preferences prefs; -... - // Write preferences - prefs.begin("M5ez", false); // Namespace = "M5ez", false means ReadWrite. Cannot change NVS otherwise. - prefs.putBool("autoconnect_on", true); // Type = bool, Key name = "autoconnect_on", value = true - prefs.end(); // Always close preferences promptly, especially when in read/write mode. -``` diff --git a/lib/M5ez/tools/get_cert b/lib/M5ez/tools/get_cert deleted file mode 100755 index debdb22..0000000 --- a/lib/M5ez/tools/get_cert +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/sh - -# This script will take an https URL as an argument. It will do the following: -# -# It will find whether this URL is the "effective URL", or whether it redirects -# (possibly multiple times) to something else. It will output this "effective URL". -# -# It will then take the host part of that URL, connect to port 443 on it, and get the -# certificate chain. It will take the last certificate on the chain (the root cert) -# and obtain the issuer details as well as the notAfter date. -# -# The certicate is then saved in a format that can be included in an Arduino sketch -# or any C/C++ environment to provide a "const char * root_cert" variable to hold -# the entire root certificate. - - -URL=`curl -w "%{url_effective}\n" -I -L -s -S $1 -o /dev/null` -HOST=`echo $URL | awk -F[/:] '{print $4}'` -FILENAME=`echo $HOST | sed -e 's/\./_/g'`.h -TMPFILE=/tmp/cert.get_cert -openssl s_client -showcerts -connect $HOST:443 < /dev/null 2>/dev/null| sed -n 'H; /^-----BEGIN CERTIFICATE-----/h; ${g;p;}' |sed -e '/-----END CERTIFICATE-----/q' > $TMPFILE - -NOTAFTER=`cat $TMPFILE | openssl x509 -noout -dates | grep 'notAfter'` -ISSUER=`cat $TMPFILE | openssl x509 -noout -issuer` - -echo "The effective download URL (after resolving forwards) is:" -echo " $URL" - -cat > $FILENAME <> $FILENAME -rm $TMPFILE - -echo "" -echo "The root certificate include file is saved as:" -echo " $FILENAME" \ No newline at end of file diff --git a/lib/M5ez/tools/gh-md-toc b/lib/M5ez/tools/gh-md-toc deleted file mode 100755 index b63b816..0000000 --- a/lib/M5ez/tools/gh-md-toc +++ /dev/null @@ -1,228 +0,0 @@ -#!/usr/bin/env bash - -# -# Steps: -# -# 1. Download corresponding html file for some README.md: -# curl -s $1 -# -# 2. Discard rows where no substring 'user-content-' (github's markup): -# awk '/user-content-/ { ... -# -# 3.1 Get last number in each row like ' ... sitemap.js.*<\/h/)+2, RLENGTH-5) -# -# 5. Find anchor and insert it inside "(...)": -# substr($0, match($0, "href=\"[^\"]+?\" ")+6, RLENGTH-8) -# - -gh_toc_version="0.5.0" - -gh_user_agent="gh-md-toc v$gh_toc_version" - -# -# Download rendered into html README.md by its url. -# -# -gh_toc_load() { - local gh_url=$1 - - if type curl &>/dev/null; then - curl --user-agent "$gh_user_agent" -s "$gh_url" - elif type wget &>/dev/null; then - wget --user-agent="$gh_user_agent" -qO- "$gh_url" - else - echo "Please, install 'curl' or 'wget' and try again." - exit 1 - fi -} - -# -# Converts local md file into html by GitHub -# -# ➥ curl -X POST --data '{"text": "Hello world github/linguist#1 **cool**, and #1!"}' https://api.github.com/markdown -#

Hello world github/linguist#1 cool, and #1!

'" -gh_toc_md2html() { - local gh_file_md=$1 - URL=https://api.github.com/markdown/raw - TOKEN="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/token.txt" - if [ -f "$TOKEN" ]; then - URL="$URL?access_token=$(cat $TOKEN)" - fi - curl -s --user-agent "$gh_user_agent" \ - --data-binary @"$gh_file_md" -H "Content-Type:text/plain" \ - $URL -} - -# -# Is passed string url -# -gh_is_url() { - case $1 in - https* | http*) - echo "yes";; - *) - echo "no";; - esac -} - -# -# TOC generator -# -gh_toc(){ - local gh_src=$1 - local gh_src_copy=$1 - local gh_ttl_docs=$2 - local need_replace=$3 - - if [ "$gh_src" = "" ]; then - echo "Please, enter URL or local path for a README.md" - exit 1 - fi - - - # Show "TOC" string only if working with one document - if [ "$gh_ttl_docs" = "1" ]; then - - echo "Table of Contents" - echo "=================" - echo "" - gh_src_copy="" - - fi - - if [ "$(gh_is_url "$gh_src")" == "yes" ]; then - gh_toc_load "$gh_src" | gh_toc_grab "$gh_src_copy" - if [ "$need_replace" = "yes" ]; then - echo - echo "!! '$gh_src' is not a local file" - echo "!! Can't insert the TOC into it." - echo - fi - else - local toc=`gh_toc_md2html "$gh_src" | gh_toc_grab "$gh_src_copy"` - echo "$toc" - if [ "$need_replace" = "yes" ]; then - local ts="<\!--ts-->" - local te="<\!--te-->" - local dt=`date +'%F_%H%M%S'` - local ext=".orig.${dt}" - local toc_path="${gh_src}.toc.${dt}" - local toc_footer="" - # http://fahdshariff.blogspot.ru/2012/12/sed-mutli-line-replacement-between-two.html - # clear old TOC - sed -i${ext} "/${ts}/,/${te}/{//!d;}" "$gh_src" - # create toc file - echo "${toc}" > "${toc_path}" - echo -e "\n${toc_footer}\n" >> "$toc_path" - # insert toc file - if [[ "`uname`" == "Darwin" ]]; then - sed -i "" "/${ts}/r ${toc_path}" "$gh_src" - else - sed -i "/${ts}/r ${toc_path}" "$gh_src" - fi - echo - echo "!! TOC was added into: '$gh_src'" - echo "!! Origin version of the file: '${gh_src}${ext}'" - echo "!! TOC added into a separate file: '${toc_path}'" - echo - fi - fi -} - -# -# Grabber of the TOC from rendered html -# -# $1 — a source url of document. -# It's need if TOC is generated for multiple documents. -# -gh_toc_grab() { - # if closed is on the new line, then move it on the prev line - # for example: - # was: The command foo1 - # - # became: The command foo1 - sed -e ':a' -e 'N' -e '$!ba' -e 's/\n<\/h/<\/h/g' | - # find strings that corresponds to template - grep -E -o '//' | sed 's/<\/code>//' | - # now all rows are like: - # ... .*<\/h/)+2, RLENGTH-5)"](" gh_url substr($0, match($0, "href=\"[^\"]+?\" ")+6, RLENGTH-8) ")"}' | sed 'y/+/ /; s/%/\\x/g')" -} - -# -# Returns filename only from full path or url -# -gh_toc_get_filename() { - echo "${1##*/}" -} - -# -# Options hendlers -# -gh_toc_app() { - local app_name="gh-md-toc" - local need_replace="no" - - if [ "$1" = '--help' ] || [ $# -eq 0 ] ; then - echo "GitHub TOC generator ($app_name): $gh_toc_version" - echo "" - echo "Usage:" - echo " $app_name [--insert] src [src] Create TOC for a README file (url or local path)" - echo " $app_name - Create TOC for markdown from STDIN" - echo " $app_name --help Show help" - echo " $app_name --version Show version" - return - fi - - if [ "$1" = '--version' ]; then - echo "$gh_toc_version" - return - fi - - if [ "$1" = "-" ]; then - if [ -z "$TMPDIR" ]; then - TMPDIR="/tmp" - elif [ -n "$TMPDIR" -a ! -d "$TMPDIR" ]; then - mkdir -p "$TMPDIR" - fi - local gh_tmp_md - gh_tmp_md=$(mktemp $TMPDIR/tmp.XXXXXX) - while read input; do - echo "$input" >> "$gh_tmp_md" - done - gh_toc_md2html "$gh_tmp_md" | gh_toc_grab "" - return - fi - - if [ "$1" = '--insert' ]; then - need_replace="yes" - shift - fi - - for md in "$@" - do - echo "" - gh_toc "$md" "$#" "$need_replace" - done - - echo "" - echo "Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc)" -} - -# -# Entry point -# -gh_toc_app "$@" diff --git a/lib/M5ez/tools/include_file b/lib/M5ez/tools/include_file deleted file mode 100755 index 648adca..0000000 --- a/lib/M5ez/tools/include_file +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh - -INPUT_PATH=$1 -INPUT_FILE=`basename $1` -# The byte array is named like the file, but in lower case with dots replaced by underscores -ARRAYNAME=`echo $INPUT_FILE | sed -e 's/\\./_/g'` -OUTPUT_FILE=$ARRAYNAME.h -BYTES=`wc -c $INPUT_PATH | awk '{print $1}'` - -echo "Converting input file:" -echo " $INPUT_PATH" -echo "Creating include file:" -echo " $OUTPUT_FILE" - -cat > $OUTPUT_FILE <> $OUTPUT_FILE -echo "};" >> $OUTPUT_FILE \ No newline at end of file diff --git a/lib/furble/Camera.cpp b/lib/furble/Camera.cpp index 4c1992f..fec12ec 100644 --- a/lib/furble/Camera.cpp +++ b/lib/furble/Camera.cpp @@ -10,21 +10,33 @@ Camera::Camera(Type type) : m_Type(type) { Camera::~Camera() { NimBLEDevice::deleteClient(m_Client); + m_Client = nullptr; } bool Camera::connect(esp_power_level_t power) { + const std::lock_guard lock(m_Mutex); + // try extending range by adjusting connection parameters - bool connected = this->connect(); + bool connected = this->_connect(); if (connected) { - // Set BLE transmit power after connection is established. - NimBLEDevice::setPower(power); - m_Client->updateConnParams(m_MinInterval, m_MaxInterval, m_Latency, m_Timeout); + if (m_Type != Type::FAUXNY) { + // Set BLE transmit power after connection is established. + NimBLEDevice::setPower(power); + m_Client->updateConnParams(m_MinInterval, m_MaxInterval, m_Latency, m_Timeout); + } m_Connected = true; } else { m_Connected = false; } - return connected; + return m_Connected; +} + +void Camera::disconnect(void) { + const std::lock_guard lock(m_Mutex); + m_Active = false; + m_Progress = 0; + this->_disconnect(); } bool Camera::isActive(void) const { @@ -47,11 +59,15 @@ const NimBLEAddress &Camera::getAddress(void) const { return m_Address; } -float Camera::getConnectProgress(void) const { +uint8_t Camera::getConnectProgress(void) const { return m_Progress.load(); } bool Camera::isConnected(void) const { + if (m_Type == Type::FAUXNY) { + return m_Connected; + } + return m_Connected && m_Client->isConnected(); } diff --git a/lib/furble/Camera.h b/lib/furble/Camera.h index bdc6c82..9dec627 100644 --- a/lib/furble/Camera.h +++ b/lib/furble/Camera.h @@ -2,6 +2,7 @@ #define CAMERA_H #include +#include #include #include @@ -26,6 +27,7 @@ class Camera { CANON_EOS_M6 = 2, CANON_EOS_RP = 3, MOBILE_DEVICE = 4, + FAUXNY = 5, }; /** @@ -52,14 +54,14 @@ class Camera { ~Camera(); /** - * Wrapper for protected pure virtual Camera::connect(). + * Wrapper for protected pure virtual Camera::_connect(). */ bool connect(esp_power_level_t power); /** - * Disconnect from the target. + * Wrapper for protected pure virtual Camera::_disconnect(); */ - virtual void disconnect(void) = 0; + void disconnect(void); /** * Send a shutter button press command. @@ -110,11 +112,12 @@ class Camera { const NimBLEAddress &getAddress(void) const; - float getConnectProgress(void) const; + /** Get connection progress percentage (0-100). */ + uint8_t getConnectProgress(void) const; protected: Camera(Type type); - std::atomic m_Progress; + std::atomic m_Progress; /** * Connect to the target camera such that it is ready for shutter control. @@ -124,9 +127,14 @@ class Camera { * * @return true if the client is now ready for shutter control */ - virtual bool connect(void) = 0; + virtual bool _connect(void) = 0; + + /** + * Disconnect from the target. + */ + virtual void _disconnect(void) = 0; - NimBLEAddress m_Address = NimBLEAddress{}; + NimBLEAddress m_Address = NimBLEAddress {}; NimBLEClient *m_Client; std::string m_Name; bool m_Connected = false; @@ -141,6 +149,9 @@ class Camera { const Type m_Type; + std::mutex m_Mutex; + + bool m_FromScan = false; bool m_Active = false; }; } // namespace Furble diff --git a/lib/furble/CameraList.cpp b/lib/furble/CameraList.cpp index 29c6afd..11138ef 100644 --- a/lib/furble/CameraList.cpp +++ b/lib/furble/CameraList.cpp @@ -3,6 +3,7 @@ #include "CanonEOSM6.h" #include "CanonEOSRP.h" +#include "FauxNY.h" #include "Fujifilm.h" #include "MobileDevice.h" @@ -42,17 +43,19 @@ void CameraList::save_index(std::vector &index) { std::vector CameraList::load_index(void) { std::vector index; - size_t bytes = m_Prefs.getBytesLength(FURBLE_PREF_INDEX); - if (bytes > 0 && (bytes % sizeof(index_entry_t) == 0)) { - uint8_t buffer[bytes] = {0}; - size_t count = bytes / sizeof(index_entry_t); - ESP_LOGI(LOG_TAG, "Index entries: %d", count); - m_Prefs.getBytes(FURBLE_PREF_INDEX, buffer, bytes); - index_entry_t *entry = (index_entry_t *)buffer; - - for (int i = 0; i < count; i++) { - ESP_LOGI(LOG_TAG, "Loading index entry: %s", entry[i].name); - index.push_back(entry[i]); + if (m_Prefs.isKey(FURBLE_PREF_INDEX)) { + size_t bytes = m_Prefs.getBytesLength(FURBLE_PREF_INDEX); + if (bytes > 0 && (bytes % sizeof(index_entry_t) == 0)) { + uint8_t buffer[bytes] = {0}; + size_t count = bytes / sizeof(index_entry_t); + ESP_LOGI(LOG_TAG, "Index entries: %d", count); + m_Prefs.getBytes(FURBLE_PREF_INDEX, buffer, bytes); + index_entry_t *entry = (index_entry_t *)buffer; + + for (int i = 0; i < count; i++) { + ESP_LOGI(LOG_TAG, "Loading index entry: %s", entry[i].name); + index.push_back(entry[i]); + } } } @@ -62,7 +65,7 @@ std::vector CameraList::load_index(void) { void CameraList::add_index(std::vector &index, index_entry_t &entry) { bool exists = false; for (auto &i : index) { - ESP_LOGI(LOG_TAG, "%s : %s", i.name, entry.name); + ESP_LOGD(LOG_TAG, "%s : %s", i.name, entry.name); if (strcmp(i.name, entry.name) == 0) { ESP_LOGI(LOG_TAG, "Overwriting existing entry: %s", entry.name); i = entry; @@ -81,7 +84,7 @@ void CameraList::save(const Furble::Camera *camera) { m_Prefs.begin(FURBLE_STR, false); std::vector index = load_index(); - index_entry_t entry = {0}; + index_entry_t entry; fillSaveEntry(entry, camera); add_index(index, entry); @@ -103,7 +106,7 @@ void CameraList::remove(Furble::Camera *camera) { m_Prefs.begin(FURBLE_STR, false); std::vector index = load_index(); - index_entry_t entry = {0}; + index_entry_t entry; fillSaveEntry(entry, camera); size_t i = 0; @@ -158,6 +161,9 @@ void CameraList::load(void) { case Camera::Type::MOBILE_DEVICE: m_ConnectList.push_back(std::unique_ptr(new MobileDevice(dbuffer, dbytes))); break; + case Camera::Type::FAUXNY: + m_ConnectList.push_back(std::unique_ptr(new FauxNY(dbuffer, dbytes))); + break; } } m_Prefs.end(); @@ -179,6 +185,10 @@ void CameraList::clear(void) { m_ConnectList.clear(); } +Furble::Camera *CameraList::last(void) { + return m_ConnectList.back().get(); +} + Furble::Camera *CameraList::get(size_t n) { return m_ConnectList[n].get(); } @@ -202,4 +212,8 @@ void CameraList::add(const NimBLEAddress &address, const std::string &name) { m_ConnectList.push_back(std::unique_ptr(new Furble::MobileDevice(address, name))); } +void CameraList::addFauxNY(void) { + m_ConnectList.push_back(std::unique_ptr(new Furble::FauxNY())); +} + } // namespace Furble diff --git a/lib/furble/CameraList.h b/lib/furble/CameraList.h index c806c05..7f36c27 100644 --- a/lib/furble/CameraList.h +++ b/lib/furble/CameraList.h @@ -44,6 +44,11 @@ class CameraList { */ static void add(const NimBLEAddress &address, const std::string &name); + /** + * Add FauxNY device to the list. + */ + static void addFauxNY(void); + /** * Number of connectable devices. */ @@ -54,6 +59,11 @@ class CameraList { */ static void clear(void); + /** + * Get last added entry. + */ + static Furble::Camera *last(void); + /** * Retrieve device by index. */ diff --git a/lib/furble/CanonEOS.cpp b/lib/furble/CanonEOS.cpp index 94d61b5..daf09d4 100644 --- a/lib/furble/CanonEOS.cpp +++ b/lib/furble/CanonEOS.cpp @@ -27,11 +27,6 @@ CanonEOS::CanonEOS(Type type, const NimBLEAdvertisedDevice *pDevice) : Camera(ty Device::getUUID128(&m_Uuid); } -CanonEOS::~CanonEOS(void) { - NimBLEDevice::deleteClient(m_Client); - m_Client = nullptr; -} - // Handle pairing notification void CanonEOS::pairCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, @@ -46,7 +41,7 @@ void CanonEOS::pairCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic, bool CanonEOS::write_value(NimBLEClient *pClient, const char *serviceUUID, const char *characteristicUUID, - uint8_t *data, + const uint8_t *data, size_t length) { NimBLERemoteService *pSvc = pClient->getService(serviceUUID); if (pSvc) { @@ -60,8 +55,8 @@ bool CanonEOS::write_value(NimBLEClient *pClient, bool CanonEOS::write_prefix(NimBLEClient *pClient, const char *serviceUUID, const char *characteristicUUID, - uint8_t prefix, - uint8_t *data, + const uint8_t prefix, + const uint8_t *data, size_t length) { uint8_t buffer[length + 1] = {0}; buffer[0] = prefix; @@ -75,7 +70,7 @@ bool CanonEOS::write_prefix(NimBLEClient *pClient, * The EOS uses the 'just works' BLE bonding to pair, all bond management is * handled by the underlying NimBLE and ESP32 libraries. */ -bool CanonEOS::connect(void) { +bool CanonEOS::_connect(void) { if (NimBLEDevice::isBonded(m_Address)) { // Already bonded? Assume pair acceptance! m_PairResult = CANON_EOS_PAIR_ACCEPT; @@ -90,14 +85,14 @@ bool CanonEOS::connect(void) { } ESP_LOGI(LOG_TAG, "Connected"); - m_Progress = 10.0f; + m_Progress = 10; ESP_LOGI(LOG_TAG, "Securing"); if (!m_Client->secureConnection()) { return false; } ESP_LOGI(LOG_TAG, "Secured!"); - m_Progress = 20.0f; + m_Progress = 20; NimBLERemoteService *pSvc = m_Client->getService(CANON_EOS_SVC_IDEN_UUID); if (pSvc) { @@ -111,26 +106,26 @@ bool CanonEOS::connect(void) { } ESP_LOGI(LOG_TAG, "Identifying 1!"); - const char *name = Device::getStringID(); + const auto name = Device::getStringID(); if (!write_prefix(m_Client, CANON_EOS_SVC_IDEN_UUID, CANON_EOS_CHR_NAME_UUID, 0x01, - (uint8_t *)name, strlen(name))) + (uint8_t *)name.c_str(), name.length())) return false; - m_Progress = 30.0f; + m_Progress = 30; ESP_LOGI(LOG_TAG, "Identifying 2!"); if (!write_prefix(m_Client, CANON_EOS_SVC_IDEN_UUID, CANON_EOS_CHR_IDEN_UUID, 0x03, m_Uuid.uint8, - UUID128_LEN)) + Device::UUID128_LEN)) return false; - m_Progress = 40.0f; + m_Progress = 40; ESP_LOGI(LOG_TAG, "Identifying 3!"); if (!write_prefix(m_Client, CANON_EOS_SVC_IDEN_UUID, CANON_EOS_CHR_IDEN_UUID, 0x04, - (uint8_t *)name, strlen(name))) + (uint8_t *)name.c_str(), name.length())) return false; - m_Progress = 50.0f; + m_Progress = 50; ESP_LOGI(LOG_TAG, "Identifying 4!"); @@ -138,18 +133,18 @@ bool CanonEOS::connect(void) { if (!write_prefix(m_Client, CANON_EOS_SVC_IDEN_UUID, CANON_EOS_CHR_IDEN_UUID, 0x05, &x, 1)) return false; - m_Progress = 60.0f; + m_Progress = 60; ESP_LOGI(LOG_TAG, "Identifying 5!"); // Give the user 60s to confirm/deny pairing ESP_LOGI(LOG_TAG, "Waiting for user to confirm/deny pairing."); for (unsigned int i = 0; i < 60; i++) { - m_Progress = 70.0f + (float(i) / 6.0f); + m_Progress = m_Progress.load() + (i % 2); if (m_PairResult != 0x00) { break; } - delay(1000); + vTaskDelay(pdMS_TO_TICKS(1000)); } if (m_PairResult != CANON_EOS_PAIR_ACCEPT) { @@ -165,7 +160,7 @@ bool CanonEOS::connect(void) { if (!write_value(m_Client, CANON_EOS_SVC_IDEN_UUID, CANON_EOS_CHR_IDEN_UUID, &x, 1)) return false; - m_Progress = 80.0f; + m_Progress = 90; ESP_LOGI(LOG_TAG, "Switching mode!"); @@ -175,7 +170,7 @@ bool CanonEOS::connect(void) { return false; ESP_LOGI(LOG_TAG, "Done!"); - m_Progress = 100.0f; + m_Progress = 100; return true; } @@ -205,10 +200,9 @@ void CanonEOS::updateGeoData(const gps_t &gps, const timesync_t ×ync) { return; } -void CanonEOS::disconnect(void) { - m_Progress = 0.0f; - m_Connected = false; +void CanonEOS::_disconnect(void) { m_Client->disconnect(); + m_Connected = false; } size_t CanonEOS::getSerialisedBytes(void) const { diff --git a/lib/furble/CanonEOS.h b/lib/furble/CanonEOS.h index 3b7a734..571385d 100644 --- a/lib/furble/CanonEOS.h +++ b/lib/furble/CanonEOS.h @@ -20,7 +20,6 @@ class CanonEOS: public Camera { CanonEOS(Type type, const void *data, size_t len); CanonEOS(Type type, const NimBLEAdvertisedDevice *pDevice); - ~CanonEOS(void); const char *CANON_EOS_SVC_IDEN_UUID = "00010000-0000-1000-0000-d8492fffa821"; /** 0xf108 */ @@ -45,28 +44,30 @@ class CanonEOS: public Camera { bool write_value(NimBLEClient *pClient, const char *serviceUUID, const char *characteristicUUID, - uint8_t *data, + const uint8_t *data, size_t length); bool write_prefix(NimBLEClient *pClient, const char *serviceUUID, const char *characteristicUUID, - uint8_t prefix, - uint8_t *data, + const uint8_t prefix, + const uint8_t *data, size_t length); - bool connect(void) override; void shutterPress(void) override; void shutterRelease(void) override; void focusPress(void) override; void focusRelease(void) override; void updateGeoData(const gps_t &gps, const timesync_t ×ync) override; - void disconnect(void) override; size_t getSerialisedBytes(void) const override; bool serialise(void *buffer, size_t bytes) const override; Device::uuid128_t m_Uuid; + protected: + bool _connect(void) override; + void _disconnect(void) override; + private: volatile uint8_t m_PairResult = 0x00; diff --git a/lib/furble/CanonEOSM6.h b/lib/furble/CanonEOSM6.h index a4e9a9e..7e5d2f5 100644 --- a/lib/furble/CanonEOSM6.h +++ b/lib/furble/CanonEOSM6.h @@ -9,8 +9,8 @@ namespace Furble { */ class CanonEOSM6: public CanonEOS { public: - CanonEOSM6(const void *data, size_t len) : CanonEOS(Type::CANON_EOS_M6, data, len){}; - CanonEOSM6(const NimBLEAdvertisedDevice *pDevice) : CanonEOS(Type::CANON_EOS_M6, pDevice){}; + CanonEOSM6(const void *data, size_t len) : CanonEOS(Type::CANON_EOS_M6, data, len) {}; + CanonEOSM6(const NimBLEAdvertisedDevice *pDevice) : CanonEOS(Type::CANON_EOS_M6, pDevice) {}; /** * Determine if the advertised BLE device is a Canon EOS M6. diff --git a/lib/furble/CanonEOSRP.h b/lib/furble/CanonEOSRP.h index a657b60..d50e741 100644 --- a/lib/furble/CanonEOSRP.h +++ b/lib/furble/CanonEOSRP.h @@ -9,8 +9,8 @@ namespace Furble { */ class CanonEOSRP: public CanonEOS { public: - CanonEOSRP(const void *data, size_t len) : CanonEOS(Type::CANON_EOS_RP, data, len){}; - CanonEOSRP(const NimBLEAdvertisedDevice *pDevice) : CanonEOS(Type::CANON_EOS_RP, pDevice){}; + CanonEOSRP(const void *data, size_t len) : CanonEOS(Type::CANON_EOS_RP, data, len) {}; + CanonEOSRP(const NimBLEAdvertisedDevice *pDevice) : CanonEOS(Type::CANON_EOS_RP, pDevice) {}; /** * Determine if the advertised BLE device is a Canon EOS RP. diff --git a/lib/furble/Device.cpp b/lib/furble/Device.cpp index da5135e..ac81f32 100644 --- a/lib/furble/Device.cpp +++ b/lib/furble/Device.cpp @@ -5,8 +5,9 @@ namespace Furble { -Device::uuid128_t Device::g_Uuid; -char Device::g_StringID[DEVICE_ID_STR_MAX]; +Device::uuid128_t Device::m_Uuid; +char Device::m_StringID[DEVICE_ID_STR_MAX]; +std::string Device::m_ID; /** * Generate a 32-bit PRNG. @@ -23,19 +24,21 @@ void Device::init(void) { uint32_t chip_id = (uint32_t)ESP.getEfuseMac(); for (size_t i = 0; i < UUID128_AS_32_LEN; i++) { chip_id = xorshift(chip_id); - g_Uuid.uint32[i] = chip_id; + m_Uuid.uint32[i] = chip_id; } // truncate ID to 5 hex characters (arbitrary, just make it 'nice' to read) - snprintf(g_StringID, DEVICE_ID_STR_MAX, "%s-%05x", FURBLE_STR, g_Uuid.uint32[0] & 0xFFFFF); + snprintf(m_StringID, DEVICE_ID_STR_MAX, "%s-%05x", FURBLE_STR, m_Uuid.uint32[0] & 0xFFFFF); + + m_ID = std::string(m_StringID); } void Device::getUUID128(uuid128_t *uuid) { - *uuid = g_Uuid; + *uuid = m_Uuid; } -const char *Device::getStringID(void) { - return g_StringID; +const std::string Device::getStringID(void) { + return m_ID; } } // namespace Furble diff --git a/lib/furble/Device.h b/lib/furble/Device.h index 4760ef8..1baf32a 100644 --- a/lib/furble/Device.h +++ b/lib/furble/Device.h @@ -3,14 +3,12 @@ #include -#define DEVICE_ID_STR_MAX (16) -#define UUID128_LEN (16) -#define UUID128_AS_32_LEN (UUID128_LEN / sizeof(uint32_t)) - namespace Furble { class Device { public: + static constexpr size_t UUID128_LEN = 16; + static constexpr size_t UUID128_AS_32_LEN = (UUID128_LEN / sizeof(uint32_t)); /** * UUID type. */ @@ -34,11 +32,14 @@ class Device { /** * Return pseudo-unique identifier string of this device. */ - static const char *getStringID(void); + static const std::string getStringID(void); private: - static uuid128_t g_Uuid; - static char g_StringID[DEVICE_ID_STR_MAX]; + static constexpr size_t DEVICE_ID_STR_MAX = 16; + + static uuid128_t m_Uuid; + static char m_StringID[DEVICE_ID_STR_MAX]; + static std::string m_ID; }; } // namespace Furble diff --git a/lib/furble/FauxNY.cpp b/lib/furble/FauxNY.cpp new file mode 100644 index 0000000..f4b7bbd --- /dev/null +++ b/lib/furble/FauxNY.cpp @@ -0,0 +1,85 @@ +#include + +#include "FauxNY.h" + +namespace Furble { + +FauxNY::FauxNY(const void *data, size_t len) : Camera(Type::FAUXNY) { + if (len != sizeof(fauxNY_t)) { + abort(); + } + + const auto *fauxNY = static_cast(data); + m_Name = std::string(fauxNY->name); + m_ID = fauxNY->id; + m_Address = NimBLEAddress(m_ID, 0); +} + +FauxNY::FauxNY(void) : Camera(Type::FAUXNY) { + m_ID = esp_random(); + m_Name = std::string("FauxNY-") + std::to_string(m_ID % 42); + m_Address = NimBLEAddress(m_ID, 0); +} + +bool FauxNY::matches(void) { + return true; +} + +bool FauxNY::_connect(void) { + ESP_LOGI(m_FauxNYStr, "Connecting"); + m_Progress = 0; + + for (int i = 0; i < 100; i += 1) { + vTaskDelay(pdMS_TO_TICKS(25)); + m_Progress = i; + } + + m_Progress = 100; + + return true; +} + +void FauxNY::shutterPress(void) { + ESP_LOGI(m_FauxNYStr, "shutterPress()"); +} + +void FauxNY::shutterRelease(void) { + ESP_LOGI(m_FauxNYStr, "shutterRelease()"); +} + +void FauxNY::focusPress(void) { + ESP_LOGI(m_FauxNYStr, "focusPress()"); +} + +void FauxNY::focusRelease(void) { + ESP_LOGI(m_FauxNYStr, "focusRelease()"); +} + +void FauxNY::updateGeoData(const gps_t &gps, const timesync_t ×ync) { + ESP_LOGI(m_FauxNYStr, "updateGeoData()"); +}; + +void FauxNY::_disconnect(void) { + ESP_LOGI(m_FauxNYStr, "Disconnecting"); + m_Connected = false; +} + +size_t FauxNY::getSerialisedBytes(void) const { + return sizeof(fauxNY_t); +} + +bool FauxNY::serialise(void *buffer, size_t bytes) const { + if (bytes != sizeof(fauxNY_t)) { + return false; + } + + auto *fauxNY = static_cast(buffer); + strncpy(fauxNY->name, m_Name.c_str(), MAX_NAME); + fauxNY->address = (uint64_t)m_Address; + fauxNY->type = m_Address.getType(); + fauxNY->id = m_ID; + + return true; +} + +} // namespace Furble diff --git a/lib/furble/FauxNY.h b/lib/furble/FauxNY.h new file mode 100644 index 0000000..d937722 --- /dev/null +++ b/lib/furble/FauxNY.h @@ -0,0 +1,43 @@ +#ifndef FAUXNY_H +#define FAUXNY_H + +#include "Camera.h" +#include "Device.h" + +namespace Furble { +/** + * FauxNY fake virtual camera + */ +class FauxNY: public Camera { + public: + FauxNY(const void *data, size_t len); + FauxNY(void); + + static bool matches(void); + + void shutterPress(void) override; + void shutterRelease(void) override; + void focusPress(void) override; + void focusRelease(void) override; + void updateGeoData(const gps_t &gps, const timesync_t ×ync) override; + size_t getSerialisedBytes(void) const override; + bool serialise(void *buffer, size_t bytes) const override; + + protected: + bool _connect(void) override; + void _disconnect(void) override; + + private: + typedef struct _eos_t { + char name[MAX_NAME]; /** Human readable device name. */ + uint64_t address; /** Device MAC address. */ + uint8_t type; /** Address type. */ + uint32_t id; /** Device ID. */ + } fauxNY_t; + + static constexpr const char *m_FauxNYStr = "FauxNY"; + uint64_t m_ID; +}; + +} // namespace Furble +#endif diff --git a/lib/furble/Fujifilm.cpp b/lib/furble/Fujifilm.cpp index c7796ed..79c0b36 100644 --- a/lib/furble/Fujifilm.cpp +++ b/lib/furble/Fujifilm.cpp @@ -121,11 +121,6 @@ Fujifilm::Fujifilm(const NimBLEAdvertisedDevice *pDevice) : Camera(Type::FUJIFIL print_token(m_Token); } -Fujifilm::~Fujifilm(void) { - NimBLEDevice::deleteClient(m_Client); - m_Client = nullptr; -} - constexpr size_t FUJIFILM_ADV_TOKEN_LEN = 7; constexpr uint8_t FUJIFILM_ID_0 = 0xd8; constexpr uint8_t FUJIFILM_ID_1 = 0x04; @@ -152,8 +147,8 @@ bool Fujifilm::matches(const NimBLEAdvertisedDevice *pDevice) { * is what we use to identify ourselves upfront and during subsequent * re-pairing. */ -bool Fujifilm::connect(void) { - m_Progress = 10.0f; +bool Fujifilm::_connect(void) { + m_Progress = 10; NimBLERemoteService *pSvc = nullptr; NimBLERemoteCharacteristic *pChr = nullptr; @@ -163,7 +158,7 @@ bool Fujifilm::connect(void) { return false; ESP_LOGI(LOG_TAG, "Connected"); - m_Progress = 20.0f; + m_Progress = 20; pSvc = m_Client->getService(FUJIFILM_SVC_PAIR_UUID); if (pSvc == nullptr) return false; @@ -179,16 +174,17 @@ bool Fujifilm::connect(void) { if (!pChr->writeValue(m_Token.data(), sizeof(m_Token), true)) return false; ESP_LOGI(LOG_TAG, "Paired!"); - m_Progress = 30.0f; + m_Progress = 30; ESP_LOGI(LOG_TAG, "Identifying"); pChr = pSvc->getCharacteristic(FUJIFILM_CHR_IDEN_UUID); if (!pChr->canWrite()) return false; - if (!pChr->writeValue(Device::getStringID(), true)) + const auto name = Device::getStringID(); + if (!pChr->writeValue(name.c_str(), name.length(), true)) return false; ESP_LOGI(LOG_TAG, "Identified!"); - m_Progress = 40.0f; + m_Progress = 40; // indications ESP_LOGI(LOG_TAG, "Configuring"); @@ -199,36 +195,33 @@ bool Fujifilm::connect(void) { if (!this->subscribe(FUJIFILM_SVC_CONF_UUID, FUJIFILM_CHR_IND2_UUID, false)) { return false; } - m_Progress = 50.0f; + m_Progress = 50; - // wait for up to 5000ms callback - for (unsigned int i = 0; i < 5000; i += 100) { + // wait for up to (10*500)ms callback + for (unsigned int i = 0; i < 10; i++) { if (m_Configured) { break; } - // 5000 / 100 = 50 - // 10 / 50 = 0.2 - float progress = m_Progress.load() + 0.2f; - m_Progress = progress; - delay(100); + m_Progress = m_Progress.load() + 1; + vTaskDelay(pdMS_TO_TICKS(500)); } - m_Progress = 60.0f; + m_Progress = 60; // notifications if (!this->subscribe(FUJIFILM_SVC_CONF_UUID, FUJIFILM_CHR_NOT1_UUID, true)) { return false; } - m_Progress = 70.0f; + m_Progress = 70; if (!this->subscribe(FUJIFILM_SVC_CONF_UUID, FUJIFILM_CHR_NOT2_UUID, true)) { return false; } - m_Progress = 80.0f; + m_Progress = 80; if (!this->subscribe(FUJIFILM_SVC_CONF_UUID, FUJIFILM_CHR_IND3_UUID, false)) { return false; } - m_Progress = 100.0f; + m_Progress = 100; ESP_LOGI(LOG_TAG, "Configured"); ESP_LOGI(LOG_TAG, "Connected"); @@ -292,11 +285,13 @@ void Fujifilm::sendGeoData(const gps_t &gps, const timesync_t ×ync) { } }; - ESP_LOGI(LOG_TAG, "Sending geotag data (%u bytes) to 0x%04x", sizeof(geotag), - pChr->getHandle()); - ESP_LOGI(LOG_TAG, " lat: %f, %d", gps.latitude, geotag.latitude); - ESP_LOGI(LOG_TAG, " lon: %f, %d", gps.longitude, geotag.longitude); - ESP_LOGI(LOG_TAG, " alt: %f, %d", gps.altitude, geotag.altitude); + ESP_LOGI(LOG_TAG, + "Sending geotag data (%u bytes) to 0x%04x\r\n" + " lat: %f, %d\r\n" + " lon: %f, %d\r\n" + " alt: %f, %d\r\n", + sizeof(geotag), pChr->getHandle(), gps.latitude, geotag.latitude, gps.longitude, + geotag.longitude, gps.altitude, geotag.altitude); pChr->writeValue((uint8_t *)&geotag, sizeof(geotag), true); } @@ -315,8 +310,7 @@ void Fujifilm::print(void) { ESP_LOGI(LOG_TAG, "Type: %d", m_Address.getType()); } -void Fujifilm::disconnect(void) { - m_Progress = 0.0f; +void Fujifilm::_disconnect(void) { m_Client->disconnect(); m_Connected = false; } diff --git a/lib/furble/Fujifilm.h b/lib/furble/Fujifilm.h index e55b9c6..f909fc3 100644 --- a/lib/furble/Fujifilm.h +++ b/lib/furble/Fujifilm.h @@ -15,23 +15,24 @@ class Fujifilm: public Camera { public: Fujifilm(const void *data, size_t len); Fujifilm(const NimBLEAdvertisedDevice *pDevice); - ~Fujifilm(void); /** * Determine if the advertised BLE device is a Fujifilm X-T30. */ static bool matches(const NimBLEAdvertisedDevice *pDevice); - bool connect(void) override; void shutterPress(void) override; void shutterRelease(void) override; void focusPress(void) override; void focusRelease(void) override; void updateGeoData(const gps_t &gps, const timesync_t ×ync) override; - void disconnect(void) override; size_t getSerialisedBytes(void) const override; bool serialise(void *buffer, size_t bytes) const override; + protected: + bool _connect(void) override; + void _disconnect(void) override; + private: /** * Time synchronisation. diff --git a/lib/furble/Furble.cpp b/lib/furble/Furble.cpp index 80886ff..f675061 100644 --- a/lib/furble/Furble.cpp +++ b/lib/furble/Furble.cpp @@ -11,7 +11,7 @@ const char *LOG_TAG = FURBLE_STR; namespace Furble { NimBLEScan *Scan::m_Scan = nullptr; -scanResultCallback *Scan::m_ScanResultCallback = nullptr; +std::function Scan::m_ScanResultCallback = nullptr; void *Scan::m_ScanResultPrivateData = nullptr; HIDServer *Scan::m_HIDServer = nullptr; @@ -19,7 +19,7 @@ HIDServer *Scan::m_HIDServer = nullptr; * BLE Advertisement callback. */ class Scan::ScanCallback: public NimBLEScanCallbacks { - void onResult(NimBLEAdvertisedDevice *pDevice) { + void onResult(const NimBLEAdvertisedDevice *pDevice) override { if (CameraList::match(pDevice)) { ESP_LOGI(LOG_TAG, "RSSI(%s) = %d", pDevice->getName().c_str(), pDevice->getRSSI()); if (m_ScanResultCallback != nullptr) { @@ -45,7 +45,7 @@ void Scan::init(esp_power_level_t power) { NimBLEDevice::init(Device::getStringID()); NimBLEDevice::setPower(power); NimBLEDevice::setSecurityAuth(true, true, true); - NimBLEDevice::setOwnAddrType(BLE_OWN_ADDR_PUBLIC); + // NimBLEDevice::setOwnAddrType(BLE_OWN_ADDR_PUBLIC); // NimBLE requires configuring server before scan m_HIDServer = HIDServer::getInstance(); @@ -57,12 +57,12 @@ void Scan::init(esp_power_level_t power) { m_Scan->setWindow(6553); } -void Scan::start(scanResultCallback scanCallback, void *scanPrivateData) { +void Scan::start(std::function scanCallback, void *scanPrivateData) { m_HIDServer->start(nullptr, new HIDServerCallback()); m_ScanResultCallback = scanCallback; m_ScanResultPrivateData = scanPrivateData; - m_Scan->start(BLE_HS_FOREVER, false); + m_Scan->start(0, false); } void Scan::stop(void) { diff --git a/lib/furble/Furble.h b/lib/furble/Furble.h index 55a419f..b60aa15 100644 --- a/lib/furble/Furble.h +++ b/lib/furble/Furble.h @@ -11,8 +11,6 @@ #define FURBLE_VERSION "unknown" #endif -typedef void(scanResultCallback(void *context)); - namespace Furble { /** * BLE advertisement scanning class. @@ -30,7 +28,7 @@ class Scan { * Start the scan for BLE advertisements with a callback function when a matching reseult is * encountered. */ - static void start(scanResultCallback scanCallBack, void *scanResultPrivateData); + static void start(std::function scanCallback, void *scanResultPrivateData); /** * Stop the scan. @@ -50,7 +48,7 @@ class Scan { class HIDServerCallback; static NimBLEScan *m_Scan; - static scanResultCallback *m_ScanResultCallback; + static std::function m_ScanResultCallback; static void *m_ScanResultPrivateData; static HIDServer *m_HIDServer; }; diff --git a/lib/furble/HIDServer.cpp b/lib/furble/HIDServer.cpp index 8bd9339..0a339b8 100644 --- a/lib/furble/HIDServer.cpp +++ b/lib/furble/HIDServer.cpp @@ -1,5 +1,6 @@ #include +#include "FurbleTypes.h" #include "HIDServer.h" #define HID_GENERIC_REMOTE (0x180) @@ -41,21 +42,23 @@ HIDServer::HIDServer() { m_Server->advertiseOnDisconnect(false); m_HID = new NimBLEHIDDevice(m_Server); - m_Input = m_HID->inputReport(1); + m_Input = m_HID->getInputReport(1); // set manufacturer name - m_HID->manufacturer()->setValue("Maker Community"); + m_HID->setManufacturer("Maker Community"); // set USB vendor and product ID - m_HID->pnp(0x02, 0xe502, 0xa111, 0x0210); + m_HID->setPnp(0x02, 0xe502, 0xa111, 0x0210); // information about HID device: device is not localized, device can be connected - m_HID->hidInfo(0x00, 0x02); + m_HID->setHidInfo(0x00, 0x02); - m_HID->reportMap((uint8_t *)hidReportDescriptor, sizeof(hidReportDescriptor)); + m_HID->setReportMap((uint8_t *)hidReportDescriptor, sizeof(hidReportDescriptor)); // advertise the services m_Advertising = m_Server->getAdvertising(); + m_Advertising->setName(FURBLE_STR); m_Advertising->setAppearance(HID_GENERIC_REMOTE); - m_Advertising->addServiceUUID(m_HID->hidService()->getUUID()); + m_Advertising->addServiceUUID(m_HID->getHidService()->getUUID()); + m_Advertising->enableScanResponse(true); } HIDServer::~HIDServer() { @@ -74,13 +77,13 @@ void HIDServer::start(NimBLEAddress *address, HIDServerCallbacks *hidCallback) { m_hidCallbacks = hidCallback; m_HID->startServices(); - m_Server->getPeerNameOnConnect((address == nullptr) ? true : false); - +#if 0 // Cannot get directed advertising working properly. - // m_Advertising->setAdvertisementType((address == nullptr) ? BLE_GAP_CONN_MODE_UND : - // BLE_GAP_CONN_MODE_DIR); - m_Advertising->setAdvertisementType(BLE_GAP_CONN_MODE_UND); - m_Advertising->start(BLE_HS_FOREVER, nullptr, address); + if (address != nullptr) { + m_Advertising->setConnectableMode(BLE_GAP_CONN_MODE_DIR); + } +#endif + m_Advertising->start(0, address); } void HIDServer::stop(void) { @@ -88,16 +91,26 @@ void HIDServer::stop(void) { m_Server->stopAdvertising(); } -void HIDServer::onAuthenticationComplete(const NimBLEConnInfo &connInfo, const std::string &name) { - NimBLEAddress address = connInfo.getIdAddress(); - if (m_hidCallbacks != nullptr) { - m_hidCallbacks->onComplete(address, name); - } +void HIDServer::onConnect(NimBLEServer *p_Server, NimBLEConnInfo &connInfo) { + // NimBLEDevice::startSecurity(connInfo.getConnHandle()); } -void HIDServer::onIdentity(const NimBLEConnInfo &connInfo) { - ESP_LOGI("HID", "identity resolved: address = %s, type = %d", - connInfo.getIdAddress().toString().c_str(), connInfo.getIdAddress().getType()); +void HIDServer::onAuthenticationComplete(NimBLEConnInfo &connInfo) { + // do nothing +} + +void HIDServer::onIdentity(NimBLEConnInfo &connInfo) { + auto address = connInfo.getIdAddress(); +#if 0 + // Moved to _connect() call in MobileDevice + auto peer = NimBLEDevice::getServer()->getClient(connInfo); + // getValue() hangs, likely due to being called within the callback + auto name = peer->getValue(NimBLEUUID((uint16_t)BLE_SVC_GAP_UUID16), NimBLEUUID((uint16_t)BLE_SVC_GAP_CHR_UUID16_DEVICE_NAME)); + ESP_LOGI("HID", "peer = %s", name.c_str()); +#endif + if (m_hidCallbacks != nullptr) { + m_hidCallbacks->onComplete(address, (std::string)address); + } } NimBLECharacteristic *HIDServer::getInput(void) { @@ -110,7 +123,7 @@ NimBLEConnInfo HIDServer::getConnInfo(NimBLEAddress &address) { void HIDServer::disconnect(NimBLEAddress &address) { NimBLEConnInfo info = m_Server->getPeerInfo(address); - m_Server->disconnect(info.getConnHandle()); + m_Server->disconnect(info); } bool HIDServer::isConnected(const NimBLEAddress &address) { diff --git a/lib/furble/HIDServer.h b/lib/furble/HIDServer.h index 3cb9051..43eff7f 100644 --- a/lib/furble/HIDServer.h +++ b/lib/furble/HIDServer.h @@ -43,8 +43,9 @@ class HIDServer: public NimBLEServerCallbacks { static HIDServer *hidServer; // singleton - void onAuthenticationComplete(const NimBLEConnInfo &connInfo, const std::string &name) override; - void onIdentity(const NimBLEConnInfo &connInfo) override; + void onConnect(NimBLEServer *p_Server, NimBLEConnInfo &connInfo) override; + void onAuthenticationComplete(NimBLEConnInfo &connInfo) override; + void onIdentity(NimBLEConnInfo &connInfo) override; NimBLEServer *m_Server = nullptr; NimBLEHIDDevice *m_HID = nullptr; diff --git a/lib/furble/MobileDevice.cpp b/lib/furble/MobileDevice.cpp index 669cd63..bceead8 100644 --- a/lib/furble/MobileDevice.cpp +++ b/lib/furble/MobileDevice.cpp @@ -3,7 +3,9 @@ #include #include #include +#include +#include "CameraList.h" #include "Device.h" #include "MobileDevice.h" @@ -43,18 +45,22 @@ bool MobileDevice::matches(NimBLEAdvertisedDevice *pDevice) { * connection. * All this logic is encapsulated in the HIDServer class. */ -bool MobileDevice::connect(void) { +bool MobileDevice::_connect(void) { unsigned int timeout_secs = 60; - m_Progress = 0.0f; + m_Progress = 0; + m_DisconnectRequested = false; m_HIDServer->start(&m_Address); ESP_LOGI(LOG_TAG, "Waiting for %us for connection from %s", timeout_secs, m_Name.c_str()); while (--timeout_secs && !isConnected()) { - float progress = m_Progress.load() + 1.0f; - m_Progress = progress; - delay(1000); + m_Progress = m_Progress.load() + 1; + if (m_DisconnectRequested) { + ESP_LOGI(LOG_TAG, "Disconnect requested."); + return false; + } + vTaskDelay(pdMS_TO_TICKS(1000)); }; if (timeout_secs == 0) { @@ -62,8 +68,19 @@ bool MobileDevice::connect(void) { return false; } + // Retrieve device name and update if needed + auto server = NimBLEDevice::getServer(); + auto peerInfo = server->getPeerInfo(m_Address); + auto peer = server->getClient(peerInfo); + auto name = peer->getValue(NimBLEUUID((uint16_t)BLE_SVC_GAP_UUID16), + NimBLEUUID((uint16_t)BLE_SVC_GAP_CHR_UUID16_DEVICE_NAME)); + if (m_Name != name.c_str()) { + m_Name = name.c_str(); + CameraList::save(this); + } + ESP_LOGI(LOG_TAG, "Connected to %s.", m_Name.c_str()); - m_Progress = 100.0f; + m_Progress = 100; m_HIDServer->stop(); return true; @@ -94,10 +111,10 @@ void MobileDevice::updateGeoData(const gps_t &gps, const timesync_t ×ync) { // not supported } -void MobileDevice::disconnect(void) { - m_Progress = 0.0f; - m_Connected = false; +void MobileDevice::_disconnect(void) { + m_DisconnectRequested = true; m_HIDServer->disconnect(m_Address); + m_Connected = false; } bool MobileDevice::isConnected(void) const { diff --git a/lib/furble/MobileDevice.h b/lib/furble/MobileDevice.h index cb97e34..2f55982 100644 --- a/lib/furble/MobileDevice.h +++ b/lib/furble/MobileDevice.h @@ -18,16 +18,18 @@ class MobileDevice: public Camera { static bool matches(NimBLEAdvertisedDevice *pDevice); - bool connect(void) override; void shutterPress(void) override; void shutterRelease(void) override; void focusPress(void) override; void focusRelease(void) override; void updateGeoData(const gps_t &gps, const timesync_t ×ync) override; - void disconnect(void) override; bool isConnected(void) const override; + protected: + bool _connect(void) override; + void _disconnect(void) override; + private: typedef struct _mobile_device_t { char name[MAX_NAME]; /** Human readable device name. */ @@ -39,6 +41,7 @@ class MobileDevice: public Camera { bool serialise(void *buffer, size_t bytes) const override; void sendKeyReport(const uint8_t key); + volatile bool m_DisconnectRequested = false; HIDServer *m_HIDServer; }; diff --git a/platformio.ini b/platformio.ini index 2c31298..1a41ced 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,6 +1,7 @@ [furble] -build_flags = -Wall +build_flags = -Wall -Wextra -DFURBLE_VERSION=\"${sysenv.FURBLE_VERSION}\" + -DFURBLE_BATTERY_DEBUG=0 -DCORE_DEBUG_LEVEL=3 -DCONFIG_BT_NIMBLE_LOG_LEVEL=3 -DCONFIG_BT_NIMBLE_MAX_CONNECTIONS=9 @@ -9,6 +10,7 @@ build_flags = -Wall -DCONFIG_NIMBLE_CPP_LOG_LEVEL=1 -DCONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT=1 -DCONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT=1 + -DLV_CONF_PATH="${platformio.include_dir}/lv_conf.h" [env] platform = espressif32 @@ -18,17 +20,22 @@ framework = arduino lib_deps = M5GFX@0.1.16 M5Unified@0.1.16 + lvgl/lvgl@9.2.2 mikalhart/TinyGPSPlus@1.0.3 - https://github.com/h2zero/NimBLE-Arduino#6ece29f + h2zero/NimBLE-Arduino@2.1.2 [env:m5stick-c] board = m5stick-c -build_flags = ${furble.build_flags} +build_flags = ${furble.build_flags} -DFURBLE_M5STICKC + +[env:m5stick-c-plus] +board = m5stick-c +build_flags = ${furble.build_flags} -DFURBLE_M5STICKC_PLUS [env:m5stack-core] board = m5stack-core-esp32 -build_flags = ${furble.build_flags} -DARDUINO_M5STACK_CORE_ESP32=1 +build_flags = ${furble.build_flags} -DFURBLE_GROVE_CORE -DFURBLE_M5COREX [env:m5stack-core2] board = m5stack-core2 -build_flags = ${furble.build_flags} -DARDUINO_M5STACK_CORE2=1 +build_flags = ${furble.build_flags} -DFURBLE_M5COREX diff --git a/src/FurbleControl.cpp b/src/FurbleControl.cpp new file mode 100644 index 0000000..f92642e --- /dev/null +++ b/src/FurbleControl.cpp @@ -0,0 +1,260 @@ +#include "FurbleControl.h" + +namespace Furble { +Control::Target::Target(Camera *camera) { + m_Camera = camera; + m_Queue = xQueueCreate(m_QueueLength, sizeof(cmd_t)); +} + +Control::Target::~Target() { + vQueueDelete(m_Queue); + m_Queue = NULL; + m_Camera->disconnect(); + m_Camera = NULL; +} + +Camera *Control::Target::getCamera(void) const { + return m_Camera; +} + +void Control::Target::sendCommand(cmd_t cmd) { + BaseType_t ret = xQueueSend(m_Queue, &cmd, 0); + if (ret != pdTRUE) { + ESP_LOGE(LOG_TAG, "Failed to send command to target."); + } +} + +Control::cmd_t Control::Target::getCommand(void) { + cmd_t cmd = CMD_ERROR; + BaseType_t ret = xQueueReceive(m_Queue, &cmd, pdMS_TO_TICKS(50)); + if (ret != pdTRUE) { + return CMD_ERROR; + } + return cmd; +} + +void Control::Target::updateGPS(Camera::gps_t &gps, Camera::timesync_t ×ync) { + m_GPS = gps; + m_Timesync = timesync; +} + +void Control::Target::task(void) { + const char *name = m_Camera->getName().c_str(); + + while (true) { + cmd_t cmd = this->getCommand(); + switch (cmd) { + case CMD_SHUTTER_PRESS: + ESP_LOGI(LOG_TAG, "shutterPress(%s)", name); + m_Camera->shutterPress(); + break; + case CMD_SHUTTER_RELEASE: + ESP_LOGI(LOG_TAG, "shutterRelease(%s)", name); + m_Camera->shutterRelease(); + break; + case CMD_FOCUS_PRESS: + ESP_LOGI(LOG_TAG, "focusPress(%s)", name); + m_Camera->focusPress(); + break; + case CMD_FOCUS_RELEASE: + ESP_LOGI(LOG_TAG, "focusRelease(%s)", name); + m_Camera->focusRelease(); + break; + case CMD_GPS_UPDATE: + ESP_LOGI(LOG_TAG, "updateGeoData(%s)", name); + m_Camera->updateGeoData(m_GPS, m_Timesync); + break; + case CMD_DISCONNECT: + m_Camera->setActive(false); + goto task_exit; + case CMD_ERROR: + // ignore continue + break; + default: + ESP_LOGE(LOG_TAG, "Invalid control command %d.", cmd); + } + } +task_exit: + vTaskDelete(NULL); +} + +Control &Control::getInstance(void) { + static Control instance; + if (instance.m_Queue == NULL) { + instance.m_Queue = xQueueCreate(m_QueueLength, sizeof(cmd_t)); + if (instance.m_Queue == NULL) { + ESP_LOGE(LOG_TAG, "Failed to create control queue."); + abort(); + } + } + + return instance; +} + +Control::state_t Control::connectAll(void) { + static uint32_t failcount = 0; + const std::lock_guard lock(m_Mutex); + + // Iterate over cameras and attempt connection. + Camera *camera = nullptr; + for (const auto &target : m_Targets) { + camera = target->getCamera(); + if (!camera->isConnected()) { + m_ConnectCamera = camera; + if (!camera->connect(m_Power)) { + failcount++; + break; + } else { + m_ConnectCamera = nullptr; + } + } + } + + if (allConnected()) { + failcount = 0; + return STATE_ACTIVE; + } + + if (m_InfiniteReconnect || (failcount < 2)) { + return STATE_CONNECT; + } + + return STATE_CONNECT_FAILED; +} + +void Control::task(void) { + while (true) { + cmd_t cmd; + BaseType_t ret = xQueueReceive(m_Queue, &cmd, pdMS_TO_TICKS(50)); + + switch (m_State) { + case STATE_IDLE: + if (ret == pdTRUE) { + if (cmd == CMD_CONNECT) { + m_State = STATE_CONNECT; + continue; + } + } + break; + + case STATE_CONNECT: + m_State = STATE_CONNECTING; + m_State = connectAll(); + break; + + case STATE_CONNECTING: + case STATE_CONNECT_FAILED: + break; + + case STATE_ACTIVE: + if (!allConnected()) { + m_State = STATE_CONNECT; + continue; + } + + if (ret == pdTRUE) { + for (const auto &target : m_Targets) { + switch (cmd) { + case CMD_SHUTTER_PRESS: + case CMD_SHUTTER_RELEASE: + case CMD_FOCUS_PRESS: + case CMD_FOCUS_RELEASE: + case CMD_GPS_UPDATE: + target->sendCommand(cmd); + break; + default: + ESP_LOGE(LOG_TAG, "Invalid control command %d.", cmd); + break; + } + } + } + break; + } + } +} + +BaseType_t Control::sendCommand(cmd_t cmd) { + return xQueueSend(m_Queue, &cmd, 0); +} + +BaseType_t Control::updateGPS(Camera::gps_t &gps, Camera::timesync_t ×ync) { + for (const auto &target : m_Targets) { + target->updateGPS(gps, timesync); + } + + cmd_t cmd = CMD_GPS_UPDATE; + return xQueueSend(m_Queue, &cmd, 0); +} + +bool Control::allConnected(void) { + for (const auto &target : m_Targets) { + if (!target->getCamera()->isConnected()) { + return false; + } + } + + return true; +} + +const std::vector> &Control::getTargets(void) { + return m_Targets; +} + +void Control::connectAll(bool infiniteReconnect) { + m_InfiniteReconnect = infiniteReconnect; + + this->sendCommand(CMD_CONNECT); +} + +void Control::disconnect(void) { + // Force cancel any active connection attempts + ble_gap_conn_cancel(); + + const std::lock_guard lock(m_Mutex); + + for (const auto &target : m_Targets) { + target->sendCommand(CMD_DISCONNECT); + } + + m_Targets.clear(); + m_State = STATE_IDLE; +} + +void Control::addActive(Camera *camera) { + const std::lock_guard lock(m_Mutex); + + auto target = std::unique_ptr(new Control::Target(camera)); + + // Create per-target task that will self-delete on disconnect + BaseType_t ret = xTaskCreatePinnedToCore( + [](void *param) { + auto *target = static_cast(param); + target->task(); + }, + camera->getName().c_str(), 4096, target.get(), 3, NULL, 1); + if (ret != pdPASS) { + ESP_LOGE(LOG_TAG, "Failed to create task for '%s'.", camera->getName()); + } else { + m_Targets.push_back(std::move(target)); + } +} + +Camera *Control::getConnectingCamera(void) { + return m_ConnectCamera; +} + +Control::state_t Control::getState(void) { + return m_State; +} + +void Control::setPower(esp_power_level_t power) { + m_Power = power; +} + +}; // namespace Furble + +void control_task(void *param) { + Furble::Control *control = static_cast(param); + + control->task(); +} diff --git a/src/FurbleGPS.cpp b/src/FurbleGPS.cpp new file mode 100644 index 0000000..8d5fc16 --- /dev/null +++ b/src/FurbleGPS.cpp @@ -0,0 +1,96 @@ +#include +#include +#include + +#include "FurbleControl.h" +#include "FurbleGPS.h" +#include "FurbleSettings.h" + +namespace Furble { + +GPS &GPS::getInstance() { + static GPS instance; + + return instance; +} + +void GPS::init(void) { + Serial2.begin(BAUD, SERIAL_8N1, RX, TX); + + getInstance().reloadSetting(); +} + +void GPS::setIcon(lv_obj_t *icon) { + m_Icon = icon; +} + +/** Refresh the setting from NVS. */ +void GPS::reloadSetting(void) { + m_Enabled = Settings::load(Settings::GPS); +} + +/** Is GPS enabled? */ +bool GPS::isEnabled(void) { + return m_Enabled; +} + +/** Start timer event to service/update GPS. */ +void GPS::startService(void) { + m_Timer = lv_timer_create( + [](lv_timer_t *timer) { + auto *gps = static_cast(lv_timer_get_user_data(timer)); + gps->serviceSerial(); + gps->update(); + }, + SERVICE_MS, this); +} + +/** Send GPS data updates to the control task. */ +void GPS::update(void) { + if (!m_Enabled || !m_HasFix) { + return; + } + + if (m_GPS.location.isUpdated() && m_GPS.location.isValid() && m_GPS.date.isUpdated() + && m_GPS.date.isValid() && m_GPS.time.isValid() && m_GPS.time.isValid()) { + Camera::gps_t dgps = { + m_GPS.location.lat(), + m_GPS.location.lng(), + m_GPS.altitude.meters(), + }; + Camera::timesync_t timesync = { + m_GPS.date.year(), m_GPS.date.month(), m_GPS.date.day(), + m_GPS.time.hour(), m_GPS.time.minute(), m_GPS.time.second(), + }; + + Control::getInstance().updateGPS(dgps, timesync); + } +} + +/** Read and decode the GPS data from serial port. */ +void GPS::serviceSerial(void) { + if (!m_Enabled) { + return; + } + + while (Serial2.available() > 0) { + m_GPS.encode(Serial2.read()); + } + + if ((m_GPS.location.age() < MAX_AGE_MS) && m_GPS.location.isValid() + && (m_GPS.date.age() < MAX_AGE_MS) && m_GPS.date.isValid() && (m_GPS.time.age() < MAX_AGE_MS) + && m_GPS.time.age()) { + m_HasFix = true; + } else { + m_HasFix = false; + } + + if (m_Icon != NULL) { + lv_image_set_src(m_Icon, m_HasFix ? LV_SYMBOL_GPS : LV_SYMBOL_WARNING); + } +} + +TinyGPSPlus &GPS::get(void) { + return m_GPS; +} +} // namespace Furble diff --git a/src/FurbleSettings.cpp b/src/FurbleSettings.cpp new file mode 100644 index 0000000..8989bed --- /dev/null +++ b/src/FurbleSettings.cpp @@ -0,0 +1,163 @@ +#include + +#include "FurbleTypes.h" + +#include "FurbleSettings.h" + +namespace Furble { +Preferences Settings::m_Prefs; + +const std::unordered_map Settings::m_Setting = { + {BRIGHTNESS, {BRIGHTNESS, "Brightness", "brightness", "M5ez"} }, + {INACTIVITY, {INACTIVITY, "Inactivity", "inactivity", "M5ez"} }, + {THEME, {THEME, "Theme", "theme", "M5ez"} }, + {TX_POWER, {TX_POWER, "TX Power", "tx_power", FURBLE_STR} }, + {GPS, {GPS, "GPS", "gps", FURBLE_STR} }, + {INTERVAL, {INTERVAL, "Interval", "interval", FURBLE_STR} }, + {MULTICONNECT, {MULTICONNECT, "Multi-Connect", "multiconnect", FURBLE_STR}}, + {RECONNECT, {RECONNECT, "Infinite-ReConnect", "reconnect", FURBLE_STR} }, + {FAUXNY, {FAUXNY, "FauxNY", "fauxNY", FURBLE_STR} } +}; + +const Settings::setting_t &Settings::get(type_t type) { + return m_Setting.at(type); +} + +template <> +bool Settings::load(type_t type) { + const auto &setting = get(type); + m_Prefs.begin(setting.nvs_namespace, true); + bool value = m_Prefs.getBool(setting.key); + m_Prefs.end(); + + return value; +} + +template <> +uint8_t Settings::load(type_t type) { + const auto &setting = get(type); + m_Prefs.begin(setting.nvs_namespace, true); + uint8_t value = m_Prefs.getUChar(setting.key); + m_Prefs.end(); + + return value; +} + +template <> +std::string Settings::load(type_t type) { + const auto &setting = get(type); + m_Prefs.begin(setting.nvs_namespace, true); + std::string value = std::string(m_Prefs.getString(setting.key).c_str()); + m_Prefs.end(); + + return value; +} + +template <> +interval_t Settings::load(type_t type) { + const auto &setting = get(type); + interval_t interval; + + m_Prefs.begin(setting.nvs_namespace, true); + size_t len = m_Prefs.getBytes(setting.key, &interval, sizeof(interval_t)); + if (len != sizeof(interval_t)) { + // default values + interval.count = INTERVAL_DEFAULT_COUNT; + interval.shutter = INTERVAL_DEFAULT_SHUTTER; + interval.delay = INTERVAL_DEFAULT_DELAY; + } + + m_Prefs.end(); + + return interval; +} + +template <> +esp_power_level_t Settings::load(type_t type) { + const auto &setting = get(type); + m_Prefs.begin(setting.nvs_namespace, true); + uint8_t value = m_Prefs.getUChar(setting.key); + m_Prefs.end(); + + switch (value) { + case 0: + return ESP_PWR_LVL_P3; + case 1: + return ESP_PWR_LVL_P6; + case 2: + return ESP_PWR_LVL_P9; + } + return ESP_PWR_LVL_P3; +} + +template <> +void Settings::save(const type_t type, const bool &value) { + const auto &setting = get(type); + m_Prefs.begin(setting.nvs_namespace, false); + m_Prefs.putBool(setting.key, value); + m_Prefs.end(); +} + +template <> +void Settings::save(const type_t type, const uint8_t &value) { + const auto &setting = get(type); + m_Prefs.begin(setting.nvs_namespace, false); + m_Prefs.putUChar(setting.key, value); + m_Prefs.end(); +} + +template <> +void Settings::save(const type_t type, const interval_t &value) { + const auto &setting = get(type); + m_Prefs.begin(setting.nvs_namespace, false); + m_Prefs.putBytes(setting.key, &value, sizeof(value)); + m_Prefs.end(); +} + +template <> +void Settings::save(const type_t type, const std::string &value) { + const auto &setting = get(type); + m_Prefs.begin(setting.nvs_namespace, false); + m_Prefs.putString(setting.key, value.c_str()); + m_Prefs.end(); +} + +void Settings::init(void) { + // Set default values for all settings + for (const auto &it : m_Setting) { + auto &setting = it.second; + m_Prefs.begin(setting.nvs_namespace, false); + if (!m_Prefs.isKey(setting.key)) { + switch (setting.type) { + case BRIGHTNESS: + save(setting.type, 128); + break; + case INACTIVITY: + save(setting.type, 0); + break; + case THEME: + save(setting.type, "Default"); + break; + case TX_POWER: + save(setting.type, 0); + break; + case INTERVAL: { + interval_t interval = { + INTERVAL_DEFAULT_COUNT, + INTERVAL_DEFAULT_DELAY, + INTERVAL_DEFAULT_SHUTTER, + }; + save(setting.type, interval); + } break; + case GPS: + case MULTICONNECT: + case RECONNECT: + case FAUXNY: + save(setting.type, false); + break; + } + } + m_Prefs.end(); + } +} +} // namespace Furble diff --git a/src/FurbleSpinValue.cpp b/src/FurbleSpinValue.cpp new file mode 100644 index 0000000..e0b845f --- /dev/null +++ b/src/FurbleSpinValue.cpp @@ -0,0 +1,33 @@ +#include "FurbleSpinValue.h" + +namespace Furble { +constexpr std::array SpinValue::m_UnitMap; + +SpinValue::SpinValue(nvs_t &nvs) : m_Value(nvs.value), m_Unit(nvs.unit) {}; + +SpinValue::nvs_t SpinValue::toNVS(void) { + return (nvs_t) {m_Value, m_Unit}; +} + +uint32_t SpinValue::toMilliseconds(void) { + switch (m_Unit) { + case UNIT_MIN: + return (m_Value * 60 * 1000); + case UNIT_SEC: + return (m_Value * 1000); + case UNIT_MS: + return (m_Value); + default: + return 0; + } + return 0; +} + +const char *SpinValue::getUnitString(void) { + return m_UnitMap[m_Unit]; +} + +SpinValue::hms_t SpinValue::toHMS(uint32_t ms) { + return (hms_t) {(ms / 1000) / (60 * 60), ((ms / 1000) / 60) % 60, (ms / 1000) % 60}; +} +} // namespace Furble diff --git a/src/FurbleUI.cpp b/src/FurbleUI.cpp new file mode 100644 index 0000000..26c4bb0 --- /dev/null +++ b/src/FurbleUI.cpp @@ -0,0 +1,1720 @@ +#include + +#include +#include +#include + +#include +#include + +#include "FurbleControl.h" +#include "FurbleGPS.h" +#include "FurbleSettings.h" +#include "FurbleUI.h" +#include "interval.h" + +void vUITask(void *param) { + using namespace Furble; + auto interval = Settings::load(Settings::INTERVAL); + auto ui = UI(interval); + + ui.task(); +} + +namespace Furble { + +std::mutex UI::m_Mutex; + +constexpr const char *UI::m_ConnectStr; +constexpr const char *UI::m_ConnectedStr; +constexpr const char *UI::m_DeleteStr; +constexpr const char *UI::m_ScanStr; +constexpr const char *UI::m_SettingsStr; +constexpr const char *UI::m_IntervalometerStr; +constexpr const char *UI::m_RemoteShutter; +constexpr const char *UI::m_IntervalometerRunStr; + +const uint32_t UI::m_KeyEnter; +const uint32_t UI::m_KeyLeft; +const uint32_t UI::m_KeyRight; + +bool UI::m_PMICHack; +bool UI::m_PMICClicked; + +lv_timer_t *UI::m_IntervalTimer; + +UI::display_buffer_t UI::m_Buffer1; +UI::display_buffer_t UI::m_Buffer2; + +lv_obj_t *UI::m_ConnectMessageBox; +lv_obj_t *UI::m_ConnectLabel; +lv_obj_t *UI::m_ConnectBar; +lv_obj_t *UI::m_ConnectCancel; +lv_timer_t *UI::m_ConnectTimer; + +lv_obj_t *UI::m_IntervalStateLabel; +lv_obj_t *UI::m_IntervalCountLabel; +lv_obj_t *UI::m_IntervalRemainingLabel; +lv_timer_t *UI::m_IntervalPageRefresh; +uint32_t UI::m_IntervalNext; + +UI::menu_t UI::m_MainMenu; + +std::unordered_map UI::m_Menu = { + {m_ConnectStr, {}}, + {m_ScanStr, {}}, + {m_DeleteStr, {}}, + {m_SettingsStr, {}}, + {m_ConnectedStr, {}}, + {m_FeaturesStr, {}}, + {m_GPSStr, {}}, + {m_GPSDataStr, {}}, + {m_IntervalometerStr, {}}, + {m_IntervalCountStr, {}}, + {m_IntervalDelayStr, {}}, + {m_IntervalShutterStr, {}}, + {m_BacklightStr, {}}, + {m_ThemeStr, {}}, + {m_TransmitPowerStr, {}}, + {m_AboutStr, {}}, + {m_RemoteShutter, {}}, + {m_RemoteInterval, {}}, + {m_IntervalometerRunStr, {}}, +}; + +UI::UI(const interval_t &interval) : m_GPS {GPS::getInstance()}, m_Intervalometer(interval) { + lv_init(); + lv_tick_set_cb(tick); + + // set display resolution + m_Width = M5.Display.width(); + m_Height = M5.Display.height(); + + // set display brightness + auto brightness = Settings::load(Settings::BRIGHTNESS); + M5.Display.setBrightness(brightness); + setInactivityTimeout(Settings::load(Settings::INACTIVITY)); + + // set minimum, ensure this is a multiple of m_BrightnessSteps so the slider steps work + switch (M5.getBoard()) { + case m5::board_t::board_M5StickCPlus2: + case m5::board_t::board_M5StackCore2: + case m5::board_t::board_M5Stack: + m_MinimumBrightness = 32; + break; + case m5::board_t::board_M5StickCPlus: + case m5::board_t::board_M5StickC: + m_MinimumBrightness = 48; + break; + default: + m_MinimumBrightness = 32; + } + + // start inactivity timer + lv_timer_create( + [](lv_timer_t *t) { + auto *ui = static_cast(lv_timer_get_user_data(t)); + ui->processInactivity(); + }, + 1000, this); + + // configure display + m_Display = lv_display_create(m_Width, m_Height); + lv_display_set_default(m_Display); + lv_display_set_flush_cb(m_Display, displayFlush); + + // configure display buffers + lv_display_set_buffers(m_Display, m_Buffer1.data(), m_Buffer2.data(), m_Buffer1.size(), + LV_DISPLAY_RENDER_MODE_PARTIAL); + + initInputDevices(); + + setTheme(Settings::load(Settings::THEME)); + + m_Screen = lv_screen_active(); + + m_Root = lv_win_create(m_Screen); + lv_obj_update_layout(m_Root); + + lv_win_add_title(m_Root, m_Title); + m_Header = lv_win_get_header(m_Root); + + m_GPS.init(); + m_Status.gps = &m_GPS; + m_Status.reconnectIcon = addIcon(LV_SYMBOL_REFRESH); + m_Status.gpsIcon = addIcon(LV_SYMBOL_GPS); +#if FURBLE_BATTERY_DEBUG == 1 + m_Status.batteryIcon = lv_label_create(m_Header); +#else + m_Status.batteryIcon = addIcon(LV_SYMBOL_BATTERY_3); +#endif + + m_GPS.setIcon(m_Status.gpsIcon); + + // refresh icons every 250ms + lv_timer_create( + [](lv_timer_t *timer) { + status_t *status = static_cast(lv_timer_get_user_data(timer)); + +#if FURBLE_BATTERY_DEBUG == 1 + int32_t current = M5.Power.getBatteryCurrent(); + static int32_t mean = current; + + // exponentially weighted moving average with alpha = 0.5 + mean = mean + (current - mean) / 2; + + lv_label_set_text_fmt(status->batteryIcon, "%d", mean); +#else + const char *symbol = NULL; + int32_t level = M5.Power.getBatteryLevel(); + if (level >= 95) { + symbol = LV_SYMBOL_BATTERY_FULL; + } else if (level >= 66) { + symbol = LV_SYMBOL_BATTERY_3; + } else if (level >= 33) { + symbol = LV_SYMBOL_BATTERY_2; + } else if (level >= 5) { + symbol = LV_SYMBOL_BATTERY_1; + } else { + symbol = LV_SYMBOL_BATTERY_EMPTY; + } + lv_image_set_src(status->batteryIcon, symbol); +#endif + + if (status->gps->isEnabled()) { + lv_obj_clear_flag(status->gpsIcon, LV_OBJ_FLAG_HIDDEN); + } else { + lv_obj_add_flag(status->gpsIcon, LV_OBJ_FLAG_HIDDEN); + } + }, + 250, &m_Status); + + lv_obj_update_layout(m_Header); + lv_obj_set_height(m_Header, 1.2f * lv_font_get_line_height(LV_FONT_DEFAULT)); + + lv_obj_t *x = lv_win_get_content(m_Root); + lv_obj_set_width(x, LV_PCT(100)); + lv_obj_set_layout(x, LV_LAYOUT_FLEX); + lv_obj_set_flex_flow(x, LV_FLEX_FLOW_COLUMN); + m_Content = lv_obj_create(x); + lv_obj_set_width(m_Content, LV_PCT(100)); + lv_obj_set_flex_grow(m_Content, 1); + + // zero the padding + lv_obj_set_style_pad_top(m_Content, 0, LV_STATE_DEFAULT); + lv_obj_set_style_pad_row(m_Content, 0, LV_STATE_DEFAULT); + lv_obj_set_style_pad_bottom(m_Content, 0, LV_STATE_DEFAULT); + lv_obj_set_style_pad_left(m_Content, 0, LV_STATE_DEFAULT); + lv_obj_set_style_pad_right(m_Content, 0, LV_STATE_DEFAULT); + + lv_obj_set_style_pad_row(x, 0, LV_STATE_DEFAULT); + lv_obj_set_style_pad_top(x, 0, LV_STATE_DEFAULT); + lv_obj_set_style_pad_bottom(x, 0, LV_STATE_DEFAULT); + lv_obj_set_style_pad_left(x, 0, LV_STATE_DEFAULT); + lv_obj_set_style_pad_right(x, 0, LV_STATE_DEFAULT); + + // add navigation buttons + if (!M5.Touch.isEnabled()) { + auto height = lv_font_get_line_height(LV_FONT_DEFAULT); + + m_NavBar = lv_obj_create(x); + lv_obj_set_width(m_NavBar, LV_PCT(100)); + lv_obj_set_height(m_NavBar, 1.2f * lv_font_get_line_height(LV_FONT_DEFAULT)); + lv_obj_set_layout(m_NavBar, LV_LAYOUT_FLEX); + lv_obj_set_flex_flow(m_NavBar, LV_FLEX_FLOW_ROW); + lv_obj_set_flex_align(m_NavBar, LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_CENTER, + LV_FLEX_ALIGN_CENTER); + lv_obj_clear_flag(m_NavBar, LV_OBJ_FLAG_SCROLLABLE); + + switch (M5.getBoard()) { + case m5::board_t::board_M5StickC: + case m5::board_t::board_M5StickCPlus: + case m5::board_t::board_M5StickCPlus2: + lv_obj_set_style_pad_left(m_Content, 0, LV_STATE_DEFAULT); + lv_obj_set_style_pad_right(m_Content, 0, LV_STATE_DEFAULT); + + m_Left = lv_button_create(m_Screen); + m_OK = lv_button_create(m_NavBar); + m_Right = lv_button_create(m_Screen); + + lv_obj_add_flag(m_Left, LV_OBJ_FLAG_FLOATING); + lv_obj_align(m_Left, LV_ALIGN_BOTTOM_LEFT, 0, 0); + + lv_obj_set_style_bg_image_src(m_Right, LV_SYMBOL_RIGHT, 0); + lv_obj_add_flag(m_Right, LV_OBJ_FLAG_FLOATING); + lv_obj_align(m_Right, LV_ALIGN_BOTTOM_RIGHT, 0, 0); + break; + + default: + m_Left = lv_button_create(m_NavBar); + m_OK = lv_button_create(m_NavBar); + m_Right = lv_button_create(m_NavBar); + lv_obj_set_style_bg_image_src(m_Right, LV_SYMBOL_RIGHT, 0); + break; + } + + // prepare shutter handling + prepareShutterControl(); + + // ensure buttons do not receive focus + lv_group_remove_obj(m_Left); + lv_group_remove_obj(m_OK); + lv_group_remove_obj(m_Right); + + lv_obj_set_style_bg_image_src(m_Left, LV_SYMBOL_LEFT, 0); + lv_obj_set_size(m_Left, height, height); + lv_obj_set_style_bg_image_src(m_OK, LV_SYMBOL_OK, 0); + lv_obj_set_size(m_OK, height, height); + lv_obj_set_size(m_Right, height, height); + } + + // create connection timer + m_ConnectTimer = lv_timer_create(connectTimerHandler, 125, NULL); + lv_timer_pause(m_ConnectTimer); + + // create intervalometer timer + m_IntervalTimer = lv_timer_create(intervalometer, 100, &m_Intervalometer); + lv_timer_pause(m_IntervalTimer); + + addMainMenu(); + + m_GPS.startService(); +} + +void UI::buttonPWRRead(lv_indev_t *drv, lv_indev_data_t *data) { + data->key = *(static_cast(lv_indev_get_user_data(drv))); + if (M5.BtnPWR.isReleased()) { + data->state = LV_INDEV_STATE_RELEASED; + } else if (M5.BtnPWR.isPressed()) { + data->state = LV_INDEV_STATE_PRESSED; + } +} + +// read power button for M5StickC and M5StickCPlus +void UI::buttonPEKRead(lv_indev_t *drv, lv_indev_data_t *data) { + data->key = *(static_cast(lv_indev_get_user_data(drv))); + if (m_PMICClicked) { + data->state = LV_INDEV_STATE_PRESSED; + m_PMICClicked = false; + } else { + data->state = LV_INDEV_STATE_RELEASED; + } +} + +void UI::buttonARead(lv_indev_t *drv, lv_indev_data_t *data) { + data->key = *(static_cast(lv_indev_get_user_data(drv))); + if (M5.BtnA.isReleased()) { + data->state = LV_INDEV_STATE_RELEASED; + } else if (M5.BtnA.isPressed()) { + data->state = LV_INDEV_STATE_PRESSED; + } +} + +void UI::buttonBRead(lv_indev_t *drv, lv_indev_data_t *data) { + data->key = *(static_cast(lv_indev_get_user_data(drv))); + if (M5.BtnB.isReleased()) { + data->state = LV_INDEV_STATE_RELEASED; + } else if (M5.BtnB.isPressed()) { + data->state = LV_INDEV_STATE_PRESSED; + } +} + +void UI::buttonCRead(lv_indev_t *drv, lv_indev_data_t *data) { + data->key = *(static_cast(lv_indev_get_user_data(drv))); + if (M5.BtnC.isReleased()) { + data->state = LV_INDEV_STATE_RELEASED; + } else if (M5.BtnC.isPressed()) { + data->state = LV_INDEV_STATE_PRESSED; + } +} + +void UI::touchRead(lv_indev_t *drv, lv_indev_data_t *data) { + auto count = M5.Touch.getCount(); + if (count == 0) { + data->state = LV_INDEV_STATE_RELEASED; + } else { + auto touch = M5.Touch.getDetail(0); + data->state = LV_INDEV_STATE_PRESSED; + data->point.x = touch.x; + data->point.y = touch.y; + } +} + +void UI::displayFlush(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map) { + uint32_t w = (area->x2 - area->x1 + 1); + uint32_t h = (area->y2 - area->y1 + 1); + + lv_draw_sw_rgb565_swap(px_map, w * h); + M5.Display.pushImageDMA(area->x1, area->y1, w, h, (uint16_t *)px_map); + lv_disp_flush_ready(disp); +} + +uint32_t UI::tick(void) { + return (esp_timer_get_time() / 1000LL); +} + +void UI::initInputDevices(void) { + m_Group = lv_group_create(); + lv_group_set_default(m_Group); + + m_ButtonL = lv_indev_create(); + lv_indev_set_type(m_ButtonL, LV_INDEV_TYPE_ENCODER); + lv_indev_set_user_data(m_ButtonL, const_cast(static_cast(&m_KeyLeft))); + lv_indev_set_group(m_ButtonL, m_Group); + + m_ButtonO = lv_indev_create(); + lv_indev_set_type(m_ButtonO, LV_INDEV_TYPE_ENCODER); + lv_indev_set_user_data(m_ButtonO, const_cast(static_cast(&m_KeyEnter))); + lv_indev_set_group(m_ButtonO, m_Group); + + m_ButtonR = lv_indev_create(); + lv_indev_set_type(m_ButtonR, LV_INDEV_TYPE_ENCODER); + lv_indev_set_user_data(m_ButtonR, const_cast(static_cast(&m_KeyRight))); + lv_indev_set_group(m_ButtonR, m_Group); + + switch (M5.getBoard()) { + case m5::board_t::board_M5StickC: + case m5::board_t::board_M5StickCPlus: + m_PMICHack = true; + lv_indev_set_read_cb(m_ButtonL, buttonPEKRead); + lv_indev_set_read_cb(m_ButtonO, buttonARead); + lv_indev_set_read_cb(m_ButtonR, buttonBRead); + break; + + case m5::board_t::board_M5StickCPlus2: + lv_indev_set_read_cb(m_ButtonL, buttonPWRRead); + lv_indev_set_read_cb(m_ButtonO, buttonARead); + lv_indev_set_read_cb(m_ButtonR, buttonBRead); + break; + + case m5::board_t::board_M5StackCore2: + m_Touch = lv_indev_create(); + lv_indev_set_type(m_Touch, LV_INDEV_TYPE_POINTER); + lv_indev_set_read_cb(m_Touch, touchRead); + __attribute__((fallthrough)); + + case m5::board_t::board_M5Stack: + lv_indev_set_read_cb(m_ButtonL, buttonARead); + lv_indev_set_read_cb(m_ButtonO, buttonBRead); + lv_indev_set_read_cb(m_ButtonR, buttonCRead); + break; + + default: + ESP_LOGE("ui", "Unknown hardware, not configuring input devices"); + } +} + +void UI::setTheme(std::string name) { + lv_display_t *display = lv_display_get_default(); + lv_color_t primary = lv_palette_main(LV_PALETTE_BLUE); + lv_color_t secondary = lv_color_black(); + bool dark = false; + + if (name == "Dark") { + static lv_theme_t theme_dark; + static lv_style_t style_dark; + lv_theme_t *theme_default = lv_theme_default_get(); + lv_style_init(&style_dark); + lv_style_set_bg_color(&style_dark, lv_color_black()); + + theme_dark = *theme_default; + lv_theme_set_parent(&theme_dark, theme_default); + lv_theme_set_apply_cb(&theme_dark, [](lv_theme_t *th, lv_obj_t *obj) { + if (!lv_obj_check_type(obj, &lv_button_class) + && !lv_obj_check_type(obj, &lv_msgbox_footer_button_class)) { + lv_obj_add_style(obj, &style_dark, 0); + } + }); + dark = true; + lv_display_set_theme(display, &theme_dark); + } else if (name == "Mono Furble") { + primary = lv_palette_main(LV_PALETTE_ORANGE); + dark = true; + lv_theme_default_init(display, primary, secondary, dark, LV_FONT_DEFAULT); + } else { + // Default + dark = false; + lv_theme_default_init(display, primary, secondary, dark, LV_FONT_DEFAULT); + } +} + +void UI::shutterLock(Control &control) { + if (!m_ShutterLock) { + control.sendCommand(Control::CMD_SHUTTER_PRESS); + m_ShutterLock = true; + ESP_LOGI("ui", "SHUTTER LOCKED"); + + if (M5.Touch.isEnabled()) { + lv_obj_add_state(m_OK, LV_STATE_DISABLED); + lv_obj_add_state(m_Right, LV_STATE_DISABLED); + } + } +} + +void UI::shutterUnlock(Control &control) { + if (m_ShutterLock) { + control.sendCommand(Control::CMD_SHUTTER_RELEASE); + m_ShutterLock = false; + ESP_LOGI("ui", "SHUTTER UNLOCKED"); + + if (M5.Touch.isEnabled()) { + lv_obj_remove_state(m_OK, LV_STATE_DISABLED); + lv_obj_remove_state(m_Right, LV_STATE_DISABLED); + } + } +} + +bool UI::isShutterLocked(void) { + return m_ShutterLock; +} + +void UI::handleShutter(lv_event_t *e) { + auto *ui = static_cast(lv_event_get_user_data(e)); + auto &control = Control::getInstance(); + lv_event_code_t code = lv_event_get_code(e); + switch (code) { + case LV_EVENT_PRESSED: + if (ui->m_FocusPressed) { + ui->shutterLock(control); + } else { + control.sendCommand(Control::CMD_SHUTTER_PRESS); + } + break; + case LV_EVENT_RELEASED: + control.sendCommand(Control::CMD_SHUTTER_RELEASE); + if (!ui->m_FocusPressed) { + ui->shutterUnlock(control); + } + break; + default: + break; + } +} + +void UI::handleFocus(lv_event_t *e) { + auto *ui = static_cast(lv_event_get_user_data(e)); + auto &control = Control::getInstance(); + lv_event_code_t code = lv_event_get_code(e); + switch (code) { + case LV_EVENT_PRESSED: + ui->shutterUnlock(control); + ui->m_FocusPressed = true; + control.sendCommand(Control::CMD_FOCUS_PRESS); + break; + case LV_EVENT_RELEASED: + ui->m_FocusPressed = false; + if (!ui->isShutterLocked()) { + control.sendCommand(Control::CMD_FOCUS_RELEASE); + } + break; + default: + break; + } +} + +void UI::handleShutterLock(lv_event_t *e) { + auto *ui = static_cast(lv_event_get_user_data(e)); + auto &control = Control::getInstance(); + lv_event_code_t code = lv_event_get_code(e); + + // Bug in LVGL? + // Initial long pressed is triggered twice, fix with requiring release event + static bool released = true; + + switch (code) { + case LV_EVENT_LONG_PRESSED: + if (released) { + if (ui->isShutterLocked()) { + ui->shutterUnlock(control); + } else { + ui->shutterLock(control); + } + released = false; + } + break; + case LV_EVENT_RELEASED: + released = true; + break; + default: + break; + } +} + +void UI::prepareShutterControl(void) { + lv_obj_add_event_cb( + m_Left, + [](lv_event_t *e) { + auto *ui = static_cast(lv_event_get_user_data(e)); + lv_obj_t *back = lv_menu_get_main_header_back_button(m_MainMenu.main); + lv_obj_send_event(back, LV_EVENT_CLICKED, m_MainMenu.main); + ui->configMenuControl(); + }, + LV_EVENT_CLICKED, this); + + lv_obj_add_event_cb(m_OK, handleShutter, LV_EVENT_ALL, this); + + lv_obj_add_event_cb(m_Right, handleFocus, LV_EVENT_ALL, this); +} + +lv_obj_t *UI::addIcon(const char *symbol) { + lv_obj_t *icon = lv_image_create(m_Header); + + setIcon(icon, symbol); + + return icon; +} + +void UI::setIcon(lv_obj_t *icon, const char *symbol) { + lv_image_set_src(icon, symbol); +} + +lv_obj_t *UI::addMenuItem(const menu_t &menu, const char *symbol, const char *text, bool checkbox) { + lv_obj_t *cont = lv_menu_cont_create(menu.page); + lv_obj_clear_flag(cont, LV_OBJ_FLAG_SCROLLABLE); + +#if defined(FURBLE_M5STICKC) + // screen is too small for icons +#else + if (symbol) { + lv_obj_t *icon = lv_image_create(cont); + lv_image_set_src(icon, symbol); + } +#endif + + if (checkbox) { + lv_obj_t *check = lv_checkbox_create(cont); + lv_checkbox_set_text(check, text); + lv_obj_set_width(check, LV_PCT(100)); + lv_obj_add_flag(check, LV_OBJ_FLAG_SCROLL_ON_FOCUS); + lv_group_add_obj(menu.group, check); + return check; + } else { + lv_obj_t *label = lv_label_create(cont); + lv_label_set_text(label, text); + lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR); + lv_obj_set_width(label, LV_PCT(100)); + lv_obj_add_flag(cont, LV_OBJ_FLAG_CLICKABLE); + lv_obj_add_flag(cont, LV_OBJ_FLAG_SCROLL_ON_FOCUS); + lv_group_add_obj(menu.group, cont); + } + + return cont; +} + +void UI::addSettingItem(lv_obj_t *page, const char *symbol, Settings::type_t setting) { + lv_obj_t *obj = lv_menu_cont_create(page); + lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW_WRAP); + + if (symbol) { + lv_obj_t *icon = lv_image_create(obj); + lv_image_set_src(icon, symbol); + } + + auto &s = Settings::get(setting); + + lv_obj_t *label = lv_label_create(obj); + lv_label_set_text(label, s.name); + lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR); + lv_obj_set_flex_grow(label, 1); + + lv_obj_t *sw = lv_switch_create(obj); + bool enable = Settings::load(setting); + lv_obj_add_state(sw, enable ? LV_STATE_CHECKED : 0); + lv_obj_add_event_cb( + sw, + [](lv_event_t *e) { + const auto *setting = static_cast(lv_event_get_user_data(e)); + lv_obj_t *sw = static_cast(lv_event_get_target(e)); + Settings::save(setting->type, lv_obj_has_state(sw, LV_STATE_CHECKED)); + }, + LV_EVENT_VALUE_CHANGED, const_cast(&s)); + + if (setting == Settings::GPS) { + lv_obj_add_event_cb( + sw, + [](lv_event_t *e) { + auto *status = static_cast(lv_event_get_user_data(e)); + status->gps->reloadSetting(); + if (status->gps->isEnabled()) { + lv_obj_clear_flag(status->gpsData, LV_OBJ_FLAG_HIDDEN); + } else { + lv_obj_add_flag(status->gpsData, LV_OBJ_FLAG_HIDDEN); + } + }, + LV_EVENT_VALUE_CHANGED, &m_Status); + } + + if (setting == Settings::RECONNECT) { + if (!enable) { + lv_obj_add_flag(m_Status.reconnectIcon, LV_OBJ_FLAG_HIDDEN); + } + + lv_obj_add_event_cb( + sw, + [](lv_event_t *e) { + auto *sw = static_cast(lv_event_get_target(e)); + auto *status = static_cast(lv_event_get_user_data(e)); + if (lv_obj_has_state(sw, LV_STATE_CHECKED)) { + lv_obj_clear_flag(status->reconnectIcon, LV_OBJ_FLAG_HIDDEN); + } else { + lv_obj_add_flag(status->reconnectIcon, LV_OBJ_FLAG_HIDDEN); + } + }, + LV_EVENT_VALUE_CHANGED, &m_Status); + } +} + +lv_obj_t *UI::addCameraItem(Camera *camera, const menu_t &menu, const CameraListMode_t mode) { + bool checkbox = (mode == MODE_MULTICONNECT); + + lv_obj_t *item = addMenuItem(menu, NULL, camera->getName().c_str(), checkbox); + + switch (mode) { + case MODE_DELETE: + lv_obj_add_event_cb( + item, + [](lv_event_t *e) { + auto *camera = static_cast(lv_event_get_user_data(e)); + + CameraList::remove(camera); + refreshDelete(); + }, + LV_EVENT_CLICKED, camera); + break; + case MODE_SCAN: + case MODE_CONNECT: + lv_obj_add_event_cb( + item, + [](lv_event_t *e) { + auto *camera = static_cast(lv_event_get_user_data(e)); + camera->setActive(true); + + doConnect(e); + }, + LV_EVENT_CLICKED, camera); + break; + case MODE_MULTICONNECT: + lv_obj_add_event_cb( + item, + [](lv_event_t *e) { + auto *camera = static_cast(lv_event_get_user_data(e)); + auto *check = static_cast(lv_event_get_target(e)); + + camera->setActive(lv_obj_has_state(check, LV_STATE_CHECKED)); + }, + LV_EVENT_VALUE_CHANGED, camera); + break; + } + + return item; +} + +UI::menu_t &UI::addMenu(const char *name, const char *symbol, bool button, const menu_t &parent) { + menu_t &menu = m_Menu.at(name); + menu.main = m_MainMenu.main; + menu.group = m_Group; + menu.page = lv_menu_page_create(m_MainMenu.main, name); + + if (button) { + menu.button = addMenuItem(parent, symbol, name); + } + + return menu; +} + +void UI::addMainMenu(void) { + lv_obj_update_layout(m_Content); + lv_obj_set_scrollbar_mode(m_Content, LV_SCROLLBAR_MODE_OFF); + + m_MainMenu.main = lv_menu_create(m_Content); + if (M5.Touch.isEnabled()) { + lv_menu_set_mode_header(m_MainMenu.main, LV_MENU_HEADER_BOTTOM_FIXED); + } else { + lv_menu_set_mode_header(m_MainMenu.main, LV_MENU_HEADER_TOP_FIXED); + } + + lv_menu_set_mode_root_back_button(m_MainMenu.main, LV_MENU_ROOT_BACK_BUTTON_DISABLED); +#if defined(FURBLE_M5COREX) + // M5StickC displays are too narrow + lv_obj_t *back = lv_menu_get_main_header_back_button(m_MainMenu.main); + lv_obj_t *back_label = lv_label_create(back); + lv_label_set_text(back_label, "Back"); +#endif + + lv_obj_set_size(m_MainMenu.main, LV_PCT(100), LV_PCT(100)); + lv_obj_center(m_MainMenu.main); + + m_MainMenu.page = lv_menu_page_create(m_MainMenu.main, NULL); + m_MainMenu.group = m_Group; + + addConnectMenu(); + addScanMenu(); + addDeleteMenu(); + addSettingsMenu(); + addConnectedMenu(); + + m_PowerOff = addMenuItem(m_MainMenu, LV_SYMBOL_POWER, "Power Off"); + lv_obj_add_event_cb( + m_PowerOff, [](lv_event_t *e) { M5.Power.powerOff(); }, LV_EVENT_CLICKED, NULL); + + lv_obj_add_event_cb( + m_MainMenu.main, + [](lv_event_t *e) { + auto *target = static_cast(lv_event_get_target(e)); + auto *page = lv_menu_get_cur_main_page(target); + auto *back = lv_menu_get_main_header_back_button(m_MainMenu.main); + + if (page == m_MainMenu.page) { + // Hide connect & delete if there are zero saved + CameraList::load(); + if (CameraList::size() == 0) { + lv_obj_add_flag(m_Menu.at(m_ConnectStr).button, LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(m_Menu.at(m_DeleteStr).button, LV_OBJ_FLAG_HIDDEN); + } else { + lv_obj_clear_flag(m_Menu.at(m_ConnectStr).button, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(m_Menu.at(m_DeleteStr).button, LV_OBJ_FLAG_HIDDEN); + } + + // Ensure no active scans + Scan::stop(); + + // Enable Back button + if (lv_obj_has_state(back, LV_STATE_DISABLED)) { + lv_obj_remove_state(back, LV_STATE_DISABLED); + } + } else if (page == m_Menu.at(m_DeleteStr).page) { + } else if (page == m_Menu.at(m_ScanStr).page) { + menu_t &menu = m_Menu.at(m_ScanStr); + lv_obj_clean(menu.page); + CameraList::clear(); + + if (Settings::load(Settings::FAUXNY)) { + CameraList::addFauxNY(); + updateItems(menu); + } + + Scan::clear(); + Scan::start( + [](void *param) { + auto *menu = static_cast(param); + // Can be called asychronously from NimBLE scan thread, + m_Mutex.lock(); + updateItems(*menu); + m_Mutex.unlock(); + }, + &menu); + + lv_timer_set_user_data(m_ConnectTimer, (void *)(m_ScanStr)); + } else if (page == m_Menu.at(m_SettingsStr).page) { + } else if (page == m_Menu.at(m_ConnectStr).page) { + } else if (page == m_Menu.at(m_ConnectedStr).page) { + // Ensure no active scans + Scan::stop(); + + // disable back button and shift focus + lv_obj_add_state(back, LV_STATE_DISABLED); + } else if (page == m_Menu.at(m_RemoteShutter).page) { + if (M5.Touch.isEnabled()) { + // if touch screen, enable back + lv_obj_remove_state(back, LV_STATE_DISABLED); + } + } else if (page == m_Menu.at(m_IntervalometerStr).page) { + // always display 'Back' in intervalometer + lv_obj_remove_state(back, LV_STATE_DISABLED); + } else if (page == m_Menu.at(m_IntervalometerRunStr).page) { + // disable 'Back' when intervalometer is running + lv_obj_remove_state(back, LV_STATE_DISABLED); + } + }, + LV_EVENT_VALUE_CHANGED, NULL); + + lv_obj_add_event_cb( + m_MainMenu.main, + [](lv_event_t *e) { + auto *ui = static_cast(lv_event_get_user_data(e)); + auto *target = static_cast(lv_event_get_current_target(e)); + auto *page = lv_menu_get_cur_main_page(target); + + if (page == m_MainMenu.page) { + } else if (page == m_Menu.at(m_DeleteStr).page) { + } else if (page == m_Menu.at(m_SettingsStr).page) { + } else if (page == m_Menu.at(m_ConnectedStr).page) { + lv_obj_t *back = lv_menu_get_main_header_back_button(m_MainMenu.main); + if (lv_obj_has_state(back, LV_STATE_FOCUS_KEY)) { + lv_group_focus_next(lv_group_get_default()); + } + ui->shutterUnlock(Control::getInstance()); + } + }, + LV_EVENT_CLICKED, this); + + lv_menu_set_page(m_MainMenu.main, m_MainMenu.page); +} + +void UI::configShutterControl(void) { + if (!M5.Touch.isEnabled()) { + lv_obj_set_style_bg_image_src(m_Right, LV_SYMBOL_EYE_OPEN, 0); + lv_obj_set_style_bg_image_src(m_OK, LV_SYMBOL_IMAGE, 0); + + lv_indev_set_type(m_ButtonL, LV_INDEV_TYPE_BUTTON); + lv_indev_set_type(m_ButtonO, LV_INDEV_TYPE_BUTTON); + lv_indev_set_type(m_ButtonR, LV_INDEV_TYPE_BUTTON); + + // map indev to button coords + lv_area_t left; + lv_area_t ok; + lv_area_t right; + + lv_obj_get_coords(m_Left, &left); + lv_obj_get_coords(m_OK, &ok); + lv_obj_get_coords(m_Right, &right); + + static const lv_point_t leftPoint[] = { + {(left.x1 + left.x2) / 2, (left.y1 + left.y2) / 2} + }; + static const lv_point_t okPoint[] = { + {(ok.x1 + ok.x2) / 2, (ok.y1 + ok.y2) / 2} + }; + static const lv_point_t rightPoint[] = { + {(right.x1 + right.x2) / 2, (right.y1 + right.y2) / 2} + }; + + lv_indev_set_button_points(m_ButtonL, leftPoint); + lv_indev_set_button_points(m_ButtonO, okPoint); + lv_indev_set_button_points(m_ButtonR, rightPoint); + } +} + +void UI::configMenuControl(void) { + if (!M5.Touch.isEnabled()) { + lv_obj_set_style_bg_image_src(m_Left, LV_SYMBOL_LEFT, 0); + lv_obj_set_style_bg_image_src(m_OK, LV_SYMBOL_OK, 0); + lv_obj_set_style_bg_image_src(m_Right, LV_SYMBOL_RIGHT, 0); + + lv_indev_set_type(m_ButtonL, LV_INDEV_TYPE_ENCODER); + lv_indev_set_type(m_ButtonO, LV_INDEV_TYPE_ENCODER); + lv_indev_set_type(m_ButtonR, LV_INDEV_TYPE_ENCODER); + } +} + +void UI::showShutterIntervalometer(bool show) { + if (show) { + lv_obj_clear_flag(m_IntervalStart, LV_OBJ_FLAG_HIDDEN); + lv_group_focus_obj(m_IntervalStart); + } else { + lv_obj_add_flag(m_IntervalStart, LV_OBJ_FLAG_HIDDEN); + } +} + +void UI::connectTimerHandler(lv_timer_t *timer) { + auto &control = Control::getInstance(); + Camera *camera = nullptr; + auto state = control.getState(); + + switch (state) { + case Control::STATE_CONNECT: + break; + + case Control::STATE_CONNECTING: + camera = control.getConnectingCamera(); + + if (lv_obj_has_flag(m_ConnectMessageBox, LV_OBJ_FLAG_HIDDEN)) { + // hide menu, unhide message box + lv_obj_add_flag(m_MainMenu.main, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(m_ConnectMessageBox, LV_OBJ_FLAG_HIDDEN); + lv_group_focus_obj(m_ConnectCancel); + } + + if (camera != nullptr) { + lv_label_set_text(m_ConnectLabel, camera->getName().c_str()); + lv_bar_set_value(m_ConnectBar, camera->getConnectProgress(), LV_ANIM_ON); + } + break; + + case Control::STATE_CONNECT_FAILED: + ESP_LOGE("ui", "Connection failed."); + doDisconnect(); + break; + + case Control::STATE_ACTIVE: + if (!lv_obj_has_flag(m_ConnectMessageBox, LV_OBJ_FLAG_HIDDEN)) { + // if from scan, save the connection + const char *from = static_cast(lv_timer_get_user_data(timer)); + if (from == m_ScanStr) { + for (const auto &target : control.getTargets()) { + CameraList::save(target->getCamera()); + } + } + + // everything connected, display menu, hide connection message box + lv_obj_add_flag(m_ConnectMessageBox, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(m_MainMenu.main, LV_OBJ_FLAG_HIDDEN); + lv_group_focus_next(lv_group_get_default()); + } + break; + + case Control::STATE_IDLE: + // nothing to do + break; + } +} + +void UI::intervalometer(lv_timer_t *timer) { + auto &control = Control::getInstance(); + auto *interval = static_cast(lv_timer_get_user_data(timer)); + uint32_t next = 0; + + static uint32_t count = 0; + + if (interval->m_Count.m_SpinValue.m_Unit == SpinValue::UNIT_INF) { + lv_label_set_text_fmt(m_IntervalCountLabel, "%09u", count); + } else { + lv_label_set_text_fmt(m_IntervalCountLabel, "%03u/%03u", count, + interval->m_Count.m_SpinValue.m_Value); + } + + switch (interval->m_State) { + case Intervalometer::STATE_IDLE: + count = 0; + lv_label_set_text(m_IntervalStateLabel, "IDLE"); + lv_timer_ready(timer); + interval->m_State = Intervalometer::STATE_SHUTTER_OPEN; + break; + + case Intervalometer::STATE_SHUTTER_OPEN: + count++; + lv_label_set_text(m_IntervalStateLabel, "SHUTTER"); + control.sendCommand(Control::CMD_SHUTTER_PRESS); + next = interval->m_Shutter.m_SpinValue.toMilliseconds(); + interval->m_State = Intervalometer::STATE_WAIT; + break; + + case Intervalometer::STATE_WAIT: + lv_label_set_text(m_IntervalStateLabel, "DELAY"); + control.sendCommand(Control::CMD_SHUTTER_RELEASE); + next = interval->m_Delay.m_SpinValue.toMilliseconds(); + if (count >= interval->m_Count.m_SpinValue.m_Value) { + interval->m_State = Intervalometer::STATE_FINISHED; + } else { + interval->m_State = Intervalometer::STATE_SHUTTER_OPEN; + } + break; + + case Intervalometer::STATE_FINISHED: + lv_label_set_text(m_IntervalStateLabel, "FINISHED"); + next = 0; + lv_timer_pause(timer); + break; + } + + if (next > 0) { + lv_timer_set_period(timer, next); + m_IntervalNext = tick() + next; + } +} + +void UI::doConnect(lv_event_t *e) { + auto &control = Control::getInstance(); + + // activate selected cameras + for (auto n = 0; n < CameraList::size(); n++) { + auto *camera = CameraList::get(n); + if (camera->isActive()) { + control.addActive(camera); + } + } + + lv_obj_add_event_cb( + m_ConnectCancel, [](lv_event_t *e) { doDisconnect(); }, LV_EVENT_CLICKED, NULL); + + control.connectAll(Settings::load(Settings::RECONNECT)); + lv_timer_reset(m_ConnectTimer); + lv_timer_resume(m_ConnectTimer); + + menu_t &menu = m_Menu.at(m_ConnectedStr); + lv_menu_set_page(m_MainMenu.main, menu.page); +} + +void UI::doDisconnect(void) { + lv_timer_pause(m_ConnectTimer); + Control::getInstance().disconnect(); + + lv_obj_add_flag(m_ConnectMessageBox, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(m_MainMenu.main, LV_OBJ_FLAG_HIDDEN); + + lv_menu_clear_history(m_MainMenu.main); + lv_menu_set_page(m_MainMenu.main, m_MainMenu.page); + menu_t &connect = m_Menu.at(m_ConnectStr); + lv_group_focus_obj(connect.button); +} + +UI::menu_t &UI::addConnectedMenu(void) { + menu_t &menuConnected = addMenu(m_ConnectedStr, NULL, false); + menu_t &menuShutter = addMenu(m_RemoteShutter, LV_SYMBOL_IMAGE, true, menuConnected); + menu_t &menuInterval = addMenu(m_RemoteInterval, LV_SYMBOL_LOOP, true, menuConnected); + lv_obj_t *disconnect = addMenuItem(menuConnected, LV_SYMBOL_CLOSE, "Disconnect"); + + if (M5.Touch.isEnabled()) { + // add remote shutter control for touch screens + lv_obj_t *cont = lv_menu_cont_create(menuShutter.page); + lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW); + lv_obj_set_flex_align(cont, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_SPACE_EVENLY, + LV_FLEX_ALIGN_CENTER); + lv_obj_set_size(cont, LV_PCT(100), LV_PCT(100)); + lv_obj_center(cont); + + std::array, 3> buttons = { + { + {nullptr, nullptr, "Shutter", LV_SYMBOL_IMAGE}, + {nullptr, nullptr, "Focus", LV_SYMBOL_EYE_OPEN}, + {nullptr, nullptr, "Shutter\nLock", LV_SYMBOL_NEXT}, + } + }; + + for (auto &i : buttons) { + auto &buttonCont = std::get<0>(i); + + buttonCont = lv_obj_create(cont); + lv_obj_set_layout(buttonCont, LV_LAYOUT_FLEX); + lv_obj_set_flex_flow(buttonCont, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(buttonCont, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, + LV_FLEX_ALIGN_CENTER); + lv_obj_clear_flag(buttonCont, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_size(buttonCont, LV_SIZE_CONTENT, LV_SIZE_CONTENT); + + auto &button = std::get<1>(i); + button = lv_button_create(buttonCont); + lv_obj_set_style_bg_image_src(button, std::get<3>(i), 0); + lv_obj_set_size(button, 64, 64); + + lv_obj_t *label = lv_label_create(buttonCont); + lv_label_set_text(label, std::get<2>(i)); + lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0); + } + + m_OK = std::get<1>(buttons[0]); + m_Right = std::get<1>(buttons[1]); + auto lockButton = std::get<1>(buttons[2]); + + lv_obj_add_event_cb(m_OK, handleShutter, LV_EVENT_ALL, this); + + lv_obj_add_event_cb(m_Right, handleFocus, LV_EVENT_ALL, this); + + lv_obj_add_event_cb(lockButton, handleShutterLock, LV_EVENT_ALL, this); + } else { + // add remote shutter text for buttons + lv_obj_t *txt = lv_label_create(menuShutter.page); + lv_label_set_text(txt, LV_SYMBOL_IMAGE ": Shutter\n" LV_SYMBOL_EYE_OPEN + ": Focus\n" LV_SYMBOL_EYE_OPEN " + " LV_SYMBOL_IMAGE + ":\n Shutter Lock\n\n" + + LV_SYMBOL_LEFT ": Back"); + lv_obj_set_size(txt, LV_PCT(100), LV_SIZE_CONTENT); + lv_obj_center(txt); + } + + lv_obj_add_event_cb( + menuShutter.button, + [](lv_event_t *e) { + auto *ui = static_cast(lv_event_get_user_data(e)); + ui->configShutterControl(); + }, + LV_EVENT_CLICKED, this); + + // add intervalometer control + menu_t &menuIntervalometer = m_Menu.at(m_IntervalometerStr); + lv_menu_set_load_page_event(menuIntervalometer.main, menuInterval.button, + menuIntervalometer.page); + + lv_obj_add_event_cb( + menuInterval.button, + [](lv_event_t *e) { + auto *ui = static_cast(lv_event_get_user_data(e)); + ui->showShutterIntervalometer(true); + }, + LV_EVENT_CLICKED, this); + + // add disconnect control + lv_obj_add_event_cb( + disconnect, + [](lv_event_t *e) { + auto *ui = static_cast(lv_event_get_user_data(e)); + doDisconnect(); + ui->showShutterIntervalometer(false); + }, + LV_EVENT_CLICKED, this); + + lv_menu_set_load_page_event(menuShutter.main, menuShutter.button, menuShutter.page); + + return menuConnected; +} + +void UI::addConnectMenu(void) { + menu_t &menu = addMenu(m_ConnectStr, LV_SYMBOL_WIFI); + + // refresh connection list every time + lv_obj_add_event_cb( + menu.button, + [](lv_event_t *e) { + auto &menu = m_Menu.at(m_ConnectStr); + bool multiconnect = Settings::load(Settings::MULTICONNECT); + + lv_obj_clean(menu.page); + + CameraList::load(); + for (size_t n = 0; n < CameraList::size(); n++) { + auto *camera = CameraList::get(n); + addCameraItem(camera, menu, multiconnect ? MODE_MULTICONNECT : MODE_CONNECT); + } + + if (multiconnect) { + lv_obj_t *multibutton = lv_button_create(menu.page); + lv_obj_t *label = lv_label_create(multibutton); + lv_label_set_text(label, "Multi-Connect"); + lv_obj_center(label); + lv_obj_add_event_cb(multibutton, doConnect, LV_EVENT_CLICKED, e); + } + + lv_timer_set_user_data(m_ConnectTimer, NULL); + }, + LV_EVENT_CLICKED, NULL); + + // create, but immediately hide the connect message box + m_ConnectMessageBox = lv_msgbox_create(m_Screen); + lv_msgbox_add_title(m_ConnectMessageBox, "Connecting"); + lv_obj_set_width(m_ConnectMessageBox, LV_PCT(100)); + lv_obj_update_layout(m_ConnectMessageBox); + lv_obj_t *c = lv_msgbox_get_content(m_ConnectMessageBox); + lv_obj_set_flex_align(c, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + lv_obj_clear_flag(c, LV_OBJ_FLAG_SCROLLABLE); + m_ConnectLabel = lv_label_create(c); + lv_label_set_long_mode(m_ConnectLabel, LV_LABEL_LONG_DOT); + lv_obj_set_width(m_ConnectLabel, LV_PCT(80)); + + m_ConnectBar = lv_bar_create(c); + lv_obj_set_width(m_ConnectBar, LV_PCT(80)); + lv_bar_set_value(m_ConnectBar, 0, LV_ANIM_ON); + + m_ConnectCancel = lv_msgbox_add_footer_button(m_ConnectMessageBox, "Cancel"); + lv_obj_t *footer = lv_msgbox_get_footer(m_ConnectMessageBox); + lv_obj_update_layout(footer); + // @todo cancel button bottom is clipped, weird + // lv_obj_set_height(m_ConnectCancel, LV_SIZE_CONTENT); + // lv_obj_set_height(footer, lv_obj_get_height(m_ConnectCancel) * 1.2f); + + lv_obj_add_flag(m_ConnectMessageBox, LV_OBJ_FLAG_HIDDEN); + + lv_menu_set_load_page_event(menu.main, menu.button, menu.page); +} + +void UI::addScanMenu(void) { + menu_t &menu = addMenu(m_ScanStr, LV_SYMBOL_EYE_OPEN); + + lv_menu_set_load_page_event(menu.main, menu.button, menu.page); +} + +void UI::refreshDelete(void) { + auto &menu = m_Menu.at(m_DeleteStr); + lv_obj_clean(menu.page); + + CameraList::load(); + for (size_t n = 0; n < CameraList::size(); n++) { + auto *camera = CameraList::get(n); + addCameraItem(camera, menu, MODE_DELETE); + } +} + +void UI::addDeleteMenu(void) { + menu_t &menu = addMenu(m_DeleteStr, LV_SYMBOL_TRASH); + + // refresh connection list every time + lv_obj_add_event_cb(menu.button, [](lv_event_t *e) { refreshDelete(); }, LV_EVENT_CLICKED, NULL); + + lv_menu_set_load_page_event(menu.main, menu.button, menu.page); +} + +void UI::addGPSMenu(const menu_t &parent) { + menu_t &menu = addMenu(m_GPSStr, NULL, true, parent); + addSettingItem(menu.page, NULL, Settings::GPS); + lv_menu_set_load_page_event(menu.main, menu.button, menu.page); + + menu_t &gpsData = addMenu(m_GPSDataStr, NULL, true, menu); + m_Status.gpsData = gpsData.button; + if (!m_Status.gps->isEnabled()) { + lv_obj_add_flag(m_Status.gpsData, LV_OBJ_FLAG_HIDDEN); + } + + static lv_timer_t *timer = lv_timer_create( + [](lv_timer_t *t) { + auto *gpsData = static_cast(lv_timer_get_user_data(t)); + auto &gps = GPS::getInstance().get(); + static lv_obj_t *valid = lv_label_create(gpsData->page); + lv_label_set_text_fmt(valid, "%s (%u)", gps.location.isValid() ? "Valid" : "Invalid", + gps.satellites.value()); + + static lv_obj_t *lat = lv_label_create(gpsData->page); + lv_label_set_text_fmt(lat, "%.2f°", gps.location.lat()); + + static lv_obj_t *lon = lv_label_create(gpsData->page); + lv_label_set_text_fmt(lon, "%.2f°", gps.location.lng()); + + static lv_obj_t *alt = lv_label_create(gpsData->page); + lv_label_set_text_fmt(alt, "%.2f m", gps.altitude.meters()); + + static lv_obj_t *date = lv_label_create(gpsData->page); + lv_label_set_text_fmt(date, "%4u-%02u-%02u", gps.date.year(), gps.date.month(), + gps.date.day()); + static lv_obj_t *time = lv_label_create(gpsData->page); + lv_label_set_text_fmt(time, "%02u:%02u:%02u", gps.time.hour(), gps.time.minute(), + gps.time.second()); + }, + 1000, &gpsData); + lv_timer_pause(timer); + + // start the update timer on 'GPS Data' button press + lv_obj_add_event_cb( + gpsData.button, + [](lv_event_t *e) { + auto *timer = static_cast(lv_event_get_user_data(e)); + lv_timer_resume(timer); + menu_t *menu = static_cast(lv_timer_get_user_data(timer)); + lv_obj_add_event_cb(menu->main, gpsDataStop, LV_EVENT_CLICKED, timer); + }, + LV_EVENT_CLICKED, timer); + + lv_menu_set_load_page_event(gpsData.main, gpsData.button, gpsData.page); +} + +void UI::gpsDataStop(lv_event_t *e) { + auto *timer = static_cast(lv_event_get_user_data(e)); + auto *target = static_cast(lv_event_get_target(e)); + lv_timer_pause(timer); + lv_obj_remove_event_cb(target, gpsDataStop); +} + +void UI::addFeaturesMenu(const menu_t &parent) { + menu_t &menu = addMenu(m_FeaturesStr, NULL, true, parent); + + addSettingItem(menu.page, NULL, Settings::FAUXNY); + addSettingItem(menu.page, NULL, Settings::RECONNECT); + addSettingItem(menu.page, NULL, Settings::MULTICONNECT); + + lv_menu_set_load_page_event(menu.main, menu.button, menu.page); +} + +lv_obj_t *UI::addSpinItem(lv_obj_t *page, const char *item, Intervalometer::Spinner &spinner) { + spinner.m_Button = lv_menu_cont_create(page); + lv_obj_set_flex_flow(spinner.m_Button, LV_FLEX_FLOW_ROW_WRAP); + + spinner.m_Label = lv_label_create(spinner.m_Button); + lv_label_set_text(spinner.m_Label, item); +#if defined(FURBLE_M5COREX) + lv_obj_set_flex_grow(spinner.m_Label, 1); +#endif + + spinner.m_Value = lv_label_create(spinner.m_Button); + lv_label_set_long_mode(spinner.m_Value, LV_LABEL_LONG_SCROLL_CIRCULAR); +#if !defined(FURBLE_M5COREX) + lv_obj_set_flex_grow(spinner.m_Value, 1); +#endif + + lv_obj_add_event_cb( + spinner.m_Value, + [](lv_event_t *e) { + auto *spinner = static_cast(lv_event_get_user_data(e)); + spinner->update(); + }, + LV_EVENT_REFRESH, &spinner); + + return spinner.m_Button; +} + +void UI::addSpinnerPage(const menu_t &parent, const char *item, Intervalometer::Spinner &spinner) { + menu_t &menu = addMenu(item, NULL, false, parent); + menu.button = addSpinItem(parent.page, item, spinner); + + lv_group_add_obj(m_Group, menu.button); + + lv_obj_set_flex_flow(menu.page, LV_FLEX_FLOW_COLUMN); + lv_obj_set_scrollbar_mode(menu.page, LV_SCROLLBAR_MODE_OFF); + spinner.m_RowInfinite = lv_obj_create(menu.page); + lv_obj_set_size(spinner.m_RowInfinite, LV_PCT(100), LV_SIZE_CONTENT); + lv_obj_set_layout(spinner.m_RowInfinite, LV_LAYOUT_FLEX); + lv_obj_set_flex_flow(spinner.m_RowInfinite, LV_FLEX_FLOW_ROW); + lv_obj_t *label = lv_label_create(spinner.m_RowInfinite); + lv_label_set_text(label, "Infinite"); + lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR); + lv_obj_set_flex_grow(label, 1); + spinner.m_SwitchInfinite = lv_switch_create(spinner.m_RowInfinite); + if (spinner.m_SpinValue.m_Unit == SpinValue::UNIT_INF) { + lv_obj_add_state(spinner.m_SwitchInfinite, LV_STATE_CHECKED); + } else { + lv_obj_remove_state(spinner.m_SwitchInfinite, LV_STATE_CHECKED); + } + + if (!spinner.m_Infinite) { + lv_obj_add_flag(spinner.m_RowInfinite, LV_OBJ_FLAG_HIDDEN); + } + + lv_obj_add_event_cb( + spinner.m_SwitchInfinite, + [](lv_event_t *e) { + auto *spinner = static_cast(lv_event_get_user_data(e)); + spinner->update(); + }, + LV_EVENT_VALUE_CHANGED, &spinner); + + spinner.m_RowSpinners = lv_obj_create(menu.page); + lv_obj_set_layout(spinner.m_RowSpinners, LV_LAYOUT_FLEX); + lv_obj_set_flex_flow(spinner.m_RowSpinners, LV_FLEX_FLOW_ROW); + switch (M5.getBoard()) { + case m5::board_t::board_M5StickC: + case m5::board_t::board_M5StickCPlus: + case m5::board_t::board_M5StickCPlus2: + lv_obj_set_flex_align(spinner.m_RowSpinners, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, + LV_FLEX_ALIGN_CENTER); + break; + + default: + lv_obj_set_flex_align(spinner.m_RowSpinners, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, + LV_FLEX_ALIGN_CENTER); + break; + } + + lv_obj_set_size(spinner.m_RowSpinners, LV_PCT(100), LV_SIZE_CONTENT); + + for (auto &r : spinner.m_Roller) { + r = lv_roller_create(spinner.m_RowSpinners); + lv_obj_add_flag(r, LV_OBJ_FLAG_SCROLL_ON_FOCUS); + lv_roller_set_options(r, Intervalometer::Spinner::m_SpinDigitRoller, LV_ROLLER_MODE_INFINITE); + + lv_roller_set_visible_row_count(r, 2); + lv_group_add_obj(m_Group, r); + lv_obj_add_event_cb( + r, + [](lv_event_t *e) { + auto *spinner = static_cast(lv_event_get_user_data(e)); + lv_obj_send_event(spinner->m_Value, LV_EVENT_REFRESH, spinner); + }, + LV_EVENT_VALUE_CHANGED, &spinner); + } + + if ((spinner.m_SpinValue.m_Unit != SpinValue::UNIT_NIL) + && (spinner.m_SpinValue.m_Unit != SpinValue::UNIT_INF)) { + spinner.m_RollerUnit = lv_roller_create(spinner.m_RowSpinners); + lv_obj_add_flag(spinner.m_RollerUnit, LV_OBJ_FLAG_SCROLL_ON_FOCUS); + lv_roller_set_options(spinner.m_RollerUnit, Intervalometer::Spinner::m_SpinUnitsRoller, + LV_ROLLER_MODE_INFINITE); + + lv_obj_add_event_cb( + spinner.m_RollerUnit, + [](lv_event_t *e) { + auto *spinner = static_cast(lv_event_get_user_data(e)); + lv_obj_send_event(spinner->m_Value, LV_EVENT_REFRESH, spinner); + }, + LV_EVENT_VALUE_CHANGED, &spinner); + + lv_group_add_obj(m_Group, spinner.m_RollerUnit); + } + + // squeeze width for smaller displays + switch (M5.getBoard()) { + case m5::board_t::board_M5StickC: + lv_obj_set_flex_align(spinner.m_RowSpinners, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, + LV_FLEX_ALIGN_CENTER); + lv_obj_add_flag(spinner.m_RowSpinners, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_scrollbar_mode(spinner.m_RowSpinners, LV_SCROLLBAR_MODE_OFF); + lv_obj_set_scroll_dir(spinner.m_RowSpinners, LV_DIR_HOR); + __attribute__((fallthrough)); + case m5::board_t::board_M5StickCPlus: + case m5::board_t::board_M5StickCPlus2: + for (auto &r : spinner.m_Roller) { + lv_obj_set_style_pad_left(r, 2, LV_STATE_DEFAULT); + lv_obj_set_style_pad_right(r, 2, LV_STATE_DEFAULT); + } + + if (spinner.m_SpinValue.m_Unit != SpinValue::UNIT_NIL) { + lv_obj_set_style_pad_left(spinner.m_RollerUnit, 2, LV_STATE_DEFAULT); + lv_obj_set_style_pad_right(spinner.m_RollerUnit, 2, LV_STATE_DEFAULT); + } + break; + default: + break; + } + + spinner.updateLabels(); + + lv_menu_set_load_page_event(menu.main, menu.button, menu.page); +} + +void UI::addIntervalometerMenu(const menu_t &parent) { + menu_t &menu = addMenu(m_IntervalometerStr, NULL, true, parent); + menu_t &menuIntervalRun = addMenu(m_IntervalometerRunStr, NULL, false, menu); + + m_IntervalStart = addMenuItem(menu, NULL, "Start"); + addSpinnerPage(menu, m_IntervalCountStr, m_Intervalometer.m_Count); + addSpinnerPage(menu, m_IntervalDelayStr, m_Intervalometer.m_Delay); + addSpinnerPage(menu, m_IntervalShutterStr, m_Intervalometer.m_Shutter); + + m_Intervalometer.m_Count.update(); + + lv_obj_add_flag(m_IntervalStart, LV_OBJ_FLAG_HIDDEN); + + lv_obj_add_event_cb( + m_IntervalStart, + [](lv_event_t *e) { + auto *timer = static_cast(lv_event_get_user_data(e)); + auto *interval = static_cast(lv_timer_get_user_data(timer)); + + interval->m_State = Intervalometer::STATE_IDLE; + lv_timer_resume(timer); + + lv_timer_resume(m_IntervalPageRefresh); + lv_timer_ready(m_IntervalPageRefresh); + }, + LV_EVENT_CLICKED, m_IntervalTimer); + + lv_obj_t *cont = lv_menu_cont_create(menuIntervalRun.page); + lv_obj_set_height(cont, LV_PCT(100)); + lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(cont, LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_CENTER, + LV_FLEX_ALIGN_CENTER); + + m_IntervalStateLabel = lv_label_create(cont); + m_IntervalCountLabel = lv_label_create(cont); + m_IntervalRemainingLabel = lv_label_create(cont); + + lv_obj_t *stop = lv_button_create(cont); + lv_obj_t *stopLabel = lv_label_create(stop); + lv_label_set_text(stopLabel, "Stop"); + lv_obj_add_event_cb( + stop, + [](lv_event_t *e) { + auto &control = Control::getInstance(); + + // pause all interval timers + lv_timer_pause(m_IntervalTimer); + lv_timer_pause(m_IntervalPageRefresh); + + // release shutter and exit + control.sendCommand(Control::CMD_SHUTTER_RELEASE); + lv_obj_t *back = lv_menu_get_main_header_back_button(m_MainMenu.main); + lv_obj_send_event(back, LV_EVENT_CLICKED, m_MainMenu.main); + }, + LV_EVENT_CLICKED, NULL); + + m_IntervalPageRefresh = lv_timer_create( + [](lv_timer_t *timer) { + uint32_t now = tick(); + uint32_t remaining = m_IntervalNext > now ? m_IntervalNext - now : 0; + SpinValue::hms_t hms = SpinValue::toHMS(remaining); + lv_label_set_text_fmt(m_IntervalRemainingLabel, "%02u:%02u:%02u", hms.hours, hms.minutes, + hms.seconds); + }, + 500, NULL); + lv_timer_pause(m_IntervalPageRefresh); + + lv_menu_set_load_page_event(menuIntervalRun.main, m_IntervalStart, menuIntervalRun.page); + + lv_menu_set_load_page_event(menu.main, menu.button, menu.page); +} + +void UI::addBacklightMenu(const menu_t &parent) { + menu_t &menu = addMenu(m_BacklightStr, NULL, true, parent); + lv_obj_t *cont = lv_menu_cont_create(menu.page); + lv_obj_set_height(cont, LV_PCT(100)); + lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(cont, LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_CENTER, + LV_FLEX_ALIGN_CENTER); + + // Add brightness control + lv_obj_t *label = lv_label_create(cont); + lv_label_set_text(label, "Backlight brightness"); + lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR); + lv_obj_set_width(label, LV_PCT(100)); + + lv_obj_t *slider = lv_slider_create(cont); + lv_obj_set_width(slider, LV_PCT(90)); + + // m_BrightnessSteps * 16 == 256 == black screen :( + // limit maximum to m_BrightnessSteps * (16 - 1) + lv_slider_set_range(slider, m_MinimumBrightness / m_BrightnessSteps, m_BrightnessSteps - 1); + + uint8_t brightness = Settings::load(Settings::BRIGHTNESS); + lv_slider_set_value(slider, brightness / m_BrightnessSteps, LV_ANIM_ON); + + lv_obj_add_event_cb( + slider, + [](lv_event_t *e) { + auto *slider = static_cast(lv_event_get_target(e)); + auto brightness = lv_slider_get_value(slider) * m_BrightnessSteps; + M5.Display.setBrightness(brightness); + }, + LV_EVENT_VALUE_CHANGED, NULL); + + lv_obj_add_event_cb( + slider, + [](lv_event_t *e) { + auto *slider = static_cast(lv_event_get_target(e)); + auto brightness = lv_slider_get_value(slider) * m_BrightnessSteps; + Settings::save(Settings::BRIGHTNESS, brightness); + }, + LV_EVENT_RELEASED, NULL); + + // Add inactivity timeout control + label = lv_label_create(cont); + lv_label_set_text(label, "Inactivity timeout"); + lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR); + lv_obj_set_width(label, LV_PCT(100)); + + lv_obj_t *roller = lv_roller_create(cont); + lv_obj_set_width(roller, LV_PCT(90)); + lv_roller_set_options(roller, "Never\n30 secs\n60 secs", LV_ROLLER_MODE_INFINITE); + lv_roller_set_visible_row_count(roller, 1); + uint8_t inactivity = Settings::load(Settings::INACTIVITY); + lv_roller_set_selected(roller, inactivity, LV_ANIM_ON); + + lv_obj_add_event_cb( + roller, + [](lv_event_t *e) { + auto *ui = static_cast(lv_event_get_user_data(e)); + auto *roller = static_cast(lv_event_get_target(e)); + auto inactivity = lv_roller_get_selected(roller); + Settings::save(Settings::INACTIVITY, inactivity); + ui->setInactivityTimeout(inactivity); + }, + LV_EVENT_VALUE_CHANGED, this); + + lv_menu_set_load_page_event(menu.main, menu.button, menu.page); +} + +void UI::addThemeMenu(const menu_t &parent) { + menu_t &menu = addMenu(m_ThemeStr, NULL, true, parent); + + static std::array themes = {"Dark", "Default", "Mono Furble"}; + + for (const auto &theme : themes) { + lv_obj_t *entry = addMenuItem(menu, NULL, theme.c_str()); + + lv_obj_add_event_cb( + entry, + [](lv_event_t *e) { + auto *theme = static_cast(lv_event_get_user_data(e)); + setTheme(*theme); + Settings::save(Settings::THEME, *theme); + }, + LV_EVENT_CLICKED, (void *)&theme); + } + + // add restart button + lv_obj_t *restart = addMenuItem(menu, NULL, "Restart"); + lv_obj_add_event_cb(restart, [](lv_event_t *e) { esp_restart(); }, LV_EVENT_CLICKED, NULL); + + lv_menu_set_load_page_event(menu.main, menu.button, menu.page); +} + +void UI::addTransmitPowerMenu(const menu_t &parent) { + menu_t &menu = addMenu(m_TransmitPowerStr, NULL, true, parent); + lv_obj_t *cont = lv_menu_cont_create(menu.page); + lv_obj_set_height(cont, LV_PCT(100)); + lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(cont, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + lv_obj_t *slider = lv_slider_create(cont); + lv_slider_set_range(slider, 0, 2); + + uint8_t power = Settings::load(Settings::TX_POWER); + lv_slider_set_value(slider, power, LV_ANIM_ON); + + lv_obj_add_event_cb( + slider, + [](lv_event_t *e) { + auto *slider = static_cast(lv_event_get_target(e)); + auto power = lv_slider_get_value(slider); + auto &control = Control::getInstance(); + Settings::save(Settings::TX_POWER, power); + control.setPower(Settings::load(Settings::TX_POWER)); + }, + LV_EVENT_RELEASED, NULL); + + lv_menu_set_load_page_event(menu.main, menu.button, menu.page); +} + +void UI::addAboutMenu(const menu_t &parent) { + menu_t &menu = addMenu(m_AboutStr, NULL, true, parent); + lv_obj_t *cont = lv_menu_cont_create(menu.page); + lv_obj_set_height(cont, LV_PCT(100)); + lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(cont, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + + lv_obj_t *version = lv_label_create(cont); + lv_label_set_text_fmt(version, "Version: %s", FURBLE_VERSION); + + lv_obj_t *id = lv_label_create(cont); + lv_label_set_text_fmt(id, "ID: %s", Device::getStringID().c_str()); + + lv_menu_set_load_page_event(menu.main, menu.button, menu.page); +} + +void UI::addSettingsMenu(void) { + menu_t &menu = addMenu(m_SettingsStr, LV_SYMBOL_SETTINGS); + + addBacklightMenu(menu); + addFeaturesMenu(menu); + addGPSMenu(menu); + addIntervalometerMenu(menu); + addThemeMenu(menu); + addTransmitPowerMenu(menu); + addAboutMenu(menu); + + lv_menu_set_load_page_event(menu.main, menu.button, menu.page); +} + +void UI::updateItems(const menu_t &menu) { + auto *camera = CameraList::last(); + + addCameraItem(camera, menu, MODE_SCAN); + if (camera->getType() == Camera::Type::MOBILE_DEVICE) { + // if we get here, mobile devices are already bonded + CameraList::save(camera); + } +} + +void UI::setInactivityTimeout(uint8_t timeout) { + m_InactivityTimeout = timeout * 30000; +} + +void UI::processInactivity(void) { + static bool inactive = false; + + if (m_InactivityTimeout > 0) { + if (lv_disp_get_inactive_time(m_Display) > m_InactivityTimeout) { + if (!inactive) { + M5.Display.setBrightness(m_MinimumBrightness); + inactive = true; + } + } else { + if (inactive) { + // restore brightness + auto brightness = Settings::load(Settings::BRIGHTNESS); + M5.Display.setBrightness(brightness); + inactive = false; + } + } + } +} + +void UI::task(void) { + while (true) { + M5.update(); + if (m_PMICHack && M5.BtnPWR.wasClicked()) { + // fake PMIC button as actual button + m_PMICClicked = true; + } + m_Mutex.lock(); + lv_task_handler(); + m_Mutex.unlock(); + + vTaskDelay(1 / portTICK_PERIOD_MS); + } +} +} // namespace Furble diff --git a/src/FurbleUIIntervalometer.cpp b/src/FurbleUIIntervalometer.cpp new file mode 100644 index 0000000..a12ab50 --- /dev/null +++ b/src/FurbleUIIntervalometer.cpp @@ -0,0 +1,99 @@ +#include + +#include "FurbleUI.h" + +namespace Furble { + +UI::Intervalometer::Intervalometer(const interval_t &interval) + : m_State {STATE_IDLE}, + m_Count(this, interval.count, true), + m_Delay(this, interval.delay), + m_Shutter(this, interval.shutter) {} + +void UI::Intervalometer::save(void) { + interval_t interval = {m_Count.m_SpinValue.toNVS(), m_Delay.m_SpinValue.toNVS(), + m_Shutter.m_SpinValue.toNVS()}; + Settings::save(Settings::INTERVAL, interval); +} + +void UI::Intervalometer::Spinner::update(void) { + if (lv_obj_has_state(m_SwitchInfinite, LV_STATE_CHECKED)) { + m_SpinValue.m_Unit = SpinValue::UNIT_INF; + lv_obj_add_flag(m_RowSpinners, LV_OBJ_FLAG_HIDDEN); + } else { + m_SpinValue.m_Unit = SpinValue::UNIT_NIL; + lv_obj_clear_flag(m_RowSpinners, LV_OBJ_FLAG_HIDDEN); + + uint32_t h = lv_roller_get_selected(m_Roller[0]); + uint32_t t = lv_roller_get_selected(m_Roller[1]); + uint32_t u = lv_roller_get_selected(m_Roller[2]); + + m_SpinValue.m_Value = (h * 100) + (t * 10) + u; + } + + if (m_RollerUnit != nullptr) { + uint32_t u = lv_roller_get_selected(m_RollerUnit); + switch (u) { + case 0: + m_SpinValue.m_Unit = SpinValue::UNIT_MS; + break; + case 1: + m_SpinValue.m_Unit = SpinValue::UNIT_SEC; + break; + case 2: + m_SpinValue.m_Unit = SpinValue::UNIT_MIN; + break; + } + } + updateLabels(); + + m_Intervalometer->save(); +} + +void UI::Intervalometer::Spinner::updateLabels(void) { + switch (m_SpinValue.m_Unit) { + case SpinValue::UNIT_INF: + lv_label_set_text_fmt(m_Value, "Infinite"); + break; + + case SpinValue::UNIT_NIL: + lv_label_set_text_fmt(m_Value, "%u", m_SpinValue.m_Value); + break; + + default: + lv_label_set_text_fmt(m_Value, "%u %s", m_SpinValue.m_Value, m_SpinValue.getUnitString()); + break; + } + + if (m_SpinValue.m_Unit != SpinValue::UNIT_INF) { + // Update rollers + uint32_t h = m_SpinValue.m_Value / 100; + uint32_t t = (m_SpinValue.m_Value % 100) / 10; + uint32_t u = (m_SpinValue.m_Value % 10); + + lv_roller_set_selected(m_Roller[0], h, LV_ANIM_ON); + lv_roller_set_selected(m_Roller[1], t, LV_ANIM_ON); + lv_roller_set_selected(m_Roller[2], u, LV_ANIM_ON); + + if (m_SpinValue.m_Unit != SpinValue::UNIT_NIL) { + uint32_t i = 0; + switch (m_SpinValue.m_Unit) { + case SpinValue::UNIT_MS: + i = 0; + break; + case SpinValue::UNIT_SEC: + i = 1; + break; + case SpinValue::UNIT_MIN: + i = 2; + break; + default: + break; + } + + lv_roller_set_selected(m_RollerUnit, i, LV_ANIM_ON); + } + } +} + +} // namespace Furble diff --git a/src/furble.cpp b/src/furble.cpp deleted file mode 100644 index 28bf819..0000000 --- a/src/furble.cpp +++ /dev/null @@ -1,494 +0,0 @@ -#include - -#include -#include -#include - -#include "furble_control.h" -#include "furble_gps.h" -#include "furble_ui.h" -#include "interval.h" -#include "settings.h" - -typedef struct { - Furble::Control *control; - ezMenu *menu; - bool scan; - bool multiconnect; -} ui_context_t; - -#define CONNECT_STAR "Connect *" - -/** - * Get seconds since boot. - */ -static uint64_t get_time_secs(void) { - return (esp_timer_get_time() / 1000000LL); -} - -/** - * Display the version. - */ -static void about(void) { - std::string version = FURBLE_VERSION; - if (version.length() < 1) { - version = "unknown"; - } - - ez.msgBox( - FURBLE_STR " - About", - {std::string("Version: ") + version, std::string("ID: ") + Furble::Device::getStringID()}, - {"Back"}, true); -} - -static void show_shutter_control(bool shutter_locked, unsigned long lock_start_ms) { - static unsigned long prev_update_ms = 0; - - if (shutter_locked) { - unsigned long now = millis(); - - if ((now - prev_update_ms) < 1000) { - // Don't update if less than 1000ms - return; - } - - unsigned long total_ms = now - lock_start_ms; - unsigned long minutes = total_ms / 1000 / 60; - unsigned long seconds = (total_ms / 1000) % 60; - prev_update_ms = now; - - char duration[16] = {0x0}; - snprintf(duration, 16, "%02lu:%02lu", minutes, seconds); - -#if ARDUINO_M5STACK_CORE_ESP32 || ARDUINO_M5STACK_CORE2 - ez.msgBox("Remote Shutter", {std::string("Shutter Locked"), std::string(duration)}, - {"Unlock", "Unlock", "Back"}, false); -#else - ez.msgBox("Remote Shutter", - {std::string("Shutter Locked"), std::string(duration), "", "Back: Power"}, - {"Unlock", "Unlock"}, false); -#endif - } else { -#if ARDUINO_M5STACK_CORE_ESP32 || ARDUINO_M5STACK_CORE2 - ez.msgBox("Remote Shutter", {"Lock: Focus+Release"}, {"Release", "Focus", "Back"}, false); -#else - ez.msgBox("Remote Shutter", {"Lock: Focus+Release", "Back: Power"}, {"Release", "Focus"}, - false); -#endif - } -} - -static void remote_control(FurbleCtx *fctx) { - auto *control = fctx->control; - static unsigned long shutter_lock_start_ms = 0; - static bool shutter_lock = false; - - ESP_LOGI(LOG_TAG, "Remote Control"); - - show_shutter_control(false, 0); - - do { - ez.yield(); - - if (M5.BtnPWR.wasClicked() || M5.BtnC.wasPressed()) { - if (shutter_lock) { - // ensure shutter is released on exit - control->sendCommand(CONTROL_CMD_SHUTTER_RELEASE); - } - ESP_LOGI(LOG_TAG, "Exit shutter"); - break; - } - - if (shutter_lock) { - // release shutter if either shutter or focus is pressed - if (M5.BtnA.wasClicked() || M5.BtnB.wasClicked()) { - shutter_lock = false; - control->sendCommand(CONTROL_CMD_SHUTTER_RELEASE); - show_shutter_control(false, 0); - } else { - show_shutter_control(true, shutter_lock_start_ms); - } - } else { - if (M5.BtnA.wasPressed()) { - control->sendCommand(CONTROL_CMD_SHUTTER_PRESS); - continue; - } - - if (M5.BtnA.wasReleased()) { - // focus + shutter = shutter lock - if (M5.BtnB.isPressed()) { - shutter_lock = true; - shutter_lock_start_ms = millis(); - show_shutter_control(true, shutter_lock_start_ms); - ESP_LOGI(LOG_TAG, "shutter lock"); - } else { - control->sendCommand(CONTROL_CMD_SHUTTER_RELEASE); - } - continue; - } - - if (M5.BtnB.wasPressed()) { - control->sendCommand(CONTROL_CMD_FOCUS_PRESS); - continue; - } - - if (M5.BtnB.wasReleased()) { - control->sendCommand(CONTROL_CMD_FOCUS_RELEASE); - continue; - } - } - } while (!fctx->cancelled && control->allConnected()); -} - -/** - * Attempt a connection with a progress bar. - * - * @return false if the attempt was cancelled - */ -static bool connect_with_progress(Furble::Control *control, Furble::Camera *camera) { - ezProgressBar progress_bar(FURBLE_STR, {std::string("Connecting to"), camera->getName()}, - {"", "Cancel"}); - control->connect(camera, settings_load_esp_tx_power()); - - // Wait up to 30 seconds - uint64_t timeout = get_time_secs() + 30; - while (get_time_secs() <= timeout) { - progress_bar.value(camera->getConnectProgress()); - ez.yield(false); - if (M5.BtnB.wasClicked()) { - // issue a cancel to the backend stack - ble_gap_conn_cancel(); - return false; - } - if (camera->isConnected()) { - return true; - } - } - - return true; -} - -/** - * Refresh the camera connection status, this includes: - * * disconnect detection - * * geotag updates - */ -static uint16_t statusRefresh(void *context) { - FurbleCtx *fctx = static_cast(context); - auto *control = fctx->control; - - static uint8_t reconnect_count = 0; - - if (control->allConnected()) { - reconnect_count = 0; - furble_gps_update(control); - return 500; - } - - if (fctx->cancelled) { - return 500; - } - - bool reconnect = settings_load_reconnect(); - auto buttons = ez.buttons.get(); - std::string header = ez.header.title(); - - for (const auto &target : control->getTargets()) { - auto camera = target->getCamera(); - if (!camera->isConnected()) { - reconnect_count++; - if ((reconnect_count > 1) && !reconnect) { - fctx->cancelled = true; - } - - if (!fctx->cancelled && !connect_with_progress(control, camera)) { - fctx->cancelled = true; - } - ez.screen.clear(); - ez.header.show(header); - ez.buttons.show(buttons); - ez.redraw(); - - return 500; - } - } - - ez.screen.clear(); - // no recovery, restart device - esp_restart(); - return 0; -} - -static void menu_remote(FurbleCtx *fctx) { - ez.backlight.inactivity(NEVER); - ezMenu submenu(FURBLE_STR " - Connected"); - submenu.buttons({"OK", "down"}); - submenu.addItem("Shutter"); - submenu.addItem("Interval"); - submenu.addItem("Disconnect"); - submenu.downOnLast("first"); - - ez.addEvent(statusRefresh, fctx, 500); - - do { - submenu.runOnce(); - - if (fctx->cancelled) { - break; - } - - if (submenu.pickName() == "Shutter") { - remote_control(fctx); - } - - if (submenu.pickName() == "Interval") { - remote_interval(fctx); - } - } while (submenu.pickName() != "Disconnect"); - - ez.removeEvent(statusRefresh); - - fctx->control->disconnect(); - ez.backlight.inactivity(USER_SET); -} - -static bool do_connect(ezMenu *menu, void *context) { - auto *ctx = static_cast(context); - - FurbleCtx fctx = {ctx->control, false}; - - if (!ctx->scan && ctx->multiconnect) { - for (int n = 0; n < Furble::CameraList::size(); n++) { - auto *camera = Furble::CameraList::get(n); - if (camera->isActive()) { - connect_with_progress(ctx->control, camera); - if (camera->isConnected()) { - ctx->control->addActive(camera); - } else { - // Fail all if any connect fails - return false; - } - } - } - } else { - auto *camera = Furble::CameraList::get(menu->pick() - 1); - connect_with_progress(ctx->control, camera); - - if (camera->isConnected()) { - if (ctx->scan) { - Furble::CameraList::save(camera); - } - ctx->control->addActive(camera); - } else { - camera->disconnect(); - return false; - } - } - - menu_remote(&fctx); - return true; -} - -static bool toggleCameraSelect(ezMenu *menu, void *context) { - auto *camera = static_cast(context); - camera->setActive(!camera->isActive()); - menu->setCaption(camera->getAddress().toString(), - camera->getName() + "\t" + (camera->isActive() ? "*" : "")); - - return true; -} - -/** - * Scan callback to update connection menu with new devices. - */ -static void updateConnectItems(void *context) { - auto *ctx = static_cast(context); - auto *menu = ctx->menu; - - if (!ctx->scan && ctx->multiconnect) { - menu->deleteItem(CONNECT_STAR); - } - menu->deleteItem("Back"); - for (int i = menu->countItems(); i < Furble::CameraList::size(); i++) { - auto *camera = Furble::CameraList::get(i); - - if (!ctx->scan && ctx->multiconnect) { - menu->addItem(camera->getAddress().toString(), - camera->getName() + "\t" + (camera->isActive() ? "*" : ""), NULL, - static_cast(camera), toggleCameraSelect); - } else { - menu->addItem(camera->getAddress().toString(), camera->getName(), NULL, ctx, do_connect); - } - } - if (!ctx->scan && ctx->multiconnect) { - menu->addItem(CONNECT_STAR, "", NULL, ctx, do_connect); - } - menu->addItem("Back"); -} - -static void menu_connect(Furble::Control *control, bool scan) { - std::string header = FURBLE_STR " - "; - if (scan) { - header += "Scanning"; - } else { - header += "Connect"; - } - - ezMenu submenu(header); - ui_context_t ui_ctx = (ui_context_t){control, &submenu, scan, settings_load_multiconnect()}; - if (scan) { - ez.backlight.inactivity(NEVER); - Furble::Scan::start(updateConnectItems, &ui_ctx); - } else { - updateConnectItems(&ui_ctx); - } - - submenu.buttons({"OK", "down"}); - if (submenu.getItemNum("Back") == 0) { - submenu.addItem("Back"); - } - submenu.downOnLast("first"); - - do { - submenu.runOnce(true); - - if (scan) { - Furble::Scan::stop(); - ez.backlight.inactivity(USER_SET); - } - - if (submenu.pick() == 0) { - break; - } - } while (!scan); -} - -/** - * Scan for devices, then present connection menu. - */ -static bool do_scan(ezMenu *menu, void *context) { - auto *control = static_cast(context); - - Furble::CameraList::clear(); - Furble::Scan::clear(); - menu_connect(control, true); - - return true; -} - -/** - * Retrieve saved devices, then present connection menu. - */ -static bool do_saved(ezMenu *menu, void *context) { - auto *control = static_cast(context); - - Furble::CameraList::load(); - menu_connect(control, false); - - return true; -} - -static void menu_delete(void) { - ezMenu submenu(FURBLE_STR " - Delete"); - submenu.buttons({"OK", "down"}); - Furble::CameraList::load(); - - for (size_t i = 0; i < Furble::CameraList::size(); i++) { - submenu.addItem(Furble::CameraList::get(i)->getName()); - } - submenu.addItem("Back"); - submenu.downOnLast("first"); - - int16_t i = submenu.runOnce(); - if (i == 0) - return; - Furble::CameraList::remove(Furble::CameraList::get(i - 1)); -} - -/** - * Toggle Multi-Connect menu setting. - */ -static bool multiconnect_toggle(ezMenu *menu, void *context) { - bool *multiconnect = static_cast(context); - *multiconnect = !*multiconnect; - menu->setCaption("multiconnectonoff", - std::string("Multi-Connect\t") + (*multiconnect ? "ON" : "OFF")); - - settings_save_multiconnect(*multiconnect); - - return true; -} - -/** - * Toggle Infinite-ReConnect menu setting. - */ -static bool reconnect_toggle(ezMenu *menu, void *context) { - bool *reconnect = static_cast(context); - *reconnect = !*reconnect; - menu->setCaption("reconnectonoff", - std::string("Infinite-ReConnect\t") + (*reconnect ? "ON" : "OFF")); - - settings_save_reconnect(*reconnect); - - return true; -} - -static void menu_settings(void) { - ezMenu submenu(FURBLE_STR " - Settings"); - - bool multiconnect = settings_load_multiconnect(); - bool reconnect = settings_load_reconnect(); - - submenu.buttons({"OK", "down"}); - submenu.addItem("Backlight", "", ez.backlight.menu); - submenu.addItem("GPS", "", settings_menu_gps); - submenu.addItem("reconnectonoff", - std::string("Infinite-ReConnect\t") + (reconnect ? "ON" : "OFF"), nullptr, - &reconnect, reconnect_toggle); - submenu.addItem("Intervalometer", "", settings_menu_interval); - submenu.addItem("multiconnectonoff", - std::string("Multi-Connect\t") + (multiconnect ? "ON" : "OFF"), nullptr, - &multiconnect, multiconnect_toggle); - submenu.addItem("Theme", "", ez.theme->menu); - submenu.addItem("Transmit Power", "", settings_menu_tx_power); - submenu.addItem("About", "", about); - submenu.addItem("Back"); - submenu.downOnLast("first"); - submenu.run(); -} - -static void mainmenu_poweroff(void) { - M5.Power.powerOff(); -} - -void vUITask(void *param) { - auto *control = static_cast(param); - -#include -#include -#include - - ez.begin(); - furble_gps_init(); - - while (true) { - size_t save_count = Furble::CameraList::getSaveCount(); - - ezMenu mainmenu(FURBLE_STR); - mainmenu.buttons({"OK", "down"}); - if (save_count > 0) { - mainmenu.addItem("Connect", "", nullptr, control, do_saved); - } - mainmenu.addItem("Scan", "", nullptr, control, do_scan); - if (save_count > 0) { - mainmenu.addItem("Delete Saved", "", menu_delete); - } - mainmenu.addItem("Settings", "", menu_settings); - mainmenu.addItem("Power Off", "", mainmenu_poweroff); - mainmenu.downOnLast("first"); - - do { - mainmenu.runOnce(); - } while (Furble::CameraList::getSaveCount() == save_count); - } -} diff --git a/src/furble_control.cpp b/src/furble_control.cpp deleted file mode 100644 index f5cc9e7..0000000 --- a/src/furble_control.cpp +++ /dev/null @@ -1,192 +0,0 @@ -#include - -#include "furble_control.h" - -static void target_task(void *param) { - auto *target = static_cast(param); - target->task(); -} - -namespace Furble { -Control::Target::Target(Camera *camera) { - m_Camera = camera; - m_Queue = xQueueCreate(TARGET_CMD_QUEUE_LEN, sizeof(control_cmd_t)); -} - -Control::Target::~Target() { - vQueueDelete(m_Queue); - m_Queue = NULL; - m_Camera->disconnect(); - m_Camera = NULL; -} - -Camera *Control::Target::getCamera(void) { - return m_Camera; -} - -void Control::Target::sendCommand(control_cmd_t cmd) { - BaseType_t ret = xQueueSend(m_Queue, &cmd, 0); - if (ret != pdTRUE) { - ESP_LOGE(LOG_TAG, "Failed to send command to target."); - } -} - -control_cmd_t Control::Target::getCommand(void) { - control_cmd_t cmd = CONTROL_CMD_ERROR; - BaseType_t ret = xQueueReceive(m_Queue, &cmd, pdMS_TO_TICKS(50)); - if (ret != pdTRUE) { - return CONTROL_CMD_ERROR; - } - return cmd; -} - -void Control::Target::updateGPS(Camera::gps_t &gps, Camera::timesync_t ×ync) { - m_GPS = gps; - m_Timesync = timesync; -} - -void Control::Target::task(void) { - const char *name = m_Camera->getName().c_str(); - - while (true) { - control_cmd_t cmd = this->getCommand(); - switch (cmd) { - case CONTROL_CMD_SHUTTER_PRESS: - ESP_LOGI(LOG_TAG, "shutterPress(%s)", name); - m_Camera->shutterPress(); - break; - case CONTROL_CMD_SHUTTER_RELEASE: - ESP_LOGI(LOG_TAG, "shutterRelease(%s)", name); - m_Camera->shutterRelease(); - break; - case CONTROL_CMD_FOCUS_PRESS: - ESP_LOGI(LOG_TAG, "focusPress(%s)", name); - m_Camera->focusPress(); - break; - case CONTROL_CMD_FOCUS_RELEASE: - ESP_LOGI(LOG_TAG, "focusRelease(%s)", name); - m_Camera->focusRelease(); - break; - case CONTROL_CMD_GPS_UPDATE: - ESP_LOGI(LOG_TAG, "updateGeoData(%s)", name); - m_Camera->updateGeoData(m_GPS, m_Timesync); - break; - case CONTROL_CMD_DISCONNECT: - goto task_exit; - case CONTROL_CMD_ERROR: - // ignore continue - break; - default: - ESP_LOGE(LOG_TAG, "Invalid control command %d.", cmd); - } - } -task_exit: - vTaskDelete(NULL); -} - -Control::Control(void) { - m_Queue = xQueueCreate(CONTROL_CMD_QUEUE_LEN, sizeof(control_cmd_t)); - if (m_Queue == NULL) { - ESP_LOGE(LOG_TAG, "Failed to create control queue."); - abort(); - } -} - -Control::~Control() { - vQueueDelete(m_Queue); - m_Queue = NULL; -} - -void Control::task(void) { - while (true) { - control_cmd_t cmd; - BaseType_t ret = xQueueReceive(m_Queue, &cmd, pdMS_TO_TICKS(50)); - if (ret == pdTRUE) { - if (cmd == CONTROL_CMD_CONNECT) { - ESP_LOGI(LOG_TAG, "connect(%s)", m_ConnectCamera->getName().c_str()); - if (m_ConnectCamera == nullptr) { - abort(); - } - m_ConnectCamera->connect(m_Power); - continue; - } - - for (const auto &target : m_Targets) { - switch (cmd) { - case CONTROL_CMD_SHUTTER_PRESS: - case CONTROL_CMD_SHUTTER_RELEASE: - case CONTROL_CMD_FOCUS_PRESS: - case CONTROL_CMD_FOCUS_RELEASE: - case CONTROL_CMD_GPS_UPDATE: - target->sendCommand(cmd); - break; - default: - ESP_LOGE(LOG_TAG, "Invalid control command %d.", cmd); - break; - } - } - } - } -} - -BaseType_t Control::sendCommand(control_cmd_t cmd) { - return xQueueSend(m_Queue, &cmd, 0); -} - -BaseType_t Control::updateGPS(Camera::gps_t &gps, Camera::timesync_t ×ync) { - for (const auto &target : m_Targets) { - target->updateGPS(gps, timesync); - } - - control_cmd_t cmd = CONTROL_CMD_GPS_UPDATE; - return xQueueSend(m_Queue, &cmd, 0); -} - -bool Control::allConnected(void) { - for (const auto &target : m_Targets) { - if (!target->getCamera()->isConnected()) { - return false; - } - } - - return true; -} - -const std::vector> &Control::getTargets(void) { - return m_Targets; -} - -void Control::connect(Camera *camera, esp_power_level_t power) { - m_ConnectCamera = camera; - m_Power = power; - this->sendCommand(CONTROL_CMD_CONNECT); -} - -void Control::disconnect(void) { - for (const auto &target : m_Targets) { - target->sendCommand(CONTROL_CMD_DISCONNECT); - } - - m_Targets.clear(); -} - -void Control::addActive(Camera *camera) { - auto target = std::unique_ptr(new Control::Target(camera)); - - // Create per-target task that will self-delete on disconnect - BaseType_t ret = xTaskCreatePinnedToCore(target_task, camera->getName().c_str(), 4096, - target.get(), 3, NULL, 1); - if (ret != pdPASS) { - ESP_LOGE(LOG_TAG, "Failed to create task for '%s'.", camera->getName()); - } else { - m_Targets.push_back(std::move(target)); - } -} - -}; // namespace Furble - -void control_task(void *param) { - Furble::Control *control = static_cast(param); - - control->task(); -} diff --git a/src/furble_gps.cpp b/src/furble_gps.cpp deleted file mode 100644 index 9b2d30f..0000000 --- a/src/furble_gps.cpp +++ /dev/null @@ -1,126 +0,0 @@ -#include -#include -#include -#include - -#include "furble_control.h" -#include "furble_gps.h" -#include "settings.h" - -static const uint32_t GPS_BAUD = 9600; -#if ARDUINO_M5STACK_CORE_ESP32 || ARDUINO_M5STACK_CORE2 -static const int8_t GPS_RX = 22; -static const int8_t GPS_TX = 21; -#else -static const int8_t GPS_RX = 33; -static const int8_t GPS_TX = 32; -#endif -static const uint16_t GPS_SERVICE_MS = 250; -static const uint32_t GPS_MAX_AGE_MS = 60 * 1000; - -static const uint8_t CURRENT_POSITION = LEFTMOST + 1; -static const uint8_t GPS_HEADER_POSITION = CURRENT_POSITION + 1; - -static bool furble_gps_has_fix = false; - -TinyGPSPlus furble_gps; -bool furble_gps_enable = false; - -/** - * GPS serial event service handler. - */ -static uint16_t service_grove_gps(void *context) { - if (!furble_gps_enable) { - return GPS_SERVICE_MS; - } - - while (Serial2.available() > 0) { - furble_gps.encode(Serial2.read()); - } - - if ((furble_gps.location.age() < GPS_MAX_AGE_MS) && furble_gps.location.isValid() - && (furble_gps.date.age() < GPS_MAX_AGE_MS) && furble_gps.date.isValid() - && (furble_gps.time.age() < GPS_MAX_AGE_MS) && furble_gps.time.age()) { - furble_gps_has_fix = true; - } else { - furble_gps_has_fix = false; - } - - return GPS_SERVICE_MS; -} - -/** - * Update geotag data. - */ -void furble_gps_update(Furble::Control *control) { - if (!furble_gps_enable) { - return; - } - - if (furble_gps.location.isUpdated() && furble_gps.location.isValid() - && furble_gps.date.isUpdated() && furble_gps.date.isValid() && furble_gps.time.isValid() - && furble_gps.time.isValid()) { - Furble::Camera::gps_t dgps = { - furble_gps.location.lat(), - furble_gps.location.lng(), - furble_gps.altitude.meters(), - }; - Furble::Camera::timesync_t timesync = { - furble_gps.date.year(), furble_gps.date.month(), furble_gps.date.day(), - furble_gps.time.hour(), furble_gps.time.minute(), furble_gps.time.second(), - }; - - control->updateGPS(dgps, timesync); - ez.header.draw("gps"); - } -} - -/** - * Draw GPS enable/fix widget. - */ -static void gps_draw_widget(uint16_t x, uint16_t y) { - if (!furble_gps_enable) { - return; - } - - int16_t r = (ez.theme->header_height * 0.8) / 2; - int16_t cx = x + r; - int16_t cy = (ez.theme->header_height / 2); - - if (furble_gps_has_fix) { - // With fix, draw solid circle - M5.Lcd.fillCircle(cx, cy, r, ez.theme->header_fgcolor); - } else { - // No fix, empty circle - M5.Lcd.drawCircle(cx, cy, r, ez.theme->header_fgcolor); - } -} - -static void current_draw_widget(uint16_t x, uint16_t y) { - // hard disable for now - return; - - M5.Lcd.fillRect(x, 0, y, ez.theme->header_height, ez.theme->header_bgcolor); - M5.Lcd.setTextColor(ez.theme->header_fgcolor); - M5.Lcd.setTextDatum(TL_DATUM); - int32_t ma = M5.Power.getBatteryCurrent(); - ESP_LOGI(LOG_TAG, "%d", ma); - M5.Lcd.drawString(std::to_string(ma).c_str(), x + ez.theme->header_hmargin, - ez.theme->header_tmargin + 2); -} - -static uint16_t current_service(void *context) { - ez.header.draw("current"); - return 1000; -} - -void furble_gps_init(void) { - furble_gps_enable = settings_load_gps(); - Serial2.begin(GPS_BAUD, SERIAL_8N1, GPS_RX, GPS_TX); - - uint8_t width = 4 * M5.Lcd.textWidth("5") + ez.theme->header_hmargin * 2; - ez.header.insert(CURRENT_POSITION, "current", width, current_draw_widget); - ez.header.insert(GPS_HEADER_POSITION, "gps", ez.theme->header_height * 0.8, gps_draw_widget); - ez.addEvent(service_grove_gps, nullptr, millis() + 500); - ez.addEvent(current_service, nullptr, millis() + 500); -} diff --git a/src/interval.cpp b/src/interval.cpp deleted file mode 100644 index 4a1aed7..0000000 --- a/src/interval.cpp +++ /dev/null @@ -1,165 +0,0 @@ -#include -#include - -#include "furble_gps.h" -#include "furble_ui.h" -#include "interval.h" -#include "settings.h" -#include "spinner.h" - -enum interval_state_t { - INTERVAL_SHUTTER_OPEN = 0, - INTERVAL_SHUTTER_WAIT = 1, - INTERVAL_SHUTTER_CLOSE = 2, - INTERVAL_DELAY = 3, - INTERVAL_EXIT = 4 -}; - -static void display_interval_msg(interval_state_t state, - unsigned int count, - const SpinValue &sv_count, - unsigned long now, - unsigned long next) { - unsigned int rem_h = 0; - unsigned int rem_m = 0; - unsigned int rem_s = 0; - - const char *statestr = NULL; - switch (state) { - case INTERVAL_SHUTTER_OPEN: - case INTERVAL_SHUTTER_WAIT: - statestr = "SHUTTER OPEN"; - break; - case INTERVAL_SHUTTER_CLOSE: - case INTERVAL_DELAY: - statestr = "DELAY"; - break; - case INTERVAL_EXIT: - break; - } - - unsigned long rem = next - now; - if (now > next) { - rem = 0; - } - - ms2hms(rem, &rem_h, &rem_m, &rem_s); - static char prev_count[16] = {0x0}; - static char prev_hms[32] = {0x0}; - char ccount[16] = {0x0}; - char hms[32] = {0x0}; - int len = 0; - int clen = 0; - - if (sv_count.unit == SPIN_UNIT_INF) { - clen = snprintf(ccount, 16, "%09u", count); - len = snprintf(hms, 32, "%02u:%02u:%02u", rem_h, rem_m, rem_s); - } else { - clen = snprintf(ccount, 16, "%03u/%03u", count, sv_count.value); - len = snprintf(hms, 32, "%02u:%02u:%02u", rem_h, rem_m, rem_s); - } - // ESP_LOGI(LOG_TAG, hms); - - if (((len > 0) && memcmp(prev_hms, hms, len)) - || ((clen > 0) && memcmp(prev_count, ccount, clen))) { - memcpy(prev_hms, hms, len); - memcpy(prev_count, ccount, clen); - ez.msgBox(statestr, {ccount, hms}, {"Stop"}, false, NULL, NO_COLOR, false); - } -} - -static void do_interval(FurbleCtx *fctx, const interval_t &interval) { - auto control = fctx->control; - const unsigned long config_delay = sv2ms(interval.delay); - const unsigned long config_shutter = sv2ms(interval.shutter); - - unsigned int icount = 0; - - interval_state_t state = INTERVAL_SHUTTER_OPEN; - - bool active = true; - - unsigned long now = 0; - unsigned long next = 0; - - ez.backlight.inactivity(NEVER); - - do { - ez.yield(); - now = millis(); - - if (fctx->cancelled) { - break; - } - - switch (state) { - case INTERVAL_SHUTTER_OPEN: - if ((icount < interval.count.value) || (interval.count.unit == SPIN_UNIT_INF)) { - control->sendCommand(CONTROL_CMD_SHUTTER_PRESS); - next = now + config_shutter; - state = INTERVAL_SHUTTER_WAIT; - } else { - state = INTERVAL_EXIT; - } - break; - case INTERVAL_SHUTTER_WAIT: - if (now > next) { - state = INTERVAL_SHUTTER_CLOSE; - } - break; - case INTERVAL_SHUTTER_CLOSE: - icount++; - control->sendCommand(CONTROL_CMD_SHUTTER_RELEASE); - next = now + config_delay; - if ((icount < interval.count.value) || (interval.count.unit == SPIN_UNIT_INF)) { - state = INTERVAL_DELAY; - } else { - state = INTERVAL_EXIT; - } - break; - case INTERVAL_DELAY: - if (now > next) { - state = INTERVAL_SHUTTER_OPEN; - } - break; - case INTERVAL_EXIT: - active = false; - break; - } - - if (M5.BtnB.wasClicked()) { - if (state == INTERVAL_SHUTTER_WAIT) { - control->sendCommand(CONTROL_CMD_SHUTTER_RELEASE); - } - active = false; - } - - display_interval_msg(state, icount, interval.count, now, next); - } while (active && control->allConnected()); - ez.backlight.inactivity(USER_SET); -} - -void remote_interval(FurbleCtx *fctx) { - interval_t interval; - - settings_load_interval(interval); - - ezMenu submenu(FURBLE_STR " - Interval"); - submenu.buttons({"OK", "down"}); - submenu.addItem("Start"); - settings_add_interval_items(&submenu, interval); - submenu.addItem("Back"); - submenu.downOnLast("first"); - - do { - int16_t i = submenu.runOnce(); - if (i == 0) { - return; - } - - if (submenu.pickName() == "Start") { - do_interval(fctx, interval); - } - - } while (submenu.pickName() != "Back"); -} diff --git a/src/main.cpp b/src/main.cpp index 578a63b..3c65f4b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,14 +1,16 @@ #include +#include #include +#include #include "nimconfig.h" #include "Device.h" #include "Furble.h" -#include "furble_control.h" -#include "furble_ui.h" -#include "settings.h" +#include "FurbleControl.h" +#include "FurbleSettings.h" +#include "FurbleUI.h" void setup() { BaseType_t xRet; @@ -19,19 +21,26 @@ void setup() { ESP_LOGI(LOG_TAG, "furble version: '%s'", FURBLE_VERSION); - Furble::Device::init(); - Furble::Scan::init(settings_load_esp_tx_power()); + auto cfg = M5.config(); + cfg.internal_imu = false; + cfg.internal_spk = false; + cfg.internal_mic = false; + M5.begin(cfg); - Furble::Control *control = new Furble::Control(); + Furble::Settings::init(); + Furble::Device::init(); + Furble::Scan::init(Furble::Settings::load(Furble::Settings::TX_POWER)); - xRet = xTaskCreatePinnedToCore(control_task, "control", 8192, control, 4, &xControlHandle, 1); + auto &control = Furble::Control::getInstance(); + xRet = xTaskCreatePinnedToCore(control_task, "control", 8192, &control, 4, &xControlHandle, 1); if (xRet != pdPASS) { ESP_LOGE(LOG_TAG, "Failed to create control task."); abort(); } // Pin UI to same core (0) as NimBLE - xRet = xTaskCreatePinnedToCore(vUITask, "UI-M5ez", 32768, control, 2, &xUIHandle, 0); + xRet = xTaskCreatePinnedToCore(vUITask, "ui", 16384, &control, 2, &xUIHandle, + CONFIG_BT_NIMBLE_PINNED_TO_CORE); if (xRet != pdPASS) { ESP_LOGE(LOG_TAG, "Failed to create UI task."); abort(); diff --git a/src/settings.cpp b/src/settings.cpp deleted file mode 100644 index 65e27bc..0000000 --- a/src/settings.cpp +++ /dev/null @@ -1,339 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -#include "furble_gps.h" -#include "interval.h" -#include "settings.h" - -const char *PREFS_TX_POWER = "txpower"; -const char *PREFS_GPS = "gps"; -const char *PREFS_INTERVAL = "interval"; -const char *PREFS_MULTICONNECT = "multiconnect"; -const char *PREFS_RECONNECT = "reconnect"; - -/** - * Save BLE transmit power to preferences. - */ -static void save_tx_power(uint8_t tx_power) { - Preferences prefs; - - prefs.begin(FURBLE_STR, false); - prefs.putUChar(PREFS_TX_POWER, tx_power); - prefs.end(); -} - -/** - * Load BLE transmit power from preferences. - */ -static uint8_t load_tx_power() { - Preferences prefs; - - prefs.begin(FURBLE_STR, true); - uint8_t power = prefs.getUChar(PREFS_TX_POWER, 1); - prefs.end(); - - return power; -} - -/** - * Load and map prefs power to ESP power. - */ -esp_power_level_t settings_load_esp_tx_power() { - uint8_t power = load_tx_power(); - switch (power) { - case 0: - return ESP_PWR_LVL_P3; - case 1: - return ESP_PWR_LVL_P6; - case 2: - return ESP_PWR_LVL_P9; - default: - return ESP_PWR_LVL_P3; - } - return ESP_PWR_LVL_P3; -} - -/** - * Transmit power menu. - * - * Options are 1, 2 and 3. - */ -void settings_menu_tx_power(void) { - uint8_t power = load_tx_power(); - ezProgressBar power_bar(FURBLE_STR, {"Set transmit power"}, {"Adjust", "Back"}); - power_bar.value(power / 0.03f); - while (true) { - std::string b = ez.buttons.poll(); - if (b == "Adjust") { - power++; - if (power > 3) { - power = 1; - } - power_bar.value(power / 0.03f); - } - if (b == "Back") { - break; - } - } - - save_tx_power(power); -} - -/** - * Display GPS data. - */ -static void show_gps_info(void) { - ESP_LOGI(LOG_TAG, "GPS Data"); - char buffer0[64] = {0x0}; - char buffer1[64] = {0x0}; - char buffer2[64] = {0x0}; - char buffer3[64] = {0x0}; - bool first = true; - - do { - if (M5.BtnB.wasReleased()) { - break; - } - - bool updated = furble_gps.location.isUpdated() || furble_gps.date.isUpdated() - || furble_gps.time.isUpdated(); - - snprintf(buffer0, 64, "%s (%d)", - furble_gps.location.isValid() && furble_gps.date.isValid() && furble_gps.time.isValid() - ? "Valid" - : "Invalid", - furble_gps.location.age()); - snprintf(buffer1, 64, "%.2f %.2f", furble_gps.location.lat(), furble_gps.location.lng()); - snprintf(buffer2, 64, "%.2f metres", furble_gps.altitude.meters()); - snprintf(buffer3, 64, "%4u-%02u-%02u %02u:%02u:%02u", furble_gps.date.year(), - furble_gps.date.month(), furble_gps.date.day(), furble_gps.time.hour(), - furble_gps.time.minute(), furble_gps.time.second()); - - if (first || updated) { - first = false; - ez.header.draw("gps"); - ez.msgBox("GPS Data", {buffer0, buffer1, buffer2, buffer3}, {"Back"}, false); - } - - ez.yield(); - } while (true); -} - -/** - * Read GPS enable setting. - */ -bool settings_load_gps() { - Preferences prefs; - - prefs.begin(FURBLE_STR, true); - bool enable = prefs.getBool(PREFS_GPS, false); - prefs.end(); - - return enable; -} - -/** - * Save GPS enable setting. - */ -static void settings_save_gps(bool enable) { - Preferences prefs; - - prefs.begin(FURBLE_STR, false); - prefs.putBool(PREFS_GPS, enable); - prefs.end(); -} - -bool settings_gps_onoff(ezMenu *menu, void *context) { - bool *gps_enable = static_cast(context); - *gps_enable = !*gps_enable; - menu->setCaption("onoff", std::string("GPS\t") + (gps_enable ? "ON" : "OFF")); - settings_save_gps(gps_enable); - - return true; -} - -/** - * GPS settings menu. - */ -void settings_menu_gps(void) { - ezMenu submenu(FURBLE_STR " - GPS settings"); - - submenu.buttons({"OK", "down"}); - submenu.addItem("onoff", std::string("GPS\t") + (furble_gps_enable ? "ON" : "OFF"), NULL, - &furble_gps_enable, settings_gps_onoff); - submenu.addItem("GPS Data", "", show_gps_info); - submenu.downOnLast("first"); - submenu.addItem("Back"); - submenu.run(); -} - -void settings_load_interval(interval_t &interval) { - Preferences prefs; - - prefs.begin(FURBLE_STR, true); - size_t len = prefs.getBytes(PREFS_INTERVAL, &interval, sizeof(interval_t)); - if (len != sizeof(interval_t)) { - // default values - interval.count.value = INTERVAL_DEFAULT_COUNT; - interval.count.unit = INTERVAL_DEFAULT_COUNT_UNIT; - interval.shutter.value = INTERVAL_DEFAULT_SHUTTER; - interval.shutter.unit = INTERVAL_DEFAULT_SHUTTER_UNIT; - interval.delay.value = INTERVAL_DEFAULT_DELAY; - interval.delay.unit = INTERVAL_DEFAULT_DELAY_UNIT; - } - - prefs.end(); -} - -void settings_save_interval(const interval_t &interval) { - Preferences prefs; - - prefs.begin(FURBLE_STR, false); - prefs.putBytes(PREFS_INTERVAL, &interval, sizeof(interval_t)); - prefs.end(); -} - -static bool configure_count(ezMenu *menu, void *context) { - interval_t *interval = static_cast(context); - - ezMenu submenu("Count"); - submenu.buttons({"OK", "down"}); - submenu.addItem("Custom"); - submenu.addItem("Infinite"); - submenu.downOnLast("first"); - - submenu.runOnce(); - if (submenu.pickName() == "Custom") { - interval->count.unit = SPIN_UNIT_NIL; - spinner_modify_value("Count", false, interval->count); - } - - if (submenu.pickName() == "Infinite") { - interval->count.unit = SPIN_UNIT_INF; - } - - std::string countstr = sv2str(interval->count); - if (interval->count.unit == SPIN_UNIT_INF) { - countstr = "INF"; - } - - menu->setCaption("interval_count", std::string("Count\t") + countstr); - settings_save_interval(*interval); - - return true; -} - -static bool configure_delay(ezMenu *menu, void *context) { - interval_t *interval = static_cast(context); - - ezMenu submenu("Delay"); - submenu.buttons({"OK", "down"}); - submenu.addItem("Custom"); - submenu.addItem("Preset"); - submenu.downOnLast("first"); - - bool preset; - - submenu.runOnce(); - if (submenu.pickName() == "Custom") { - preset = false; - } - if (submenu.pickName() == "Preset") { - preset = true; - } - - spinner_modify_value("Delay", preset, interval->delay); - menu->setCaption("interval_delay", "Delay\t" + sv2str(interval->delay)); - settings_save_interval(*interval); - - return true; -} - -static bool configure_shutter(ezMenu *menu, void *context) { - interval_t *interval = static_cast(context); - - ezMenu submenu("Shutter"); - submenu.buttons({"OK", "down"}); - submenu.addItem("Custom"); - submenu.addItem("Preset"); - submenu.downOnLast("first"); - - bool preset; - - submenu.runOnce(); - if (submenu.pickName() == "Custom") { - preset = false; - } - if (submenu.pickName() == "Preset") { - preset = true; - } - - spinner_modify_value("Shutter", preset, interval->shutter); - menu->setCaption("interval_shutter", "Shutter\t" + sv2str(interval->shutter)); - settings_save_interval(*interval); - - return true; -} - -void settings_add_interval_items(ezMenu *submenu, interval_t &interval) { - submenu->addItem("interval_count", std::string("Count\t") + sv2str(interval.count), NULL, - &interval, configure_count); - submenu->addItem("interval_delay", std::string("Delay\t") + sv2str(interval.delay), NULL, - &interval, configure_delay); - submenu->addItem("interval_shutter", std::string("Shutter\t") + sv2str(interval.shutter), NULL, - &interval, configure_shutter); -} - -void settings_menu_interval(void) { - interval_t interval; - - settings_load_interval(interval); - - ezMenu submenu(FURBLE_STR " - Intervalometer settings"); - submenu.buttons({"OK", "down"}); - settings_add_interval_items(&submenu, interval); - submenu.addItem("Back"); - submenu.downOnLast("first"); - submenu.run(); -} - -bool settings_load_multiconnect(void) { - Preferences prefs; - - prefs.begin(FURBLE_STR, true); - bool multiconnect = prefs.getBool(PREFS_MULTICONNECT, false); - prefs.end(); - - return multiconnect; -} - -void settings_save_multiconnect(bool multiconnect) { - Preferences prefs; - - prefs.begin(FURBLE_STR, false); - prefs.putBool(PREFS_MULTICONNECT, multiconnect); - prefs.end(); -} - -bool settings_load_reconnect(void) { - Preferences prefs; - - prefs.begin(FURBLE_STR, true); - bool reconnect = prefs.getBool(PREFS_RECONNECT, false); - prefs.end(); - - return reconnect; -} - -void settings_save_reconnect(bool reconnect) { - Preferences prefs; - - prefs.begin(FURBLE_STR, false); - prefs.putBool(PREFS_RECONNECT, reconnect); - prefs.end(); -} \ No newline at end of file diff --git a/src/spinner.cpp b/src/spinner.cpp deleted file mode 100644 index 43cd921..0000000 --- a/src/spinner.cpp +++ /dev/null @@ -1,257 +0,0 @@ -#include -#include - -#include "spinner.h" - -static constexpr std::array unit2str = { - " ", // SPIN_UNIT_NIL - " ", // SPIN_UNIT_INF - "msec", // SPIN_UNIT_MS - "secs", // SPIN_UNIT_SEC - "mins" // SPIN_UNIT_MIN -}; - -static constexpr std::array spin_preset = {1, 2, 4, 8, 15, 30, 60, 125, 250, 500}; - -static constexpr std::array fmt_none = { - " %1u %1u %1u ", - "[%1u] %1u %1u ", - " %1u [%1u] %1u ", - " %1u %1u [%1u]", -}; - -static constexpr std::array fmt_unit = { - " %1u %1u %1u %4s ", "[%1u] %1u %1u %4s ", " %1u [%1u] %1u %4s ", - " %1u %1u [%1u] %4s ", " %1u %1u %1u [%4s]", -}; - -static constexpr std::array fmt_preset_none = { - " %1u %1u %1u ", - "[%1u %1u %1u]", -}; - -static constexpr std::array fmt_preset_unit = { - " %1u %1u %1u %4s ", - "[%1u %1u %1u] %4s ", - " %1u %1u %1u [%4s]", -}; - -#define SPIN_ROW_LEN (32) - -static void value2htu(uint16_t value, unsigned int *h, unsigned int *t, unsigned int *u) { - // deconstruct the value - *h = value / 100; - *t = (value % 100) / 10; - *u = value % 10; -} - -static uint16_t htu2value(unsigned int h, unsigned int t, unsigned int u) { - // reconstruct the value - return (h * 100) + (t * 10) + u; -} - -static void display_spinner(const char *title, - bool preset, - unsigned int index, - unsigned int h, - unsigned int t, - unsigned int u, - spin_unit_t unit) { - char spin_row[SPIN_ROW_LEN] = {0x0}; - - if (preset) { - if (unit == SPIN_UNIT_NIL) { - snprintf(spin_row, SPIN_ROW_LEN, fmt_preset_none[index], h, t, u); - } else { - snprintf(spin_row, SPIN_ROW_LEN, fmt_preset_unit[index], h, t, u, unit2str[unit]); - } - } else { - if (unit == SPIN_UNIT_NIL) { - snprintf(spin_row, SPIN_ROW_LEN, fmt_none[index], h, t, u); - } else { - snprintf(spin_row, SPIN_ROW_LEN, fmt_unit[index], h, t, u, unit2str[unit]); - } - } - - std::vector buttons = {"Adjust", "Next"}; - if (index == 0) { - buttons[0] = "OK"; - } - - ez.msgBox(title, {spin_row}, buttons, false); -} - -static void spinner_preset(const char *title, SpinValue &sv) { - const unsigned int imax = - sv.unit == SPIN_UNIT_NIL ? fmt_preset_none.size() : fmt_preset_unit.size(); - unsigned int i = 0; - unsigned int n = 0; - bool ok = false; - - uint16_t value = sv.value; - unsigned int h; - unsigned int t; - unsigned int u; - - // find closest preset equal or lower - for (n = (spin_preset.size() - 1); n > 0; n--) { - if (value >= spin_preset[n]) { - break; - } - } - - value2htu(value, &h, &t, &u); - display_spinner(title, true, i, h, t, u, sv.unit); - M5.update(); - - do { - if (M5.BtnA.wasClicked()) { - switch (i) { - case 0: - ok = true; - break; - case 1: - n++; - if (n >= spin_preset.size()) { - n = 0; - } - value = spin_preset[n]; - break; - case 2: - switch (sv.unit) { - case SPIN_UNIT_MS: - sv.unit = SPIN_UNIT_SEC; - break; - case SPIN_UNIT_SEC: - sv.unit = SPIN_UNIT_MIN; - break; - case SPIN_UNIT_MIN: - sv.unit = SPIN_UNIT_MS; - break; - default: - sv.unit = SPIN_UNIT_NIL; - } - break; - } - value2htu(value, &h, &t, &u); - display_spinner(title, true, i, h, t, u, sv.unit); - } - - if (M5.BtnB.wasClicked()) { - i++; - if (i >= imax) { - i = 0; - } - display_spinner(title, true, i, h, t, u, sv.unit); - } - - ez.yield(); - } while (!ok); - - // reconstruct the value - sv.value = htu2value(h, t, u); -} - -static void spinner_custom(const char *title, SpinValue &sv) { - const unsigned int imax = sv.unit == SPIN_UNIT_NIL ? fmt_none.size() : fmt_unit.size(); - unsigned int i = 0; - bool ok = false; - - unsigned int h = 0; - unsigned int t = 0; - unsigned int u = 0; - - value2htu(sv.value, &h, &t, &u); - display_spinner(title, false, i, h, t, u, sv.unit); - M5.update(); - - do { - if (M5.BtnA.wasClicked()) { - switch (i) { - case 0: - ok = true; - break; - case 1: - h++; - if (h > 9) { - h = 0; - } - break; - case 2: - t++; - if (t > 9) { - t = 0; - } - break; - case 3: - u++; - if (u > 9) { - u = 0; - } - break; - case 4: - switch (sv.unit) { - case SPIN_UNIT_MS: - sv.unit = SPIN_UNIT_SEC; - break; - case SPIN_UNIT_SEC: - sv.unit = SPIN_UNIT_MIN; - break; - case SPIN_UNIT_MIN: - sv.unit = SPIN_UNIT_MS; - break; - default: - sv.unit = SPIN_UNIT_NIL; - } - break; - } - display_spinner(title, false, i, h, t, u, sv.unit); - } - - if (M5.BtnB.wasClicked()) { - i++; - if (i >= imax) { - i = 0; - } - display_spinner(title, false, i, h, t, u, sv.unit); - } - - ez.yield(); - delay(50); - } while (!ok); - - // reconstruct the value - sv.value = htu2value(h, t, u); -} - -void spinner_modify_value(const char *title, bool preset, SpinValue &sv) { - if (preset) { - spinner_preset(title, sv); - } else { - spinner_custom(title, sv); - } -} - -std::string sv2str(const SpinValue &sv) { - return std::to_string(sv.value) + unit2str[sv.unit]; -} - -unsigned long sv2ms(const SpinValue &sv) { - switch (sv.unit) { - case SPIN_UNIT_MIN: - return (sv.value * 60 * 1000); - case SPIN_UNIT_SEC: - return (sv.value * 1000); - case SPIN_UNIT_MS: - return (sv.value); - default: - return 0; - } - return 0; -} - -void ms2hms(unsigned long ms, unsigned int *h, unsigned int *m, unsigned int *s) { - *h = (ms / 1000) / (60 * 60); - *m = ((ms / 1000) / 60) % 60; - *s = (ms / 1000) % 60; -} diff --git a/web-installer/index.html b/web-installer/index.html index 1367e1e..f365a80 100644 --- a/web-installer/index.html +++ b/web-installer/index.html @@ -19,7 +19,7 @@

furble web installer

Select your device:

  • - +
  • @@ -27,6 +27,9 @@

    furble web installer

  • +
  • + +