6 Commits

Author SHA1 Message Date
kenji 84aa9a2e74 add Pico 2 W firmware 2025-08-18 08:09:28 -04:00
kenji cf17420973 update VID/PID/serial 2025-08-12 11:30:03 -04:00
kenji 692c5af8c9 code cleanup & fix compile with pico-sdk 2.2.0 2025-08-12 07:49:12 -04:00
kenji 4f313b3336 improve websocket implementation 2025-07-28 10:15:49 -04:00
kenji 99824ee2a6 fix mouse reports and improve timeout handling 2025-07-20 21:34:56 -04:00
kenji c7d70c669b send keys over WebSocket 2025-07-20 17:43:10 -04:00
14 changed files with 559 additions and 522 deletions
+6 -1
View File
@@ -1,7 +1,10 @@
cmake_minimum_required(VERSION 3.13) cmake_minimum_required(VERSION 3.13)
set(PROJECT webkeyboard) set(PROJECT webkeyboard)
set(PICO_SDK_PATH /home/kenji/programming/pico/c/pico-sdk) set(PICO_SDK_PATH /home/kenji/programming/pico/c/pico-sdk)
if (NOT DEFINED PICO_BOARD)
set(PICO_BOARD pico_w) set(PICO_BOARD pico_w)
endif()
#set(PICO_BOARD pico2_w)
include (${PICO_SDK_PATH}/external/pico_sdk_import.cmake) include (${PICO_SDK_PATH}/external/pico_sdk_import.cmake)
project(${PROJECT} C CXX ASM) project(${PROJECT} C CXX ASM)
@@ -37,7 +40,8 @@ target_sources(${PROJECT} PRIVATE
) )
pico_enable_stdio_usb(${PROJECT} 1) pico_enable_stdio_usb(${PROJECT} 1)
pico_enable_stdio_uart(${PROJECT} 1)
set_target_properties(${PROJECT} PROPERTIES OUTPUT_NAME "${PROJECT}-${PICO_BOARD}")
pico_add_extra_outputs(${PROJECT}) pico_add_extra_outputs(${PROJECT})
@@ -49,6 +53,7 @@ target_link_libraries(${PROJECT}
pico_stdlib pico_stdlib
pico_multicore pico_multicore
pico_mbedtls pico_mbedtls
tinyusb_board
tinyusb_device tinyusb_device
) )
+16 -13
View File
@@ -2,28 +2,31 @@
## About ## About
This project turns a Raspberry Pi Pico W into a web-based USB HID keyboard This project turns a Raspberry Pi Pico W or Raspberry Pi Pico 2 W into a
device. Users can access a webpage served over HTTP to enter keyboard input web-based USB HID keyboard device. Users can access a webpage served over HTTP
onto the host device of the Raspberry Pi Pico W. Input keys can be entered to enter keyboard input onto the host device of the Raspberry Pi Pico (2) W.
through a keyboard on the client device or by pressing on the keys on the Input keys can be entered through a keyboard on the client device or by
graphical interface served on the webpage. pressing on the keys on the graphical interface served on the webpage.
![Web interface for Web Keyboard](web-ui.jpg) ![Web interface for Web Keyboard](web-ui.jpg)
## Setup ## Setup
Download the webkeyboard.uf2 file from the latest [Installation and demo video](https://youtu.be/uORnxt5DLTw)
[release](https://git.kkozai.com/kenji/webkeyboard/releases) and flash
onto the Raspberry Pi Pico W by holding down the BOOTSEL button while plugging
into your computer so that it appears as a USB drive, then transfer the
firmware file onto the Pico W. After unmounting, connect the Raspberry Pi Pico
W with a micro USB cable to the host computer or other device.
On initial boot, the Raspberry Pi Pico W will enter Access Point mode with Download the appropriate firmware file for your board, either
webkeyboard-pico_w.uf2 or webkeyboard-pico2_w.uf2, from the latest
[release](https://git.kkozai.com/kenji/webkeyboard/releases) and flash
onto the Raspberry Pi Pico (2) W by holding down the BOOTSEL button while
plugging into your computer so that it appears as a USB drive, then transfer the
firmware file onto the Pico (2) W. After unmounting, connect the Raspberry Pi
Pico (2) W with a micro USB cable to the host computer or other device.
On initial boot, the Raspberry Pi Pico (2) W will enter Access Point mode with
SSID "picokb" and password "password". Connect to this network with another SSID "picokb" and password "password". Connect to this network with another
device and open a browser window to http://192.168.0.1. device and open a browser window to http://192.168.0.1.
Go to the "Configure W-Fi" page and enter the SSID and password to the Go to the "Configure W-Fi" page and enter the SSID and password to the
network that you would like the Raspberry Pi Pico W to connect. Optionally, network that you would like the Raspberry Pi Pico (2) W to connect. Optionally,
also enter a hostname for the device and static IP information. Hit the "Save" also enter a hostname for the device and static IP information. Hit the "Save"
button to save the network information to device flash. button to save the network information to device flash.
+5 -2
View File
@@ -250,8 +250,6 @@ window.onload = (event) => {
window.addEventListener("keyup", onKeyUp, true); window.addEventListener("keyup", onKeyUp, true);
socket = new WebSocket("ws://" + window.location.hostname + ":8080/"); socket = new WebSocket("ws://" + window.location.hostname + ":8080/");
//socket.onopen = function (event) { document.getElementById("websocket").innerHTML="connected"; };
//socket.onclose = function (event) { document.getElementById("websocket").innerHTML="closed"; };
socket.onmessage = function (event) { updateLEDs(event.data); }; socket.onmessage = function (event) { updateLEDs(event.data); };
} }
@@ -332,6 +330,11 @@ function sendKeys(curKeys, is_mouse) {
} else { } else {
socket.send("K: " + curKeys); socket.send("K: " + curKeys);
} }
} else if (socket.readyState == WebSocket.CLOSED) {
socket = new WebSocket("ws://" + window.location.hostname + ":8080/");
setTimeout( function () { sendKeys(curKeys, is_mouse); }, 10);
} else {
setTimeout( function () { sendKeys(curKeys, is_mouse); }, 10);
} }
} }
-15
View File
@@ -1,15 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Keyboard Indicators</title>
</head>
<body>
<div id="num" class="<!--#num-->">NumLock</div>
<div id="caps" class="<!--#caps-->">CapsLock</div>
<div id="scroll" class="<!--#scroll-->">ScrollLock</div>
</body>
</html>
+3 -2
View File
@@ -1,5 +1,5 @@
#ifndef MBEDTLS_CONFIG_EXAMPLES_COMMON_H #ifndef MBEDTLS_CONFIG_H
#define MBEDTLS_CONFIG_EXAMPLES_COMMON_H #define MBEDTLS_CONFIG_H
/* Workaround for some mbedtls source files using INT_MAX without including limits.h */ /* Workaround for some mbedtls source files using INT_MAX without including limits.h */
#include <limits.h> #include <limits.h>
@@ -11,6 +11,7 @@
#define MBEDTLS_ALLOW_PRIVATE_ACCESS #define MBEDTLS_ALLOW_PRIVATE_ACCESS
#define MBEDTLS_HAVE_TIME #define MBEDTLS_HAVE_TIME
#define MBEDTLS_PLATFORM_MS_TIME_ALT
#define MBEDTLS_CIPHER_MODE_CBC #define MBEDTLS_CIPHER_MODE_CBC
#define MBEDTLS_ECP_DP_SECP192R1_ENABLED #define MBEDTLS_ECP_DP_SECP192R1_ENABLED
+324 -360
View File
@@ -13,46 +13,6 @@ static const unsigned char data_fsdata_c[] = {
0x2f, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0xd, 0xa, 0xd, 0xa, 0x2f, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0xd, 0xa, 0xd, 0xa,
}; };
static const unsigned char data_indicators_shtml[] = {
/* /indicators.shtml */
0x2f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x2e, 0x73, 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, 0x4b, 0x65,
0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x20, 0x49, 0x6e, 0x64,
0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x3c, 0x2f, 0x74,
0x69, 0x74, 0x6c, 0x65, 0x3e, 0xa, 0x3c, 0x2f, 0x68, 0x65,
0x61, 0x64, 0x3e, 0xa, 0xa, 0x3c, 0x62, 0x6f, 0x64, 0x79,
0x3e, 0xa, 0x3c, 0x64, 0x69, 0x76, 0x20, 0x69, 0x64, 0x3d,
0x22, 0x6e, 0x75, 0x6d, 0x22, 0x20, 0x63, 0x6c, 0x61, 0x73,
0x73, 0x3d, 0x22, 0x3c, 0x21, 0x2d, 0x2d, 0x23, 0x6e, 0x75,
0x6d, 0x2d, 0x2d, 0x3e, 0x22, 0x3e, 0x4e, 0x75, 0x6d, 0x4c,
0x6f, 0x63, 0x6b, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0xa,
0x3c, 0x64, 0x69, 0x76, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x63,
0x61, 0x70, 0x73, 0x22, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73,
0x3d, 0x22, 0x3c, 0x21, 0x2d, 0x2d, 0x23, 0x63, 0x61, 0x70,
0x73, 0x2d, 0x2d, 0x3e, 0x22, 0x3e, 0x43, 0x61, 0x70, 0x73,
0x4c, 0x6f, 0x63, 0x6b, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e,
0xa, 0x3c, 0x64, 0x69, 0x76, 0x20, 0x69, 0x64, 0x3d, 0x22,
0x73, 0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x22, 0x20, 0x63, 0x6c,
0x61, 0x73, 0x73, 0x3d, 0x22, 0x3c, 0x21, 0x2d, 0x2d, 0x23,
0x73, 0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x2d, 0x2d, 0x3e, 0x22,
0x3e, 0x53, 0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x4c, 0x6f, 0x63,
0x6b, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0xa, 0x3c, 0x2f,
0x62, 0x6f, 0x64, 0x79, 0x3e, 0xa, 0xa, 0x3c, 0x2f, 0x68,
0x74, 0x6d, 0x6c, 0x3e, 0xa, };
static const unsigned char data_wifi_shtml[] = { static const unsigned char data_wifi_shtml[] = {
/* /wifi.shtml */ /* /wifi.shtml */
0x2f, 0x77, 0x69, 0x66, 0x69, 0x2e, 0x73, 0x68, 0x74, 0x6d, 0x6c, 0, 0x2f, 0x77, 0x69, 0x66, 0x69, 0x2e, 0x73, 0x68, 0x74, 0x6d, 0x6c, 0,
@@ -1015,27 +975,7 @@ static const unsigned char data_index_html[] = {
0x77, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x77, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x2e, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x20,
0x2b, 0x20, 0x22, 0x3a, 0x38, 0x30, 0x38, 0x30, 0x2f, 0x22, 0x2b, 0x20, 0x22, 0x3a, 0x38, 0x30, 0x38, 0x30, 0x2f, 0x22,
0x29, 0x3b, 0xa, 0x9, 0x2f, 0x2f, 0x73, 0x6f, 0x63, 0x6b, 0x29, 0x3b, 0xa, 0x9, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74,
0x65, 0x74, 0x2e, 0x6f, 0x6e, 0x6f, 0x70, 0x65, 0x6e, 0x20,
0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e,
0x20, 0x28, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x29, 0x20, 0x7b,
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, 0x77, 0x65, 0x62, 0x73,
0x6f, 0x63, 0x6b, 0x65, 0x74, 0x22, 0x29, 0x2e, 0x69, 0x6e,
0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x3d, 0x22, 0x63,
0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x22, 0x3b,
0x20, 0x7d, 0x3b, 0xa, 0x9, 0x2f, 0x2f, 0x73, 0x6f, 0x63,
0x6b, 0x65, 0x74, 0x2e, 0x6f, 0x6e, 0x63, 0x6c, 0x6f, 0x73,
0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
0x6f, 0x6e, 0x20, 0x28, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x29,
0x20, 0x7b, 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, 0x77, 0x65,
0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x22, 0x29, 0x2e,
0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x3d,
0x22, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x22, 0x3b, 0x20,
0x7d, 0x3b, 0xa, 0x9, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74,
0x2e, 0x6f, 0x6e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x6f, 0x6e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x20, 0x28, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x29, 0x20, 0x6e, 0x20, 0x28, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x29, 0x20,
@@ -1221,325 +1161,351 @@ static const unsigned char data_index_html[] = {
0x74, 0x2e, 0x73, 0x65, 0x6e, 0x64, 0x28, 0x22, 0x4b, 0x3a, 0x74, 0x2e, 0x73, 0x65, 0x6e, 0x64, 0x28, 0x22, 0x4b, 0x3a,
0x20, 0x22, 0x20, 0x2b, 0x20, 0x63, 0x75, 0x72, 0x4b, 0x65, 0x20, 0x22, 0x20, 0x2b, 0x20, 0x63, 0x75, 0x72, 0x4b, 0x65,
0x79, 0x73, 0x29, 0x3b, 0xa, 0x9, 0x9, 0x7d, 0xa, 0x9, 0x79, 0x73, 0x29, 0x3b, 0xa, 0x9, 0x9, 0x7d, 0xa, 0x9,
0x7d, 0xa, 0x7d, 0xa, 0xa, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20,
0x69, 0x6f, 0x6e, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x28, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x72, 0x65,
0x4b, 0x65, 0x79, 0x73, 0x28, 0x64, 0x69, 0x76, 0x5f, 0x69, 0x61, 0x64, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x20, 0x3d,
0x64, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x69, 0x73, 0x3d, 0x20, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65,
0x74, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x6c, 0x65, 0x74, 0x20, 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, 0x65, 0x74,
0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x28, 0x20, 0x66,
0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x29,
0x20, 0x7b, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x4b, 0x65, 0x79,
0x73, 0x28, 0x63, 0x75, 0x72, 0x4b, 0x65, 0x79, 0x73, 0x2c,
0x20, 0x69, 0x73, 0x5f, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x29,
0x3b, 0x20, 0x7d, 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, 0x74, 0x69,
0x6f, 0x6e, 0x20, 0x28, 0x29, 0x20, 0x7b, 0x20, 0x73, 0x65,
0x6e, 0x64, 0x4b, 0x65, 0x79, 0x73, 0x28, 0x63, 0x75, 0x72,
0x4b, 0x65, 0x79, 0x73, 0x2c, 0x20, 0x69, 0x73, 0x5f, 0x6d,
0x6f, 0x75, 0x73, 0x65, 0x29, 0x3b, 0x20, 0x7d, 0x2c, 0x20,
0x31, 0x30, 0x29, 0x3b, 0xa, 0x9, 0x7d, 0xa, 0x7d, 0xa,
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, 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, 0x76, 0x20, 0x3d, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65,
0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x6e, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45,
0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x64, 0x69, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x64, 0x69,
0x76, 0x5f, 0x69, 0x64, 0x29, 0x3b, 0xa, 0x9, 0x66, 0x6f, 0x76, 0x22, 0x29, 0x3b, 0xa, 0x9, 0x9, 0x9, 0x69, 0x66,
0x72, 0x20, 0x28, 0x6c, 0x65, 0x74, 0x20, 0x72, 0x6f, 0x77, 0x20, 0x28, 0x6b, 0x65, 0x79, 0x2e, 0x69, 0x64, 0x29, 0x20,
0x20, 0x6f, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x69, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x6e, 0x65, 0x77, 0x44,
0x73, 0x74, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x6c, 0x65, 0x69, 0x76, 0x2e, 0x69, 0x64, 0x20, 0x3d, 0x20, 0x6b, 0x65,
0x74, 0x20, 0x6e, 0x65, 0x77, 0x52, 0x6f, 0x77, 0x20, 0x3d, 0x79, 0x2e, 0x69, 0x64, 0x3b, 0xa, 0x9, 0x9, 0x9, 0x9,
0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x6e, 0x65, 0x77, 0x44, 0x69, 0x76, 0x2e, 0x69, 0x6e, 0x6e,
0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x20, 0x3d, 0x20, 0x6b,
0x65, 0x6e, 0x74, 0x28, 0x22, 0x64, 0x69, 0x76, 0x22, 0x29, 0x65, 0x79, 0x2e, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3b, 0xa,
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, 0x9, 0x9, 0x9, 0x9, 0x6e, 0x65, 0x77, 0x44, 0x69, 0x76,
0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x2e, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65,
0x20, 0x3d, 0x20, 0x6b, 0x65, 0x79, 0x2e, 0x6c, 0x61, 0x62, 0x20, 0x3d, 0x20, 0x22, 0x6b, 0x65, 0x79, 0x22, 0x3b, 0xa,
0x65, 0x6c, 0x3b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x6e, 0x65, 0x9, 0x9, 0x9, 0x9, 0x69, 0x66, 0x20, 0x28, 0x6b, 0x65,
0x77, 0x44, 0x69, 0x76, 0x2e, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x79, 0x2e, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x29, 0x20,
0x4e, 0x61, 0x6d, 0x65, 0x20, 0x3d, 0x20, 0x22, 0x6b, 0x65, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x9, 0x6e, 0x65, 0x77,
0x79, 0x22, 0x3b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x69, 0x66, 0x44, 0x69, 0x76, 0x2e, 0x61, 0x64, 0x64, 0x45, 0x76, 0x65,
0x20, 0x28, 0x6b, 0x65, 0x79, 0x2e, 0x72, 0x65, 0x70, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72,
0x61, 0x74, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x28, 0x22, 0x74, 0x6f, 0x75, 0x63, 0x68, 0x73, 0x74, 0x61,
0x9, 0x6e, 0x65, 0x77, 0x44, 0x69, 0x76, 0x2e, 0x61, 0x64, 0x72, 0x74, 0x22, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74,
0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x65, 0x76, 0x65, 0x6e, 0x74,
0x65, 0x6e, 0x65, 0x72, 0x28, 0x22, 0x74, 0x6f, 0x75, 0x63, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9,
0x68, 0x73, 0x74, 0x61, 0x72, 0x74, 0x22, 0x2c, 0x20, 0x66, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x65, 0x76,
0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x65, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74,
0x76, 0x65, 0x6e, 0x74, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x28, 0x29, 0x3b, 0x20, 0x70, 0x72, 0x65, 0x73, 0x73, 0x4b,
0x9, 0x9, 0x9, 0x9, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x65, 0x79, 0x28, 0x6b, 0x65, 0x79, 0x2e, 0x69, 0x64, 0x2c,
0x70, 0x72, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x66, 0x20, 0x74, 0x72, 0x75, 0x65, 0x29, 0x3b, 0x20, 0x7d, 0x29,
0x61, 0x75, 0x6c, 0x74, 0x28, 0x29, 0x3b, 0x20, 0x70, 0x72, 0x3b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x9, 0x6e, 0x65, 0x77,
0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x28, 0x6b, 0x65, 0x79, 0x44, 0x69, 0x76, 0x2e, 0x61, 0x64, 0x64, 0x45, 0x76, 0x65,
0x2e, 0x69, 0x64, 0x2c, 0x20, 0x74, 0x72, 0x75, 0x65, 0x29, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72,
0x3b, 0x20, 0x7d, 0x29, 0x3b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x28, 0x22, 0x74, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x6e, 0x64,
0x9, 0x6e, 0x65, 0x77, 0x44, 0x69, 0x76, 0x2e, 0x61, 0x64, 0x22, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x6e, 0x20, 0x28, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x29, 0x20,
0x65, 0x6e, 0x65, 0x72, 0x28, 0x22, 0x74, 0x6f, 0x75, 0x63, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x65, 0x76,
0x68, 0x65, 0x6e, 0x64, 0x22, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x65, 0x76, 0x65, 0x6e,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x65, 0x76, 0x65, 0x74, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x28, 0x29,
0x6e, 0x74, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x3b, 0x20, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x4b,
0x9, 0x9, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x65, 0x79, 0x28, 0x6b, 0x65, 0x79, 0x2e, 0x69, 0x64, 0x2c,
0x65, 0x76, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x66, 0x61, 0x75, 0x20, 0x74, 0x72, 0x75, 0x65, 0x29, 0x3b, 0x20, 0x7d, 0x29,
0x6c, 0x74, 0x28, 0x29, 0x3b, 0x20, 0x72, 0x65, 0x6c, 0x65, 0x3b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x9, 0x6e, 0x65, 0x77,
0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x28, 0x6b, 0x65, 0x79, 0x44, 0x69, 0x76, 0x2e, 0x61, 0x64, 0x64, 0x45, 0x76, 0x65,
0x2e, 0x69, 0x64, 0x2c, 0x20, 0x74, 0x72, 0x75, 0x65, 0x29, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72,
0x3b, 0x20, 0x7d, 0x29, 0x3b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x28, 0x22, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x64, 0x6f, 0x77,
0x9, 0x6e, 0x65, 0x77, 0x44, 0x69, 0x76, 0x2e, 0x61, 0x64, 0x6e, 0x22, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x6f, 0x6e, 0x20, 0x28, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x9,
0x65, 0x6e, 0x65, 0x72, 0x28, 0x22, 0x6d, 0x6f, 0x75, 0x73, 0x9, 0x9, 0x9, 0x9, 0x70, 0x72, 0x65, 0x73, 0x73, 0x4b,
0x65, 0x64, 0x6f, 0x77, 0x6e, 0x22, 0x2c, 0x20, 0x66, 0x75, 0x65, 0x79, 0x28, 0x6b, 0x65, 0x79, 0x2e, 0x69, 0x64, 0x2c,
0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x29, 0x20, 0x20, 0x74, 0x72, 0x75, 0x65, 0x29, 0x3b, 0x20, 0x7d, 0x29,
0x7b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x70, 0x72, 0x3b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x9, 0x6e, 0x65, 0x77,
0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x28, 0x6b, 0x65, 0x79, 0x44, 0x69, 0x76, 0x2e, 0x61, 0x64, 0x64, 0x45, 0x76, 0x65,
0x2e, 0x69, 0x64, 0x2c, 0x20, 0x74, 0x72, 0x75, 0x65, 0x29, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72,
0x3b, 0x20, 0x7d, 0x29, 0x3b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x28, 0x22, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x75, 0x70, 0x22,
0x9, 0x6e, 0x65, 0x77, 0x44, 0x69, 0x76, 0x2e, 0x61, 0x64, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e,
0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x20, 0x28, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x9,
0x65, 0x6e, 0x65, 0x72, 0x28, 0x22, 0x6d, 0x6f, 0x75, 0x73, 0x9, 0x9, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x4b,
0x65, 0x75, 0x70, 0x22, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x65, 0x79, 0x28, 0x6b, 0x65, 0x79, 0x2e, 0x69, 0x64, 0x2c,
0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x29, 0x20, 0x7b, 0xa, 0x20, 0x74, 0x72, 0x75, 0x65, 0x29, 0x3b, 0x20, 0x7d, 0x29,
0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x72, 0x65, 0x6c, 0x65, 0x3b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x7d, 0x20, 0x65, 0x6c,
0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x28, 0x6b, 0x65, 0x79, 0x73, 0x65, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x9,
0x2e, 0x69, 0x64, 0x2c, 0x20, 0x74, 0x72, 0x75, 0x65, 0x29, 0x6e, 0x65, 0x77, 0x44, 0x69, 0x76, 0x2e, 0x61, 0x64, 0x64,
0x3b, 0x20, 0x7d, 0x29, 0x3b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65,
0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0xa, 0x9, 0x6e, 0x65, 0x72, 0x28, 0x22, 0x74, 0x6f, 0x75, 0x63, 0x68,
0x73, 0x74, 0x61, 0x72, 0x74, 0x22, 0x2c, 0x20, 0x66, 0x75,
0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x65, 0x76,
0x65, 0x6e, 0x74, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x9,
0x9, 0x9, 0x9, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70,
0x72, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x66, 0x61,
0x75, 0x6c, 0x74, 0x28, 0x29, 0x3b, 0x20, 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, 0x9, 0x9, 0x6e, 0x65, 0x77, 0x44, 0x69, 0x76, 0x9, 0x9, 0x9, 0x9, 0x6e, 0x65, 0x77, 0x44, 0x69, 0x76,
0x2e, 0x61, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x2e, 0x61, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c,
0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x28, 0x22, 0x74, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x28, 0x22, 0x74,
0x6f, 0x75, 0x63, 0x68, 0x73, 0x74, 0x61, 0x72, 0x74, 0x22, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x6e, 0x64, 0x22, 0x2c, 0x20,
0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28,
0x20, 0x28, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x29, 0x20, 0x7b, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x29, 0x20, 0x7b, 0xa, 0x9,
0xa, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x65, 0x76, 0x65, 0x9, 0x9, 0x9, 0x9, 0x9, 0x65, 0x76, 0x65, 0x6e, 0x74,
0x6e, 0x74, 0x2e, 0x70, 0x72, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x44, 0x65,
0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x28, 0x29, 0x3b, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x28, 0x29, 0x3b, 0x20, 0x72,
0x20, 0x70, 0x72, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x28, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x28,
0x6b, 0x65, 0x79, 0x2e, 0x69, 0x64, 0x29, 0x3b, 0x20, 0x7d, 0x6b, 0x65, 0x79, 0x2e, 0x69, 0x64, 0x29, 0x3b, 0x20, 0x7d,
0x29, 0x3b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x9, 0x6e, 0x65, 0x29, 0x3b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x9, 0x6e, 0x65,
0x77, 0x44, 0x69, 0x76, 0x2e, 0x61, 0x64, 0x64, 0x45, 0x76, 0x77, 0x44, 0x69, 0x76, 0x2e, 0x61, 0x64, 0x64, 0x45, 0x76,
0x65, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65,
0x72, 0x28, 0x22, 0x74, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x6e, 0x72, 0x28, 0x22, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x64, 0x6f,
0x64, 0x22, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x77, 0x6e, 0x22, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74,
0x6f, 0x6e, 0x20, 0x28, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x29, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x29, 0x20, 0x7b, 0xa, 0x9,
0x20, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x65, 0x9, 0x9, 0x9, 0x9, 0x9, 0x70, 0x72, 0x65, 0x73, 0x73,
0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x65, 0x76, 0x65,
0x6e, 0x74, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x28,
0x29, 0x3b, 0x20, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65,
0x4b, 0x65, 0x79, 0x28, 0x6b, 0x65, 0x79, 0x2e, 0x69, 0x64, 0x4b, 0x65, 0x79, 0x28, 0x6b, 0x65, 0x79, 0x2e, 0x69, 0x64,
0x29, 0x3b, 0x20, 0x7d, 0x29, 0x3b, 0xa, 0x9, 0x9, 0x9, 0x29, 0x3b, 0x20, 0x7d, 0x29, 0x3b, 0xa, 0x9, 0x9, 0x9,
0x9, 0x9, 0x6e, 0x65, 0x77, 0x44, 0x69, 0x76, 0x2e, 0x61, 0x9, 0x9, 0x6e, 0x65, 0x77, 0x44, 0x69, 0x76, 0x2e, 0x61,
0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x73,
0x74, 0x65, 0x6e, 0x65, 0x72, 0x28, 0x22, 0x6d, 0x6f, 0x75, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x28, 0x22, 0x6d, 0x6f, 0x75,
0x73, 0x65, 0x75, 0x70, 0x22, 0x2c, 0x20, 0x66, 0x75, 0x6e,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x29, 0x20, 0x7b,
0xa, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x72, 0x65, 0x6c,
0x65, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x28, 0x6b, 0x65,
0x79, 0x2e, 0x69, 0x64, 0x29, 0x3b, 0x20, 0x7d, 0x29, 0x3b,
0xa, 0x9, 0x9, 0x9, 0x9, 0x7d, 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, 0x20, 0x3d, 0x20,
0x22, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x22, 0x20, 0x2b,
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, 0x3b, 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, 0x20, 0x3d, 0x20, 0x22, 0x68, 0x65,
0x69, 0x67, 0x68, 0x74, 0x3a, 0x22, 0x20, 0x2b, 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, 0x3b, 0x20,
0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x20, 0x72, 0x69, 0x67,
0x68, 0x74, 0x3b, 0x22, 0x3b, 0xa, 0x9, 0x9, 0x9, 0x7d,
0xa, 0x9, 0x9, 0x7d, 0xa, 0x9, 0x7d, 0xa, 0x7d, 0xa,
0xa, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20,
0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x72,
0x6f, 0x73, 0x28, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x5f, 0x69,
0x64, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x6c, 0x65, 0x74, 0x20,
0x6d, 0x61, 0x63, 0x72, 0x6f, 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, 0x6d, 0x61, 0x63, 0x72, 0x6f,
0x5f, 0x69, 0x64, 0x29, 0x3b, 0xa, 0x9, 0x66, 0x6f, 0x72,
0x20, 0x28, 0x6c, 0x65, 0x74, 0x20, 0x6d, 0x61, 0x63, 0x72,
0x6f, 0x20, 0x6f, 0x66, 0x20, 0x6d, 0x61, 0x63, 0x72, 0x6f,
0x5f, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7b, 0xa, 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, 0x6e, 0x65, 0x77,
0x44, 0x69, 0x76, 0x2e, 0x69, 0x64, 0x20, 0x3d, 0x20, 0x6d,
0x61, 0x63, 0x72, 0x6f, 0x2e, 0x69, 0x64, 0x3b, 0xa, 0x9,
0x9, 0x6e, 0x65, 0x77, 0x44, 0x69, 0x76, 0x2e, 0x74, 0x65,
0x78, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20,
0x3d, 0x20, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x2e, 0x6c, 0x61,
0x62, 0x65, 0x6c, 0x3b, 0xa, 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, 0x74, 0x6f, 0x75, 0x63, 0x68, 0x73, 0x74, 0x61,
0x72, 0x74, 0x22, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x20, 0x28, 0x65, 0x76, 0x65, 0x6e, 0x74,
0x29, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x65, 0x76, 0x65,
0x6e, 0x74, 0x2e, 0x70, 0x72, 0x65, 0x76, 0x65, 0x6e, 0x74,
0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x28, 0x29, 0x3b,
0x20, 0x73, 0x65, 0x6e, 0x64, 0x4b, 0x65, 0x79, 0x73, 0x28,
0x6d, 0x61, 0x63, 0x72, 0x6f, 0x2e, 0x6b, 0x65, 0x79, 0x73,
0x29, 0x3b, 0x20, 0x7d, 0x29, 0x3b, 0xa, 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, 0x74, 0x6f, 0x75, 0x63, 0x68, 0x65,
0x6e, 0x64, 0x22, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x20, 0x28, 0x65, 0x76, 0x65, 0x6e, 0x74,
0x29, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x65, 0x76, 0x65,
0x6e, 0x74, 0x2e, 0x70, 0x72, 0x65, 0x76, 0x65, 0x6e, 0x74,
0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x28, 0x29, 0x3b,
0x20, 0x73, 0x65, 0x6e, 0x64, 0x4b, 0x65, 0x79, 0x73, 0x28,
0x5b, 0x5d, 0x29, 0x3b, 0x3b, 0x20, 0x7d, 0x29, 0x3b, 0xa,
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, 0x6d, 0x6f, 0x75,
0x73, 0x65, 0x64, 0x6f, 0x77, 0x6e, 0x22, 0x2c, 0x20, 0x66, 0x73, 0x65, 0x64, 0x6f, 0x77, 0x6e, 0x22, 0x2c, 0x20, 0x66,
0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x29, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x29,
0x20, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x70, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x73, 0x65, 0x6e, 0x64,
0x72, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x28, 0x6b, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x28, 0x6d, 0x61, 0x63, 0x72, 0x6f,
0x79, 0x2e, 0x69, 0x64, 0x29, 0x3b, 0x20, 0x7d, 0x29, 0x3b, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x29, 0x3b, 0x20, 0x7d, 0x29,
0xa, 0x9, 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, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x75, 0x70, 0x22, 0x2c,
0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20,
0x28, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x9,
0x9, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x4b, 0x65,
0x79, 0x28, 0x6b, 0x65, 0x79, 0x2e, 0x69, 0x64, 0x29, 0x3b,
0x20, 0x7d, 0x29, 0x3b, 0xa, 0x9, 0x9, 0x9, 0x9, 0x7d,
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, 0x20, 0x3d, 0x20, 0x22, 0x77, 0x69, 0x64, 0x74, 0x68,
0x3a, 0x22, 0x20, 0x2b, 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, 0x3b, 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, 0x20, 0x3d,
0x20, 0x22, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x22,
0x20, 0x2b, 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, 0x3b, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3a,
0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x3b, 0x22, 0x3b, 0xa,
0x9, 0x9, 0x9, 0x7d, 0xa, 0x9, 0x9, 0x7d, 0xa, 0x9,
0x7d, 0xa, 0x7d, 0xa, 0xa, 0x66, 0x75, 0x6e, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65,
0x4d, 0x61, 0x63, 0x72, 0x6f, 0x73, 0x28, 0x6d, 0x61, 0x63,
0x72, 0x6f, 0x5f, 0x69, 0x64, 0x29, 0x20, 0x7b, 0xa, 0x9,
0x6c, 0x65, 0x74, 0x20, 0x6d, 0x61, 0x63, 0x72, 0x6f, 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, 0x6d,
0x61, 0x63, 0x72, 0x6f, 0x5f, 0x69, 0x64, 0x29, 0x3b, 0xa,
0x9, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x6c, 0x65, 0x74, 0x20,
0x6d, 0x61, 0x63, 0x72, 0x6f, 0x20, 0x6f, 0x66, 0x20, 0x6d,
0x61, 0x63, 0x72, 0x6f, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x29,
0x20, 0x7b, 0xa, 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, 0x6e, 0x65, 0x77, 0x44, 0x69, 0x76, 0x2e, 0x69, 0x64,
0x20, 0x3d, 0x20, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x2e, 0x69,
0x64, 0x3b, 0xa, 0x9, 0x9, 0x6e, 0x65, 0x77, 0x44, 0x69,
0x76, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x43, 0x6f, 0x6e, 0x74,
0x65, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x6d, 0x61, 0x63, 0x72,
0x6f, 0x2e, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3b, 0xa, 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, 0x74, 0x6f, 0x75, 0x63,
0x68, 0x73, 0x74, 0x61, 0x72, 0x74, 0x22, 0x2c, 0x20, 0x66,
0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x65,
0x76, 0x65, 0x6e, 0x74, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x9,
0x9, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x65,
0x76, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c,
0x74, 0x28, 0x29, 0x3b, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x4b,
0x65, 0x79, 0x73, 0x28, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x2e,
0x6b, 0x65, 0x79, 0x73, 0x29, 0x3b, 0x20, 0x7d, 0x29, 0x3b,
0xa, 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, 0x74, 0x6f,
0x75, 0x63, 0x68, 0x65, 0x6e, 0x64, 0x22, 0x2c, 0x20, 0x66,
0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x65,
0x76, 0x65, 0x6e, 0x74, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x9,
0x9, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x65,
0x76, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c,
0x74, 0x28, 0x29, 0x3b, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x4b,
0x65, 0x79, 0x73, 0x28, 0x5b, 0x5d, 0x29, 0x3b, 0x3b, 0x20,
0x7d, 0x29, 0x3b, 0xa, 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, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x64, 0x6f, 0x77, 0x6e,
0x22, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x20, 0x28, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x9,
0x73, 0x65, 0x6e, 0x64, 0x4b, 0x65, 0x79, 0x73, 0x28, 0x6d,
0x61, 0x63, 0x72, 0x6f, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x29,
0x3b, 0x20, 0x7d, 0x29, 0x3b, 0xa, 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, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x75, 0x70,
0x22, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x20, 0x28, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x9,
0x73, 0x65, 0x6e, 0x64, 0x4b, 0x65, 0x79, 0x73, 0x28, 0x5b,
0x5d, 0x29, 0x3b, 0x20, 0x7d, 0x29, 0x3b, 0xa, 0x9, 0x9,
0x6d, 0x61, 0x63, 0x72, 0x6f, 0x44, 0x69, 0x76, 0x2e, 0x61,
0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x69, 0x6c, 0x64,
0x28, 0x6e, 0x65, 0x77, 0x44, 0x69, 0x76, 0x29, 0x3b, 0xa,
0x9, 0x7d, 0xa, 0x7d, 0xa, 0xa, 0x66, 0x75, 0x6e, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74,
0x65, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72,
0x73, 0x28, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x6f,
0x72, 0x5f, 0x69, 0x64, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x6c,
0x65, 0x74, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74,
0x6f, 0x72, 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, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x6f,
0x72, 0x5f, 0x69, 0x64, 0x29, 0x3b, 0xa, 0x9, 0x66, 0x6f,
0x72, 0x20, 0x28, 0x6c, 0x65, 0x74, 0x20, 0x69, 0x6e, 0x64,
0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x6f, 0x66, 0x20,
0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x5f,
0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7b, 0xa, 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, 0x6e, 0x65, 0x77, 0x44,
0x69, 0x76, 0x2e, 0x69, 0x64, 0x20, 0x3d, 0x20, 0x69, 0x6e,
0x64, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x69, 0x64,
0x3b, 0xa, 0x9, 0x9, 0x6e, 0x65, 0x77, 0x44, 0x69, 0x76, 0x3b, 0xa, 0x9, 0x9, 0x6e, 0x65, 0x77, 0x44, 0x69, 0x76,
0x2e, 0x74, 0x65, 0x78, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x2e, 0x61, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c,
0x6e, 0x74, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x28, 0x22, 0x6d,
0x61, 0x74, 0x6f, 0x72, 0x2e, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x6f, 0x75, 0x73, 0x65, 0x75, 0x70, 0x22, 0x2c, 0x20, 0x66,
0x3b, 0xa, 0x9, 0x9, 0x6e, 0x65, 0x77, 0x44, 0x69, 0x76, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x29,
0x2e, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x73, 0x65, 0x6e, 0x64,
0x20, 0x3d, 0x20, 0x22, 0x6f, 0x66, 0x66, 0x22, 0x3b, 0xa, 0x4b, 0x65, 0x79, 0x73, 0x28, 0x5b, 0x5d, 0x29, 0x3b, 0x20,
0x9, 0x9, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x7d, 0x29, 0x3b, 0xa, 0x9, 0x9, 0x6d, 0x61, 0x63, 0x72,
0x72, 0x44, 0x69, 0x76, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x6f, 0x44, 0x69, 0x76, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e,
0x64, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x28, 0x6e, 0x65, 0x77, 0x64, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x28, 0x6e, 0x65, 0x77,
0x44, 0x69, 0x76, 0x29, 0x3b, 0xa, 0x9, 0x7d, 0xa, 0x7d, 0x44, 0x69, 0x76, 0x29, 0x3b, 0xa, 0x9, 0x7d, 0xa, 0x7d,
0xa, 0xa, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0xa, 0xa, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e,
0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x45, 0x44, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x64,
0x73, 0x28, 0x63, 0x75, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x28, 0x69, 0x6e,
0x65, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x6c, 0x65, 0x74, 0x20, 0x64, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x64,
0x6f, 0x6e, 0x5f, 0x6c, 0x65, 0x64, 0x73, 0x20, 0x3d, 0x20, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x6c, 0x65, 0x74, 0x20, 0x69,
0x63, 0x75, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x44, 0x69,
0x73, 0x70, 0x6c, 0x69, 0x74, 0x28, 0x22, 0x2c, 0x22, 0x29, 0x76, 0x20, 0x3d, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65,
0x3b, 0xa, 0x9, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x6c, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d,
0x74, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x69, 0x6e,
0x72, 0x20, 0x6f, 0x66, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x64, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x64,
0x61, 0x74, 0x6f, 0x72, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x29, 0x3b, 0xa, 0x9, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x6c,
0x20, 0x7b, 0xa, 0x9, 0x9, 0x6c, 0x65, 0x74, 0x20, 0x69, 0x65, 0x74, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74,
0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x64, 0x6f, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x69, 0x6e, 0x64, 0x69,
0x69, 0x76, 0x20, 0x3d, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x6c, 0x69, 0x73, 0x74,
0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x29, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x6c, 0x65, 0x74, 0x20,
0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x69, 0x6e, 0x65, 0x77, 0x44, 0x69, 0x76, 0x20, 0x3d, 0x20, 0x64,
0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x69, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x72,
0x64, 0x29, 0x3b, 0xa, 0x9, 0x9, 0x69, 0x66, 0x20, 0x28, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e,
0x6f, 0x6e, 0x5f, 0x6c, 0x65, 0x64, 0x73, 0x2e, 0x69, 0x6e, 0x74, 0x28, 0x22, 0x64, 0x69, 0x76, 0x22, 0x29, 0x3b, 0xa,
0x63, 0x6c, 0x75, 0x64, 0x65, 0x73, 0x28, 0x69, 0x6e, 0x64, 0x9, 0x9, 0x6e, 0x65, 0x77, 0x44, 0x69, 0x76, 0x2e, 0x69,
0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x69, 0x64, 0x29, 0x64, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61,
0x29, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x69, 0x6e, 0x64, 0x74, 0x6f, 0x72, 0x2e, 0x69, 0x64, 0x3b, 0xa, 0x9, 0x9,
0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x64, 0x69, 0x76, 0x6e, 0x65, 0x77, 0x44, 0x69, 0x76, 0x2e, 0x74, 0x65, 0x78,
0x2e, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x3d,
0x20, 0x3d, 0x20, 0x22, 0x6f, 0x6e, 0x22, 0x3b, 0xa, 0x9, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72,
0x9, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0xa, 0x2e, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3b, 0xa, 0x9, 0x9,
0x6e, 0x65, 0x77, 0x44, 0x69, 0x76, 0x2e, 0x63, 0x6c, 0x61,
0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x20, 0x3d, 0x20, 0x22,
0x6f, 0x66, 0x66, 0x22, 0x3b, 0xa, 0x9, 0x9, 0x69, 0x6e,
0x64, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x44, 0x69, 0x76,
0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x69,
0x6c, 0x64, 0x28, 0x6e, 0x65, 0x77, 0x44, 0x69, 0x76, 0x29,
0x3b, 0xa, 0x9, 0x7d, 0xa, 0x7d, 0xa, 0xa, 0x66, 0x75,
0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x75, 0x70, 0x64,
0x61, 0x74, 0x65, 0x4c, 0x45, 0x44, 0x73, 0x28, 0x63, 0x75,
0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x29, 0x20, 0x7b,
0xa, 0x9, 0x6c, 0x65, 0x74, 0x20, 0x6f, 0x6e, 0x5f, 0x6c,
0x65, 0x64, 0x73, 0x20, 0x3d, 0x20, 0x63, 0x75, 0x72, 0x5f,
0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x73, 0x70, 0x6c, 0x69,
0x74, 0x28, 0x22, 0x2c, 0x22, 0x29, 0x3b, 0xa, 0x9, 0x66,
0x6f, 0x72, 0x20, 0x28, 0x6c, 0x65, 0x74, 0x20, 0x69, 0x6e,
0x64, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x6f, 0x66,
0x20, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72,
0x5f, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7b, 0xa, 0x9,
0x9, 0x6c, 0x65, 0x74, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x63,
0x61, 0x74, 0x6f, 0x72, 0x5f, 0x64, 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, 0x69, 0x6e, 0x64, 0x69, 0x63,
0x61, 0x74, 0x6f, 0x72, 0x2e, 0x69, 0x64, 0x29, 0x3b, 0xa,
0x9, 0x9, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x6e, 0x5f, 0x6c,
0x65, 0x64, 0x73, 0x2e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64,
0x65, 0x73, 0x28, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74,
0x6f, 0x72, 0x2e, 0x69, 0x64, 0x29, 0x29, 0x20, 0x7b, 0xa,
0x9, 0x9, 0x9, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x9, 0x9, 0x9, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74,
0x6f, 0x72, 0x5f, 0x64, 0x69, 0x76, 0x2e, 0x63, 0x6c, 0x61, 0x6f, 0x72, 0x5f, 0x64, 0x69, 0x76, 0x2e, 0x63, 0x6c, 0x61,
0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x20, 0x3d, 0x20, 0x22, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x20, 0x3d, 0x20, 0x22,
0x6f, 0x66, 0x66, 0x22, 0x3b, 0xa, 0x9, 0x9, 0x7d, 0xa, 0x6f, 0x6e, 0x22, 0x3b, 0xa, 0x9, 0x9, 0x7d, 0x20, 0x65,
0x9, 0x7d, 0xa, 0x7d, 0xa, 0xa, 0x3c, 0x2f, 0x73, 0x63, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x69,
0x72, 0x69, 0x70, 0x74, 0x3e, 0xa, 0xa, 0x3c, 0x2f, 0x68, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x64,
0x65, 0x61, 0x64, 0x3e, 0xa, 0xa, 0x3c, 0x62, 0x6f, 0x64, 0x69, 0x76, 0x2e, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61,
0x79, 0x3e, 0xa, 0x3c, 0x64, 0x69, 0x76, 0x20, 0x63, 0x6c, 0x6d, 0x65, 0x20, 0x3d, 0x20, 0x22, 0x6f, 0x66, 0x66, 0x22,
0x61, 0x73, 0x73, 0x3d, 0x22, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x3b, 0xa, 0x9, 0x9, 0x7d, 0xa, 0x9, 0x7d, 0xa, 0x7d,
0x69, 0x6e, 0x65, 0x72, 0x22, 0x3e, 0xa, 0x9, 0x3c, 0x64, 0xa, 0xa, 0x3c, 0x2f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
0x69, 0x76, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x6b, 0x65, 0x79, 0x3e, 0xa, 0xa, 0x3c, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x3e,
0x62, 0x6f, 0x61, 0x72, 0x64, 0x22, 0x3e, 0x3c, 0x2f, 0x64, 0xa, 0xa, 0x3c, 0x62, 0x6f, 0x64, 0x79, 0x3e, 0xa, 0x3c,
0x69, 0x76, 0x3e, 0xa, 0x9, 0x3c, 0x64, 0x69, 0x76, 0x3e, 0x64, 0x69, 0x76, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d,
0xa, 0x9, 0x9, 0x3c, 0x64, 0x69, 0x76, 0x20, 0x69, 0x64, 0x22, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72,
0x3d, 0x22, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x22, 0x3e, 0xa, 0x9, 0x3c, 0x64, 0x69, 0x76, 0x20, 0x69,
0x72, 0x73, 0x22, 0x3e, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0x64, 0x3d, 0x22, 0x6b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72,
0xa, 0x9, 0x9, 0x3c, 0x64, 0x69, 0x76, 0x3e, 0x4d, 0x6f, 0x64, 0x22, 0x3e, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0xa,
0x75, 0x73, 0x65, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x9, 0x3c, 0x64, 0x69, 0x76, 0x3e, 0xa, 0x9, 0x9, 0x3c,
0x6c, 0x73, 0x3a, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0xa, 0x64, 0x69, 0x76, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x69, 0x6e,
0x9, 0x9, 0x3c, 0x64, 0x69, 0x76, 0x20, 0x69, 0x64, 0x3d, 0x64, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x22, 0x3e,
0x22, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x22, 0x3e, 0x3c, 0x2f, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0xa, 0x9, 0x9, 0x3c,
0x64, 0x69, 0x76, 0x3e, 0xa, 0x9, 0x3c, 0x2f, 0x64, 0x69, 0x64, 0x69, 0x76, 0x3e, 0x4d, 0x6f, 0x75, 0x73, 0x65, 0x20,
0x76, 0x3e, 0xa, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0xa, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x73, 0x3a, 0x3c,
0xa, 0x3c, 0x64, 0x69, 0x76, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0xa, 0x9, 0x9, 0x3c, 0x64,
0x6d, 0x61, 0x63, 0x72, 0x6f, 0x73, 0x22, 0x3e, 0x3c, 0x2f, 0x69, 0x76, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x6d, 0x6f, 0x75,
0x64, 0x69, 0x76, 0x3e, 0xa, 0xa, 0x3c, 0x64, 0x69, 0x76, 0x73, 0x65, 0x22, 0x3e, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e,
0x3e, 0xa, 0x9, 0x3c, 0x64, 0x69, 0x76, 0x20, 0x63, 0x6c, 0xa, 0x9, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0xa, 0x3c,
0x61, 0x73, 0x73, 0x3d, 0x22, 0x6d, 0x65, 0x6e, 0x75, 0x22, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0xa, 0xa, 0x3c, 0x64, 0x69,
0x3e, 0xa, 0x9, 0x9, 0x3c, 0x61, 0x20, 0x68, 0x72, 0x65, 0x76, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x6d, 0x61, 0x63, 0x72,
0x66, 0x3d, 0x22, 0x77, 0x69, 0x66, 0x69, 0x2e, 0x73, 0x68, 0x6f, 0x73, 0x22, 0x3e, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e,
0x74, 0x6d, 0x6c, 0x22, 0x3e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0xa, 0xa, 0x3c, 0x64, 0x69, 0x76, 0x3e, 0xa, 0x9, 0x3c,
0x67, 0x75, 0x72, 0x65, 0x20, 0x57, 0x69, 0x2d, 0x46, 0x69, 0x64, 0x69, 0x76, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d,
0x3c, 0x2f, 0x61, 0x3e, 0xa, 0x9, 0x3c, 0x2f, 0x64, 0x69, 0x22, 0x6d, 0x65, 0x6e, 0x75, 0x22, 0x3e, 0xa, 0x9, 0x9,
0x76, 0x3e, 0xa, 0x9, 0x3c, 0x64, 0x69, 0x76, 0x20, 0x63, 0x3c, 0x61, 0x20, 0x68, 0x72, 0x65, 0x66, 0x3d, 0x22, 0x77,
0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x6d, 0x65, 0x6e, 0x75, 0x69, 0x66, 0x69, 0x2e, 0x73, 0x68, 0x74, 0x6d, 0x6c, 0x22,
0x22, 0x3e, 0xa, 0x9, 0x9, 0x3c, 0x61, 0x20, 0x68, 0x72, 0x3e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65,
0x65, 0x66, 0x3d, 0x22, 0x72, 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x20, 0x57, 0x69, 0x2d, 0x46, 0x69, 0x3c, 0x2f, 0x61, 0x3e,
0x2e, 0x63, 0x67, 0x69, 0x22, 0x3e, 0x52, 0x65, 0x62, 0x6f, 0xa, 0x9, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0xa, 0x9,
0x6f, 0x74, 0x3c, 0x2f, 0x61, 0x3e, 0xa, 0x9, 0x3c, 0x2f, 0x3c, 0x64, 0x69, 0x76, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73,
0x64, 0x69, 0x76, 0x3e, 0xa, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3d, 0x22, 0x6d, 0x65, 0x6e, 0x75, 0x22, 0x3e, 0xa, 0x9,
0x3e, 0xa, 0xa, 0x3c, 0x2f, 0x62, 0x6f, 0x64, 0x79, 0x3e, 0x9, 0x3c, 0x61, 0x20, 0x68, 0x72, 0x65, 0x66, 0x3d, 0x22,
0xa, 0xa, 0x3c, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3e, 0xa, 0x72, 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x67, 0x69,
}; 0x22, 0x3e, 0x52, 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x3c, 0x2f,
0x61, 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, };
static const unsigned char data_success_html[] = { static const unsigned char data_success_html[] = {
/* /success.html */ /* /success.html */
@@ -1602,9 +1568,7 @@ static const unsigned char data_success_html[] = {
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_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_indicators_shtml[] = {{file_fsdata_c, data_indicators_shtml, data_indicators_shtml + 18, sizeof(data_indicators_shtml) - 18, FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT}}; const struct fsdata_file file_wifi_shtml[] = {{file_fsdata_c, data_wifi_shtml, data_wifi_shtml + 12, sizeof(data_wifi_shtml) - 12, FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT}};
const struct fsdata_file file_wifi_shtml[] = {{file_indicators_shtml, data_wifi_shtml, data_wifi_shtml + 12, sizeof(data_wifi_shtml) - 12, FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT}};
const struct fsdata_file file_index_html[] = {{file_wifi_shtml, data_index_html, data_index_html + 12, sizeof(data_index_html) - 12, FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT}}; const struct fsdata_file file_index_html[] = {{file_wifi_shtml, data_index_html, data_index_html + 12, sizeof(data_index_html) - 12, FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT}};
@@ -1612,4 +1576,4 @@ const struct fsdata_file file_success_html[] = {{file_index_html, data_success_h
#define FS_ROOT file_success_html #define FS_ROOT file_success_html
#define FS_NUMFILES 5 #define FS_NUMFILES 4
+6 -4
View File
@@ -7,6 +7,8 @@
#include "parse_keys.h" #include "parse_keys.h"
#include "usb_descriptors.h" #include "usb_descriptors.h"
static unsigned char boot_buf[8];
// take a list of Javascript keys representing pressed keys and turn into // take a list of Javascript keys representing pressed keys and turn into
// a 8 byte USB boot keyboard report format // a 8 byte USB boot keyboard report format
void parse_key_list(char * keys) { void parse_key_list(char * keys) {
@@ -50,8 +52,8 @@ void parse_key_list(char * keys) {
} }
void parse_mouse_list (char * keys) { void parse_mouse_list (char * keys) {
static unsigned char mouse_report[3]; static unsigned char mouse_report[5];
memset(mouse_report, 0x00, 3); memset(mouse_report, 0x00, 5);
// Javascript sends the list as comma delimited, so split into individual // Javascript sends the list as comma delimited, so split into individual
// keys by splitting at commas // keys by splitting at commas
@@ -75,11 +77,11 @@ void parse_mouse_list (char * keys) {
// print resulting HID boot mouse report to CDC for debugging // print resulting HID boot mouse report to CDC for debugging
printf("Mouse report: "); printf("Mouse report: ");
for(int i=0; i<3; i++){ for(int i=0; i<5; i++){
printf("%02X ",mouse_report[i]); printf("%02X ",mouse_report[i]);
} }
printf("\n"); printf("\n");
tud_hid_report(REPORT_ID_MOUSE, mouse_report, 3); tud_hid_report(REPORT_ID_MOUSE, mouse_report, 5);
} }
// take a single Javascript key code and convert it to USB HID scancode // take a single Javascript key code and convert it to USB HID scancode
-2
View File
@@ -3,7 +3,6 @@
#define MOUSE_SPEED 10 #define MOUSE_SPEED 10
static unsigned char boot_buf[8];
void parse_key_list(char * keys); void parse_key_list(char * keys);
void parse_mouse_list(char * keys); void parse_mouse_list(char * keys);
@@ -12,7 +11,6 @@ uint8_t parse_mod(char * key);
typedef struct { char * key; uint8_t val; } keycode_dict; typedef struct { char * key; uint8_t val; } keycode_dict;
// modifier key bit codes // modifier key bit codes
#define KEY_MOD_LCTRL 0x01 #define KEY_MOD_LCTRL 0x01
#define KEY_MOD_LSHIFT 0x02 #define KEY_MOD_LSHIFT 0x02
+3 -17
View File
@@ -66,7 +66,6 @@ const char * ip_parts[] = {
#define NPARTS ( sizeof( ip_parts )/ sizeof(ip_parts[0]) ) #define NPARTS ( sizeof( ip_parts )/ sizeof(ip_parts[0]) )
static const tCGI cgi_handlers[] = { static const tCGI cgi_handlers[] = {
{ "/sendkeys.cgi", sendkeys_cgi },
{ "/reboot.cgi", reboot_cgi }, { "/reboot.cgi", reboot_cgi },
}; };
@@ -133,7 +132,7 @@ void run_http_server() {
// start the HTTP web server for keyboard input and config page // start the HTTP web server for keyboard input and config page
httpd_init(); httpd_init();
http_set_cgi_handlers(cgi_handlers, 2); http_set_cgi_handlers(cgi_handlers, 1);
for (size_t i = 0; i < LWIP_ARRAYSIZE(ssi_tags); i++) { for (size_t i = 0; i < LWIP_ARRAYSIZE(ssi_tags); i++) {
LWIP_ASSERT("tag too long for LWIP_HTTPD_MAX_TAG_NAME_LEN", LWIP_ASSERT("tag too long for LWIP_HTTPD_MAX_TAG_NAME_LEN",
strlen(ssi_tags[i]) <= LWIP_HTTPD_MAX_TAG_NAME_LEN); strlen(ssi_tags[i]) <= LWIP_HTTPD_MAX_TAG_NAME_LEN);
@@ -189,21 +188,6 @@ void run_http_server() {
} }
} }
// lwIP cgi handler for GET requests to sendkeys.cgi
const char * sendkeys_cgi (int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) {
for (int i=0; i < iNumParams; i++) {
// search GET parameters for keys and forward for processing to USB
if (strcmp(pcParam[i], "keys") == 0 ) {
parse_key_list(pcValue[i]);
}
}
lastActive= get_absolute_time();
// send return page
return "/success.html";
}
// lwIP cgi handler for reboots initiated from the web // lwIP cgi handler for reboots initiated from the web
const char * reboot_cgi(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) { const char * reboot_cgi(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) {
// turn on reboot flag to prevent watchdog from updating // turn on reboot flag to prevent watchdog from updating
@@ -469,6 +453,8 @@ const void ws_receive_handler(uint8_t *data, uint16_t len) {
} else if (strncmp(data, "M: ", 3) == 0) { } else if (strncmp(data, "M: ", 3) == 0) {
parse_mouse_list(&data[3]); parse_mouse_list(&data[3]);
} }
lastActive= get_absolute_time();
} }
// save keyboard's LED indicator status to memory // save keyboard's LED indicator status to memory
-1
View File
@@ -23,7 +23,6 @@ typedef struct {
} net_config; } net_config;
void run_http_server(); void run_http_server();
const char * sendkeys_cgi(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]);
const char * reboot_cgi(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]); const char * reboot_cgi(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]);
uint16_t __time_critical_func(ssi_handler)(int iIndex, char *pcInsert, int iInsertLen); uint16_t __time_critical_func(ssi_handler)(int iIndex, char *pcInsert, int iInsertLen);
void set_indicator(uint8_t const* buffer); void set_indicator(uint8_t const* buffer);
+17 -32
View File
@@ -25,20 +25,10 @@
*/ */
#include "tusb.h" #include "tusb.h"
#include "bsp/board_api.h"
#include "usb_descriptors.h" #include "usb_descriptors.h"
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
*
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
_PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
#define USB_VID 0xCafe
#define USB_BCD 0x0200
//--------------------------------------------------------------------+ //--------------------------------------------------------------------+
// Device Descriptors // Device Descriptors
@@ -80,13 +70,6 @@ uint8_t const * tud_descriptor_device_cb(void)
// Configuration Descriptor // Configuration Descriptor
//--------------------------------------------------------------------+ //--------------------------------------------------------------------+
#define EPNUM_CDC_NOTIF 0x81
#define EPNUM_CDC_OUT 0x02
#define EPNUM_CDC_IN 0x82
#define EPNUM_HID 0x83
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_HID_DESC_LEN)
uint8_t const desc_hid_report[] = uint8_t const desc_hid_report[] =
{ {
TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(REPORT_ID_KEYBOARD)), TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(REPORT_ID_KEYBOARD)),
@@ -103,7 +86,7 @@ uint8_t const desc_fs_configuration[] =
// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64), TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
TUD_HID_DESCRIPTOR(ITF_NUM_HID, 5, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 5), TUD_HID_DESCRIPTOR(ITF_NUM_HID, 5, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 8),
}; };
// Invoked when received GET CONFIGURATION DESCRIPTOR // Invoked when received GET CONFIGURATION DESCRIPTOR
@@ -126,12 +109,12 @@ char const* string_desc_arr [] =
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"Raspberry Pi", // 1: Manufacturer "Raspberry Pi", // 1: Manufacturer
"Pico Web Keyboard", // 2: Product "Pico Web Keyboard", // 2: Product
"1234567890123456789", // 3: Serials, should use chip ID NULL, // 3: Serials, should use chip ID
"Pico Web Keyboard CDC", // 4: CDC Interface "Pico Web Keyboard CDC", // 4: CDC
"Pico Web Keyboard HID", // 5: HID Keyboard Interface "Pico Web Keyboard HID", // 5: HID
}; };
static uint16_t _desc_str[32]; static uint16_t _desc_str[32+1];
// Invoked when received GET STRING DESCRIPTOR request // Invoked when received GET STRING DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
@@ -141,28 +124,31 @@ uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
uint8_t chr_count; uint8_t chr_count;
if ( index == 0) switch (index) {
{ case 0: // langid
memcpy(&_desc_str[1], string_desc_arr[0], 2); memcpy(&_desc_str[1], string_desc_arr[0], 2);
chr_count = 1; chr_count = 1;
}else break;
{ case 3: // serial
chr_count = board_usb_get_serial(_desc_str+1, 32);
break;
default:
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. // 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 // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL; if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL;
const char* str = string_desc_arr[index]; char* str = string_desc_arr[index];
// Cap at max char // Cap at max char
chr_count = (uint8_t) strlen(str); chr_count = (uint8_t) strlen(str);
if ( chr_count > 31 ) chr_count = 31; if ( chr_count > 31 ) chr_count = 31;
// Convert ASCII string into UTF-16 // Convert ASCII string into UTF-16
for(uint8_t i=0; i<chr_count; i++) for(uint8_t i=0; i<chr_count; i++) {
{
_desc_str[1+i] = str[i]; _desc_str[1+i] = str[i];
} }
break;
} }
// first byte is length (including header), second byte is string type // first byte is length (including header), second byte is string type
@@ -171,7 +157,6 @@ uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
return _desc_str; return _desc_str;
} }
//--------------------------------------------------------------------+ //--------------------------------------------------------------------+
// Device HID // Device HID
//--------------------------------------------------------------------+ //--------------------------------------------------------------------+
+10
View File
@@ -41,5 +41,15 @@ enum
ITF_NUM_TOTAL ITF_NUM_TOTAL
}; };
#define USB_PID 0x0EBD
#define USB_VID 0xCEC0
#define USB_BCD 0x0200
#define EPNUM_CDC_NOTIF 0x81
#define EPNUM_CDC_OUT 0x02
#define EPNUM_CDC_IN 0x82
#define EPNUM_HID 0x83
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_HID_DESC_LEN)
#endif /* USB_DESCRIPTORS_H_ */ #endif /* USB_DESCRIPTORS_H_ */
+112 -11
View File
@@ -13,12 +13,30 @@ static const char WS_RESPONSE[] = "HTTP/1.1 101 Switching Protocols\r\n" \
"Connection: Upgrade\r\n" \ "Connection: Upgrade\r\n" \
"Sec-WebSocket-Accept: "; "Sec-WebSocket-Accept: ";
static uint8_t buf[WS_BUFFER_SIZE];
static uint16_t buf_len=0;
static tWSHandler ws_receive_cb = NULL; static tWSHandler ws_receive_cb = NULL;
static tWSOpenHandler ws_open_cb = NULL; static tWSOpenHandler ws_open_cb = NULL;
static struct ws_state * ws_connections; static struct ws_state * ws_connections;
static uint8_t ws_num_conns = 0; 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 // allocate memory for ws_state instance
static struct ws_state * ws_state_alloc(void) { static struct ws_state * ws_state_alloc(void) {
struct ws_state *ret = WS_ALLOC_WS_STATE(); struct ws_state *ret = WS_ALLOC_WS_STATE();
@@ -120,6 +138,7 @@ static err_t ws_accept(void *arg, struct altcp_pcb *pcb, err_t err) {
// register callbacks for tcp events // register callbacks for tcp events
altcp_recv(pcb, ws_recv); altcp_recv(pcb, ws_recv);
altcp_sent(pcb, ws_sent);
altcp_poll(pcb, ws_poll, WS_POLL_INTERVAL); altcp_poll(pcb, ws_poll, WS_POLL_INTERVAL);
altcp_err(pcb, ws_err); altcp_err(pcb, ws_err);
@@ -166,6 +185,24 @@ static err_t ws_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err
return ERR_OK; 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) {
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) { static void ws_err (void *arg, err_t err) {
struct ws_state *wss = (struct ws_state *) arg; struct ws_state *wss = (struct ws_state *) arg;
LWIP_UNUSED_ARG(err); LWIP_UNUSED_ARG(err);
@@ -192,6 +229,7 @@ static err_t ws_close_or_abort_conn(struct altcp_pcb *pcb, struct ws_state *wss,
// clear callbacks // clear callbacks
altcp_arg(pcb, NULL); altcp_arg(pcb, NULL);
altcp_recv(pcb, NULL); altcp_recv(pcb, NULL);
altcp_sent(pcb, NULL);
altcp_poll(pcb, NULL, 0); altcp_poll(pcb, NULL, 0);
altcp_err(pcb, NULL); altcp_err(pcb, NULL);
@@ -310,30 +348,94 @@ static err_t ws_read(struct altcp_pcb *pcb, struct ws_state *wss, struct pbuf *p
// successful read, reset timeout // successful read, reset timeout
wss->retries = 0; wss->retries = 0;
uint8_t mode = data[0] & 0x0F; 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; uint16_t msg_len = data[1] & 0x7F;
switch (mode) { uint8_t *msg;
case 0x01: //text
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;
//memcpy(&msg_len, &data[2], 4);
//if (len >= 10) {
// msg = &data[10];
//}
//break;
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")); LWIP_DEBUGF(WS_DEBUG, ("ws_read: received text data\n"));
case 0x02: //binary case OP_BINARY:
LWIP_DEBUGF(WS_DEBUG, ("ws_read: decoding data\n")); LWIP_DEBUGF(WS_DEBUG, ("ws_read: decoding data, len=%u\n", msg_len));
if (len >= 6 && ws_receive_cb != NULL) { if (msg && ws_receive_cb != NULL) {
// unmask the data if mask bit is received
if (masked) {
uint8_t *mask = &data[2]; uint8_t *mask = &data[2];
uint8_t *msg = &data[6];
for (int i=0; i<msg_len; i++) { for (int i=0; i<msg_len; i++) {
msg[i] ^= mask[i % 4]; msg[i] ^= mask[i % 4];
} }
} else {
// messages from client must be masked - disconnect
LWIP_DEBUGF(WS_DEBUG, ("ws_read: received unmasked message"));
return ERR_CLSD;
}
msg[msg_len]=0; msg[msg_len]=0;
ws_receive_cb(msg, msg_len); if (opcode != OP_CONT) { // not a continuation frame, reset buffer
buf_len=0;
memset(buf, 0x00, sizeof(buf));
}
if (buf_len + msg_len > 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; break;
case 0x08: //close case OP_CLOSE:
LWIP_DEBUGF(WS_DEBUG, ("ws_read: close request")); LWIP_DEBUGF(WS_DEBUG, ("ws_read: close request"));
return ERR_CLSD; 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: default:
LWIP_DEBUGF(WS_DEBUG, ("ws_read: invalid data mode %02X\n", mode)); LWIP_DEBUGF(WS_DEBUG, ("ws_read: invalid opcode %02X\n", opcode));
return ERR_ARG; return ERR_ARG;
} }
@@ -353,7 +455,6 @@ static err_t ws_send(struct ws_state *wss, uint8_t *data, uint16_t len) {
err = altcp_write(wss->pcb, buf, len+2, TCP_WRITE_FLAG_COPY); err = altcp_write(wss->pcb, buf, len+2, TCP_WRITE_FLAG_COPY);
if (err == ERR_OK) { if (err == ERR_OK) {
altcp_output(wss->pcb); altcp_output(wss->pcb);
wss->retries = 0;
} }
return err; return err;
+8 -13
View File
@@ -7,6 +7,14 @@
#define WS_MAX_RETRIES 10 #define WS_MAX_RETRIES 10
#define WS_POLL_INTERVAL 60 // WS_POLL_INTERVAL/2 seconds #define WS_POLL_INTERVAL 60 // WS_POLL_INTERVAL/2 seconds
#define WS_MAX_CONN 4 #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 { struct ws_state {
bool active; bool active;
@@ -20,20 +28,7 @@ struct ws_state {
typedef void (* tWSHandler ) (uint8_t *data, uint16_t len); typedef void (* tWSHandler ) (uint8_t *data, uint16_t len);
typedef void (* tWSOpenHandler ) (struct ws_state * wss); typedef void (* tWSOpenHandler ) (struct ws_state * wss);
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);
void ws_server_init(void); void ws_server_init(void);
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 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);
void ws_send_all(uint8_t *data, uint16_t len); void ws_send_all(uint8_t *data, uint16_t len);
void ws_set_receive_handler( tWSHandler ws_handler); void ws_set_receive_handler( tWSHandler ws_handler);
void ws_set_open_handler( tWSOpenHandler ws_handler); void ws_set_open_handler( tWSOpenHandler ws_handler);