diff --git a/.gitignore b/.gitignore
index be9312e..d5cb742 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,2 @@
/build/**/*
-!/build/**/*.uf2
+/external/**/*
diff --git a/CMakeLists.txt b/CMakeLists.txt
index af7482d..361ffa5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,10 +1,29 @@
-set(PROJECT hyperx_kb_rgb)
+set(PROJECT elite2_rgb)
cmake_minimum_required(VERSION 3.13)
set(PICO_SDK_PATH /home/kenji/programming/pico/c/pico-sdk)
set(PICO_PIO_USB_PATH /home/kenji/programming/pico/c/Pico-PIO-USB)
+set(TUSB_NETWORKING_PATH ${PICO_SDK_PATH}/lib/tinyusb/lib/networking)
include (${PICO_SDK_PATH}/external/pico_sdk_import.cmake)
project(${PROJECT} C CXX ASM)
+set(MAKE_FS_DATA_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/external/makefsdata)
+set(HTML_DIR ${CMAKE_CURRENT_LIST_DIR}/html)
+
+if (NOT EXISTS ${MAKE_FS_DATA_SCRIPT})
+ file(DOWNLOAD
+ https://raw.githubusercontent.com/lwip-tcpip/lwip/e799c266facc3c70190676eccad49d6c2db2caac/src/apps/http/makefsdata/makefsdata
+ ${MAKE_FS_DATA_SCRIPT}
+ )
+endif()
+message("Running makefsdata script")
+execute_process(COMMAND
+ perl ${MAKE_FS_DATA_SCRIPT}
+ WORKING_DIRECTORY ${HTML_DIR}
+ ECHO_OUTPUT_VARIABLE
+ ECHO_ERROR_VARIABLE
+)
+file(RENAME ${HTML_DIR}/fsdata.c ${CMAKE_CURRENT_LIST_DIR}/my_fsdata.c)
+
pico_sdk_init()
add_subdirectory(${PICO_PIO_USB_PATH} pico_pio_usb)
@@ -15,6 +34,10 @@ target_sources(${PROJECT} PRIVATE
main.c
usb_host.c
usb_device.c
+ usb_server.c
+ websocket.c
+ ${TUSB_NETWORKING_PATH}/dhserver.c
+ ${TUSB_NETWORKING_PATH}/dnserver.c
)
# print memory usage, enable all warnings
@@ -25,9 +48,16 @@ target_compile_options(${PROJECT} PRIVATE ) #-Wall -Wextra
target_compile_definitions(${PROJECT} PRIVATE PIO_USB_USE_TINYUSB)
# needed so tinyusb can find tusb_config.h
-target_include_directories(${PROJECT} PRIVATE ${CMAKE_CURRENT_LIST_DIR})
+target_include_directories(${PROJECT} PRIVATE
+ ${CMAKE_CURRENT_LIST_DIR}
+ ${TUSB_NETWORKING_PATH}
+)
target_link_libraries(${PROJECT} PRIVATE
+ pico_lwip
+ pico_lwip_arch
+ pico_lwip_http
+ pico_mbedtls
pico_stdlib
pico_pio_usb
tinyusb_board
diff --git a/COPYING b/COPYING
index 529e42c..91c4b4a 100644
--- a/COPYING
+++ b/COPYING
@@ -1,5 +1,5 @@
-HyperX Alloy Elite 2 Automatic Backlight - turn on/off backlight using
-a light dependent resistor
+HyperX Alloy Elite 2 RGB Controller - configure individual LED settings using
+a webpage served over USB and adjust brightness with a LDR
Copyright (C) 2025 Kenji Kozai
This program is free software: you can redistribute it and/or modify
diff --git a/README.md b/README.md
index cbb86cb..4598a60 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,11 @@
-# HyperX Alloy Elite 2 Automatic Backlight
+# HyperX Alloy Elite 2 RGB Controller
-This project automatically controls the RGB backlight of a HyperX Alloy Elite 2
-keyboard based on ADC readings from an attached light dependent resistor. The
-backlight automatically turns on to white when the LDR reading is below
-a programmed threshold and otherwise turns all RGB lights completely off.
+This project provides individual controls of the RGB LEDs of a HyperX Alloy
+Elite 2 keyboard using a webpage served over a USB connection. ADC readings
+from an attached light dependent resistor can also be used to make the
+lighting adaptive to the environment.
-
+
## Setup
@@ -49,28 +49,34 @@ hole for the LDR to detect ambient light.
## Software
-Flash the [hyperx_kb_rgb.uf2](hyperx_kb_rgb.uf2) file to the Raspberry Pi Pico,
-and connect the keyboard to the female USB port and insert the male USB
-connector of the device into your host device such as PC.
+Flash the elite2_rgb.uf2 file from the latest
+[release](https://git.kkozai.com/kenji/alloy_elite2_rgb/releases) to the
+Raspberry Pi Pico, and connect the keyboard to the female USB port and insert
+the male USB connector of the device into your host device such as PC.
-If you wish to change the ADC reading threshold for changing the lighting,
-you can do so by changing the values of `LDR_OFF_THRESHOLD` and
-`LDR_ON_THRESHOLD` in [hyperx_elite2.h](hyperx_elite2.h).
+To load the UI for configuring the RGB lighting, open a browser to the page at
+http://alloyelite2.usb, or if that doesn't load, to http://192.168.226.1
+(226 is E2 for "Elite 2" in hexademical). From the webpage, you can click on
+any individual key that you want to configure and change the color using the
+color selector or by manually inputting the RGB color value into the text
+boxes. To finalize setting the color for the selected key(s), click on the
+"Set Color" button.
-You can change the color used in the two different modes inside the `rgb_task`
-function of [hyperx_elite2.c](hyperx_elite2.c).
+To save the lighting configuration to the Pico 2 so that it loads the next time
+it is powered on, click on the "Save" button under the "Flash Memory" section.
+If you make unsaved changes and want to reload the configuration from memory,
+you can also click the "Load" button to reset to the last saved setting.
-If you used a different ADC pin than ADC2, you can change the selected ADC pin
-and ADC channel by changing `LDR_PIN` and `LDR_ADC`
-[hyperx_elite2.h](hyperx_elite2.h).
+If the checkbox next to "Adaptive" is selected when setting the color, the key's
+brightness will automatically adjust with the ambient lighting, as determined by
+the ADC reading of the LDR. Leaving it unchecked will set the RGB color to be
+constant regardless of the ambient light level.
-For any of the changes above, you will need to modify the
-[CMakeLists.txt](CMakeLists.txt) file and compile the program yourself. You
-will need to change `PICO_SDK_PATH` and `PICO_PIO_USB_PATH` to the
-directories where you have the
-[Raspberry Pi Pico SDK](https://github.com/raspberrypi/pico-sdk) and
-[Pico-PIO-USB](https://github.com/sekigon-gonnoc/Pico-PIO-USB) library
-downloaded on your machine.
+The mute button has special behavior when set to adaptive mode. It will toggle
+between the user-configured color and red each time that it is pressed as a
+way to indicate whether system sounds are muted or not. The keyboard will
+always boot in the "unmuted" setting, so if your system starts off muted,
+the LED color on the keyboard and your system may not match.
## Licensing
@@ -86,5 +92,6 @@ The project uses code from the following sources:
- [Pico-PIO-USB](https://github.com/sekigon-gonnoc/Pico-PIO-USB/) for templates
used from the [host_hid_to_device_cdc](https://github.com/sekigon-gonnoc/Pico-PIO-USB/tree/control-keyboard-led/examples/host_hid_to_device_cdc)
example released under the MIT license
-- [TinyUSB](https://github.com/hathach/tinyusb) for templates used in
- [tusb_config.h](tusb_config.h) distributed under the MIT license
+- [TinyUSB](https://github.com/hathach/tinyusb) for templates used from the
+ [net_lwip_webserver](https://github.com/hathach/tinyusb/tree/master/examples/device/net_lwip_webserver)
+ example distributed under the MIT license
diff --git a/build/hyperx_kb_rgb.uf2 b/build/hyperx_kb_rgb.uf2
deleted file mode 100644
index ef9e00f..0000000
Binary files a/build/hyperx_kb_rgb.uf2 and /dev/null differ
diff --git a/html/index.html b/html/index.html
new file mode 100644
index 0000000..ed0a258
--- /dev/null
+++ b/html/index.html
@@ -0,0 +1,413 @@
+
+
+
+
+
+HyperX Alloy Elite 2 RGB Configuration
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/hyperx_elite2.c b/hyperx_elite2.c
index f0cc008..aef2e61 100644
--- a/hyperx_elite2.c
+++ b/hyperx_elite2.c
@@ -1,39 +1,171 @@
#include
#include "pico/stdlib.h"
-#include "tusb.h"
+#include "pico/multicore.h"
#include "hardware/adc.h"
+#include "hardware/flash.h"
+#include "tusb.h"
+
+#include "usb_device.h"
+#include "websocket.h"
#include "hyperx_elite2.h"
static absolute_time_t lastSend;
static absolute_time_t lastRead;
-static bool backlight = false;
static uint16_t adc_value = 0;
static bool mute = false;
-static const unsigned int SKIP_INDICES[] = { 23, 29, 41, 47, 70, 71, 76, 87, 88, 93, 99, 100, 102, 108, 113 };
-
static unsigned char buf[BUF_SIZE];
static uint8_t buf_idx=0;
+static uint8_t key_idx=0;
+static uint8_t color_idx=0;
static uint8_t packets_sent=0;
-static uint8_t color_idx = 0;
-static uint8_t skipped = 0;
-const unsigned int* skip_idx = &SKIP_INDICES[0];
+static int64_t delay=0;
+static uint8_t ws_buf[12];
+static uint16_t ws_len;
-static void send_color(uint8_t dev_addr, uint8_t red, uint8_t green, uint8_t blue);
+static void send_color(uint8_t dev_addr);
static void send_initial(uint8_t dev_addr);
+static struct key * find_key(char * name);
+static void set_color(char * name, uint8_t red, uint8_t green, uint8_t blue, uint8_t mode);
+static uint8_t hexint (const char c);
+static uint8_t hexbyte (const char * s);
+
+static struct key key_list[NUM_KEYS] =
+{
+ INIT_KEY("Escape", KEY_ESC),
+ INIT_KEY("Backquote", KEY_GRAVE),
+ INIT_KEY("Tab", KEY_TAB),
+ INIT_KEY("CapsLock", KEY_CAPSLOCK),
+ INIT_KEY("ShiftLeft", KEY_LEFTSHIFT),
+ INIT_KEY("ControlLeft", KEY_LEFTCTRL),
+ INIT_KEY("Digit1", KEY_1),
+ INIT_KEY("KeyQ", KEY_Q),
+ INIT_KEY("KeyA", KEY_A),
+ INIT_KEY("KeyZ", KEY_Z),
+ INIT_KEY("MetaLeft", KEY_LEFTMETA),
+ INIT_KEY("F1", KEY_F1),
+ INIT_KEY("Digit2", KEY_2),
+ INIT_KEY("KeyW", KEY_W),
+ INIT_KEY("KeyS", KEY_S),
+ INIT_KEY("KeyX", KEY_X),
+ INIT_KEY("AltLeft", KEY_LEFTALT),
+ INIT_KEY("F2", KEY_F2),
+ INIT_KEY("Digit3", KEY_3),
+ INIT_KEY("KeyE", KEY_E),
+ INIT_KEY("KeyD", KEY_D),
+ INIT_KEY("KeyC", KEY_C),
+ INIT_KEY("F3", KEY_F3),
+ INIT_KEY("Digit4", KEY_4),
+ INIT_KEY("KeyR", KEY_R),
+ INIT_KEY("KeyF", KEY_F),
+ INIT_KEY("KeyV", KEY_V),
+ INIT_KEY("F4", KEY_F4),
+ INIT_KEY("Digit5", KEY_5),
+ INIT_KEY("KeyT", KEY_T),
+ INIT_KEY("KeyG", KEY_G),
+ INIT_KEY("KeyB", KEY_B),
+ INIT_KEY("Space", KEY_SPACE),
+ INIT_KEY("F5", KEY_F5),
+ INIT_KEY("Digit6", KEY_6),
+ INIT_KEY("KeyY", KEY_Y),
+ INIT_KEY("KeyH", KEY_H),
+ INIT_KEY("KeyN", KEY_N),
+ INIT_KEY("F6", KEY_F6),
+ INIT_KEY("Digit7", KEY_7),
+ INIT_KEY("KeyU", KEY_U),
+ INIT_KEY("KeyJ", KEY_J),
+ INIT_KEY("KeyM", KEY_M),
+ INIT_KEY("F7", KEY_F7),
+ INIT_KEY("Digit8", KEY_8),
+ INIT_KEY("KeyI", KEY_I),
+ INIT_KEY("KeyK", KEY_K),
+ INIT_KEY("Comma", KEY_COMMA),
+ INIT_KEY("AltRight", KEY_RIGHTALT),
+ INIT_KEY("F8", KEY_F8),
+ INIT_KEY("Digit9", KEY_9),
+ INIT_KEY("KeyO", KEY_O),
+ INIT_KEY("KeyL", KEY_L),
+ INIT_KEY("Period", KEY_DOT),
+ INIT_KEY("F9", KEY_F9),
+ INIT_KEY("Digit0", KEY_0),
+ INIT_KEY("KeyP", KEY_P),
+ INIT_KEY("Semicolon", KEY_SEMICOLON),
+ INIT_KEY("Slash", KEY_SLASH),
+ INIT_KEY("MetaRight", KEY_RIGHTMETA),
+ INIT_KEY("F10", KEY_F10),
+ INIT_KEY("Minus", KEY_MINUS),
+ INIT_KEY("BracketLeft", KEY_LEFTBRACE),
+ INIT_KEY("Quote", KEY_APOSTROPHE),
+ INIT_KEY("F11", KEY_F11),
+ INIT_KEY("Equal", KEY_EQUAL),
+ INIT_KEY("BracketRight", KEY_RIGHTBRACE),
+ INIT_KEY("ContextMenu", KEY_MENU),
+ INIT_KEY("F12", KEY_F12),
+ INIT_KEY("Backspace", KEY_BACKSPACE),
+ INIT_KEY("Backslash", KEY_BACKSLASH),
+ INIT_KEY("Enter", KEY_ENTER),
+ INIT_KEY("ShiftRight", KEY_RIGHTSHIFT),
+ INIT_KEY("ControlRight", KEY_RIGHTCTRL),
+ INIT_KEY("PrintScreen", KEY_SYSRQ),
+ INIT_KEY("Insert", KEY_INSERT),
+ INIT_KEY("Delete", KEY_DELETE),
+ INIT_KEY("ArrowLeft", KEY_LEFT),
+ INIT_KEY("ScrollLock", KEY_SCROLLLOCK),
+ INIT_KEY("Home", KEY_HOME),
+ INIT_KEY("End", KEY_END),
+ INIT_KEY("ArrowUp", KEY_UP),
+ INIT_KEY("ArrowDown", KEY_DOWN),
+ INIT_KEY("Pause", KEY_PAUSE),
+ INIT_KEY("PageUp", KEY_PAGEUP),
+ INIT_KEY("PageDown", KEY_PAGEDOWN),
+ INIT_KEY("ArrowRight", KEY_RIGHT),
+ INIT_KEY("NumLock", KEY_NUMLOCK),
+ INIT_KEY("Numpad7", KEY_KP7),
+ INIT_KEY("Numpad4", KEY_KP4),
+ INIT_KEY("Numpad1", KEY_KP1),
+ INIT_KEY("Numpad0", KEY_KP0),
+ INIT_KEY("NumpadDivide", KEY_KPSLASH),
+ INIT_KEY("Numpad8", KEY_KP8),
+ INIT_KEY("Numpad5", KEY_KP5),
+ INIT_KEY("Numpad2", KEY_KP2),
+ INIT_KEY_MUTE("Mute", KEY_MUTE),
+ INIT_KEY("NumpadMultiply", KEY_KPASTERISK),
+ INIT_KEY("Numpad9", KEY_KP9),
+ INIT_KEY("Numpad6", KEY_KP6),
+ INIT_KEY("Numpad3", KEY_KP3),
+ INIT_KEY("NumpadDecimal", KEY_KPDOT),
+ INIT_KEY("MediaTrackPrevious", KEY_MEDIA_PREVIOUSSONG),
+ INIT_KEY("NumpadSubtract", KEY_KPMINUS),
+ INIT_KEY("NumpadAdd", KEY_KPPLUS),
+ INIT_KEY("MediaPlayPause", KEY_MEDIA_PLAYPAUSE),
+ INIT_KEY("MediaTrackNext", KEY_MEDIA_NEXTSONG),
+ INIT_KEY("NumpadEnter", KEY_KPENTER),
+ INIT_KEY("Bar1", LED_BAR1),
+ INIT_KEY("Bar2", LED_BAR2),
+ INIT_KEY("Bar3", LED_BAR3),
+ INIT_KEY("Bar4", LED_BAR4),
+ INIT_KEY("Bar5", LED_BAR5),
+ INIT_KEY("Bar6", LED_BAR6),
+ INIT_KEY("Bar7", LED_BAR7),
+ INIT_KEY("Bar8", LED_BAR8),
+ INIT_KEY("Bar9", LED_BAR9),
+ INIT_KEY("Bar10", LED_BAR10),
+ INIT_KEY("Bar11", LED_BAR11),
+ INIT_KEY("Bar12", LED_BAR12),
+ INIT_KEY("Bar13", LED_BAR13),
+ INIT_KEY("Bar14", LED_BAR14),
+ INIT_KEY("Bar15", LED_BAR15),
+ INIT_KEY("Bar16", LED_BAR16),
+ INIT_KEY("Bar17", LED_BAR17),
+ INIT_KEY("Bar18", LED_BAR18)
+};
void get_light() {
// get ADC reading from LDR every 500ms
- // if above threshold, set backlight to off
if ( absolute_time_diff_us(lastRead, get_absolute_time()) >= 500000) {
adc_value = adc_read();
- if (backlight && adc_value >= LDR_OFF_THRESHOLD) {
- backlight = false;
- } else if (!backlight && adc_value <= LDR_ON_THRESHOLD) {
- backlight = true;
- }
}
}
@@ -44,7 +176,8 @@ void rgb_task(uint8_t dev_addr) {
// updated color info (packets_sent>0) and continue to send the next
// packet if so at a rate of one packet every 20ms
// otherwise, wait 0.5s before sending the next set of color packets
- if (packets_sent>0) {
+
+ /*if (packets_sent>0) {
if ( absolute_time_diff_us(lastSend, get_absolute_time()) >= 20000) {
if (backlight) {
send_color(dev_addr, 0x20, 0x20, 0x20);
@@ -60,11 +193,21 @@ void rgb_task(uint8_t dev_addr) {
send_initial(dev_addr);
lastSend = get_absolute_time();
}
+ }*/
+ if ( absolute_time_diff_us(lastSend, get_absolute_time()) >= delay) {
+ if ( packets_sent == 0) {
+ // first packet is initialization packet
+ send_initial(dev_addr);
+ } else {
+ // remaining packets are color packets
+ send_color(dev_addr);
+ }
+ lastSend = get_absolute_time();
}
}
// send an individual color packett with the desired RGB color
-static void send_color(uint8_t dev_addr, uint8_t red, uint8_t green, uint8_t blue) {
+static void send_color(uint8_t dev_addr) {
memset(buf, 0x00, BUF_SIZE);
buf_idx = 0;
@@ -72,75 +215,58 @@ static void send_color(uint8_t dev_addr, uint8_t red, uint8_t green, uint8_t blu
// check if there are still keys left to send and send updated colors
// for those keys
// each key gets 4 bytes - an init byte (0x81) plus 3 bytes for RGB
- if(color_idx < NUM_KEYS)
- {
- while (color_idx < NUM_KEYS && buf_idx < BUF_SIZE) {
- if (*skip_idx == color_idx + skipped) {
- // keys in skip_idx are not assigned to a key, so send all 0x00
- buf[buf_idx] = 0x00;
- buf[buf_idx + 1] = 0x00;
- buf[buf_idx + 2] = 0x00;
- buf[buf_idx + 3] = 0x00;
-
- skip_idx++;
-
- if(skip_idx >= SKIP_INDICES + sizeof(SKIP_INDICES) / sizeof(unsigned int))
- {
- skip_idx = SKIP_INDICES;
- }
- skipped++;
- } else {
- // start by sending color init byte for the current key
- buf[buf_idx] = 0x81;
- // turn rewind, play, and fast forward keys to white
- if(color_idx==105 || color_idx==108 || color_idx==109) {
- buf[buf_idx + 1] = 0x20;
- buf[buf_idx + 2] = 0x20;
- buf[buf_idx + 3] = 0x20;
- } else if(color_idx==99) {
- // set color of mute button to green or red based on mute
- // toggle - note that this toggle is not synced to the PC's
- // audio state and is presumed to be unmuted when the keyboard
- // first receives power
- if(mute) {
- buf[buf_idx + 1] = 0x40;
- buf[buf_idx + 2] = 0x00;
- buf[buf_idx + 3] = 0x00;
+ while (key_idx < NUM_KEYS && buf_idx < BUF_SIZE) {
+ if (key_list[key_idx].val != color_idx) {
+ // some color_idx are not assigned to a key, so send all 0x00
+ buf[buf_idx] = 0x00;
+ buf[buf_idx + 1] = 0x00;
+ buf[buf_idx + 2] = 0x00;
+ buf[buf_idx + 3] = 0x00;
+ } else {
+ // start by sending color init byte for the current key
+ buf[buf_idx] = 0x81;
+ switch (key_list[key_idx].mode) {
+ case RGB_MODE_ADAPTIVE: // adjust brightness based on LDR ADC reading
+ buf[buf_idx+1] = (ADC_MAX-adc_value)*key_list[key_idx].red/ADC_MAX;
+ buf[buf_idx+2] = (ADC_MAX-adc_value)*key_list[key_idx].green/ADC_MAX;
+ buf[buf_idx+3] = (ADC_MAX-adc_value)*key_list[key_idx].blue/ADC_MAX;
+ break;
+ case RGB_MODE_MUTE:
+ if (mute) {
+ buf[buf_idx+1] = 0xFF; // red
+ buf[buf_idx+2] = 0x00;
+ buf[buf_idx+3] = 0x00;
} else {
- buf[buf_idx + 1] = 0x00;
- buf[buf_idx + 2] = 0x40;
- buf[buf_idx + 3] = 0x00;
+ buf[buf_idx+1] = key_list[key_idx].red;
+ buf[buf_idx+2] = key_list[key_idx].green;
+ buf[buf_idx+3] = key_list[key_idx].blue;
}
- } else {
- // for a normal key, send the desired RGB colors
- buf[buf_idx + 1] = red;
- buf[buf_idx + 2] = green;
- buf[buf_idx + 3] = blue;
- }
- color_idx++;
+ break;
+ default:
+ buf[buf_idx+1] = key_list[key_idx].red;
+ buf[buf_idx+2] = key_list[key_idx].green;
+ buf[buf_idx+3] = key_list[key_idx].blue;
+ break;
}
- buf_idx += 4;
+ key_idx++;
}
+ color_idx++;
+ buf_idx += 4;
+ }
- if(tuh_hid_set_report(dev_addr, 0, 0, HID_REPORT_TYPE_FEATURE, buf, BUF_SIZE))
- {
- // packet sent successfully, increment
- packets_sent++;
- }
- } else {
- if(packets_sent < NUM_PACKETS)
- {
- // all keys have been sent, but the protocol expects NUM_PACKETS
- // packets to be sent in total; if we have not sent enough packets,
- // send extra packets of all 0x00 until done
- if(tuh_hid_set_report(dev_addr, 0, 0, HID_REPORT_TYPE_FEATURE, buf, BUF_SIZE))
- {
- packets_sent++;
- }
- } else {
- // a full round of color packets completed, reset packets to 0
- packets_sent=0;
- }
+ if(tuh_hid_set_report(dev_addr, RGB_ITF, RGB_REPORT_ID, HID_REPORT_TYPE_FEATURE, buf, BUF_SIZE))
+ {
+ // packet sent successfully, increment
+ packets_sent++;
+ delay = 20000;
+ }
+
+ if (key_idx >= NUM_KEYS) {
+ // all keys have been sent, reset and setup next cycle
+ packets_sent = 0;
+ delay = 500000;
+ key_idx = 0;
+ color_idx = 0;
}
}
@@ -149,19 +275,96 @@ static void send_color(uint8_t dev_addr, uint8_t red, uint8_t green, uint8_t blu
static void send_initial(uint8_t dev_addr) {
memset(buf, 0x00, BUF_SIZE);
- color_idx = 0;
- skipped = 0;
- skip_idx = &SKIP_INDICES[0];
-
// send initialization packet
buf[0x00] = 0x04;
buf[0x01] = 0xf2;
- if (tuh_hid_set_report(dev_addr, 0, 0, HID_REPORT_TYPE_FEATURE, buf, BUF_SIZE))
+ if (tuh_hid_set_report(dev_addr, RGB_ITF, RGB_REPORT_ID, HID_REPORT_TYPE_FEATURE, buf, BUF_SIZE))
{
// we have begun sending packets, so indicate first packet was sent
// remaining packets will be sent at regular intervals
packets_sent=1;
+ delay = 20000;
+ }
+}
+
+// find key by name
+static struct key * find_key(char * name) {
+ for (uint8_t i=0; ired = red;
+ set_key->green = green;
+ set_key->blue = blue;
+ if ( set_key->val == KEY_MUTE && mode == RGB_MODE_ADAPTIVE ) {
+ mode = RGB_MODE_MUTE;
+ }
+ set_key->mode = mode;
+ cdc_count = sprintf(cdc_buf, "key: %02X color: (%u,%u,%u) mode: %u\n", set_key->val, set_key->red, set_key->green, set_key->blue, set_key->mode);
+ tud_cdc_write(cdc_buf, cdc_count);
+ }
+}
+
+// parse color request from webpage and update keyboard colors
+void parse_colors(char * data, uint16_t len) {
+ (void) len;
+
+ // Javascript sends the list of keys in a comma delimited format with the
+ // first entry being the color code, so split at commas
+
+ char * token = strtok(data, ",");
+ if (token != NULL) {
+ // first string is the RGB color code
+ uint8_t red, green, blue;
+ // sscanf(token, "%2x%2x%2x", &red, &green, &blue);
+ // sscanf causes Pico 1 to crash
+ red = hexbyte(token);
+ green = hexbyte(token+2);
+ blue = hexbyte(token+4);
+
+ token = strtok(NULL, ",");
+ if (token != NULL) {
+ // second string is mode flag
+ uint8_t mode = RGB_MODE_SOLID;
+ if ( strcmp(token, "1") == 0 ) {
+ mode = RGB_MODE_ADAPTIVE;
+ }
+
+ // extract the keys from the rest of the string and set to the preceding color
+ token = strtok(NULL, ",");
+ while (token != NULL) {
+ set_color(token, red, green, blue, mode);
+ token = strtok(NULL, ",");
+ }
+ }
+ }
+}
+
+// parse send color request and return color of first selected key
+void get_color(char * data, uint16_t len) {
+ (void) len;
+
+ // split at commas and get just the first key
+ char * token = strtok(data, ",");
+ if (token != NULL){
+ struct key * getkey;
+ getkey = find_key(token);
+ if (getkey != NULL) {
+ ws_len=sprintf(ws_buf, "#%02x%02X%02X,%u", getkey->red, getkey->green, getkey->blue, getkey->mode);
+ ws_send_all(ws_buf, ws_len);
+ }
}
}
@@ -181,3 +384,74 @@ bool forward_report(uint8_t instance, uint8_t const* report, uint16_t len) {
return tud_hid_n_report(instance, 0, report, len);
}
+
+// save RGB configuration to flash
+void save_rgb_config(void) {
+ // set save signature and number of bytes to be written into config
+ uint16_t signature = CFG_SIGNATURE;
+ uint16_t key_list_size=sizeof(key_list);
+ uint8_t pages = (key_list_size+sizeof(signature)+sizeof(key_list_size) + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE;
+
+ uint8_t flash_buf[pages*FLASH_PAGE_SIZE];
+ memset(flash_buf, 0x00, pages*FLASH_PAGE_SIZE);
+
+ // stage data for copying to flash
+ memcpy(flash_buf, &signature, sizeof(signature));
+ memcpy(flash_buf+sizeof(signature), &key_list_size, sizeof(key_list_size));
+ memcpy(flash_buf+sizeof(signature)+sizeof(key_list_size), key_list, key_list_size);
+
+ // turn off web server and USB interrupts to allow flash writes
+ uint32_t interrupts = save_and_disable_interrupts();
+
+ // host core must have its TinyUSB interrupts disabled to allow flash writes
+ multicore_lockout_start_blocking();
+
+ // erase sector where save will go in flash
+ flash_range_erase(FLASH_TARGET_OFFSET, FLASH_SECTOR_SIZE);
+
+ // write config to flash
+ flash_range_program(FLASH_TARGET_OFFSET, flash_buf, pages*FLASH_PAGE_SIZE);
+
+ // restore interrupts on both core 1 and core 0
+ multicore_lockout_end_blocking();
+ restore_interrupts(interrupts);
+
+ cdc_count = sprintf(cdc_buf, "Configuration saved to flash (%u:%u)\n", key_list_size, pages);
+ tud_cdc_write(cdc_buf, cdc_count);
+}
+
+// load RGB configuration from flash - return true if valid config, false otherwise
+bool load_rgb_config(void) {
+ uint16_t signature;
+ uint16_t key_list_size;
+ const uint8_t *data = (const uint8_t *) (XIP_BASE+FLASH_TARGET_OFFSET);
+ memcpy(&signature, data, sizeof(signature));
+ memcpy(&key_list_size, data+sizeof(signature), sizeof(key_list_size));
+ if (signature == CFG_SIGNATURE && key_list_size == sizeof(key_list) ) {
+ memcpy(&key_list, data+sizeof(signature)+sizeof(key_list_size), key_list_size);
+ cdc_count = sprintf(cdc_buf, "Configuration loaded from flash %u (%04x)\n", key_list_size, signature);
+ tud_cdc_write(cdc_buf, cdc_count);
+ return true;
+ }
+
+ tud_cdc_write_str("Configuration failed to load\n");
+ return false;
+}
+
+// simple helper to convert hex char to uint8_t value
+static uint8_t hexint (const char c) {
+ if (c >= '0' && c <= '9') {
+ return c - '0';
+ } else if ( c >= 'a' && c <= 'f') {
+ return c - 'a' + 10;
+ } else if (c >= 'A' && c <= 'F') {
+ return c- 'A' + 10;
+ } else {
+ return 0;
+ }
+}
+
+// simple helper to convert 2 hex characters into a byte (uint8_t)
+static uint8_t hexbyte (const char * s) {
+ return (hexint(s[0]) << 4) | (hexint(s[1]));
+}
diff --git a/hyperx_elite2.h b/hyperx_elite2.h
index 755550b..b301b5d 100644
--- a/hyperx_elite2.h
+++ b/hyperx_elite2.h
@@ -3,18 +3,190 @@
#define LDR_PIN 28
#define LDR_ADC 2
-#define LDR_OFF_THRESHOLD 500
-#define LDR_ON_THRESHOLD 400
#define HYPERX_KEYBOARD_VID 0x0951
#define HYPERX_ELITE2_PID 0x1711
-#define NUM_KEYS 128
+#define RGB_ITF 0
+#define RGB_REPORT_ID 0
+#define NUM_KEYS 126
#define BUF_SIZE 64
-#define NUM_PACKETS 10
+#define ADC_MAX 4096
+
+enum {
+ RGG_MODE_INVALID=0,
+ RGB_MODE_SOLID,
+ RGB_MODE_ADAPTIVE,
+ RGB_MODE_MUTE,
+};
+
+struct key {
+ unsigned char name[19];
+ uint8_t val;
+ uint8_t red;
+ uint8_t green;
+ uint8_t blue;
+ uint8_t mode;
+};
void get_light();
void rgb_task(uint8_t dev_addr);
void startADC();
bool forward_report(uint8_t instance, uint8_t const* report, uint16_t len);
+void parse_colors(char * data, uint16_t len);
+void get_color(char * data, uint16_t len);
+void save_rgb_config(void);
+bool load_rgb_config(void);
+
+#define KEY_ESC 0
+#define KEY_GRAVE 1
+#define KEY_TAB 2
+#define KEY_CAPSLOCK 3
+#define KEY_LEFTSHIFT 4
+#define KEY_LEFTCTRL 5
+// Skip index 6 (backslash?)
+#define KEY_1 7
+#define KEY_Q 8
+#define KEY_A 9
+#define KEY_Z 10
+#define KEY_LEFTMETA 11
+#define KEY_F1 12
+#define KEY_2 13
+#define KEY_W 14
+#define KEY_S 15
+#define KEY_X 16
+#define KEY_LEFTALT 17
+#define KEY_F2 18
+#define KEY_3 19
+#define KEY_E 20
+#define KEY_D 21
+#define KEY_C 22
+// Skip index 23
+#define KEY_F3 24
+#define KEY_4 25
+#define KEY_R 26
+#define KEY_F 27
+#define KEY_V 28
+// Skip index 29
+#define KEY_F4 30
+#define KEY_5 31
+#define KEY_T 32
+#define KEY_G 33
+#define KEY_B 34
+#define KEY_SPACE 35
+#define KEY_F5 36
+#define KEY_6 37
+#define KEY_Y 38
+#define KEY_H 39
+#define KEY_N 40
+// Skip index 41
+#define KEY_F6 42
+#define KEY_7 43
+#define KEY_U 44
+#define KEY_J 45
+#define KEY_M 46
+// Skip index 47
+#define KEY_F7 48
+#define KEY_8 49
+#define KEY_I 50
+#define KEY_K 51
+#define KEY_COMMA 52
+#define KEY_RIGHTALT 53
+#define KEY_F8 54
+#define KEY_9 55
+#define KEY_O 56
+#define KEY_L 57
+#define KEY_DOT 58
+// Skip index 59
+#define KEY_F9 60
+#define KEY_0 61
+#define KEY_P 62
+#define KEY_SEMICOLON 63
+#define KEY_SLASH 64
+#define KEY_RIGHTMETA 65
+#define KEY_F10 66
+#define KEY_MINUS 67
+#define KEY_LEFTBRACE 68
+#define KEY_APOSTROPHE 69
+// Skip index 70
+// Skip index 71
+#define KEY_F11 72
+#define KEY_EQUAL 73
+#define KEY_RIGHTBRACE 74
+// Skip index 75 (maybe pound?)
+// Skip index 76
+#define KEY_MENU 77
+#define KEY_F12 78
+#define KEY_BACKSPACE 79
+#define KEY_BACKSLASH 80
+#define KEY_ENTER 81
+#define KEY_RIGHTSHIFT 82
+#define KEY_RIGHTCTRL 83
+#define KEY_SYSRQ 84
+#define KEY_INSERT 85
+#define KEY_DELETE 86
+// Skip index 87
+// Skip index 88
+#define KEY_LEFT 89
+#define KEY_SCROLLLOCK 90
+#define KEY_HOME 91
+#define KEY_END 92
+// Skip index 93
+#define KEY_UP 94
+#define KEY_DOWN 95
+#define KEY_PAUSE 96
+#define KEY_PAGEUP 97
+#define KEY_PAGEDOWN 98
+// Skip index 99
+// Skip index 100
+#define KEY_RIGHT 101
+// Skip index 102
+#define KEY_NUMLOCK 103
+#define KEY_KP7 104
+#define KEY_KP4 105
+#define KEY_KP1 106
+#define KEY_KP0 107
+// Skip index 108
+#define KEY_KPSLASH 109
+#define KEY_KP8 110
+#define KEY_KP5 111
+#define KEY_KP2 112
+// Skip index 113
+#define KEY_MUTE 114
+#define KEY_KPASTERISK 115
+#define KEY_KP9 116
+#define KEY_KP6 117
+#define KEY_KP3 118
+#define KEY_KPDOT 119
+#define KEY_MEDIA_PREVIOUSSONG 120
+#define KEY_KPMINUS 121
+#define KEY_KPPLUS 122
+#define KEY_MEDIA_PLAYPAUSE 123
+#define KEY_MEDIA_NEXTSONG 124
+#define KEY_KPENTER 125
+#define LED_BAR1 126
+#define LED_BAR2 127
+#define LED_BAR3 128
+#define LED_BAR4 129
+#define LED_BAR5 130
+#define LED_BAR6 131
+#define LED_BAR7 132
+#define LED_BAR8 133
+#define LED_BAR9 134
+#define LED_BAR10 135
+#define LED_BAR11 136
+#define LED_BAR12 137
+#define LED_BAR13 138
+#define LED_BAR14 139
+#define LED_BAR15 140
+#define LED_BAR16 141
+#define LED_BAR17 142
+#define LED_BAR18 143
+
+#define INIT_KEY(name, code) {name, code, 0x80, 0x80, 0x80, RGB_MODE_ADAPTIVE}
+#define INIT_KEY_MUTE(name, code) {name, code, 0x80, 0x80, 0x80, RGB_MODE_MUTE}
+
+#define CFG_SIGNATURE 0x9e4c
+
+#define FLASH_TARGET_OFFSET (PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE)
#endif
diff --git a/lightonoff.gif b/lightonoff.gif
deleted file mode 100644
index 692b178..0000000
Binary files a/lightonoff.gif and /dev/null differ
diff --git a/lwipopts.h b/lwipopts.h
new file mode 100644
index 0000000..b068d05
--- /dev/null
+++ b/lwipopts.h
@@ -0,0 +1,68 @@
+#ifndef __LWIPOPTS_H__
+#define __LWIPOPTS_H__
+
+#define NO_SYS 1
+#define MEM_ALIGNMENT 4
+#define LWIP_RAW 0
+#define LWIP_NETCONN 0
+#define LWIP_SOCKET 0
+#define LWIP_DHCP 0
+#define LWIP_ICMP 1
+#define LWIP_UDP 1
+#define LWIP_TCP 1
+#define LWIP_IPV4 1
+#define LWIP_IPV6 0
+#define ETH_PAD_SIZE 0
+#define LWIP_IP_ACCEPT_UDP_PORT(p) ((p) == PP_NTOHS(67))
+
+#define TCP_MSS (1500 /*mtu*/ - 20 /*iphdr*/ - 20 /*tcphhr*/)
+#define TCP_SND_BUF (4 * TCP_MSS)
+#define TCP_WND (4 * TCP_MSS)
+
+#define ETHARP_SUPPORT_STATIC_ENTRIES 1
+
+#define LWIP_SINGLE_NETIF 1
+#define LWIP_NETIF_LINK_CALLBACK 1
+
+#define PBUF_POOL_SIZE 4
+
+#define ETHARP_DEBUG LWIP_DBG_OFF
+#define NETIF_DEBUG LWIP_DBG_OFF
+#define PBUF_DEBUG LWIP_DBG_OFF
+#define API_LIB_DEBUG LWIP_DBG_OFF
+#define API_MSG_DEBUG LWIP_DBG_OFF
+#define SOCKETS_DEBUG LWIP_DBG_OFF
+#define ICMP_DEBUG LWIP_DBG_OFF
+#define INET_DEBUG LWIP_DBG_OFF
+#define IP_DEBUG LWIP_DBG_OFF
+#define IP_REASS_DEBUG LWIP_DBG_OFF
+#define RAW_DEBUG LWIP_DBG_OFF
+#define MEM_DEBUG LWIP_DBG_OFF
+#define MEMP_DEBUG LWIP_DBG_OFF
+#define SYS_DEBUG LWIP_DBG_OFF
+#define TCP_DEBUG LWIP_DBG_OFF
+#define TCP_INPUT_DEBUG LWIP_DBG_OFF
+#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF
+#define TCP_RTO_DEBUG LWIP_DBG_OFF
+#define TCP_CWND_DEBUG LWIP_DBG_OFF
+#define TCP_WND_DEBUG LWIP_DBG_OFF
+#define TCP_FR_DEBUG LWIP_DBG_OFF
+#define TCP_QLEN_DEBUG LWIP_DBG_OFF
+#define TCP_RST_DEBUG LWIP_DBG_OFF
+#define UDP_DEBUG LWIP_DBG_OFF
+#define TCPIP_DEBUG LWIP_DBG_OFF
+#define PPP_DEBUG LWIP_DBG_OFF
+#define SLIP_DEBUG LWIP_DBG_OFF
+#define DHCP_DEBUG LWIP_DBG_OFF
+
+
+// HTTPD stuff
+#define LWIP_HTTPD 1
+#define LWIP_HTTPD_CGI 0
+#define LWIP_HTTPD_SSI 0
+#define LWIP_HTTPD_SSI_INCLUDE_TAG 0
+#define LWIP_HTTPD_SUPPORT_POST 0
+#define HTTPD_FSDATA_FILE "my_fsdata.c"
+
+
+#endif /* __LWIPOPTS_H__ */
diff --git a/mbedtls_config.h b/mbedtls_config.h
new file mode 100644
index 0000000..0e766d6
--- /dev/null
+++ b/mbedtls_config.h
@@ -0,0 +1,75 @@
+#ifndef MBEDTLS_CONFIG_H
+#define MBEDTLS_CONFIG_H
+
+/* Workaround for some mbedtls source files using INT_MAX without including limits.h */
+#include
+
+#define MBEDTLS_NO_PLATFORM_ENTROPY
+#define MBEDTLS_ENTROPY_HARDWARE_ALT
+
+#define MBEDTLS_SSL_OUT_CONTENT_LEN 2048
+
+#define MBEDTLS_ALLOW_PRIVATE_ACCESS
+#define MBEDTLS_HAVE_TIME
+#define MBEDTLS_PLATFORM_MS_TIME_ALT
+
+#define MBEDTLS_CIPHER_MODE_CBC
+#define MBEDTLS_ECP_DP_SECP192R1_ENABLED
+#define MBEDTLS_ECP_DP_SECP224R1_ENABLED
+#define MBEDTLS_ECP_DP_SECP256R1_ENABLED
+#define MBEDTLS_ECP_DP_SECP384R1_ENABLED
+#define MBEDTLS_ECP_DP_SECP521R1_ENABLED
+#define MBEDTLS_ECP_DP_SECP192K1_ENABLED
+#define MBEDTLS_ECP_DP_SECP224K1_ENABLED
+#define MBEDTLS_ECP_DP_SECP256K1_ENABLED
+#define MBEDTLS_ECP_DP_BP256R1_ENABLED
+#define MBEDTLS_ECP_DP_BP384R1_ENABLED
+#define MBEDTLS_ECP_DP_BP512R1_ENABLED
+#define MBEDTLS_ECP_DP_CURVE25519_ENABLED
+#define MBEDTLS_KEY_EXCHANGE_RSA_ENABLED
+#define MBEDTLS_PKCS1_V15
+#define MBEDTLS_SHA256_SMALLER
+#define MBEDTLS_SSL_SERVER_NAME_INDICATION
+#define MBEDTLS_AES_C
+#define MBEDTLS_ASN1_PARSE_C
+#define MBEDTLS_BIGNUM_C
+#define MBEDTLS_CIPHER_C
+#define MBEDTLS_CTR_DRBG_C
+#define MBEDTLS_ENTROPY_C
+#define MBEDTLS_ERROR_C
+#define MBEDTLS_MD_C
+#define MBEDTLS_MD5_C
+#define MBEDTLS_OID_C
+#define MBEDTLS_PKCS5_C
+#define MBEDTLS_PK_C
+#define MBEDTLS_PK_PARSE_C
+#define MBEDTLS_PLATFORM_C
+#define MBEDTLS_RSA_C
+#define MBEDTLS_SHA1_C
+#define MBEDTLS_SHA224_C
+#define MBEDTLS_SHA256_C
+#define MBEDTLS_SHA512_C
+#define MBEDTLS_SSL_CLI_C
+#define MBEDTLS_SSL_SRV_C
+#define MBEDTLS_SSL_TLS_C
+#define MBEDTLS_X509_CRT_PARSE_C
+#define MBEDTLS_X509_USE_C
+#define MBEDTLS_AES_FEWER_TABLES
+
+/* TLS 1.2 */
+#define MBEDTLS_SSL_PROTO_TLS1_2
+#define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
+#define MBEDTLS_GCM_C
+#define MBEDTLS_ECDH_C
+#define MBEDTLS_ECP_C
+#define MBEDTLS_ECDSA_C
+#define MBEDTLS_ASN1_WRITE_C
+
+// The following is needed to parse a certificate
+#define MBEDTLS_PEM_PARSE_C
+#define MBEDTLS_BASE64_C
+
+// The following significantly speeds up mbedtls due to NIST optimizations.
+#define MBEDTLS_ECP_NIST_OPTIM
+
+#endif
diff --git a/my_fsdata.c b/my_fsdata.c
new file mode 100644
index 0000000..b43e45a
--- /dev/null
+++ b/my_fsdata.c
@@ -0,0 +1,1181 @@
+static const unsigned char data_fsdata_c[] = {
+ /* /fsdata.c */
+ 0x2f, 0x66, 0x73, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x63, 0,
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x30, 0x20, 0x32,
+ 0x30, 0x30, 0x20, 0x4f, 0x4b, 0xd, 0xa, 0x53, 0x65, 0x72,
+ 0x76, 0x65, 0x72, 0x3a, 0x20, 0x6c, 0x77, 0x49, 0x50, 0x2f,
+ 0x70, 0x72, 0x65, 0x2d, 0x30, 0x2e, 0x36, 0x20, 0x28, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
+ 0x73, 0x69, 0x63, 0x73, 0x2e, 0x73, 0x65, 0x2f, 0x7e, 0x61,
+ 0x64, 0x61, 0x6d, 0x2f, 0x6c, 0x77, 0x69, 0x70, 0x2f, 0x29,
+ 0xd, 0xa, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d,
+ 0x74, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x74, 0x65, 0x78, 0x74,
+ 0x2f, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0xd, 0xa, 0xd, 0xa,
+};
+
+static const unsigned char data_index_html[] = {
+ /* /index.html */
+ 0x2f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0,
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x30, 0x20, 0x32,
+ 0x30, 0x30, 0x20, 0x4f, 0x4b, 0xd, 0xa, 0x53, 0x65, 0x72,
+ 0x76, 0x65, 0x72, 0x3a, 0x20, 0x6c, 0x77, 0x49, 0x50, 0x2f,
+ 0x70, 0x72, 0x65, 0x2d, 0x30, 0x2e, 0x36, 0x20, 0x28, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
+ 0x73, 0x69, 0x63, 0x73, 0x2e, 0x73, 0x65, 0x2f, 0x7e, 0x61,
+ 0x64, 0x61, 0x6d, 0x2f, 0x6c, 0x77, 0x69, 0x70, 0x2f, 0x29,
+ 0xd, 0xa, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d,
+ 0x74, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x74, 0x65, 0x78, 0x74,
+ 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0xd, 0xa, 0xd, 0xa, 0x3c,
+ 0x21, 0x44, 0x4f, 0x43, 0x54, 0x59, 0x50, 0x45, 0x20, 0x68,
+ 0x74, 0x6d, 0x6c, 0x3e, 0xa, 0xa, 0x3c, 0x68, 0x74, 0x6d,
+ 0x6c, 0x3e, 0xa, 0xa, 0x3c, 0x68, 0x65, 0x61, 0x64, 0x3e,
+ 0xa, 0x3c, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x3e, 0x48, 0x79,
+ 0x70, 0x65, 0x72, 0x58, 0x20, 0x41, 0x6c, 0x6c, 0x6f, 0x79,
+ 0x20, 0x45, 0x6c, 0x69, 0x74, 0x65, 0x20, 0x32, 0x20, 0x52,
+ 0x47, 0x42, 0x20, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75,
+ 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3c, 0x2f, 0x74, 0x69,
+ 0x74, 0x6c, 0x65, 0x3e, 0xa, 0xa, 0x3c, 0x73, 0x74, 0x79,
+ 0x6c, 0x65, 0x3e, 0xa, 0xa, 0x64, 0x69, 0x76, 0x2e, 0x63,
+ 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x20, 0x3e,
+ 0x20, 0x64, 0x69, 0x76, 0x20, 0x7b, 0xa, 0x9, 0x64, 0x69,
+ 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x20, 0x62, 0x6c, 0x6f,
+ 0x63, 0x6b, 0x3b, 0xa, 0x7d, 0xa, 0xa, 0x64, 0x69, 0x76,
+ 0x2e, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x20, 0x7b, 0xa,
+ 0x9, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x34, 0x30,
+ 0x65, 0x6d, 0x3b, 0xa, 0x9, 0x77, 0x6f, 0x72, 0x64, 0x2d,
+ 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3a, 0x20, 0x62, 0x72, 0x65,
+ 0x61, 0x6b, 0x2d, 0x61, 0x6c, 0x6c, 0x3b, 0xa, 0x7d, 0xa,
+ 0xa, 0x64, 0x69, 0x76, 0x23, 0x63, 0x6f, 0x6e, 0x66, 0x69,
+ 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x7b,
+ 0xa, 0x9, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a,
+ 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2d, 0x62, 0x6c,
+ 0x6f, 0x63, 0x6b, 0x3b, 0xa, 0x9, 0x74, 0x65, 0x78, 0x74,
+ 0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20, 0x72, 0x69,
+ 0x67, 0x68, 0x74, 0x3b, 0xa, 0x7d, 0xa, 0xa, 0x64, 0x69,
+ 0x76, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x20, 0x7b,
+ 0xa, 0x9, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a,
+ 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2d, 0x62, 0x6c,
+ 0x6f, 0x63, 0x6b, 0x3b, 0xa, 0x9, 0x76, 0x65, 0x72, 0x74,
+ 0x69, 0x63, 0x61, 0x6c, 0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e,
+ 0x3a, 0x20, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x3b, 0xa,
+ 0x7d, 0xa, 0xa, 0x64, 0x69, 0x76, 0x23, 0x6b, 0x65, 0x79,
+ 0x62, 0x6f, 0x61, 0x72, 0x64, 0x20, 0x7b, 0xa, 0x9, 0x64,
+ 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x20, 0x69, 0x6e,
+ 0x6c, 0x69, 0x6e, 0x65, 0x2d, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
+ 0x3b, 0xa, 0x9, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f,
+ 0x75, 0x6e, 0x64, 0x2d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a,
+ 0x20, 0x23, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3b, 0xa,
+ 0x7d, 0xa, 0xa, 0x64, 0x69, 0x76, 0x23, 0x6b, 0x65, 0x79,
+ 0x73, 0x20, 0x7b, 0xa, 0x9, 0x64, 0x69, 0x73, 0x70, 0x6c,
+ 0x61, 0x79, 0x3a, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65,
+ 0x2d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0xa, 0x7d, 0xa,
+ 0xa, 0x64, 0x69, 0x76, 0x2e, 0x6c, 0x61, 0x62, 0x65, 0x6c,
+ 0x20, 0x7b, 0xa, 0x9, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a,
+ 0x20, 0x31, 0x30, 0x65, 0x6d, 0x3b, 0xa, 0x9, 0x74, 0x65,
+ 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20,
+ 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0xa, 0x9, 0x74,
+ 0x65, 0x78, 0x74, 0x2d, 0x64, 0x65, 0x63, 0x6f, 0x72, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x75, 0x6e, 0x64, 0x65,
+ 0x72, 0x6c, 0x69, 0x6e, 0x65, 0x3b, 0xa, 0x9, 0x66, 0x6f,
+ 0x6e, 0x74, 0x2d, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a,
+ 0x20, 0x62, 0x6f, 0x6c, 0x64, 0x3b, 0xa, 0x7d, 0xa, 0xa,
+ 0x64, 0x69, 0x76, 0x2e, 0x6b, 0x65, 0x79, 0x20, 0x7b, 0xa,
+ 0x9, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67,
+ 0x6e, 0x3a, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0xa,
+ 0x9, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x2d,
+ 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20, 0x74, 0x6f, 0x70,
+ 0x3b, 0xa, 0x9, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79,
+ 0x3a, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2d, 0x62,
+ 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0xa, 0x9, 0x62, 0x61, 0x63,
+ 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x63, 0x6f,
+ 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x64, 0x64, 0x64, 0x64,
+ 0x64, 0x64, 0x3b, 0xa, 0x9, 0x6d, 0x61, 0x72, 0x67, 0x69,
+ 0x6e, 0x3a, 0x20, 0x31, 0x70, 0x78, 0x3b, 0xa, 0x9, 0x77,
+ 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x32, 0x2e, 0x35, 0x65,
+ 0x6d, 0x3b, 0xa, 0x9, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74,
+ 0x3a, 0x20, 0x33, 0x65, 0x6d, 0x3b, 0xa, 0x9, 0x66, 0x6f,
+ 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, 0x30,
+ 0x2e, 0x38, 0x65, 0x6d, 0x3b, 0xa, 0x9, 0x62, 0x6f, 0x72,
+ 0x64, 0x65, 0x72, 0x2d, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3a,
+ 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x3b, 0xa, 0x9,
+ 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x6c,
+ 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x64, 0x64, 0x64, 0x64, 0x64,
+ 0x64, 0xa, 0x7d, 0xa, 0xa, 0x64, 0x69, 0x76, 0x2e, 0x73,
+ 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x20, 0x7b, 0xa,
+ 0x9, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x73, 0x74,
+ 0x79, 0x6c, 0x65, 0x3a, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c,
+ 0x65, 0x3b, 0xa, 0x9, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72,
+ 0x2d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x62, 0x6c,
+ 0x75, 0x65, 0x3b, 0xa, 0x9, 0x62, 0x6f, 0x72, 0x64, 0x65,
+ 0x72, 0x2d, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x32,
+ 0x70, 0x78, 0x3b, 0xa, 0x7d, 0xa, 0xa, 0x64, 0x69, 0x76,
+ 0x2e, 0x62, 0x6c, 0x61, 0x6e, 0x6b, 0x20, 0x7b, 0xa, 0x9,
+ 0x6f, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x3a, 0x20, 0x30,
+ 0x3b, 0xa, 0x7d, 0xa, 0xa, 0x69, 0x6e, 0x70, 0x75, 0x74,
+ 0x2e, 0x62, 0x79, 0x74, 0x65, 0x20, 0x7b, 0xa, 0x9, 0x77,
+ 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x32, 0x65, 0x6d, 0x3b,
+ 0xa, 0x7d, 0xa, 0xa, 0x3c, 0x2f, 0x73, 0x74, 0x79, 0x6c,
+ 0x65, 0x3e, 0xa, 0xa, 0x3c, 0x73, 0x63, 0x72, 0x69, 0x70,
+ 0x74, 0x3e, 0xa, 0x76, 0x61, 0x72, 0x20, 0x73, 0x65, 0x6c,
+ 0x65, 0x63, 0x74, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x5b, 0x5d,
+ 0x3b, 0xa, 0xa, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x6b,
+ 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x5f, 0x6c, 0x69,
+ 0x73, 0x74, 0x20, 0x3d, 0x20, 0x5b, 0xa, 0x9, 0x5b, 0xa,
+ 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x22, 0x2c,
+ 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x22,
+ 0x2c, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x31,
+ 0x39, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a,
+ 0x20, 0x22, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x54, 0x72, 0x61,
+ 0x63, 0x6b, 0x50, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73,
+ 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20,
+ 0x22, 0x52, 0x65, 0x2d, 0x3c, 0x62, 0x72, 0x3e, 0x77, 0x69,
+ 0x6e, 0x64, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69,
+ 0x64, 0x3a, 0x20, 0x22, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x50,
+ 0x6c, 0x61, 0x79, 0x50, 0x61, 0x75, 0x73, 0x65, 0x22, 0x2c,
+ 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x50,
+ 0x6c, 0x61, 0x79, 0x3c, 0x62, 0x72, 0x3e, 0x50, 0x61, 0x75,
+ 0x73, 0x65, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69,
+ 0x64, 0x3a, 0x20, 0x22, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x54,
+ 0x72, 0x61, 0x63, 0x6b, 0x4e, 0x65, 0x78, 0x74, 0x22, 0x2c,
+ 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x46,
+ 0x61, 0x73, 0x74, 0x3c, 0x62, 0x72, 0x3e, 0x46, 0x6f, 0x72,
+ 0x77, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64,
+ 0x3a, 0x20, 0x22, 0x4d, 0x75, 0x74, 0x65, 0x22, 0x2c, 0x20,
+ 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x4d, 0x75,
+ 0x74, 0x65, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x5d, 0x2c, 0xa,
+ 0x9, 0x5b, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20,
+ 0x22, 0x42, 0x61, 0x72, 0x31, 0x22, 0x2c, 0x20, 0x6c, 0x61,
+ 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x22, 0x2c, 0x20, 0x77,
+ 0x69, 0x64, 0x74, 0x68, 0x3a, 0x31, 0x2e, 0x32, 0x35, 0x2c,
+ 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x30,
+ 0x2e, 0x35, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64,
+ 0x3a, 0x20, 0x22, 0x42, 0x61, 0x72, 0x32, 0x22, 0x2c, 0x20,
+ 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x22, 0x2c,
+ 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x31, 0x2e, 0x32,
+ 0x35, 0x2c, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a,
+ 0x20, 0x30, 0x2e, 0x35, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b,
+ 0x69, 0x64, 0x3a, 0x20, 0x22, 0x42, 0x61, 0x72, 0x33, 0x22,
+ 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22,
+ 0x22, 0x2c, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x31,
+ 0x2e, 0x32, 0x35, 0x2c, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68,
+ 0x74, 0x3a, 0x20, 0x30, 0x2e, 0x35, 0x7d, 0x2c, 0xa, 0x9,
+ 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x42, 0x61, 0x72,
+ 0x34, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a,
+ 0x20, 0x22, 0x22, 0x2c, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68,
+ 0x3a, 0x31, 0x2e, 0x32, 0x35, 0x2c, 0x20, 0x68, 0x65, 0x69,
+ 0x67, 0x68, 0x74, 0x3a, 0x20, 0x30, 0x2e, 0x35, 0x7d, 0x2c,
+ 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x42,
+ 0x61, 0x72, 0x35, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65,
+ 0x6c, 0x3a, 0x20, 0x22, 0x22, 0x2c, 0x20, 0x77, 0x69, 0x64,
+ 0x74, 0x68, 0x3a, 0x31, 0x2e, 0x32, 0x35, 0x2c, 0x20, 0x68,
+ 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x30, 0x2e, 0x35,
+ 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20,
+ 0x22, 0x42, 0x61, 0x72, 0x36, 0x22, 0x2c, 0x20, 0x6c, 0x61,
+ 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x22, 0x2c, 0x20, 0x77,
+ 0x69, 0x64, 0x74, 0x68, 0x3a, 0x31, 0x2e, 0x32, 0x35, 0x2c,
+ 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x30,
+ 0x2e, 0x35, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64,
+ 0x3a, 0x20, 0x22, 0x42, 0x61, 0x72, 0x37, 0x22, 0x2c, 0x20,
+ 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x22, 0x2c,
+ 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x31, 0x2e, 0x32,
+ 0x35, 0x2c, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a,
+ 0x20, 0x30, 0x2e, 0x35, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b,
+ 0x69, 0x64, 0x3a, 0x20, 0x22, 0x42, 0x61, 0x72, 0x38, 0x22,
+ 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22,
+ 0x22, 0x2c, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x31,
+ 0x2e, 0x32, 0x35, 0x2c, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68,
+ 0x74, 0x3a, 0x20, 0x30, 0x2e, 0x35, 0x7d, 0x2c, 0xa, 0x9,
+ 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x42, 0x61, 0x72,
+ 0x39, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a,
+ 0x20, 0x22, 0x22, 0x2c, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68,
+ 0x3a, 0x31, 0x2e, 0x32, 0x35, 0x2c, 0x20, 0x68, 0x65, 0x69,
+ 0x67, 0x68, 0x74, 0x3a, 0x20, 0x30, 0x2e, 0x35, 0x7d, 0x2c,
+ 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x42,
+ 0x61, 0x72, 0x31, 0x30, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62,
+ 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x22, 0x2c, 0x20, 0x77, 0x69,
+ 0x64, 0x74, 0x68, 0x3a, 0x31, 0x2e, 0x32, 0x35, 0x2c, 0x20,
+ 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x30, 0x2e,
+ 0x35, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a,
+ 0x20, 0x22, 0x42, 0x61, 0x72, 0x31, 0x31, 0x22, 0x2c, 0x20,
+ 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x22, 0x2c,
+ 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x31, 0x2e, 0x32,
+ 0x35, 0x2c, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a,
+ 0x20, 0x30, 0x2e, 0x35, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b,
+ 0x69, 0x64, 0x3a, 0x20, 0x22, 0x42, 0x61, 0x72, 0x31, 0x32,
+ 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20,
+ 0x22, 0x22, 0x2c, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a,
+ 0x31, 0x2e, 0x32, 0x35, 0x2c, 0x20, 0x68, 0x65, 0x69, 0x67,
+ 0x68, 0x74, 0x3a, 0x20, 0x30, 0x2e, 0x35, 0x7d, 0x2c, 0xa,
+ 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x42, 0x61,
+ 0x72, 0x31, 0x33, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65,
+ 0x6c, 0x3a, 0x20, 0x22, 0x22, 0x2c, 0x20, 0x77, 0x69, 0x64,
+ 0x74, 0x68, 0x3a, 0x31, 0x2e, 0x32, 0x35, 0x2c, 0x20, 0x68,
+ 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x30, 0x2e, 0x35,
+ 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20,
+ 0x22, 0x42, 0x61, 0x72, 0x31, 0x34, 0x22, 0x2c, 0x20, 0x6c,
+ 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x22, 0x2c, 0x20,
+ 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x31, 0x2e, 0x32, 0x35,
+ 0x2c, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20,
+ 0x30, 0x2e, 0x35, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69,
+ 0x64, 0x3a, 0x20, 0x22, 0x42, 0x61, 0x72, 0x31, 0x35, 0x22,
+ 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22,
+ 0x22, 0x2c, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x31,
+ 0x2e, 0x32, 0x35, 0x2c, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68,
+ 0x74, 0x3a, 0x20, 0x30, 0x2e, 0x35, 0x7d, 0x2c, 0xa, 0x9,
+ 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x42, 0x61, 0x72,
+ 0x31, 0x36, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c,
+ 0x3a, 0x20, 0x22, 0x22, 0x2c, 0x20, 0x77, 0x69, 0x64, 0x74,
+ 0x68, 0x3a, 0x31, 0x2e, 0x32, 0x35, 0x2c, 0x20, 0x68, 0x65,
+ 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x30, 0x2e, 0x35, 0x7d,
+ 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22,
+ 0x42, 0x61, 0x72, 0x31, 0x37, 0x22, 0x2c, 0x20, 0x6c, 0x61,
+ 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x22, 0x2c, 0x20, 0x77,
+ 0x69, 0x64, 0x74, 0x68, 0x3a, 0x31, 0x2e, 0x32, 0x35, 0x2c,
+ 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x30,
+ 0x2e, 0x35, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64,
+ 0x3a, 0x20, 0x22, 0x42, 0x61, 0x72, 0x31, 0x38, 0x22, 0x2c,
+ 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x22,
+ 0x2c, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x31, 0x2e,
+ 0x32, 0x35, 0x2c, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74,
+ 0x3a, 0x20, 0x30, 0x2e, 0x35, 0x7d, 0x2c, 0xa, 0x9, 0x5d,
+ 0x2c, 0xa, 0x9, 0x5b, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64,
+ 0x3a, 0x20, 0x22, 0x45, 0x73, 0x63, 0x61, 0x70, 0x65, 0x22,
+ 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22,
+ 0x45, 0x53, 0x43, 0x22, 0x2c, 0x20, 0x77, 0x69, 0x64, 0x74,
+ 0x68, 0x3a, 0x20, 0x32, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b,
+ 0x69, 0x64, 0x3a, 0x20, 0x22, 0x22, 0x2c, 0x20, 0x6c, 0x61,
+ 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x22, 0x7d, 0x2c, 0xa,
+ 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x46, 0x31,
+ 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20,
+ 0x22, 0x46, 0x31, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b,
+ 0x69, 0x64, 0x3a, 0x20, 0x22, 0x46, 0x32, 0x22, 0x2c, 0x20,
+ 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x46, 0x32,
+ 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a,
+ 0x20, 0x22, 0x46, 0x33, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62,
+ 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x46, 0x33, 0x22, 0x7d, 0x2c,
+ 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x46,
+ 0x34, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a,
+ 0x20, 0x22, 0x46, 0x34, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9,
+ 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x46, 0x35, 0x22, 0x2c,
+ 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x46,
+ 0x35, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64,
+ 0x3a, 0x20, 0x22, 0x46, 0x36, 0x22, 0x2c, 0x20, 0x6c, 0x61,
+ 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x46, 0x36, 0x22, 0x7d,
+ 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22,
+ 0x46, 0x37, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c,
+ 0x3a, 0x20, 0x22, 0x46, 0x37, 0x22, 0x7d, 0x2c, 0xa, 0x9,
+ 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x46, 0x38, 0x22,
+ 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22,
+ 0x46, 0x38, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69,
+ 0x64, 0x3a, 0x20, 0x22, 0x46, 0x39, 0x22, 0x2c, 0x20, 0x6c,
+ 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x46, 0x39, 0x22,
+ 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20,
+ 0x22, 0x46, 0x31, 0x30, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62,
+ 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x46, 0x31, 0x30, 0x22, 0x7d,
+ 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22,
+ 0x46, 0x31, 0x31, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65,
+ 0x6c, 0x3a, 0x20, 0x22, 0x46, 0x31, 0x31, 0x22, 0x7d, 0x2c,
+ 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x46,
+ 0x31, 0x32, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c,
+ 0x3a, 0x20, 0x22, 0x46, 0x31, 0x32, 0x22, 0x7d, 0x2c, 0xa,
+ 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x50, 0x72,
+ 0x69, 0x6e, 0x74, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x22,
+ 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22,
+ 0x50, 0x72, 0x69, 0x6e, 0x74, 0x3c, 0x62, 0x72, 0x3e, 0x53,
+ 0x63, 0x72, 0x65, 0x65, 0x6e, 0x22, 0x7d, 0x2c, 0xa, 0x9,
+ 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x53, 0x63, 0x72,
+ 0x6f, 0x6c, 0x6c, 0x4c, 0x6f, 0x63, 0x6b, 0x22, 0x2c, 0x20,
+ 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x53, 0x63,
+ 0x72, 0x6f, 0x6c, 0x6c, 0x3c, 0x62, 0x72, 0x3e, 0x4c, 0x6f,
+ 0x63, 0x6b, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69,
+ 0x64, 0x3a, 0x20, 0x22, 0x50, 0x61, 0x75, 0x73, 0x65, 0x22,
+ 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22,
+ 0x50, 0x61, 0x75, 0x73, 0x65, 0x22, 0x7d, 0x2c, 0xa, 0x9,
+ 0x5d, 0x2c, 0xa, 0x9, 0x5b, 0xa, 0x9, 0x9, 0x7b, 0x69,
+ 0x64, 0x3a, 0x20, 0x22, 0x42, 0x61, 0x63, 0x6b, 0x71, 0x75,
+ 0x6f, 0x74, 0x65, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65,
+ 0x6c, 0x3a, 0x20, 0x22, 0x7e, 0x3c, 0x62, 0x72, 0x3e, 0x60,
+ 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a,
+ 0x20, 0x22, 0x44, 0x69, 0x67, 0x69, 0x74, 0x31, 0x22, 0x2c,
+ 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x21,
+ 0x3c, 0x62, 0x72, 0x3e, 0x31, 0x22, 0x7d, 0x2c, 0xa, 0x9,
+ 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x44, 0x69, 0x67,
+ 0x69, 0x74, 0x32, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65,
+ 0x6c, 0x3a, 0x20, 0x22, 0x40, 0x3c, 0x62, 0x72, 0x3e, 0x32,
+ 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a,
+ 0x20, 0x22, 0x44, 0x69, 0x67, 0x69, 0x74, 0x33, 0x22, 0x2c,
+ 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x23,
+ 0x3c, 0x62, 0x72, 0x3e, 0x33, 0x22, 0x7d, 0x2c, 0xa, 0x9,
+ 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x44, 0x69, 0x67,
+ 0x69, 0x74, 0x34, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65,
+ 0x6c, 0x3a, 0x20, 0x22, 0x24, 0x3c, 0x62, 0x72, 0x3e, 0x34,
+ 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a,
+ 0x20, 0x22, 0x44, 0x69, 0x67, 0x69, 0x74, 0x35, 0x22, 0x2c,
+ 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x25,
+ 0x3c, 0x62, 0x72, 0x3e, 0x35, 0x22, 0x7d, 0x2c, 0xa, 0x9,
+ 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x44, 0x69, 0x67,
+ 0x69, 0x74, 0x36, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65,
+ 0x6c, 0x3a, 0x20, 0x22, 0x5e, 0x3c, 0x62, 0x72, 0x3e, 0x36,
+ 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a,
+ 0x20, 0x22, 0x44, 0x69, 0x67, 0x69, 0x74, 0x37, 0x22, 0x2c,
+ 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x26,
+ 0x3c, 0x62, 0x72, 0x3e, 0x37, 0x22, 0x7d, 0x2c, 0xa, 0x9,
+ 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x44, 0x69, 0x67,
+ 0x69, 0x74, 0x38, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65,
+ 0x6c, 0x3a, 0x20, 0x22, 0x2a, 0x3c, 0x62, 0x72, 0x3e, 0x38,
+ 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a,
+ 0x20, 0x22, 0x44, 0x69, 0x67, 0x69, 0x74, 0x39, 0x22, 0x2c,
+ 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x28,
+ 0x3c, 0x62, 0x72, 0x3e, 0x39, 0x22, 0x7d, 0x2c, 0xa, 0x9,
+ 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x44, 0x69, 0x67,
+ 0x69, 0x74, 0x30, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65,
+ 0x6c, 0x3a, 0x20, 0x22, 0x29, 0x3c, 0x62, 0x72, 0x3e, 0x30,
+ 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a,
+ 0x20, 0x22, 0x4d, 0x69, 0x6e, 0x75, 0x73, 0x22, 0x2c, 0x20,
+ 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x5f, 0x3c,
+ 0x62, 0x72, 0x3e, 0x2d, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9,
+ 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x45, 0x71, 0x75, 0x61,
+ 0x6c, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a,
+ 0x20, 0x22, 0x2b, 0x3c, 0x62, 0x72, 0x3e, 0x3d, 0x22, 0x7d,
+ 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22,
+ 0x42, 0x61, 0x63, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22,
+ 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22,
+ 0x42, 0x61, 0x63, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22,
+ 0x2c, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x32,
+ 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20,
+ 0x22, 0x49, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x22, 0x2c, 0x20,
+ 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x49, 0x6e,
+ 0x73, 0x65, 0x72, 0x74, 0x22, 0x20, 0x7d, 0x2c, 0xa, 0x9,
+ 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x48, 0x6f, 0x6d,
+ 0x65, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a,
+ 0x20, 0x22, 0x48, 0x6f, 0x6d, 0x65, 0x22, 0x20, 0x7d, 0x2c,
+ 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x50,
+ 0x61, 0x67, 0x65, 0x55, 0x70, 0x22, 0x2c, 0x20, 0x6c, 0x61,
+ 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x50, 0x61, 0x67, 0x65,
+ 0x3c, 0x62, 0x72, 0x3e, 0x55, 0x70, 0x22, 0x7d, 0x2c, 0xa,
+ 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4e, 0x75,
+ 0x6d, 0x4c, 0x6f, 0x63, 0x6b, 0x22, 0x2c, 0x20, 0x6c, 0x61,
+ 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x4e, 0x75, 0x6d, 0x22,
+ 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20,
+ 0x22, 0x4e, 0x75, 0x6d, 0x70, 0x61, 0x64, 0x44, 0x69, 0x76,
+ 0x69, 0x64, 0x65, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65,
+ 0x6c, 0x3a, 0x20, 0x22, 0x2f, 0x22, 0x7d, 0x2c, 0xa, 0x9,
+ 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4e, 0x75, 0x6d,
+ 0x70, 0x61, 0x64, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c,
+ 0x79, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a,
+ 0x20, 0x22, 0x2a, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b,
+ 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4e, 0x75, 0x6d, 0x70, 0x61,
+ 0x64, 0x53, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x22,
+ 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22,
+ 0x2d, 0x22, 0x7d, 0xa, 0x9, 0x5d, 0x2c, 0xa, 0x9, 0x5b,
+ 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x54,
+ 0x61, 0x62, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c,
+ 0x3a, 0x20, 0x22, 0x54, 0x61, 0x62, 0x22, 0x2c, 0x20, 0x77,
+ 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x31, 0x2e, 0x35, 0x7d,
+ 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22,
+ 0x4b, 0x65, 0x79, 0x51, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62,
+ 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x51, 0x22, 0x7d, 0x2c, 0xa,
+ 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4b, 0x65,
+ 0x79, 0x57, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c,
+ 0x3a, 0x20, 0x22, 0x57, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9,
+ 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4b, 0x65, 0x79, 0x45,
+ 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20,
+ 0x22, 0x45, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69,
+ 0x64, 0x3a, 0x20, 0x22, 0x4b, 0x65, 0x79, 0x52, 0x22, 0x2c,
+ 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x52,
+ 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a,
+ 0x20, 0x22, 0x4b, 0x65, 0x79, 0x54, 0x22, 0x2c, 0x20, 0x6c,
+ 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x54, 0x22, 0x7d,
+ 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22,
+ 0x4b, 0x65, 0x79, 0x59, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62,
+ 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x59, 0x22, 0x7d, 0x2c, 0xa,
+ 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4b, 0x65,
+ 0x79, 0x55, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c,
+ 0x3a, 0x20, 0x22, 0x55, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9,
+ 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4b, 0x65, 0x79, 0x49,
+ 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20,
+ 0x22, 0x49, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69,
+ 0x64, 0x3a, 0x20, 0x22, 0x4b, 0x65, 0x79, 0x4f, 0x22, 0x2c,
+ 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x4f,
+ 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a,
+ 0x20, 0x22, 0x4b, 0x65, 0x79, 0x50, 0x22, 0x2c, 0x20, 0x6c,
+ 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x50, 0x22, 0x7d,
+ 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22,
+ 0x42, 0x72, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x4c, 0x65, 0x66,
+ 0x74, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a,
+ 0x20, 0x22, 0x7b, 0x3c, 0x62, 0x72, 0x3e, 0x5b, 0x22, 0x7d,
+ 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22,
+ 0x42, 0x72, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x69, 0x67,
+ 0x68, 0x74, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c,
+ 0x3a, 0x20, 0x22, 0x7d, 0x3c, 0x62, 0x72, 0x3e, 0x5d, 0x22,
+ 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20,
+ 0x22, 0x42, 0x61, 0x63, 0x6b, 0x73, 0x6c, 0x61, 0x73, 0x68,
+ 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20,
+ 0x22, 0x7c, 0x3c, 0x62, 0x72, 0x3e, 0x5c, 0x5c, 0x22, 0x2c,
+ 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x31, 0x2e,
+ 0x35, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a,
+ 0x20, 0x22, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x22, 0x2c,
+ 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x44,
+ 0x65, 0x6c, 0x65, 0x74, 0x65, 0x22, 0x7d, 0x2c, 0xa, 0x9,
+ 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x45, 0x6e, 0x64,
+ 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20,
+ 0x22, 0x45, 0x6e, 0x64, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9,
+ 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x50, 0x61, 0x67, 0x65,
+ 0x44, 0x6f, 0x77, 0x6e, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62,
+ 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x50, 0x61, 0x67, 0x65, 0x3c,
+ 0x62, 0x72, 0x3e, 0x44, 0x6f, 0x77, 0x6e, 0x22, 0x7d, 0x2c,
+ 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4e,
+ 0x75, 0x6d, 0x70, 0x61, 0x64, 0x37, 0x22, 0x2c, 0x20, 0x6c,
+ 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x48, 0x6f, 0x6d,
+ 0x65, 0x3c, 0x62, 0x72, 0x3e, 0x37, 0x22, 0x7d, 0x2c, 0xa,
+ 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4e, 0x75,
+ 0x6d, 0x70, 0x61, 0x64, 0x38, 0x22, 0x2c, 0x20, 0x6c, 0x61,
+ 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x55, 0x70, 0x3c, 0x62,
+ 0x72, 0x3e, 0x38, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b,
+ 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4e, 0x75, 0x6d, 0x70, 0x61,
+ 0x64, 0x39, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c,
+ 0x3a, 0x20, 0x22, 0x50, 0x67, 0x55, 0x70, 0x3c, 0x62, 0x72,
+ 0x3e, 0x39, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69,
+ 0x64, 0x3a, 0x20, 0x22, 0x4e, 0x75, 0x6d, 0x70, 0x61, 0x64,
+ 0x41, 0x64, 0x64, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65,
+ 0x6c, 0x3a, 0x20, 0x22, 0x2b, 0x22, 0x2c, 0x20, 0x68, 0x65,
+ 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x32, 0x2e, 0x32, 0x7d,
+ 0xa, 0x9, 0x5d, 0x2c, 0xa, 0x9, 0x5b, 0xa, 0x9, 0x9,
+ 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x43, 0x61, 0x70, 0x73,
+ 0x4c, 0x6f, 0x63, 0x6b, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62,
+ 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x43, 0x61, 0x70, 0x73, 0x3c,
+ 0x62, 0x72, 0x3e, 0x4c, 0x6f, 0x63, 0x6b, 0x22, 0x2c, 0x20,
+ 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x32, 0x7d, 0x2c,
+ 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4b,
+ 0x65, 0x79, 0x41, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65,
+ 0x6c, 0x3a, 0x20, 0x22, 0x41, 0x22, 0x7d, 0x2c, 0xa, 0x9,
+ 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4b, 0x65, 0x79,
+ 0x53, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a,
+ 0x20, 0x22, 0x53, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b,
+ 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4b, 0x65, 0x79, 0x44, 0x22,
+ 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22,
+ 0x44, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64,
+ 0x3a, 0x20, 0x22, 0x4b, 0x65, 0x79, 0x46, 0x22, 0x2c, 0x20,
+ 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x46, 0x22,
+ 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20,
+ 0x22, 0x4b, 0x65, 0x79, 0x47, 0x22, 0x2c, 0x20, 0x6c, 0x61,
+ 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x47, 0x22, 0x7d, 0x2c,
+ 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4b,
+ 0x65, 0x79, 0x48, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65,
+ 0x6c, 0x3a, 0x20, 0x22, 0x48, 0x22, 0x7d, 0x2c, 0xa, 0x9,
+ 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4b, 0x65, 0x79,
+ 0x4a, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a,
+ 0x20, 0x22, 0x4a, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b,
+ 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4b, 0x65, 0x79, 0x4b, 0x22,
+ 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22,
+ 0x4b, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64,
+ 0x3a, 0x20, 0x22, 0x4b, 0x65, 0x79, 0x4c, 0x22, 0x2c, 0x20,
+ 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x4c, 0x22,
+ 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20,
+ 0x22, 0x53, 0x65, 0x6d, 0x69, 0x63, 0x6f, 0x6c, 0x6f, 0x6e,
+ 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20,
+ 0x22, 0x3a, 0x3c, 0x62, 0x72, 0x3e, 0x3b, 0x22, 0x7d, 0x2c,
+ 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x51,
+ 0x75, 0x6f, 0x74, 0x65, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62,
+ 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x5c, 0x22, 0x3c, 0x62, 0x72,
+ 0x3e, 0x5c, 0x27, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b,
+ 0x69, 0x64, 0x3a, 0x20, 0x22, 0x45, 0x6e, 0x74, 0x65, 0x72,
+ 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20,
+ 0x22, 0x45, 0x6e, 0x74, 0x65, 0x72, 0x22, 0x2c, 0x20, 0x77,
+ 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x32, 0x2e, 0x32, 0x7d,
+ 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22,
+ 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20,
+ 0x22, 0x22, 0x2c, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a,
+ 0x20, 0x33, 0x2e, 0x35, 0x35, 0x7d, 0x2c, 0xa, 0x9, 0x9,
+ 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4e, 0x75, 0x6d, 0x70,
+ 0x61, 0x64, 0x34, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65,
+ 0x6c, 0x3a, 0x20, 0x22, 0x4c, 0x65, 0x66, 0x74, 0x3c, 0x62,
+ 0x72, 0x3e, 0x34, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b,
+ 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4e, 0x75, 0x6d, 0x70, 0x61,
+ 0x64, 0x35, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c,
+ 0x3a, 0x20, 0x22, 0x35, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9,
+ 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4e, 0x75, 0x6d, 0x70,
+ 0x61, 0x64, 0x36, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65,
+ 0x6c, 0x3a, 0x20, 0x22, 0x52, 0x69, 0x67, 0x68, 0x74, 0x3c,
+ 0x62, 0x72, 0x3e, 0x36, 0x22, 0x7d, 0xa, 0x9, 0x5d, 0x2c,
+ 0xa, 0x9, 0x5b, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a,
+ 0x20, 0x22, 0x53, 0x68, 0x69, 0x66, 0x74, 0x4c, 0x65, 0x66,
+ 0x74, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a,
+ 0x20, 0x22, 0x53, 0x68, 0x69, 0x66, 0x74, 0x22, 0x2c, 0x20,
+ 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x32, 0x2e, 0x35,
+ 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20,
+ 0x22, 0x4b, 0x65, 0x79, 0x5a, 0x22, 0x2c, 0x20, 0x6c, 0x61,
+ 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x5a, 0x22, 0x7d, 0x2c,
+ 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4b,
+ 0x65, 0x79, 0x58, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65,
+ 0x6c, 0x3a, 0x20, 0x22, 0x58, 0x22, 0x7d, 0x2c, 0xa, 0x9,
+ 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4b, 0x65, 0x79,
+ 0x43, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a,
+ 0x20, 0x22, 0x43, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b,
+ 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4b, 0x65, 0x79, 0x56, 0x22,
+ 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22,
+ 0x56, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64,
+ 0x3a, 0x20, 0x22, 0x4b, 0x65, 0x79, 0x42, 0x22, 0x2c, 0x20,
+ 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x42, 0x22,
+ 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20,
+ 0x22, 0x4b, 0x65, 0x79, 0x4e, 0x22, 0x2c, 0x20, 0x6c, 0x61,
+ 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x4e, 0x22, 0x7d, 0x2c,
+ 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4b,
+ 0x65, 0x79, 0x4d, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65,
+ 0x6c, 0x3a, 0x20, 0x22, 0x4d, 0x22, 0x7d, 0x2c, 0xa, 0x9,
+ 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x43, 0x6f, 0x6d,
+ 0x6d, 0x61, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c,
+ 0x3a, 0x20, 0x22, 0x5c, 0x3c, 0x3c, 0x62, 0x72, 0x3e, 0x2c,
+ 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a,
+ 0x20, 0x22, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x22, 0x2c,
+ 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x5c,
+ 0x3e, 0x3c, 0x62, 0x72, 0x3e, 0x2e, 0x22, 0x7d, 0x2c, 0xa,
+ 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x53, 0x6c,
+ 0x61, 0x73, 0x68, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65,
+ 0x6c, 0x3a, 0x20, 0x22, 0x3f, 0x3c, 0x62, 0x72, 0x3e, 0x2f,
+ 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a,
+ 0x20, 0x22, 0x53, 0x68, 0x69, 0x66, 0x74, 0x52, 0x69, 0x67,
+ 0x68, 0x74, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c,
+ 0x3a, 0x20, 0x22, 0x53, 0x68, 0x69, 0x66, 0x74, 0x22, 0x2c,
+ 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x33, 0x7d,
+ 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22,
+ 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20,
+ 0x22, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64,
+ 0x3a, 0x20, 0x22, 0x41, 0x72, 0x72, 0x6f, 0x77, 0x55, 0x70,
+ 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20,
+ 0x22, 0x55, 0x70, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b,
+ 0x69, 0x64, 0x3a, 0x20, 0x22, 0x22, 0x2c, 0x20, 0x6c, 0x61,
+ 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x22, 0x7d, 0x2c, 0xa,
+ 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4e, 0x75,
+ 0x6d, 0x70, 0x61, 0x64, 0x31, 0x22, 0x2c, 0x20, 0x6c, 0x61,
+ 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x45, 0x6e, 0x64, 0x3c,
+ 0x62, 0x72, 0x3e, 0x31, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9,
+ 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4e, 0x75, 0x6d, 0x70,
+ 0x61, 0x64, 0x32, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65,
+ 0x6c, 0x3a, 0x20, 0x22, 0x44, 0x6f, 0x77, 0x6e, 0x3c, 0x62,
+ 0x72, 0x3e, 0x32, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b,
+ 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4e, 0x75, 0x6d, 0x70, 0x61,
+ 0x64, 0x33, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c,
+ 0x3a, 0x20, 0x22, 0x50, 0x67, 0x44, 0x6e, 0x3c, 0x62, 0x72,
+ 0x3e, 0x33, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69,
+ 0x64, 0x3a, 0x20, 0x22, 0x4e, 0x75, 0x6d, 0x70, 0x61, 0x64,
+ 0x45, 0x6e, 0x74, 0x65, 0x72, 0x22, 0x2c, 0x20, 0x6c, 0x61,
+ 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x45, 0x6e, 0x74, 0x65,
+ 0x72, 0x22, 0x2c, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74,
+ 0x3a, 0x20, 0x32, 0x2e, 0x32, 0x7d, 0xa, 0x9, 0x5d, 0x2c,
+ 0xa, 0x9, 0x5b, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a,
+ 0x20, 0x22, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x4c,
+ 0x65, 0x66, 0x74, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65,
+ 0x6c, 0x3a, 0x20, 0x22, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
+ 0x6c, 0x22, 0x2c, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a,
+ 0x20, 0x32, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64,
+ 0x3a, 0x20, 0x22, 0x4d, 0x65, 0x74, 0x61, 0x4c, 0x65, 0x66,
+ 0x74, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a,
+ 0x20, 0x22, 0x4d, 0x65, 0x74, 0x61, 0x22, 0x7d, 0x2c, 0xa,
+ 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x41, 0x6c,
+ 0x74, 0x4c, 0x65, 0x66, 0x74, 0x22, 0x2c, 0x20, 0x6c, 0x61,
+ 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x41, 0x6c, 0x74, 0x22,
+ 0x2c, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x31,
+ 0x2e, 0x35, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64,
+ 0x3a, 0x20, 0x22, 0x53, 0x70, 0x61, 0x63, 0x65, 0x22, 0x2c,
+ 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x53,
+ 0x70, 0x61, 0x63, 0x65, 0x22, 0x2c, 0x20, 0x77, 0x69, 0x64,
+ 0x74, 0x68, 0x3a, 0x20, 0x36, 0x2e, 0x34, 0x7d, 0x2c, 0xa,
+ 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x41, 0x6c,
+ 0x74, 0x52, 0x69, 0x67, 0x68, 0x74, 0x22, 0x2c, 0x20, 0x6c,
+ 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x41, 0x6c, 0x74,
+ 0x22, 0x2c, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20,
+ 0x32, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a,
+ 0x20, 0x22, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x69, 0x67, 0x68,
+ 0x74, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a,
+ 0x20, 0x22, 0x46, 0x4e, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9,
+ 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x43, 0x6f, 0x6e, 0x74,
+ 0x65, 0x78, 0x74, 0x4d, 0x65, 0x6e, 0x75, 0x22, 0x2c, 0x20,
+ 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x22, 0x4d, 0x65, 0x6e,
+ 0x75, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64,
+ 0x3a, 0x20, 0x22, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
+ 0x52, 0x69, 0x67, 0x68, 0x74, 0x22, 0x2c, 0x20, 0x6c, 0x61,
+ 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x43, 0x6f, 0x6e, 0x74,
+ 0x72, 0x6f, 0x6c, 0x22, 0x2c, 0x20, 0x77, 0x69, 0x64, 0x74,
+ 0x68, 0x3a, 0x20, 0x31, 0x2e, 0x36, 0x7d, 0x2c, 0xa, 0x9,
+ 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x41, 0x72, 0x72,
+ 0x6f, 0x77, 0x4c, 0x65, 0x66, 0x74, 0x22, 0x2c, 0x20, 0x6c,
+ 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x4c, 0x65, 0x66,
+ 0x74, 0x22, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64,
+ 0x3a, 0x20, 0x22, 0x41, 0x72, 0x72, 0x6f, 0x77, 0x44, 0x6f,
+ 0x77, 0x6e, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c,
+ 0x3a, 0x20, 0x22, 0x44, 0x6f, 0x77, 0x6e, 0x22, 0x7d, 0x2c,
+ 0xa, 0x9, 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x41,
+ 0x72, 0x72, 0x6f, 0x77, 0x52, 0x69, 0x67, 0x68, 0x74, 0x22,
+ 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22,
+ 0x52, 0x69, 0x67, 0x68, 0x74, 0x22, 0x7d, 0x2c, 0xa, 0x9,
+ 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4e, 0x75, 0x6d,
+ 0x70, 0x61, 0x64, 0x30, 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62,
+ 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x49, 0x6e, 0x73, 0x3c, 0x62,
+ 0x72, 0x3e, 0x30, 0x22, 0x2c, 0x20, 0x77, 0x69, 0x64, 0x74,
+ 0x68, 0x3a, 0x20, 0x32, 0x2e, 0x32, 0x7d, 0x2c, 0xa, 0x9,
+ 0x9, 0x7b, 0x69, 0x64, 0x3a, 0x20, 0x22, 0x4e, 0x75, 0x6d,
+ 0x70, 0x61, 0x64, 0x44, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c,
+ 0x22, 0x2c, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20,
+ 0x22, 0x44, 0x65, 0x6c, 0x3c, 0x62, 0x72, 0x3e, 0x2e, 0x22,
+ 0x7d, 0xa, 0x9, 0x5d, 0x2c, 0xa, 0x5d, 0x3b, 0xa, 0xa,
+ 0x2f, 0x2f, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c,
+ 0x69, 0x7a, 0x65, 0x20, 0x70, 0x61, 0x67, 0x65, 0x20, 0x61,
+ 0x6e, 0x64, 0x20, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b,
+ 0x65, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x6e, 0x20, 0x6c, 0x6f, 0x61,
+ 0x64, 0xa, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x6f,
+ 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x3d, 0x20, 0x28, 0x65,
+ 0x76, 0x65, 0x6e, 0x74, 0x29, 0x20, 0x3d, 0x3e, 0x20, 0x7b,
+ 0xa, 0x9, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65,
+ 0x79, 0x73, 0x28, 0x22, 0x6b, 0x65, 0x79, 0x62, 0x6f, 0x61,
+ 0x72, 0x64, 0x22, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x62, 0x6f,
+ 0x61, 0x72, 0x64, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x3b,
+ 0xa, 0xa, 0x9, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x20,
+ 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x57, 0x65, 0x62, 0x53,
+ 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x28, 0x22, 0x77, 0x73, 0x3a,
+ 0x2f, 0x2f, 0x22, 0x20, 0x2b, 0x20, 0x77, 0x69, 0x6e, 0x64,
+ 0x6f, 0x77, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65,
+ 0x20, 0x2b, 0x20, 0x22, 0x3a, 0x38, 0x30, 0x38, 0x30, 0x2f,
+ 0x22, 0x29, 0x3b, 0xa, 0x9, 0x73, 0x6f, 0x63, 0x6b, 0x65,
+ 0x74, 0x2e, 0x6f, 0x6e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
+ 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
+ 0x6f, 0x6e, 0x20, 0x28, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x29,
+ 0x20, 0x7b, 0x20, 0x73, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6f,
+ 0x72, 0x28, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x64, 0x61,
+ 0x74, 0x61, 0x29, 0x3b, 0x20, 0x7d, 0x3b, 0xa, 0x7d, 0xa,
+ 0xa, 0x2f, 0x2f, 0x20, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x20,
+ 0x66, 0x6f, 0x72, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x69, 0x6e,
+ 0x67, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x20, 0x77, 0x65, 0x62,
+ 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x61, 0x6e, 0x64,
+ 0x20, 0x72, 0x65, 0x6f, 0x70, 0x65, 0x6e, 0x69, 0x6e, 0x67,
+ 0x20, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x69, 0x66, 0x20, 0x63, 0x6c, 0x6f, 0x73, 0x65,
+ 0x64, 0xa, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x73, 0x65, 0x6e, 0x64, 0x72, 0x65, 0x6c, 0x6f, 0x61,
+ 0x64, 0x28, 0x6d, 0x73, 0x67, 0x2c, 0x20, 0x66, 0x75, 0x6e,
+ 0x63, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x69, 0x66, 0x20, 0x28,
+ 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x72, 0x65, 0x61,
+ 0x64, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x20, 0x3d, 0x3d,
+ 0x20, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74,
+ 0x2e, 0x4f, 0x50, 0x45, 0x4e, 0x29, 0x20, 0x7b, 0xa, 0x9,
+ 0x9, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x65,
+ 0x6e, 0x64, 0x28, 0x6d, 0x73, 0x67, 0x29, 0x3b, 0xa, 0x9,
+ 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20,
+ 0x28, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x72, 0x65,
+ 0x61, 0x64, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x20, 0x3d,
+ 0x3d, 0x20, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65,
+ 0x74, 0x2e, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x44, 0x29, 0x20,
+ 0x7b, 0xa, 0x9, 0x9, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74,
+ 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x57, 0x65, 0x62,
+ 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x28, 0x22, 0x77, 0x73,
+ 0x3a, 0x2f, 0x2f, 0x22, 0x20, 0x2b, 0x20, 0x77, 0x69, 0x6e,
+ 0x64, 0x6f, 0x77, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d,
+ 0x65, 0x20, 0x2b, 0x20, 0x22, 0x3a, 0x38, 0x30, 0x38, 0x30,
+ 0x2f, 0x22, 0x29, 0x3b, 0xa, 0x9, 0x9, 0x73, 0x6f, 0x63,
+ 0x6b, 0x65, 0x74, 0x2e, 0x6f, 0x6e, 0x6d, 0x65, 0x73, 0x73,
+ 0x61, 0x67, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x65, 0x76, 0x65, 0x6e,
+ 0x74, 0x29, 0x20, 0x7b, 0x20, 0x73, 0x65, 0x74, 0x43, 0x6f,
+ 0x6c, 0x6f, 0x72, 0x28, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e,
+ 0x64, 0x61, 0x74, 0x61, 0x29, 0x3b, 0x20, 0x7d, 0x3b, 0xa,
+ 0x9, 0x9, 0x73, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f,
+ 0x75, 0x74, 0x28, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x2c, 0x20,
+ 0x31, 0x30, 0x29, 0x3b, 0xa, 0x9, 0x7d, 0x20, 0x65, 0x6c,
+ 0x73, 0x65, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x73, 0x65, 0x74,
+ 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x28, 0x20, 0x66,
+ 0x75, 0x6e, 0x63, 0x2c, 0x20, 0x31, 0x30, 0x29, 0x3b, 0xa,
+ 0x9, 0x7d, 0xa, 0x7d, 0xa, 0xa, 0x2f, 0x2f, 0x20, 0x74,
+ 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x20, 0x73, 0x65, 0x6c, 0x65,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x70,
+ 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x20, 0x6b, 0x65, 0x79,
+ 0xa, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x70, 0x72, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x28, 0x63,
+ 0x6f, 0x64, 0x65, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x69, 0x66,
+ 0x20, 0x28, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64,
+ 0x2e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x73, 0x28,
+ 0x63, 0x6f, 0x64, 0x65, 0x29, 0x29, 0x20, 0x7b, 0xa, 0x9,
+ 0x9, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x2e,
+ 0x73, 0x70, 0x6c, 0x69, 0x63, 0x65, 0x28, 0x73, 0x65, 0x6c,
+ 0x65, 0x63, 0x74, 0x65, 0x64, 0x2e, 0x69, 0x6e, 0x64, 0x65,
+ 0x78, 0x4f, 0x66, 0x28, 0x63, 0x6f, 0x64, 0x65, 0x29, 0x2c,
+ 0x31, 0x29, 0x3b, 0xa, 0x9, 0x7d, 0x20, 0x65, 0x6c, 0x73,
+ 0x65, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x73, 0x65, 0x6c, 0x65,
+ 0x63, 0x74, 0x65, 0x64, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28,
+ 0x63, 0x6f, 0x64, 0x65, 0x29, 0x3b, 0xa, 0x9, 0x7d, 0xa,
+ 0xa, 0x9, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65,
+ 0x79, 0x73, 0x28, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65,
+ 0x64, 0x29, 0x3b, 0xa, 0x7d, 0xa, 0xa, 0x2f, 0x2f, 0x20,
+ 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x47, 0x55, 0x49,
+ 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x63, 0x75, 0x72, 0x72,
+ 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x20, 0x73, 0x65, 0x6c, 0x65,
+ 0x63, 0x74, 0x65, 0x64, 0x20, 0x6b, 0x65, 0x79, 0x73, 0xa,
+ 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x75,
+ 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x28,
+ 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x6b,
+ 0x65, 0x79, 0x73, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x2f, 0x2f,
+ 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x68, 0x69,
+ 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67,
+ 0x20, 0x6f, 0x66, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74,
+ 0x65, 0x64, 0x20, 0x6b, 0x65, 0x79, 0x73, 0xa, 0x9, 0x70,
+ 0x72, 0x65, 0x76, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x20, 0x3d,
+ 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
+ 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74,
+ 0x73, 0x42, 0x79, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61,
+ 0x6d, 0x65, 0x28, 0x22, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74,
+ 0x65, 0x64, 0x22, 0x29, 0x3b, 0xa, 0x9, 0x66, 0x6f, 0x72,
+ 0x20, 0x28, 0x6c, 0x65, 0x74, 0x20, 0x6b, 0x65, 0x79, 0x20,
+ 0x6f, 0x66, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65,
+ 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x29, 0x20, 0x7b, 0xa,
+ 0x9, 0x9, 0x6b, 0x65, 0x79, 0x44, 0x69, 0x76, 0x20, 0x3d,
+ 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
+ 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74,
+ 0x42, 0x79, 0x49, 0x64, 0x28, 0x6b, 0x65, 0x79, 0x29, 0x3b,
+ 0xa, 0x9, 0x9, 0x6b, 0x65, 0x79, 0x44, 0x69, 0x76, 0x2e,
+ 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x2e,
+ 0x61, 0x64, 0x64, 0x28, 0x22, 0x73, 0x65, 0x6c, 0x65, 0x63,
+ 0x74, 0x65, 0x64, 0x22, 0x29, 0x3b, 0xa, 0x9, 0x7d, 0xa,
+ 0x9, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x6c, 0x65, 0x74, 0x20,
+ 0x6b, 0x65, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x70, 0x72, 0x65,
+ 0x76, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x29, 0x20, 0x7b, 0xa,
+ 0x9, 0x9, 0x69, 0x66, 0x20, 0x28, 0x21, 0x73, 0x65, 0x6c,
+ 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x73,
+ 0x2e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x73, 0x28,
+ 0x6b, 0x65, 0x79, 0x2e, 0x69, 0x64, 0x29, 0x29, 0x20, 0x7b,
+ 0xa, 0x9, 0x9, 0x9, 0x6b, 0x65, 0x79, 0x2e, 0x63, 0x6c,
+ 0x61, 0x73, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x2e, 0x72, 0x65,
+ 0x6d, 0x6f, 0x76, 0x65, 0x28, 0x22, 0x73, 0x65, 0x6c, 0x65,
+ 0x63, 0x74, 0x65, 0x64, 0x22, 0x29, 0x3b, 0xa, 0x9, 0x9,
+ 0x7d, 0xa, 0x9, 0x7d, 0xa, 0xa, 0x9, 0x2f, 0x2f, 0x20,
+ 0x73, 0x68, 0x6f, 0x77, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74,
+ 0x65, 0x64, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66,
+ 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x20,
+ 0x6b, 0x65, 0x79, 0x73, 0xa, 0x9, 0x64, 0x6f, 0x63, 0x75,
+ 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c,
+ 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28,
+ 0x22, 0x6b, 0x65, 0x79, 0x73, 0x22, 0x29, 0x2e, 0x69, 0x6e,
+ 0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x20, 0x3d, 0x20,
+ 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x6b,
+ 0x65, 0x79, 0x73, 0x3b, 0xa, 0x9, 0x2f, 0x2f, 0x20, 0x72,
+ 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6c,
+ 0x6f, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x65, 0x6c, 0x65,
+ 0x63, 0x74, 0x65, 0x64, 0x20, 0x6b, 0x65, 0x79, 0xa, 0x9,
+ 0x67, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x73,
+ 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x6b, 0x65,
+ 0x79, 0x73, 0x29, 0x3b, 0xa, 0x7d, 0xa, 0xa, 0x2f, 0x2f,
+ 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x6b, 0x65,
+ 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x20, 0x6c, 0x61, 0x79,
+ 0x6f, 0x75, 0x74, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61,
+ 0x72, 0x72, 0x61, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x6b, 0x65,
+ 0x79, 0x73, 0xa, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65,
+ 0x79, 0x73, 0x28, 0x64, 0x69, 0x76, 0x5f, 0x69, 0x64, 0x2c,
+ 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x29,
+ 0x20, 0x7b, 0xa, 0x9, 0x6c, 0x65, 0x74, 0x20, 0x6b, 0x65,
+ 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x44, 0x69, 0x76, 0x20,
+ 0x3d, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74,
+ 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e,
+ 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x64, 0x69, 0x76, 0x5f,
+ 0x69, 0x64, 0x29, 0x3b, 0xa, 0x9, 0x66, 0x6f, 0x72, 0x20,
+ 0x28, 0x6c, 0x65, 0x74, 0x20, 0x72, 0x6f, 0x77, 0x20, 0x6f,
+ 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x69, 0x73, 0x74,
+ 0x29, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x6c, 0x65, 0x74, 0x20,
+ 0x6e, 0x65, 0x77, 0x52, 0x6f, 0x77, 0x20, 0x3d, 0x20, 0x64,
+ 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x72,
+ 0x65, 0x61, 0x74, 0x65, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e,
+ 0x74, 0x28, 0x22, 0x64, 0x69, 0x76, 0x22, 0x29, 0x3b, 0xa,
+ 0x9, 0x9, 0x6b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64,
+ 0x44, 0x69, 0x76, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64,
+ 0x43, 0x68, 0x69, 0x6c, 0x64, 0x28, 0x6e, 0x65, 0x77, 0x52,
+ 0x6f, 0x77, 0x29, 0x3b, 0xa, 0x9, 0x9, 0x66, 0x6f, 0x72,
+ 0x20, 0x28, 0x6c, 0x65, 0x74, 0x20, 0x6b, 0x65, 0x79, 0x20,
+ 0x6f, 0x66, 0x20, 0x72, 0x6f, 0x77, 0x29, 0x20, 0x7b, 0xa,
+ 0x9, 0x9, 0x9, 0x6c, 0x65, 0x74, 0x20, 0x6e, 0x65, 0x77,
+ 0x44, 0x69, 0x76, 0x20, 0x3d, 0x20, 0x64, 0x6f, 0x63, 0x75,
+ 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74,
+ 0x65, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22,
+ 0x64, 0x69, 0x76, 0x22, 0x29, 0x3b, 0xa, 0x9, 0x9, 0x9,
+ 0x69, 0x66, 0x20, 0x28, 0x6b, 0x65, 0x79, 0x2e, 0x69, 0x64,
+ 0x29, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x6e, 0x65,
+ 0x77, 0x44, 0x69, 0x76, 0x2e, 0x69, 0x64, 0x20, 0x3d, 0x20,
+ 0x6b, 0x65, 0x79, 0x2e, 0x69, 0x64, 0x3b, 0xa, 0x9, 0x9,
+ 0x9, 0x9, 0x6e, 0x65, 0x77, 0x44, 0x69, 0x76, 0x2e, 0x69,
+ 0x6e, 0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x20, 0x3d,
+ 0x20, 0x6b, 0x65, 0x79, 0x2e, 0x6c, 0x61, 0x62, 0x65, 0x6c,
+ 0x3b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x6e, 0x65, 0x77, 0x44,
+ 0x69, 0x76, 0x2e, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61,
+ 0x6d, 0x65, 0x20, 0x3d, 0x20, 0x22, 0x6b, 0x65, 0x79, 0x22,
+ 0x3b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x6e, 0x65, 0x77, 0x44,
+ 0x69, 0x76, 0x2e, 0x61, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e,
+ 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x28,
+ 0x22, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x22, 0x2c, 0x20, 0x66,
+ 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x29,
+ 0x20, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x9, 0x70, 0x72,
+ 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x28, 0x6b, 0x65, 0x79,
+ 0x2e, 0x69, 0x64, 0x29, 0x3b, 0x20, 0x7d, 0x29, 0x3b, 0xa,
+ 0x9, 0x9, 0x9, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20,
+ 0x7b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x6e, 0x65, 0x77, 0x44,
+ 0x69, 0x76, 0x2e, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61,
+ 0x6d, 0x65, 0x20, 0x3d, 0x20, 0x22, 0x6b, 0x65, 0x79, 0x20,
+ 0x62, 0x6c, 0x61, 0x6e, 0x6b, 0x22, 0x3b, 0xa, 0x9, 0x9,
+ 0x9, 0x7d, 0xa, 0x9, 0x9, 0x9, 0x6e, 0x65, 0x77, 0x52,
+ 0x6f, 0x77, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43,
+ 0x68, 0x69, 0x6c, 0x64, 0x28, 0x6e, 0x65, 0x77, 0x44, 0x69,
+ 0x76, 0x29, 0x3b, 0xa, 0x9, 0x9, 0x9, 0x69, 0x66, 0x20,
+ 0x28, 0x6b, 0x65, 0x79, 0x2e, 0x77, 0x69, 0x64, 0x74, 0x68,
+ 0x29, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x6e, 0x65,
+ 0x77, 0x44, 0x69, 0x76, 0x2e, 0x73, 0x74, 0x79, 0x6c, 0x65,
+ 0x2e, 0x77, 0x69, 0x64, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x28,
+ 0x6b, 0x65, 0x79, 0x2e, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2a,
+ 0x6e, 0x65, 0x77, 0x44, 0x69, 0x76, 0x2e, 0x63, 0x6c, 0x69,
+ 0x65, 0x6e, 0x74, 0x57, 0x69, 0x64, 0x74, 0x68, 0x20, 0x29,
+ 0x20, 0x2b, 0x20, 0x22, 0x70, 0x78, 0x22, 0x3b, 0xa, 0x9,
+ 0x9, 0x9, 0x7d, 0xa, 0x9, 0x9, 0x9, 0x69, 0x66, 0x20,
+ 0x28, 0x6b, 0x65, 0x79, 0x2e, 0x68, 0x65, 0x69, 0x67, 0x68,
+ 0x74, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x6e,
+ 0x65, 0x77, 0x44, 0x69, 0x76, 0x2e, 0x73, 0x74, 0x79, 0x6c,
+ 0x65, 0x2e, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x20, 0x3d,
+ 0x20, 0x28, 0x6b, 0x65, 0x79, 0x2e, 0x68, 0x65, 0x69, 0x67,
+ 0x68, 0x74, 0x2a, 0x6e, 0x65, 0x77, 0x44, 0x69, 0x76, 0x2e,
+ 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x48, 0x65, 0x69, 0x67,
+ 0x68, 0x74, 0x20, 0x29, 0x20, 0x2b, 0x20, 0x22, 0x70, 0x78,
+ 0x22, 0x3b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x69, 0x66, 0x20,
+ 0x28, 0x6b, 0x65, 0x79, 0x2e, 0x68, 0x65, 0x69, 0x67, 0x68,
+ 0x74, 0x20, 0x3e, 0x20, 0x31, 0x29, 0x20, 0x7b, 0xa, 0x9,
+ 0x9, 0x9, 0x9, 0x9, 0x6e, 0x65, 0x77, 0x44, 0x69, 0x76,
+ 0x2e, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x2e, 0x66, 0x6c, 0x6f,
+ 0x61, 0x74, 0x20, 0x3d, 0x20, 0x22, 0x72, 0x69, 0x67, 0x68,
+ 0x74, 0x22, 0x3b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x7d, 0xa,
+ 0x9, 0x9, 0x9, 0x7d, 0xa, 0x9, 0x9, 0x7d, 0xa, 0x9,
+ 0x7d, 0xa, 0x7d, 0xa, 0xa, 0x2f, 0x2f, 0x20, 0x73, 0x65,
+ 0x6e, 0x64, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x52, 0x47, 0x42,
+ 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x74, 0x6f, 0x20, 0x68,
+ 0x6f, 0x73, 0x74, 0xa, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
+ 0x6f, 0x6e, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x4b, 0x65, 0x79,
+ 0x73, 0x28, 0x6b, 0x65, 0x79, 0x73, 0x29, 0x20, 0x7b, 0xa,
+ 0x9, 0x69, 0x66, 0x20, 0x28, 0x73, 0x65, 0x6c, 0x65, 0x63,
+ 0x74, 0x65, 0x64, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68,
+ 0x20, 0x3e, 0x20, 0x30, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x9,
+ 0x6c, 0x65, 0x74, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20,
+ 0x3d, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74,
+ 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e,
+ 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x72, 0x67, 0x62,
+ 0x22, 0x29, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3b, 0xa,
+ 0x9, 0x9, 0x6c, 0x65, 0x74, 0x20, 0x61, 0x64, 0x61, 0x70,
+ 0x74, 0x20, 0x3d, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65,
+ 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d,
+ 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x61,
+ 0x64, 0x61, 0x70, 0x74, 0x69, 0x76, 0x65, 0x22, 0x29, 0x2e,
+ 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x64, 0x20, 0x3f, 0x20,
+ 0x31, 0x3a, 0x30, 0x3b, 0xa, 0x9, 0x9, 0x73, 0x65, 0x6e,
+ 0x64, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x22, 0x53,
+ 0x2c, 0x22, 0x20, 0x2b, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72,
+ 0x2e, 0x73, 0x75, 0x62, 0x73, 0x74, 0x72, 0x28, 0x31, 0x2c,
+ 0x36, 0x29, 0x20, 0x2b, 0x20, 0x22, 0x2c, 0x22, 0x20, 0x2b,
+ 0x20, 0x61, 0x64, 0x61, 0x70, 0x74, 0x20, 0x2b, 0x20, 0x22,
+ 0x2c, 0x22, 0x20, 0x2b, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x2c,
+ 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x28, 0x29, 0x20, 0x7b, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x4b,
+ 0x65, 0x79, 0x73, 0x28, 0x6b, 0x65, 0x79, 0x73, 0x29, 0x3b,
+ 0x20, 0x7d, 0x20, 0x29, 0x3b, 0xa, 0x9, 0x7d, 0xa, 0x7d,
+ 0xa, 0xa, 0x2f, 0x2f, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
+ 0x73, 0x61, 0x76, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x68, 0x6f, 0x73, 0x74,
+ 0xa, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x73, 0x61, 0x76, 0x65, 0x28, 0x29, 0x20, 0x7b, 0xa, 0x9,
+ 0x73, 0x65, 0x6e, 0x64, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64,
+ 0x28, 0x22, 0x46, 0x2c, 0x22, 0x2c, 0x20, 0x66, 0x75, 0x6e,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x65, 0x76, 0x65,
+ 0x6e, 0x74, 0x29, 0x20, 0x7b, 0x20, 0x73, 0x61, 0x76, 0x65,
+ 0x28, 0x29, 0x3b, 0x20, 0x7d, 0x20, 0x29, 0x3b, 0xa, 0x7d,
+ 0xa, 0xa, 0x2f, 0x2f, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
+ 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x68, 0x6f, 0x73, 0x74,
+ 0xa, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x29, 0x20, 0x7b, 0xa, 0x9,
+ 0x73, 0x65, 0x6e, 0x64, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64,
+ 0x28, 0x22, 0x4c, 0x2c, 0x22, 0x2c, 0x20, 0x66, 0x75, 0x6e,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x65, 0x76, 0x65,
+ 0x6e, 0x74, 0x29, 0x20, 0x7b, 0x20, 0x6c, 0x6f, 0x61, 0x64,
+ 0x28, 0x29, 0x3b, 0x20, 0x7d, 0x20, 0x29, 0x3b, 0xa, 0x7d,
+ 0xa, 0xa, 0x2f, 0x2f, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74,
+ 0x65, 0x20, 0x52, 0x47, 0x42, 0x20, 0x63, 0x6f, 0x6c, 0x6f,
+ 0x72, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63,
+ 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63,
+ 0x74, 0x6f, 0x72, 0xa, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
+ 0x6f, 0x6e, 0x20, 0x73, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6f,
+ 0x72, 0x28, 0x6e, 0x65, 0x77, 0x5f, 0x63, 0x6f, 0x6c, 0x6f,
+ 0x72, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x64, 0x6f, 0x63, 0x75,
+ 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c,
+ 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28,
+ 0x22, 0x72, 0x67, 0x62, 0x22, 0x29, 0x2e, 0x76, 0x61, 0x6c,
+ 0x75, 0x65, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x5f, 0x63,
+ 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x73, 0x75, 0x62, 0x73, 0x74,
+ 0x72, 0x69, 0x6e, 0x67, 0x28, 0x30, 0x2c, 0x37, 0x29, 0x3b,
+ 0xa, 0x9, 0x6c, 0x65, 0x74, 0x20, 0x61, 0x64, 0x61, 0x70,
+ 0x74, 0x20, 0x3d, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65,
+ 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d,
+ 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x61,
+ 0x64, 0x61, 0x70, 0x74, 0x69, 0x76, 0x65, 0x22, 0x29, 0x3b,
+ 0xa, 0x9, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x65, 0x77, 0x5f,
+ 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x73, 0x75, 0x62, 0x73,
+ 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x38, 0x2c, 0x39, 0x29,
+ 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x31, 0x22, 0x29, 0x20, 0x7b,
+ 0xa, 0x9, 0x9, 0x61, 0x64, 0x61, 0x70, 0x74, 0x2e, 0x63,
+ 0x68, 0x65, 0x63, 0x6b, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x66,
+ 0x61, 0x6c, 0x73, 0x65, 0x3b, 0xa, 0x9, 0x7d, 0x20, 0x65,
+ 0x6c, 0x73, 0x65, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x61, 0x64,
+ 0x61, 0x70, 0x74, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65,
+ 0x64, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0xa,
+ 0x9, 0x7d, 0xa, 0x9, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65,
+ 0x52, 0x47, 0x42, 0x28, 0x29, 0x3b, 0xa, 0x7d, 0xa, 0xa,
+ 0x2f, 0x2f, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x72, 0x65,
+ 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x72,
+ 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x20, 0x63, 0x6f,
+ 0x6c, 0x6f, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x66, 0x69, 0x72,
+ 0x73, 0x74, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65,
+ 0x64, 0x20, 0x6b, 0x65, 0x79, 0xa, 0x66, 0x75, 0x6e, 0x63,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x67, 0x65, 0x74, 0x43, 0x6f,
+ 0x6c, 0x6f, 0x72, 0x28, 0x6b, 0x65, 0x79, 0x73, 0x29, 0x20,
+ 0x7b, 0xa, 0x9, 0x69, 0x66, 0x20, 0x28, 0x73, 0x65, 0x6c,
+ 0x65, 0x63, 0x74, 0x65, 0x64, 0x2e, 0x6c, 0x65, 0x6e, 0x67,
+ 0x74, 0x68, 0x20, 0x3e, 0x20, 0x30, 0x29, 0x20, 0x7b, 0xa,
+ 0x9, 0x9, 0x73, 0x65, 0x6e, 0x64, 0x72, 0x65, 0x6c, 0x6f,
+ 0x61, 0x64, 0x28, 0x22, 0x47, 0x2c, 0x22, 0x20, 0x2b, 0x20,
+ 0x6b, 0x65, 0x79, 0x73, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x29, 0x20, 0x7b, 0x20,
+ 0x67, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x6b,
+ 0x65, 0x79, 0x73, 0x29, 0x3b, 0x20, 0x7d, 0x20, 0x29, 0x3b,
+ 0xa, 0x9, 0x7d, 0xa, 0x7d, 0xa, 0xa, 0x2f, 0x2f, 0x20,
+ 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x69, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61,
+ 0x6c, 0x20, 0x52, 0x2c, 0x20, 0x47, 0x2c, 0x20, 0x42, 0x20,
+ 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x77, 0x68, 0x65,
+ 0x6e, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x69, 0x73,
+ 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x66, 0x72,
+ 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x61, 0x6c,
+ 0x65, 0x74, 0x74, 0x65, 0xa, 0x66, 0x75, 0x6e, 0x63, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65,
+ 0x52, 0x47, 0x42, 0x28, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x6c,
+ 0x65, 0x74, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d,
+ 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
+ 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74,
+ 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x72, 0x67, 0x62, 0x22,
+ 0x29, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3b, 0xa, 0x9,
+ 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67,
+ 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42,
+ 0x79, 0x49, 0x64, 0x28, 0x22, 0x72, 0x65, 0x64, 0x22, 0x29,
+ 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3d, 0x20, 0x70,
+ 0x61, 0x72, 0x73, 0x65, 0x49, 0x6e, 0x74, 0x28, 0x63, 0x6f,
+ 0x6c, 0x6f, 0x72, 0x2e, 0x73, 0x75, 0x62, 0x73, 0x74, 0x72,
+ 0x69, 0x6e, 0x67, 0x28, 0x31, 0x2c, 0x33, 0x29, 0x2c, 0x20,
+ 0x31, 0x36, 0x29, 0x3b, 0xa, 0x9, 0x64, 0x6f, 0x63, 0x75,
+ 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c,
+ 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28,
+ 0x22, 0x67, 0x72, 0x65, 0x65, 0x6e, 0x22, 0x29, 0x2e, 0x76,
+ 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3d, 0x20, 0x70, 0x61, 0x72,
+ 0x73, 0x65, 0x49, 0x6e, 0x74, 0x28, 0x63, 0x6f, 0x6c, 0x6f,
+ 0x72, 0x2e, 0x73, 0x75, 0x62, 0x73, 0x74, 0x72, 0x69, 0x6e,
+ 0x67, 0x28, 0x33, 0x2c, 0x35, 0x29, 0x2c, 0x20, 0x31, 0x36,
+ 0x29, 0x3b, 0xa, 0x9, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65,
+ 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d,
+ 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x62,
+ 0x6c, 0x75, 0x65, 0x22, 0x29, 0x2e, 0x76, 0x61, 0x6c, 0x75,
+ 0x65, 0x20, 0x3d, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x49,
+ 0x6e, 0x74, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x73,
+ 0x75, 0x62, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x35,
+ 0x2c, 0x37, 0x29, 0x2c, 0x20, 0x31, 0x36, 0x29, 0x3b, 0xa,
+ 0x7d, 0xa, 0xa, 0x2f, 0x2f, 0x20, 0x75, 0x70, 0x64, 0x61,
+ 0x74, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6c,
+ 0x6f, 0x72, 0x20, 0x70, 0x61, 0x6c, 0x65, 0x74, 0x74, 0x65,
+ 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x69, 0x6e, 0x64, 0x69,
+ 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x20, 0x52, 0x2c, 0x20,
+ 0x47, 0x2c, 0x20, 0x42, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x20, 0x69, 0x73, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65,
+ 0x64, 0xa, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6c,
+ 0x6f, 0x72, 0x28, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x2f, 0x2f,
+ 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x6d, 0x61, 0x6b,
+ 0x65, 0x20, 0x73, 0x75, 0x72, 0x65, 0x20, 0x74, 0x68, 0x61,
+ 0x74, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75,
+ 0x61, 0x6c, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c,
+ 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x61, 0x72,
+ 0x65, 0x20, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x20,
+ 0x30, 0x2d, 0x32, 0x35, 0x35, 0x3b, 0xa, 0x9, 0x6c, 0x65,
+ 0x74, 0x20, 0x72, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x64, 0x6f,
+ 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74,
+ 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49,
+ 0x64, 0x28, 0x22, 0x72, 0x65, 0x64, 0x22, 0x29, 0x3b, 0xa,
+ 0x9, 0x6c, 0x65, 0x74, 0x20, 0x67, 0x72, 0x65, 0x65, 0x6e,
+ 0x20, 0x3d, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e,
+ 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65,
+ 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x67, 0x72,
+ 0x65, 0x65, 0x6e, 0x22, 0x29, 0x3b, 0xa, 0x9, 0x6c, 0x65,
+ 0x74, 0x20, 0x62, 0x6c, 0x75, 0x65, 0x20, 0x3d, 0x20, 0x64,
+ 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65,
+ 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79,
+ 0x49, 0x64, 0x28, 0x22, 0x62, 0x6c, 0x75, 0x65, 0x22, 0x29,
+ 0x3b, 0xa, 0xa, 0x9, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x6c,
+ 0x65, 0x74, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c,
+ 0x20, 0x6f, 0x66, 0x20, 0x5b, 0x72, 0x65, 0x64, 0x2c, 0x20,
+ 0x67, 0x72, 0x65, 0x65, 0x6e, 0x2c, 0x20, 0x62, 0x6c, 0x75,
+ 0x65, 0x5d, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x69, 0x66,
+ 0x20, 0x28, 0x70, 0x61, 0x72, 0x73, 0x65, 0x49, 0x6e, 0x74,
+ 0x28, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x2e, 0x76,
+ 0x61, 0x6c, 0x75, 0x65, 0x29, 0x20, 0x3e, 0x20, 0x32, 0x35,
+ 0x35, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x63, 0x68,
+ 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x2e, 0x76, 0x61, 0x6c, 0x75,
+ 0x65, 0x20, 0x3d, 0x20, 0x32, 0x35, 0x35, 0x3b, 0xa, 0x9,
+ 0x9, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x69, 0x66,
+ 0x20, 0x28, 0x70, 0x61, 0x72, 0x73, 0x65, 0x49, 0x6e, 0x74,
+ 0x28, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x2e, 0x76,
+ 0x61, 0x6c, 0x75, 0x65, 0x29, 0x20, 0x3c, 0x20, 0x30, 0x29,
+ 0x20, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x63, 0x68, 0x61, 0x6e,
+ 0x6e, 0x65, 0x6c, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20,
+ 0x3d, 0x20, 0x30, 0x3b, 0xa, 0x9, 0x9, 0x7d, 0xa, 0x9,
+ 0x7d, 0xa, 0xa, 0x9, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65,
+ 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d,
+ 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x72,
+ 0x67, 0x62, 0x22, 0x29, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x20, 0x3d, 0x20, 0x22, 0x23, 0x22, 0x20, 0x2b, 0x20, 0x70,
+ 0x61, 0x72, 0x73, 0x65, 0x49, 0x6e, 0x74, 0x28, 0x72, 0x65,
+ 0x64, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x2e, 0x74,
+ 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x31, 0x36,
+ 0x29, 0x2e, 0x70, 0x61, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74,
+ 0x28, 0x32, 0x2c, 0x22, 0x30, 0x22, 0x29, 0x20, 0x2b, 0xa,
+ 0x9, 0x9, 0x70, 0x61, 0x72, 0x73, 0x65, 0x49, 0x6e, 0x74,
+ 0x28, 0x67, 0x72, 0x65, 0x65, 0x6e, 0x2e, 0x76, 0x61, 0x6c,
+ 0x75, 0x65, 0x29, 0x2e, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69,
+ 0x6e, 0x67, 0x28, 0x31, 0x36, 0x29, 0x2e, 0x70, 0x61, 0x64,
+ 0x53, 0x74, 0x61, 0x72, 0x74, 0x28, 0x32, 0x2c, 0x22, 0x30,
+ 0x22, 0x29, 0x20, 0x2b, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65,
+ 0x49, 0x6e, 0x74, 0x28, 0x62, 0x6c, 0x75, 0x65, 0x2e, 0x76,
+ 0x61, 0x6c, 0x75, 0x65, 0x29, 0x2e, 0x74, 0x6f, 0x53, 0x74,
+ 0x72, 0x69, 0x6e, 0x67, 0x28, 0x31, 0x36, 0x29, 0x2e, 0x70,
+ 0x61, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x28, 0x32, 0x2c,
+ 0x22, 0x30, 0x22, 0x29, 0x3b, 0xa, 0x7d, 0xa, 0xa, 0x3c,
+ 0x2f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3e, 0xa, 0xa,
+ 0x3c, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x3e, 0xa, 0xa, 0x3c,
+ 0x62, 0x6f, 0x64, 0x79, 0x3e, 0xa, 0x3c, 0x64, 0x69, 0x76,
+ 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x63, 0x6f,
+ 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x22, 0x3e, 0xa,
+ 0x9, 0x3c, 0x64, 0x69, 0x76, 0x20, 0x69, 0x64, 0x3d, 0x22,
+ 0x6b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x22, 0x3e,
+ 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0xa, 0x9, 0x3c, 0x64,
+ 0x69, 0x76, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x6d, 0x65, 0x6e,
+ 0x75, 0x22, 0x3e, 0xa, 0x9, 0x9, 0x3c, 0x64, 0x69, 0x76,
+ 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x6c, 0x61,
+ 0x62, 0x65, 0x6c, 0x22, 0x3e, 0x4c, 0x45, 0x44, 0x20, 0x53,
+ 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x3c, 0x2f, 0x64,
+ 0x69, 0x76, 0x3e, 0xa, 0x9, 0x9, 0x3c, 0x64, 0x69, 0x76,
+ 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x6f, 0x75,
+ 0x74, 0x70, 0x75, 0x74, 0x22, 0x3e, 0x4b, 0x65, 0x79, 0x28,
+ 0x73, 0x29, 0x3a, 0x20, 0x3c, 0x64, 0x69, 0x76, 0x20, 0x69,
+ 0x64, 0x3d, 0x22, 0x6b, 0x65, 0x79, 0x73, 0x22, 0x3e, 0x3c,
+ 0x2f, 0x64, 0x69, 0x76, 0x3e, 0x3c, 0x2f, 0x64, 0x69, 0x76,
+ 0x3e, 0xa, 0x9, 0x9, 0x3c, 0x64, 0x69, 0x76, 0x20, 0x69,
+ 0x64, 0x3d, 0x22, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75,
+ 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x3e, 0xa, 0x9,
+ 0x9, 0x9, 0x3c, 0x64, 0x69, 0x76, 0x20, 0x63, 0x6c, 0x61,
+ 0x73, 0x73, 0x3d, 0x22, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+ 0x22, 0x3e, 0xa, 0x9, 0x9, 0x9, 0x9, 0x43, 0x6f, 0x6c,
+ 0x6f, 0x72, 0x3a, 0x20, 0x3c, 0x69, 0x6e, 0x70, 0x75, 0x74,
+ 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x63, 0x6f, 0x6c,
+ 0x6f, 0x72, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x72, 0x67,
+ 0x62, 0x22, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x72,
+ 0x67, 0x62, 0x22, 0x20, 0x6f, 0x6e, 0x63, 0x68, 0x61, 0x6e,
+ 0x67, 0x65, 0x3d, 0x22, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65,
+ 0x52, 0x47, 0x42, 0x28, 0x29, 0x22, 0x3e, 0x3c, 0x62, 0x72,
+ 0x3e, 0xa, 0x9, 0x9, 0x9, 0x9, 0x52, 0x65, 0x64, 0x3a,
+ 0x20, 0x3c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x74, 0x79,
+ 0x70, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x22, 0x20,
+ 0x69, 0x64, 0x3d, 0x22, 0x72, 0x65, 0x64, 0x22, 0x20, 0x6e,
+ 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x72, 0x65, 0x64, 0x22, 0x20,
+ 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x62, 0x79, 0x74,
+ 0x65, 0x22, 0x20, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e,
+ 0x3d, 0x22, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x7b, 0x31, 0x2c,
+ 0x32, 0x7d, 0x22, 0x20, 0x6f, 0x6e, 0x63, 0x68, 0x61, 0x6e,
+ 0x67, 0x65, 0x3d, 0x22, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65,
+ 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x29, 0x22, 0x3e, 0x3c,
+ 0x62, 0x72, 0x3e, 0xa, 0x9, 0x9, 0x9, 0x9, 0x47, 0x72,
+ 0x65, 0x65, 0x6e, 0x3a, 0x20, 0x3c, 0x69, 0x6e, 0x70, 0x75,
+ 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x74, 0x65,
+ 0x78, 0x74, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x67, 0x72,
+ 0x65, 0x65, 0x6e, 0x22, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d,
+ 0x22, 0x67, 0x72, 0x65, 0x65, 0x6e, 0x22, 0x20, 0x63, 0x6c,
+ 0x61, 0x73, 0x73, 0x3d, 0x22, 0x62, 0x79, 0x74, 0x65, 0x22,
+ 0x20, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x3d, 0x22,
+ 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x7b, 0x31, 0x2c, 0x32, 0x7d,
+ 0x22, 0x20, 0x6f, 0x6e, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65,
+ 0x3d, 0x22, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f,
+ 0x6c, 0x6f, 0x72, 0x28, 0x29, 0x22, 0x3e, 0x3c, 0x62, 0x72,
+ 0x3e, 0xa, 0x9, 0x9, 0x9, 0x9, 0x42, 0x6c, 0x75, 0x65,
+ 0x3a, 0x20, 0x3c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x74,
+ 0x79, 0x70, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x22,
+ 0x20, 0x69, 0x64, 0x3d, 0x22, 0x62, 0x6c, 0x75, 0x65, 0x22,
+ 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x62, 0x6c, 0x75,
+ 0x65, 0x22, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22,
+ 0x62, 0x79, 0x74, 0x65, 0x22, 0x20, 0x70, 0x61, 0x74, 0x74,
+ 0x65, 0x72, 0x6e, 0x3d, 0x22, 0x5b, 0x30, 0x2d, 0x39, 0x5d,
+ 0x7b, 0x31, 0x2c, 0x32, 0x7d, 0x22, 0x20, 0x6f, 0x6e, 0x63,
+ 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3d, 0x22, 0x75, 0x70, 0x64,
+ 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x29,
+ 0x22, 0x3e, 0x3c, 0x62, 0x72, 0x3e, 0xa, 0x9, 0x9, 0x9,
+ 0x9, 0x41, 0x64, 0x61, 0x70, 0x74, 0x69, 0x76, 0x65, 0x3a,
+ 0x20, 0x3c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x74, 0x79,
+ 0x70, 0x65, 0x3d, 0x22, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x62,
+ 0x6f, 0x78, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x61, 0x64,
+ 0x61, 0x70, 0x74, 0x69, 0x76, 0x65, 0x22, 0x20, 0x6e, 0x61,
+ 0x6d, 0x65, 0x3d, 0x22, 0x61, 0x64, 0x61, 0x70, 0x74, 0x69,
+ 0x76, 0x65, 0x22, 0x3e, 0x3c, 0x62, 0x72, 0x3e, 0x3c, 0x62,
+ 0x72, 0x3e, 0xa, 0x9, 0x9, 0x9, 0x9, 0x3c, 0x62, 0x75,
+ 0x74, 0x74, 0x6f, 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d,
+ 0x22, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x22, 0x20, 0x6f,
+ 0x6e, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x3d, 0x22, 0x73, 0x65,
+ 0x6e, 0x64, 0x4b, 0x65, 0x79, 0x73, 0x28, 0x73, 0x65, 0x6c,
+ 0x65, 0x63, 0x74, 0x65, 0x64, 0x29, 0x22, 0x3e, 0x53, 0x65,
+ 0x74, 0x20, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x3c, 0x2f, 0x62,
+ 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x3e, 0x20, 0x3c, 0x62, 0x75,
+ 0x74, 0x74, 0x6f, 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d,
+ 0x22, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x22, 0x20, 0x6f,
+ 0x6e, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x3d, 0x22, 0x67, 0x65,
+ 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x73, 0x65, 0x6c,
+ 0x65, 0x63, 0x74, 0x65, 0x64, 0x29, 0x22, 0x3e, 0x52, 0x65,
+ 0x73, 0x65, 0x74, 0x3c, 0x2f, 0x62, 0x75, 0x74, 0x74, 0x6f,
+ 0x6e, 0x3e, 0xa, 0x9, 0x9, 0x9, 0x3c, 0x2f, 0x64, 0x69,
+ 0x76, 0x3e, 0xa, 0x9, 0x9, 0x9, 0x3c, 0x64, 0x69, 0x76,
+ 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x63, 0x6f,
+ 0x6e, 0x66, 0x69, 0x67, 0x22, 0x3e, 0xa, 0x9, 0x9, 0x9,
+ 0x9, 0x3c, 0x64, 0x69, 0x76, 0x20, 0x63, 0x6c, 0x61, 0x73,
+ 0x73, 0x3d, 0x22, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x22, 0x3e,
+ 0x46, 0x6c, 0x61, 0x73, 0x68, 0x20, 0x6d, 0x65, 0x6d, 0x6f,
+ 0x72, 0x79, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0xa, 0x9,
+ 0x9, 0x9, 0x9, 0x3c, 0x64, 0x69, 0x76, 0x3e, 0xa, 0x9,
+ 0x9, 0x9, 0x9, 0x9, 0x3c, 0x62, 0x75, 0x74, 0x74, 0x6f,
+ 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x62, 0x75,
+ 0x74, 0x74, 0x6f, 0x6e, 0x22, 0x20, 0x6f, 0x6e, 0x63, 0x6c,
+ 0x69, 0x63, 0x6b, 0x3d, 0x22, 0x73, 0x61, 0x76, 0x65, 0x28,
+ 0x29, 0x22, 0x3e, 0x53, 0x61, 0x76, 0x65, 0x3c, 0x2f, 0x62,
+ 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x3e, 0xa, 0x9, 0x9, 0x9,
+ 0x9, 0x9, 0x3c, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x20,
+ 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x62, 0x75, 0x74, 0x74,
+ 0x6f, 0x6e, 0x22, 0x20, 0x6f, 0x6e, 0x63, 0x6c, 0x69, 0x63,
+ 0x6b, 0x3d, 0x22, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x29, 0x22,
+ 0x3e, 0x4c, 0x6f, 0x61, 0x64, 0x3c, 0x2f, 0x62, 0x75, 0x74,
+ 0x74, 0x6f, 0x6e, 0x3e, 0xa, 0x9, 0x9, 0x9, 0x9, 0x3c,
+ 0x2f, 0x64, 0x69, 0x76, 0x3e, 0xa, 0x9, 0x9, 0x9, 0x3c,
+ 0x2f, 0x64, 0x69, 0x76, 0x3e, 0xa, 0x9, 0x9, 0x3c, 0x2f,
+ 0x64, 0x69, 0x76, 0x3e, 0xa, 0x9, 0x3c, 0x2f, 0x64, 0x69,
+ 0x76, 0x3e, 0xa, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0xa,
+ 0xa, 0x3c, 0x2f, 0x62, 0x6f, 0x64, 0x79, 0x3e, 0xa, 0xa,
+ 0x3c, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3e, 0xa, };
+
+const struct fsdata_file file_fsdata_c[] = {{NULL, data_fsdata_c, data_fsdata_c + 10, sizeof(data_fsdata_c) - 10, FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT}};
+
+const struct fsdata_file file_index_html[] = {{file_fsdata_c, data_index_html, data_index_html + 12, sizeof(data_index_html) - 12, FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT}};
+
+#define FS_ROOT file_index_html
+
+#define FS_NUMFILES 2
diff --git a/tusb_config.h b/tusb_config.h
index eae9eb8..31b0396 100644
--- a/tusb_config.h
+++ b/tusb_config.h
@@ -83,8 +83,9 @@
//-----------------------Driver configuration-------------------------
-#define CFG_TUD_CDC 1
-#define CFG_TUD_HID 4
+#define CFG_TUD_CDC 1
+#define CFG_TUD_HID 4
+#define CFG_TUD_NCM 1
// CDC FIFO size of TX and RX
#define CFG_TUD_CDC_RX_BUFSIZE 128
diff --git a/ui.png b/ui.png
new file mode 100644
index 0000000..b72a4d2
Binary files /dev/null and b/ui.png differ
diff --git a/usb_device.c b/usb_device.c
index 4aa0873..d184a6a 100644
--- a/usb_device.c
+++ b/usb_device.c
@@ -9,6 +9,7 @@
#include "hyperx_elite2.h"
#include "usb_host.h"
+#include "usb_server.h"
#include "usb_device.h"
@@ -44,6 +45,7 @@ static tusb_desc_device_t const desc_device =
.bNumConfigurations = 0x01
};
+
static char const* string_desc_arr [] =
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
@@ -51,7 +53,9 @@ static char const* string_desc_arr [] =
"Pico HyperX Elite 2 RGB Controller", // 2: Product
NULL, // 3: Serials, should use chip ID
"Pico HyperX Elite 2 CDC", // 4: CDC
- "Pico HyperX Elite 2 HID", // 5: HID
+ "Pico HyperX Elite 2 NCM", // 5: NCM
+ NULL, // 6: MAC address for NCM
+ "Pico HyperX Elite 2 HID", // 7: HID
};
static void usb_device_init(void);
@@ -65,6 +69,9 @@ void usb_device_main(void) {
device_state = DEVICE_INACTIVE;
usb_device_init();
+ // start the web server on USB
+ usb_server_init();
+
while (true) {
switch ( device_state ) {
case DEVICE_ACTIVE:
@@ -116,21 +123,24 @@ uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
{
(void) index; // for multiple configurations
+ // set configuration descriptor and CDC descriptor
memset(desc_configuration, 0, sizeof(desc_configuration));
- uint8_t desc_initial[TUD_CONFIG_DESC_LEN+TUD_CDC_DESC_LEN+1] = {
- TUD_CONFIG_DESCRIPTOR(1, 2+num_mounted, 0, TUD_CONFIG_DESC_LEN+TUD_CDC_DESC_LEN+num_mounted*TUD_HID_DESC_LEN, 0x00, 100),
- TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64)
+ uint8_t desc_initial[TUD_CONFIG_DESC_LEN+TUD_CDC_DESC_LEN+TUD_CDC_NCM_DESC_LEN+1] = {
+ TUD_CONFIG_DESCRIPTOR(1, 4+num_mounted, 0, TUD_CONFIG_DESC_LEN+TUD_CDC_DESC_LEN+TUD_CDC_NCM_DESC_LEN+num_mounted*TUD_HID_DESC_LEN, 0x00, 100),
+ TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
+ TUD_CDC_NCM_DESCRIPTOR(ITF_NUM_NCM, 5, 6, EPNUM_NCM_NOTIF, 64, EPNUM_NCM_OUT, EPNUM_NCM_IN, CFG_TUD_NET_ENDPOINT_SIZE, CFG_TUD_NET_MTU)
};
- memcpy(desc_configuration, desc_initial, TUD_CONFIG_DESC_LEN+TUD_CDC_DESC_LEN);
+ memcpy(desc_configuration, desc_initial, TUD_CONFIG_DESC_LEN+TUD_CDC_DESC_LEN+TUD_CDC_NCM_DESC_LEN);
+ // add a HID descriptor for each interface mounted on host
if ( descriptors != NULL) {
struct report_desc *descriptor;
for (uint8_t i=0; idev_addr, i);
uint8_t hid_desc[TUD_HID_DESC_LEN+1] = {
- TUD_HID_DESCRIPTOR(ITF_NUM_HID+i, 5, HID_ITF_PROTOCOL_NONE, descriptor->desc_len, EPNUM_HID+i, CFG_TUD_HID_EP_BUFSIZE, 1)
+ TUD_HID_DESCRIPTOR(ITF_NUM_HID+i, 7, HID_ITF_PROTOCOL_NONE, descriptor->desc_len, EPNUM_HID+i, CFG_TUD_HID_EP_BUFSIZE, 1)
};
- memcpy(&desc_configuration[TUD_CONFIG_DESC_LEN+TUD_CDC_DESC_LEN+i*TUD_HID_DESC_LEN], hid_desc, TUD_HID_DESC_LEN);
+ memcpy(&desc_configuration[TUD_CONFIG_DESC_LEN+TUD_CDC_DESC_LEN+TUD_CDC_NCM_DESC_LEN+i*TUD_HID_DESC_LEN], hid_desc, TUD_HID_DESC_LEN);
}
}
@@ -153,6 +163,17 @@ uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
case 3: // serial
chr_count = board_usb_get_serial(_desc_str+1, 32);
break;
+ case 6: // MAC address: link-local prefix of 02 and last 10 digits from board serial
+ chr_count = board_usb_get_serial(_desc_str+1, 32);
+ if (chr_count > 12) {
+ _desc_str[1] = '0';
+ _desc_str[2] = '2';
+ for (uint8_t i=0; i<10; i++) {
+ _desc_str[3+i] = _desc_str[chr_count-9+i];
+ }
+ chr_count=12;
+ }
+ break;
default:
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
diff --git a/usb_device.h b/usb_device.h
index 8d65b4b..9d0b662 100644
--- a/usb_device.h
+++ b/usb_device.h
@@ -1,7 +1,7 @@
#ifndef USB_DEVICE_H_
#define USB_DEVICE_H_
-#define DESC_CFG_MAX TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + CFG_TUD_HID*TUD_HID_DESC_LEN
+#define DESC_CFG_MAX TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_CDC_NCM_DESC_LEN + CFG_TUD_HID*TUD_HID_DESC_LEN
#define USB_PID 0xE2BD
#define USB_VID 0xCEC0
@@ -10,12 +10,17 @@
#define EPNUM_CDC_NOTIF 0x81
#define EPNUM_CDC_OUT 0x02
#define EPNUM_CDC_IN 0x82
-#define EPNUM_HID 0x83
+#define EPNUM_NCM_NOTIF 0x83
+#define EPNUM_NCM_OUT 0x03
+#define EPNUM_NCM_IN 0x84
+#define EPNUM_HID 0x85
enum
{
ITF_NUM_CDC=0,
ITF_NUM_CDC_DATA,
+ ITF_NUM_NCM,
+ ITF_NUM_NCM_DATA,
ITF_NUM_HID
};
diff --git a/usb_host.c b/usb_host.c
index 4e28179..b9828c6 100644
--- a/usb_host.c
+++ b/usb_host.c
@@ -4,6 +4,7 @@
#include
#include "pico/stdlib.h"
+#include "pico/multicore.h"
#include "pio_usb.h"
#include "tusb.h"
@@ -50,8 +51,15 @@ static void usb_host_init(void) {
}
void usb_host_main(void) {
+ // allow other core to pause host process - required for saving to flash
+ multicore_lockout_victim_init();
+
usb_host_init();
+ if (load_rgb_config()) {
+ tud_cdc_write_str("found previous RGB configuration\n");
+ }
+
while (true) {
switch ( host_state ) {
case HOST_MOUNTED:
@@ -242,7 +250,7 @@ static void report_desc_init(struct report_desc *descriptor) {
descriptor->next = NULL;
}
-// free memory and teardown usb->bt report ID mappings for report descriptor struct
+// free memory for report descriptor struct
static void report_desc_free(struct report_desc *descriptor) {
if (descriptor != NULL) {
if (descriptors == descriptor) {
diff --git a/usb_server.c b/usb_server.c
new file mode 100644
index 0000000..8f4b010
--- /dev/null
+++ b/usb_server.c
@@ -0,0 +1,234 @@
+#include "bsp/board_api.h"
+#include "tusb.h"
+#include "dhserver.h"
+#include "dnserver.h"
+#include "lwip/apps/httpd.h"
+#include "lwip/init.h"
+#include "lwip/timeouts.h"
+
+#include "websocket.h"
+#include "usb_device.h"
+#include "hyperx_elite2.h"
+
+#include "usb_server.h"
+
+// ip address of the USB server and dhcp address(es) it will give out
+static const ip4_addr_t usb_ip = INIT_IP4(192, 168, 226, 1);
+static const ip4_addr_t usb_netmask = INIT_IP4(255, 255, 255, 0);
+static const ip4_addr_t usb_gateway = INIT_IP4(0, 0, 0, 0);
+static dhcp_entry_t dhcp_clients[] = {
+ { {0}, INIT_IP4(192, 168, 226, 2), 4*3600 },
+};
+static const dhcp_config_t dhcp_config = {
+ .router = INIT_IP4(0,0,0,0),
+ .port = 67,
+ .dns = usb_ip,
+ "usb",
+ TU_ARRAY_SIZE(dhcp_clients),
+ dhcp_clients
+};
+
+static struct netif netif_data;
+
+static err_t netif_init_cb(struct netif *netif);
+static err_t ip4_output_fn(struct netif *netif, struct pbuf *p, const ip4_addr_t *addr);
+static err_t linkoutput_fn(struct netif *netif, struct pbuf *p);
+static void usb_server_netif_link_cb(struct netif *netif);
+static bool dns_request(const char *name, ip4_addr_t *addr);
+
+// called to initialize the USB network and HTTP server
+void usb_server_init(void) {
+ struct netif *netif = &netif_data;
+
+ lwip_init();
+
+ // use 02 followed by last 10 digits from board serial as MAC address
+ uint8_t board_serial[16];
+ size_t count = board_get_unique_id(board_serial, sizeof(board_serial));
+ netif->hwaddr_len = 6;
+ memcpy(netif->hwaddr, &board_serial[count-6], 6);
+ netif->hwaddr[0]=0x02;
+ // lwip virtual MAC address msut differ from the host MAC - toggle last bit
+ netif->hwaddr[5] ^= 0x01;
+
+ netif = netif_add(netif, &usb_ip, &usb_netmask, &usb_gateway, NULL, netif_init_cb, ethernet_input);
+ netif_set_default(netif);
+
+#if LWIP_NETIF_LINK_CALLBACK
+ netif_set_link_callback(netif, usb_server_netif_link_cb);
+ netif_set_link_up(netif);
+#else
+ //tud_network_link_state(BOARD_TUD_RHPORT, true);
+ // unsupported in current version - add when Pico SDK updates TinyUSB version
+#endif
+
+ while (!netif_is_up(&netif_data));
+ while (dhserv_init(&dhcp_config) != ERR_OK);
+ while (dnserv_init(IP_ADDR_ANY, 53, dns_request) != ERR_OK);
+
+ httpd_init();
+
+ // start the websocket server
+ ws_server_init();
+ ws_set_open_handler(ws_open_handler);
+ ws_set_receive_handler(ws_receive_handler);
+}
+
+// callback when data is received on USB network
+// return true if the packet buffer was accepted
+bool tud_network_recv_cb(const uint8_t *src, uint16_t size) {
+ struct netif *netif = &netif_data;
+
+ if (size) {
+ struct pbuf *p = pbuf_alloc(PBUF_RAW, size, PBUF_POOL);
+
+ if (p == NULL) {
+ printf("ERROR: Failed to allocate pbuf of size %d\n", size);
+ return false;
+ }
+
+ /* Copy buf to pbuf */
+ pbuf_take(p, src, size);
+
+ // Surrender ownership of our pbuf unless there was an error
+ // Only call pbuf_free if not Ok else it will panic with "pbuf_free: p->ref > 0"
+ // or steal it from whatever took ownership of it with undefined consequences.
+ // See: https://savannah.nongnu.org/patch/index.php?10121
+ if (netif->input(p, netif) != ERR_OK) {
+ printf("ERROR: netif input failed\n");
+ pbuf_free(p);
+ }
+ // Signal tinyusb that the current frame has been processed.
+ tud_network_recv_renew();
+ }
+
+ return true;
+}
+
+// callback when network interface is initialized
+// save the network configuration
+static err_t netif_init_cb(struct netif *netif) {
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+ netif->mtu = CFG_TUD_NET_MTU;
+ netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP | NETIF_FLAG_UP;
+ netif->state = NULL;
+ netif->name[0] = 'E';
+ netif->name[1] = 'X';
+ netif->linkoutput = linkoutput_fn;
+ netif->output = ip4_output_fn;
+
+ return ERR_OK;
+}
+
+// callback for sending data over USB network interface
+// copy from network stack packet pointer to dst
+uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) {
+ struct pbuf *p = (struct pbuf *) ref;
+
+ (void) arg; /* unused for this example */
+
+ return pbuf_copy_partial(p, dst, p->tot_len, 0);
+}
+
+static err_t ip4_output_fn(struct netif *netif, struct pbuf *p, const ip4_addr_t *addr) {
+ return etharp_output(netif, p, addr);
+}
+
+static err_t linkoutput_fn(struct netif *netif, struct pbuf *p) {
+ (void) netif;
+
+ for (;;) {
+ // if TinyUSB isn't ready, we must signal back to lwip that there is nothing we can do
+ if (!tud_ready())
+ return ERR_USE;
+
+ // if the network driver can accept another packet, we make it happen
+ if (tud_network_can_xmit(p->tot_len)) {
+ tud_network_xmit(p, 0 /* unused for this example */);
+ return ERR_OK;
+ }
+
+ // transfer execution to TinyUSB in the hopes that it will finish transmitting the prior packet
+ tud_task();
+ }
+}
+
+// notify USB host about link state changes
+static void usb_server_netif_link_cb(struct netif *netif) {
+ bool link_up = netif_is_link_up(netif);
+ //tud_network_link_state(BOARD_TUD_RHPORT, link_up);
+ // unsupported in current version - add when Pico SDK updates TinyUSB version
+}
+
+// handle DNS requests and serve on designed domain
+static bool dns_request(const char *name, ip4_addr_t *addr) {
+ if (0 == strcmp(name, "alloyelite2.usb")) {
+ *addr = usb_ip;
+ return true;
+ }
+ return false;
+}
+
+// handler called when websocket connection is opened
+const void ws_open_handler(struct ws_state * wss) {
+ (void) wss;
+
+ // nothing to do
+}
+
+// handler for data received on websocket connection
+const void ws_receive_handler(uint8_t *data, uint16_t len) {
+ if (strncmp(data, "S,", 2) == 0) {
+ // set color command
+ parse_colors(&data[2], len-2);
+ } else if ( strncmp(data, "G,", 2) == 0) {
+ // get color comand
+ get_color(&data[2], len-2);
+ } else if ( strncmp(data, "F,", 2) == 0) {
+ // save to flash memory
+ save_rgb_config();
+ } else if ( strncmp(data, "L,", 2) == 0) {
+ // load from flash memory
+ load_rgb_config();
+ }
+}
+
+// Pico specific routines needed by lwip
+auto_init_mutex(lwip_mutex);
+static int lwip_mutex_count = 0;
+
+sys_prot_t sys_arch_protect(void)
+{
+ uint32_t owner;
+ if (!mutex_try_enter(&lwip_mutex, &owner))
+ {
+ if (owner != get_core_num())
+ {
+ // Wait until other core releases mutex
+ mutex_enter_blocking(&lwip_mutex);
+ }
+ }
+
+ lwip_mutex_count++;
+
+ return 0;
+}
+
+void sys_arch_unprotect(sys_prot_t pval)
+{
+ (void)pval;
+
+ if (lwip_mutex_count)
+ {
+ lwip_mutex_count--;
+ if (!lwip_mutex_count)
+ {
+ mutex_exit(&lwip_mutex);
+ }
+ }
+}
+
+uint32_t sys_now(void)
+{
+ return to_ms_since_boot( get_absolute_time() );
+}
diff --git a/usb_server.h b/usb_server.h
new file mode 100644
index 0000000..02c8e5f
--- /dev/null
+++ b/usb_server.h
@@ -0,0 +1,12 @@
+#ifndef USB_SERVER_H_
+#define USB_SERVER_H_
+
+#define INIT_IP4(a, b, c, d) \
+ { PP_HTONL(LWIP_MAKEU32(a, b, c, d)) }
+
+void usb_server_init(void);
+const void ws_receive_handler(uint8_t *data, uint16_t len);
+const void ws_open_handler(struct ws_state * wss);
+
+#endif
+
diff --git a/websocket.c b/websocket.c
new file mode 100644
index 0000000..9df358a
--- /dev/null
+++ b/websocket.c
@@ -0,0 +1,470 @@
+#include
+
+#include "lwip/altcp.h"
+#include "lwip/debug.h"
+#include "mbedtls/base64.h"
+#include "mbedtls/sha1.h"
+
+#include "websocket.h"
+
+static const char WS_GUID[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+static const char WS_RESPONSE[] = "HTTP/1.1 101 Switching Protocols\r\n" \
+ "Upgrade: websocket\r\n" \
+ "Connection: Upgrade\r\n" \
+ "Sec-WebSocket-Accept: ";
+
+static uint8_t buf[WS_BUFFER_SIZE];
+static uint16_t buf_len=0;
+
+static tWSHandler ws_receive_cb = NULL;
+static tWSOpenHandler ws_open_cb = NULL;
+
+static struct ws_state * ws_connections;
+static uint8_t ws_num_conns = 0;
+
+static struct ws_state* ws_state_alloc(void);
+static void ws_state_init(struct ws_state *wss);
+static void ws_state_free(struct ws_state *wss);
+static void ws_server_init_pcb( struct altcp_pcb *pcb, uint16_t port);
+static err_t ws_accept(void *arg, struct altcp_pcb *pcb, err_t err);
+static err_t ws_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err);
+static err_t ws_sent(void *arg, struct altcp_pcb *pcb, uint16_t len);
+static void ws_err (void *arg, err_t err);
+static err_t ws_close_conn(struct altcp_pcb *pcb, struct ws_state *wss);
+static err_t ws_close_or_abort_conn(struct altcp_pcb *pcb, struct ws_state *wss, uint8_t abort_conn);
+static err_t ws_poll(void *arg, struct altcp_pcb *pcb);
+static err_t ws_handshake(struct altcp_pcb *pcb, struct ws_state *wss, struct pbuf *p);
+static err_t ws_read(struct altcp_pcb *pcb, struct ws_state *wss, struct pbuf *p);
+static err_t ws_send(struct ws_state *wss, uint8_t *data, uint16_t len);
+
+// allocate memory for ws_state instance
+static struct ws_state * ws_state_alloc(void) {
+ struct ws_state *ret = WS_ALLOC_WS_STATE();
+
+ if ( ret != NULL) {
+ ws_state_init(ret);
+ if (ws_connections == NULL) {
+ ws_connections = ret;
+ } else {
+ struct ws_state *last;
+ for (last=ws_connections; last->next != NULL; last=last->next);
+ LWIP_ASSERT("last != NULL", last != NULL);
+ last->next = ret;
+ }
+ }
+
+ return ret;
+}
+
+// initiate ws_state instance
+static void ws_state_init(struct ws_state *wss) {
+ memset(wss, 0, sizeof(struct ws_state));
+ wss->active = false;
+}
+
+// free memory from ws_state instance
+static void ws_state_free(struct ws_state *wss) {
+ if (wss != NULL) {
+ if (ws_connections == wss) {
+ ws_connections = wss->next;
+ } else {
+ struct ws_state * last;
+ for (last = ws_connections; last->next != NULL; last = last->next) {
+ if (last->next == wss) {
+ last->next = wss->next;
+ break;
+ }
+ }
+ }
+ mem_free(wss);
+ }
+}
+
+// initiate websocket server on specified pcb
+static void ws_server_init_pcb( struct altcp_pcb *pcb, uint16_t port) {
+ err_t err;
+
+ if (pcb) {
+ altcp_setprio(pcb, TCP_PRIO_MIN);
+ err = altcp_bind(pcb, IP_ANY_TYPE, port);
+ LWIP_UNUSED_ARG(err);
+ LWIP_ASSERT("ws_server_init: tcp_bind failed", err == ERR_OK);
+ pcb = altcp_listen(pcb);
+ LWIP_ASSERT("ws_server_init: tcp_listen failed", pcb != NULL);
+ altcp_accept(pcb, ws_accept);
+ }
+}
+
+// initiate a websocket server
+void ws_server_init(void) {
+ struct altcp_pcb *pcb = altcp_tcp_new_ip_type(IPADDR_TYPE_ANY);
+ LWIP_ASSERT("ws_server_init: tcp_new failed", pcb != NULL);
+ ws_server_init_pcb(pcb, WS_PORT);
+}
+
+// set ws_receive_handler
+void ws_set_receive_handler( tWSHandler ws_handler)
+{
+ ws_receive_cb = ws_handler;
+}
+
+// set ws_open_handler
+void ws_set_open_handler( tWSOpenHandler ws_handler)
+{
+ ws_open_cb = ws_handler;
+}
+
+// callback for accepted websocket connection
+static err_t ws_accept(void *arg, struct altcp_pcb *pcb, err_t err) {
+ struct ws_state *wss;
+ LWIP_UNUSED_ARG(err);
+ LWIP_UNUSED_ARG(arg);
+ LWIP_DEBUGF(WS_DEBUG, ("ws_accept %p / %p\n", (void *)pcb, arg));
+
+ if ((err != ERR_OK) || (pcb == NULL)) {
+ return ERR_VAL;
+ }
+
+ // create new ws_state object
+ wss = ws_state_alloc();
+ if (wss == NULL) {
+ LWIP_DEBUGF(WS_DEBUG, ("ws_accept: Out of memory, RST\n"));
+ return ERR_MEM;
+ }
+ wss->pcb = pcb;
+
+ // make ws_state object the argument of callbacks
+ altcp_arg(pcb, wss);
+
+ // register callbacks for tcp events
+ altcp_recv(pcb, ws_recv);
+ altcp_sent(pcb, ws_sent);
+ altcp_poll(pcb, ws_poll, WS_POLL_INTERVAL);
+ altcp_err(pcb, ws_err);
+
+ return ERR_OK;
+}
+
+// call when data is received
+static err_t ws_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err) {
+ struct ws_state *wss = (struct ws_state *) arg;
+
+ if ((err != ERR_OK) || (p == NULL) || (wss == NULL)) {
+ // error or closed by client
+ if (p != NULL) {
+ // inform TCP that we have taken the data
+ altcp_recved(pcb, p->tot_len);
+ pbuf_free(p);
+ }
+ if (wss == NULL) {
+ // should not occur
+ LWIP_DEBUGF(WS_DEBUG, ("Error, ws_recv: wss is NULL, close\n"));
+ }
+ ws_close_conn(pcb, wss);
+ return ERR_OK;
+ }
+
+ if (wss->active) {
+ // process websocket message
+ err = ws_read(pcb, wss, p);
+ } else {
+ // init websocket connection
+ LWIP_DEBUGF(WS_DEBUG, ("ws_recv: websocket inactive, checking for handshake\n"));
+
+ err = ws_handshake(pcb, wss, p);
+ }
+
+ // inform TCP that we have taken the data.
+ altcp_recved(pcb, p->tot_len);
+ pbuf_free(p);
+
+ if (err == ERR_CLSD) {
+ ws_close_conn(pcb, wss);
+ }
+
+ return ERR_OK;
+}
+
+// called when data has been sent over the websocket
+static err_t ws_sent(void *arg, struct altcp_pcb *pcb, uint16_t len) {
+ (void) pcb;
+
+ struct ws_state *wss = (struct ws_state *)arg;
+
+ LWIP_DEBUGF(WS_DEBUG | LWIP_DBG_TRACE, ("ws_sent %p\n", (void*) pcb));
+
+ LWIP_UNUSED_ARG(len);
+
+ if (wss == NULL) {
+ return ERR_OK;
+ }
+
+ wss->retries = 0;
+
+ return ERR_OK;
+}
+
+// called when there is a websocket error
+static void ws_err (void *arg, err_t err) {
+ struct ws_state *wss = (struct ws_state *) arg;
+ LWIP_UNUSED_ARG(err);
+
+ LWIP_DEBUGF(WS_DEBUG, ("ws_err: %s", lwip_strerr(err)));
+
+ if (wss != NULL) {
+ ws_state_free(wss);
+ }
+}
+
+// initiate close of connection
+static err_t ws_close_conn(struct altcp_pcb *pcb, struct ws_state *wss) {
+ return ws_close_or_abort_conn(pcb, wss, 0);
+}
+
+// call when closing connection or connection was aborted
+static err_t ws_close_or_abort_conn(struct altcp_pcb *pcb, struct ws_state *wss,
+ uint8_t abort_conn) {
+
+ err_t err;
+ LWIP_DEBUGF(WS_DEBUG, ("Closing connection %p\n", (void *)pcb));
+
+ // clear callbacks
+ altcp_arg(pcb, NULL);
+ altcp_recv(pcb, NULL);
+ altcp_sent(pcb, NULL);
+ altcp_poll(pcb, NULL, 0);
+ altcp_err(pcb, NULL);
+
+ // remove and free memory from ws_state object
+ if (wss != NULL) {
+ ws_state_free(wss);
+ }
+
+ if (abort_conn) {
+ altcp_abort(pcb);
+ return ERR_OK;
+ }
+ err = altcp_close(pcb);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(WS_DEBUG, ("Error %d closing %p\n", err, (void *)pcb));
+ // error closing, try again later in poll
+ altcp_poll(pcb, ws_poll, WS_POLL_INTERVAL);
+ }
+ return err;
+}
+
+// callback for polling process
+static err_t ws_poll(void *arg, struct altcp_pcb *pcb) {
+ struct ws_state *wss = (struct ws_state *) arg;
+ if (wss == NULL) {
+ err_t closed;
+ LWIP_DEBUGF(WS_DEBUG, ("ws_poll: arg is NULL, close\n"));
+ closed = ws_close_conn(pcb, NULL);
+ LWIP_UNUSED_ARG(closed);
+ if (closed == ERR_MEM) {
+ altcp_abort(pcb);
+ return ERR_ABRT;
+ }
+ return ERR_OK;
+ } else {
+ wss->retries++;
+ if (wss->retries == WS_MAX_RETRIES) {
+ LWIP_DEBUGF(WS_DEBUG, ("ws_poll: too may retries, close\n"));
+ ws_close_conn(pcb, wss);
+ return ERR_OK;
+ }
+ }
+
+ return ERR_OK;
+}
+
+// check for and complete handshake with client
+static err_t ws_handshake(struct altcp_pcb *pcb, struct ws_state *wss, struct pbuf *p){
+ uint8_t *data = (uint8_t *) p->payload;
+ uint16_t len = p->len;
+
+ // check if client is initiating a websocket connecttion
+ if (strstr(data, "Upgrade: websocket")) {
+ LWIP_DEBUGF(WS_DEBUG, ("ws_handshake: received websocket upgrade request\n"));
+
+ // search for websocket security key
+ char *key_start = strstr(data, "Sec-WebSocket-Key: ");
+
+ if (key_start) {
+ key_start += 19;
+ const char *key_end = strstr(key_start, "\r\n");
+ if (key_end) {
+ char key[64];
+ uint16_t key_len = key_end-key_start;
+ if ( (key_len>0) && (key_len + sizeof(WS_GUID) < sizeof(key)) ) {
+ // create response key by concatenating with websocket GUID,
+ // taking SHA1 hash, then encoding in base 64
+ strncpy(key, key_start, key_len);
+ strlcpy(&key[key_len], WS_GUID, sizeof(key)-key_len);
+
+ key_len += sizeof(WS_GUID)-1;
+ unsigned char key_sha1[20];
+ unsigned char key_base64[29];
+ size_t encoded_len;
+ mbedtls_sha1( (unsigned char *) key, key_len, key_sha1);
+ mbedtls_base64_encode( key_base64, 29, &encoded_len, key_sha1, 20);
+
+ // create response packet with encoded response key
+ unsigned char response[sizeof(WS_RESPONSE) + sizeof(key_base64)+3];
+ size_t count = sprintf(response, "%s%s\r\n\r\n", WS_RESPONSE, key_base64);
+
+ // send completed data packet
+ LWIP_DEBUGF(WS_DEBUG, ("ws_handshake: sending response\n"));
+ if(altcp_write(pcb, response, count, TCP_WRITE_FLAG_COPY) == ERR_OK) {
+ wss->active = true;
+ }
+
+ if (ws_open_cb != NULL) {
+ ws_open_cb(wss);
+ }
+
+ return ERR_OK;
+ }
+ }
+
+ LWIP_DEBUGF(WS_DEBUG, ("ws_handshake: key overflow\n"));
+ return ERR_MEM;
+ } else {
+ LWIP_DEBUGF(WS_DEBUG, ("ws_handshake: key not received\n"));
+ return ERR_ARG;
+ }
+ }
+
+ LWIP_DEBUGF(WS_DEBUG, ("ws_handshake: not a websocket request\n"));
+ return ERR_ARG;
+}
+
+// handle reading of websocket data and pass to ws_receive_cb
+static err_t ws_read(struct altcp_pcb *pcb, struct ws_state *wss, struct pbuf *p) {
+ (void) pcb;
+ uint8_t *data = (uint8_t *) p->payload;
+ uint16_t len = p->len;
+
+ if (data != NULL && len > 1) {
+ // successful read, reset timeout
+ wss->retries = 0;
+
+ uint8_t fin = data[0] & 0x80;
+ uint8_t opcode = data[0] & 0x0F;
+ uint8_t masked = data[1] & 0x80;
+ uint16_t msg_len = data[1] & 0x7F;
+ uint8_t *msg;
+
+ switch (msg_len) {
+ case 126: // next two bytes are length
+ memcpy(&msg_len, &data[2], 2);
+ if (len >= 8) {
+ msg = &data[8];
+ }
+ break;
+ case 127: // next four bytes are length
+ // lwIP's pbuf only handles 16-bit lengths, so error
+ LWIP_DEBUGF(WS_DEBUG, ("ws_read: received 64-bit length %u\n", msg_len));
+ return ERR_MEM;
+ default:
+ if (len >= 6) {
+ msg = &data[6];
+ }
+ break;
+ }
+ switch (opcode) {
+ case OP_CONT:
+ LWIP_DEBUGF(WS_DEBUG, ("ws_read: received continuation frame\n"));
+ case OP_TEXT:
+ LWIP_DEBUGF(WS_DEBUG, ("ws_read: received text data\n"));
+ case OP_BINARY:
+ LWIP_DEBUGF(WS_DEBUG, ("ws_read: decoding data, len=%u\n", msg_len));
+ if (msg && ws_receive_cb != NULL) {
+ // unmask the data if mask bit is received
+ if (masked) {
+ uint8_t *mask = &data[2];
+
+ for (int i=0; i WS_BUFFER_SIZE) {
+ LWIP_DEBUGF(WS_DEBUG, ("ws_read: message exceeds buffer size %u+%u\n", buf_len, msg_len));
+ return ERR_MEM;
+ }
+
+ memcpy(&buf[buf_len], msg, msg_len);
+ buf_len += msg_len;
+
+ if (fin) { // last packet in message, process completed message
+ ws_receive_cb(buf, buf_len);
+ }
+ }
+ break;
+ case OP_CLOSE:
+ LWIP_DEBUGF(WS_DEBUG, ("ws_read: close request"));
+ return ERR_CLSD;
+ case OP_PING:
+ // control frames cannot exceed 125 bytes in length
+ if (msg && msg_len <= 125) {
+ // send back a pong
+ uint8_t pong[2+msg_len];
+ pong[0]=0x8A;
+ pong[1]=msg_len;
+ memcpy(&pong[2], msg, msg_len);
+
+ return ws_send(wss, pong, msg_len+2);
+ }
+ return ERR_ARG;
+ case OP_PONG: // no response required for pong
+ return ERR_OK;
+ default:
+ LWIP_DEBUGF(WS_DEBUG, ("ws_read: invalid opcode %02X\n", opcode));
+ return ERR_ARG;
+ }
+
+ return ERR_OK;
+ }
+ LWIP_DEBUGF(WS_DEBUG, ("ws_read: received empty payload\n"));
+ return ERR_VAL;
+}
+
+static err_t ws_send(struct ws_state *wss, uint8_t *data, uint16_t len) {
+ uint8_t buf[128];
+ buf[0] = 0x81;
+ buf[1] = len & 0x7F;
+ memcpy(&buf[2], data, len);
+
+ err_t err;
+ err = altcp_write(wss->pcb, buf, len+2, TCP_WRITE_FLAG_COPY);
+ if (err == ERR_OK) {
+ altcp_output(wss->pcb);
+ }
+
+ return err;
+}
+
+void ws_send_all(uint8_t *data, uint16_t len) {
+ // send message to all connections
+ if (ws_connections != NULL) {
+ struct ws_state *wss;
+ err_t err;
+ for (wss=ws_connections; wss != NULL; wss=wss->next) {
+ err = ws_send(wss, data, len);
+ if (err != ERR_OK ) {
+ LWIP_DEBUGF(WS_DEBUG, ("ws_send_all: error sending to %p\n", wss));
+ }
+ }
+ }
+}
diff --git a/websocket.h b/websocket.h
new file mode 100644
index 0000000..adeb8cc
--- /dev/null
+++ b/websocket.h
@@ -0,0 +1,36 @@
+#ifndef WEBSOCKET_H_
+#define WEBSOCKET_H_
+
+#define WS_PORT 8080
+#define WS_TIMEOUT 10
+#define WS_DEBUG LWIP_DBG_ON
+#define WS_MAX_RETRIES 10
+#define WS_POLL_INTERVAL 60 // WS_POLL_INTERVAL/2 seconds
+#define WS_MAX_CONN 4
+#define WS_BUFFER_SIZE 512
+
+#define OP_CONT 0x00
+#define OP_TEXT 0x01
+#define OP_BINARY 0x02
+#define OP_CLOSE 0x08
+#define OP_PING 0x09
+#define OP_PONG 0x0A
+
+struct ws_state {
+ bool active;
+ uint8_t retries;
+ struct altcp_pcb *pcb;
+ struct ws_state *next;
+};
+
+#define WS_ALLOC_WS_STATE() (struct ws_state *)mem_malloc(sizeof(struct ws_state))
+
+typedef void (* tWSHandler ) (uint8_t *data, uint16_t len);
+typedef void (* tWSOpenHandler ) (struct ws_state * wss);
+
+void ws_server_init(void);
+void ws_send_all(uint8_t *data, uint16_t len);
+void ws_set_receive_handler( tWSHandler ws_handler);
+void ws_set_open_handler( tWSOpenHandler ws_handler);
+
+#endif