Merge branch 'main' into PJH_ADD_Linearbounce

This commit is contained in:
Blaz Kristan 2023-09-05 21:20:55 +02:00
commit 885f4ded0c
33 changed files with 3051 additions and 2671 deletions

View File

@ -1,5 +1,19 @@
## WLED changelog
#### Build 2308110
- Release of WLED beta version 0.14.0-b4 "Hoshi"
- Reset effect data immediately upon mode change
#### Build 2308030
- Improved random palette handling and blending
- Soap bugfix
- Fix ESP-NOW crash with AP mode Always
#### Build 2307180
- Bus-level global buffering (#3280)
- Removed per-segment LED buffer (SEGMENT.leds)
- various fixes and improvements (ESP variants platform 5.3.0, effect optimizations, /json/cfg pin allocation)
#### Build 2307130
- larger `oappend()` stack buffer (3.5k) for ESP32
- Preset cycle bugfix (#3262)

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "wled",
"version": "0.14.0-b3",
"version": "0.14.0-b4",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "wled",
"version": "0.14.0-b3",
"version": "0.14.0-b4",
"description": "Tools for WLED project",
"main": "tools/cdata.js",
"directories": {

View File

@ -433,6 +433,7 @@ platform_packages = ${esp32.platform_packages}
upload_speed = 921600
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_Ethernet -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1
-D WLED_DISABLE_ESPNOW ;; ESP-NOW requires wifi, may crash with ethernet only
lib_deps = ${esp32.lib_deps}
board_build.partitions = ${esp32.default_partitions}
@ -457,7 +458,7 @@ platform_packages = ${esp32c3.platform_packages}
framework = arduino
board = esp32-c3-devkitm-1
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
build_flags = ${common.build_flags} ${esp32c3.build_flags} #-D WLED_RELEASE_NAME=ESP32-C3
build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_RELEASE_NAME=ESP32-C3
-D WLED_WATCHDOG_TIMEOUT=0
-DLOLIN_WIFI_FIX ; seems to work much better with this
-DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB
@ -473,7 +474,7 @@ platform = ${esp32s3.platform}
platform_packages = ${esp32s3.platform_packages}
upload_speed = 921600 ; or 460800
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s3.build_flags}
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_8MB
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
;-D ARDUINO_USB_CDC_ON_BOOT=1 ;; -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
@ -580,7 +581,7 @@ platform_packages = ${esp32s2.platform_packages}
board = lolin_s2_mini
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
build_unflags = ${common.build_unflags} #-DARDUINO_USB_CDC_ON_BOOT=1
build_flags = ${common.build_flags} ${esp32s2.build_flags} #-D WLED_RELEASE_NAME=LolinS2
build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=ESP32-S2
-DBOARD_HAS_PSRAM
-DARDUINO_USB_CDC_ON_BOOT=1 # try disabling and enabling unflag above in case of board-specific issues, will disable Serial
-DARDUINO_USB_MSC_ON_BOOT=0

View File

@ -25,6 +25,7 @@ class Animated_Staircase : public Usermod {
bool useUSSensorBottom = false; // using PIR or UltraSound sensor?
unsigned int topMaxDist = 50; // default maximum measured distance in cm, top
unsigned int bottomMaxDist = 50; // default maximum measured distance in cm, bottom
bool togglePower = false; // toggle power on/off with staircase on/off
/* runtime variables */
bool initDone = false;
@ -90,7 +91,8 @@ class Animated_Staircase : public Usermod {
static const char _bottomEcho_pin[];
static const char _topEchoCm[];
static const char _bottomEchoCm[];
static const char _togglePower[];
void publishMqtt(bool bottom, const char* state) {
#ifndef WLED_DISABLE_MQTT
//Check if MQTT Connected, otherwise it will crash the 8266
@ -196,6 +198,7 @@ class Animated_Staircase : public Usermod {
if (on) {
lastSensor = topSensorRead;
} else {
if (togglePower && onIndex == offIndex && offMode) toggleOnOff(); // toggle power on if off
// If the bottom sensor triggered, we need to swipe up, ON
swipe = bottomSensorRead;
@ -249,7 +252,10 @@ class Animated_Staircase : public Usermod {
offIndex = MAX(onIndex, offIndex - 1);
}
}
if (oldOn != onIndex || oldOff != offIndex) updateSegments(); // reduce the number of updates to necessary ones
if (oldOn != onIndex || oldOff != offIndex) {
updateSegments(); // reduce the number of updates to necessary ones
if (togglePower && onIndex == offIndex && !offMode && !on) toggleOnOff(); // toggle power off for all segments off
}
}
}
@ -295,6 +301,7 @@ class Animated_Staircase : public Usermod {
strip.setTransition(segment_delay_ms/100);
strip.trigger();
} else {
if (togglePower && !on && offMode) toggleOnOff(); // toggle power on if off
// Restore segment options
for (int i = 0; i <= strip.getLastActiveSegmentId(); i++) {
Segment &seg = strip.getSegment(i);
@ -444,6 +451,7 @@ class Animated_Staircase : public Usermod {
staircase[FPSTR(_bottomEcho_pin)] = useUSSensorBottom ? bottomEchoPin : -1;
staircase[FPSTR(_topEchoCm)] = topMaxDist;
staircase[FPSTR(_bottomEchoCm)] = bottomMaxDist;
staircase[FPSTR(_togglePower)] = togglePower;
DEBUG_PRINTLN(F("Staircase config saved."));
}
@ -488,6 +496,8 @@ class Animated_Staircase : public Usermod {
bottomMaxDist = top[FPSTR(_bottomEchoCm)] | bottomMaxDist;
bottomMaxDist = min(150,max(30,(int)bottomMaxDist)); // max distance ~1.5m (a lag of 9ms may be expected)
togglePower = top[FPSTR(_togglePower)] | togglePower; // staircase toggles power on/off
DEBUG_PRINT(FPSTR(_name));
if (!initDone) {
// first run: reading from cfg.json
@ -511,7 +521,7 @@ class Animated_Staircase : public Usermod {
if (changed) setup();
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return true;
return !top[FPSTR(_togglePower)].isNull();
}
/*
@ -551,3 +561,4 @@ const char Animated_Staircase::_bottomPIRorTrigger_pin[] PROGMEM = "bottomPIR
const char Animated_Staircase::_bottomEcho_pin[] PROGMEM = "bottomEcho_pin";
const char Animated_Staircase::_topEchoCm[] PROGMEM = "top-dist-cm";
const char Animated_Staircase::_bottomEchoCm[] PROGMEM = "bottom-dist-cm";
const char Animated_Staircase::_togglePower[] PROGMEM = "toggle-on-off";

View File

@ -0,0 +1,17 @@
# Internal Temperature Usermod
This usermod adds the temperature readout to the Info tab and also publishes that over the topic `mcutemp` topic.
## Important
A shown temp of 53,33°C might indicate that the internal temp is not supported.
ESP8266 does not have a internal temp sensor
ESP32S2 seems to crash on reading the sensor -> disabled
## Installation
Add a build flag `-D USERMOD_INTERNAL_TEMPERATURE` to your `platformio.ini` (or `platformio_override.ini`).
## Authors
Soeren Willrodt [@lost-hope](https://github.com/lost-hope)
Dimitry Zhemkov [@dima-zhemkov](https://github.com/dima-zhemkov)

View File

@ -0,0 +1,117 @@
#pragma once
#include "wled.h"
class InternalTemperatureUsermod : public Usermod
{
private:
unsigned long loopInterval = 10000;
unsigned long lastTime = 0;
bool isEnabled = false;
float temperature = 0;
static const char _name[];
static const char _enabled[];
static const char _loopInterval[];
// any private methods should go here (non-inline methosd should be defined out of class)
void publishMqtt(const char *state, bool retain = false); // example for publishing MQTT message
public:
void setup()
{
}
void loop()
{
// if usermod is disabled or called during strip updating just exit
// NOTE: on very long strips strip.isUpdating() may always return true so update accordingly
if (!isEnabled || strip.isUpdating() || millis() - lastTime <= loopInterval)
return;
lastTime = millis();
#ifdef ESP8266 // ESP8266
// does not seem possible
temperature = -1;
#elif defined(CONFIG_IDF_TARGET_ESP32S2) // ESP32S2
temperature = -1;
#else // ESP32 ESP32S3 and ESP32C3
temperature = roundf(temperatureRead() * 10) / 10;
#endif
#ifndef WLED_DISABLE_MQTT
if (WLED_MQTT_CONNECTED)
{
char array[10];
snprintf(array, sizeof(array), "%f", temperature);
publishMqtt(array);
}
#endif
}
void addToJsonInfo(JsonObject &root)
{
if (!isEnabled)
return;
// if "u" object does not exist yet wee need to create it
JsonObject user = root["u"];
if (user.isNull())
user = root.createNestedObject("u");
JsonArray userTempArr = user.createNestedArray(FPSTR(_name));
userTempArr.add(temperature);
userTempArr.add(F(" °C"));
// if "sensor" object does not exist yet wee need to create it
JsonObject sensor = root[F("sensor")];
if (sensor.isNull())
sensor = root.createNestedObject(F("sensor"));
JsonArray sensorTempArr = sensor.createNestedArray(FPSTR(_name));
sensorTempArr.add(temperature);
sensorTempArr.add(F("°C"));
}
void addToConfig(JsonObject &root)
{
JsonObject top = root.createNestedObject(FPSTR(_name));
top[FPSTR(_enabled)] = isEnabled;
top[FPSTR(_loopInterval)] = loopInterval;
}
bool readFromConfig(JsonObject &root)
{
JsonObject top = root[FPSTR(_name)];
bool configComplete = !top.isNull();
configComplete &= getJsonValue(top[FPSTR(_enabled)], isEnabled);
configComplete &= getJsonValue(top[FPSTR(_loopInterval)], loopInterval);
return configComplete;
}
uint16_t getId()
{
return USERMOD_ID_INTERNAL_TEMPERATURE;
}
};
const char InternalTemperatureUsermod::_name[] PROGMEM = "Internal Temperature";
const char InternalTemperatureUsermod::_enabled[] PROGMEM = "Enabled";
const char InternalTemperatureUsermod::_loopInterval[] PROGMEM = "Loop Interval";
void InternalTemperatureUsermod::publishMqtt(const char *state, bool retain)
{
#ifndef WLED_DISABLE_MQTT
// Check if MQTT Connected, otherwise it will crash the 8266
if (WLED_MQTT_CONNECTED)
{
char subuf[64];
strcpy(subuf, mqttDeviceTopic);
strcat_P(subuf, PSTR("/mcutemp"));
mqtt->publish(subuf, 0, retain, state);
}
#endif
}

View File

@ -1081,7 +1081,7 @@ bool FourLineDisplayUsermod::handleButton(uint8_t b) {
static bool buttonLongPressed = false;
static unsigned long buttonPressedTime = 0;
static unsigned long buttonWaitTime = 0;
bool handled = true;
bool handled = false;
//momentary button logic
if (isButtonPressed(b)) { //pressed
@ -1090,11 +1090,12 @@ bool FourLineDisplayUsermod::handleButton(uint8_t b) {
buttonPressedBefore = true;
if (now - buttonPressedTime > 600) { //long press
buttonLongPressed = true;
//TODO: handleButton() handles button 0 without preset in a different way for double click
//so we need to override with same behaviour
longPressAction(0);
//handled = false;
//DEBUG_PRINTLN(F("4LD action."));
//if (!buttonLongPressed) longPressAction(0);
buttonLongPressed = true;
return false;
}
} else if (!isButtonPressed(b) && buttonPressedBefore) { //released
@ -1126,7 +1127,7 @@ bool FourLineDisplayUsermod::handleButton(uint8_t b) {
buttonWaitTime = 0;
//TODO: handleButton() handles button 0 without preset in a different way for double click
//so we need to override with same behaviour
shortPressAction(0);
//shortPressAction(0);
//handled = false;
}
return handled;

View File

@ -398,8 +398,14 @@ void RotaryEncoderUIUsermod::sortModesAndPalettes() {
modes_alpha_indexes = re_initIndexArray(strip.getModeCount());
re_sortModes(modes_qstrings, modes_alpha_indexes, strip.getModeCount(), MODE_SORT_SKIP_COUNT);
palettes_qstrings = re_findModeStrings(JSON_palette_names, strip.getPaletteCount());
palettes_alpha_indexes = re_initIndexArray(strip.getPaletteCount()); // only use internal palettes
palettes_qstrings = re_findModeStrings(JSON_palette_names, strip.getPaletteCount()+strip.customPalettes.size());
palettes_alpha_indexes = re_initIndexArray(strip.getPaletteCount()+strip.customPalettes.size());
if (strip.customPalettes.size()) {
for (int i=0; i<strip.customPalettes.size(); i++) {
palettes_alpha_indexes[strip.getPaletteCount()+i] = 255-i;
palettes_qstrings[strip.getPaletteCount()+i] = PSTR("~Custom~");
}
}
// How many palette names start with '*' and should not be sorted?
// (Also skipping the first one, 'Default').
@ -496,7 +502,7 @@ void RotaryEncoderUIUsermod::setup()
}
} else {
PinManagerPinType pins[3] = { { pinA, false }, { pinB, false }, { pinC, false } };
if (!pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_RotaryEncoderUI)) {
if (pinA<0 || pinB<0 || !pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_RotaryEncoderUI)) {
pinA = pinB = pinC = -1;
enabled = false;
return;
@ -507,7 +513,7 @@ void RotaryEncoderUIUsermod::setup()
#endif
pinMode(pinA, USERMOD_ROTARY_ENCODER_GPIO);
pinMode(pinB, USERMOD_ROTARY_ENCODER_GPIO);
pinMode(pinC, USERMOD_ROTARY_ENCODER_GPIO);
if (pinC>=0) pinMode(pinC, USERMOD_ROTARY_ENCODER_GPIO);
}
loopTime = millis();
@ -682,21 +688,25 @@ void RotaryEncoderUIUsermod::displayNetworkInfo() {
void RotaryEncoderUIUsermod::findCurrentEffectAndPalette() {
DEBUG_PRINTLN(F("Finding current mode and palette."));
currentEffectAndPaletteInitialized = true;
for (uint8_t i = 0; i < strip.getModeCount(); i++) {
effectCurrentIndex = 0;
for (int i = 0; i < strip.getModeCount(); i++) {
if (modes_alpha_indexes[i] == effectCurrent) {
effectCurrentIndex = i;
DEBUG_PRINTLN(F("Found current mode."));
break;
}
}
DEBUG_PRINTLN(F("Found current mode."));
for (uint8_t i = 0; i < strip.getPaletteCount(); i++) {
effectPaletteIndex = 0;
DEBUG_PRINTLN(effectPalette);
for (uint8_t i = 0; i < strip.getPaletteCount()+strip.customPalettes.size(); i++) {
if (palettes_alpha_indexes[i] == effectPalette) {
effectPaletteIndex = i;
DEBUG_PRINTLN(F("Found palette."));
break;
}
}
DEBUG_PRINTLN(F("Found palette."));
}
bool RotaryEncoderUIUsermod::changeState(const char *stateName, byte markedLine, byte markedCol, byte glyph) {
@ -731,7 +741,9 @@ void RotaryEncoderUIUsermod::changeBrightness(bool increase) {
}
display->updateRedrawTime();
#endif
bri = max(min((increase ? bri+fadeAmount : bri-fadeAmount), 255), 0);
//bri = max(min((increase ? bri+fadeAmount : bri-fadeAmount), 255), 0);
if (bri < 40) bri = max(min((increase ? bri+fadeAmount/2 : bri-fadeAmount/2), 255), 0); // slower steps when brightness < 16%
else bri = max(min((increase ? bri+fadeAmount : bri-fadeAmount), 255), 0);
lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY
display->updateBrightness();
@ -878,7 +890,7 @@ void RotaryEncoderUIUsermod::changePalette(bool increase) {
}
display->updateRedrawTime();
#endif
effectPaletteIndex = max(min((increase ? effectPaletteIndex+1 : effectPaletteIndex-1), strip.getPaletteCount()-1), 0);
effectPaletteIndex = max(min((unsigned)(increase ? effectPaletteIndex+1 : effectPaletteIndex-1), strip.getPaletteCount()+strip.customPalettes.size()-1), 0U);
effectPalette = palettes_alpha_indexes[effectPaletteIndex];
stateChanged = true;
if (applyToAll) {

View File

@ -604,11 +604,6 @@ static const char _data_FX_MODE_TWINKLE[] PROGMEM = "Twinkle@!,!;!,!;!;;m12=0";
* Dissolve function
*/
uint16_t dissolve(uint32_t color) {
//bool wa = (SEGCOLOR(1) != 0 && strip.getBrightness() < 255); //workaround, can't compare getPixel to color if not full brightness
if (SEGENV.call == 0) {
SEGMENT.fill(SEGCOLOR(1));
}
for (int j = 0; j <= SEGLEN / 15; j++) {
if (random8() <= SEGMENT.intensity) {
for (size_t times = 0; times < 10; times++) //attempt to spawn a new pixel 10 times
@ -713,7 +708,7 @@ uint16_t mode_hyper_sparkle(void) {
if (strip.now - SEGENV.aux0 > SEGENV.step) {
if (random8((255-SEGMENT.intensity) >> 4) == 0) {
for (int i = 0; i < MAX(1, SEGLEN/3); i++) {
for (int i = 0; i < max(1, SEGLEN/3); i++) {
SEGMENT.setPixelColor(random16(SEGLEN), SEGCOLOR(1));
}
}
@ -764,7 +759,7 @@ uint16_t mode_android(void) {
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1));
}
if (SEGENV.aux1 > ((float)SEGMENT.intensity/255.0)*(float)SEGLEN)
if (SEGENV.aux1 > (SEGMENT.intensity*SEGLEN)/255)
{
SEGENV.aux0 = 1;
} else
@ -814,7 +809,7 @@ static const char _data_FX_MODE_ANDROID[] PROGMEM = "Android@!,Width;!,!;!;;m12=
*/
uint16_t chase(uint32_t color1, uint32_t color2, uint32_t color3, bool do_palette) {
uint16_t counter = strip.now * ((SEGMENT.speed >> 2) + 1);
uint16_t a = counter * SEGLEN >> 16;
uint16_t a = (counter * SEGLEN) >> 16;
bool chase_random = (SEGMENT.mode == FX_MODE_CHASE_RANDOM);
if (chase_random) {
@ -828,7 +823,7 @@ uint16_t chase(uint32_t color1, uint32_t color2, uint32_t color3, bool do_palett
SEGENV.step = a;
// Use intensity setting to vary chase up to 1/2 string length
uint8_t size = 1 + (SEGMENT.intensity * SEGLEN >> 10);
uint8_t size = 1 + ((SEGMENT.intensity * SEGLEN) >> 10);
uint16_t b = a + size; //"trail" of chase, filled with color1
if (b > SEGLEN) b -= SEGLEN;
@ -1116,8 +1111,9 @@ static const char _data_FX_MODE_RUNNING_RANDOM[] PROGMEM = "Stream@!,Zone size;;
uint16_t larson_scanner(bool dual) {
if (SEGLEN == 1) return mode_static();
uint16_t counter = strip.now * ((SEGMENT.speed >> 2) +8);
uint16_t index = counter * SEGLEN >> 16;
uint16_t index = (counter * SEGLEN) >> 16;
SEGMENT.fade_out(SEGMENT.intensity);
@ -1204,7 +1200,6 @@ uint16_t mode_fireworks() {
const uint16_t height = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.fill(SEGCOLOR(1));
SEGENV.aux0 = UINT16_MAX;
SEGENV.aux1 = UINT16_MAX;
}
@ -1220,7 +1215,7 @@ uint16_t mode_fireworks() {
if (valid1) { if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(x, y, sv1); else SEGMENT.setPixelColor(SEGENV.aux0, sv1); } // restore spark color after blur
if (valid2) { if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(x, y, sv2); else SEGMENT.setPixelColor(SEGENV.aux1, sv2); } // restore old spark color after blur
for (int i=0; i<MAX(1, width/20); i++) {
for (int i=0; i<max(1, width/20); i++) {
if (random8(129 - (SEGMENT.intensity >> 1)) == 0) {
uint16_t index = random16(width*height);
x = index % width;
@ -1305,24 +1300,23 @@ static const char _data_FX_MODE_FIRE_FLICKER[] PROGMEM = "Fire Flicker@!,!;!;!;0
* Gradient run base function
*/
uint16_t gradient_base(bool loading) {
if (SEGLEN == 1) return mode_static();
uint16_t counter = strip.now * ((SEGMENT.speed >> 2) + 1);
uint16_t pp = counter * SEGLEN >> 16;
uint16_t pp = (counter * SEGLEN) >> 16;
if (SEGENV.call == 0) pp = 0;
float val; //0.0 = sec 1.0 = pri
float brd = loading ? SEGMENT.intensity : SEGMENT.intensity/2;
if (brd <1.0) brd = 1.0;
int val; //0 = sec 1 = pri
int brd = 1 + loading ? SEGMENT.intensity/2 : SEGMENT.intensity/4;
//if (brd < 1) brd = 1;
int p1 = pp-SEGLEN;
int p2 = pp+SEGLEN;
for (int i = 0; i < SEGLEN; i++)
{
if (loading)
{
val = abs(((i>pp) ? p2:pp) -i);
for (int i = 0; i < SEGLEN; i++) {
if (loading) {
val = abs(((i>pp) ? p2:pp) - i);
} else {
val = MIN(abs(pp-i),MIN(abs(p1-i),abs(p2-i)));
val = min(abs(pp-i),min(abs(p1-i),abs(p2-i)));
}
val = (brd > val) ? val/brd * 255 : 255;
val = (brd > val) ? (val * 255) / brd : 255;
SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(0), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1), val));
}
@ -1903,12 +1897,8 @@ static const char _data_FX_MODE_PRIDE_2015[] PROGMEM = "Pride 2015@!;;";
//eight colored dots, weaving in and out of sync with each other
uint16_t mode_juggle(void) {
if (SEGLEN == 1) return mode_static();
if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
}
SEGMENT.fadeToBlackBy(192 - (3*SEGMENT.intensity/4));
CRGB fastled_col;
byte dothue = 0;
for (int i = 0; i < 8; i++) {
@ -2073,12 +2063,9 @@ static const char _data_FX_MODE_COLORWAVES[] PROGMEM = "Colorwaves@!,Hue;!;!";
// colored stripes pulsing at a defined Beats-Per-Minute (BPM)
uint16_t mode_bpm() {
//CRGB fastled_col;
uint32_t stp = (strip.now / 20) & 0xFF;
uint8_t beat = beatsin8(SEGMENT.speed, 64, 255);
for (int i = 0; i < SEGLEN; i++) {
//fastled_col = ColorFromPalette(SEGPALETTE, stp + (i * 2), beat - stp + (i * 10));
//SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue);
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(stp + (i * 2), false, PALETTE_SOLID_WRAP, 0, beat - stp + (i * 10)));
}
@ -2475,12 +2462,6 @@ static const char _data_FX_MODE_RIPPLE_RAINBOW[] PROGMEM = "Ripple Rainbow@!,Wav
//
// TwinkleFOX: Twinkling 'holiday' lights that fade in and out.
// Colors are chosen from a palette. Read more about this effect using the link above!
// If COOL_LIKE_INCANDESCENT is set to 1, colors will
// fade out slighted 'reddened', similar to how
// incandescent bulbs change color as they get dim down.
#define COOL_LIKE_INCANDESCENT 1
CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat)
{
// Overall twinkle speed (changed)
@ -2519,7 +2500,7 @@ CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat)
CRGB c;
if (bright > 0) {
c = ColorFromPalette(SEGPALETTE, hue, bright, NOBLEND);
if(COOL_LIKE_INCANDESCENT == 1) {
if (!SEGMENT.check1) {
// This code takes a pixel, and if its in the 'fading down'
// part of the cycle, it adjusts the color a little bit like the
// way that incandescent bulbs fade toward 'red' as they dim.
@ -2605,14 +2586,14 @@ uint16_t mode_twinklefox()
{
return twinklefox_base(false);
}
static const char _data_FX_MODE_TWINKLEFOX[] PROGMEM = "Twinklefox@!,Twinkle rate;;!";
static const char _data_FX_MODE_TWINKLEFOX[] PROGMEM = "Twinklefox@!,Twinkle rate,,,,Cool;;!";
uint16_t mode_twinklecat()
{
return twinklefox_base(true);
}
static const char _data_FX_MODE_TWINKLECAT[] PROGMEM = "Twinklecat@!,Twinkle rate;;!";
static const char _data_FX_MODE_TWINKLECAT[] PROGMEM = "Twinklecat@!,Twinkle rate,,,,Cool;;!";
//inspired by https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectBlinkingHalloweenEyes
@ -2877,8 +2858,10 @@ static uint16_t rolling_balls(void) {
// number of balls based on intensity setting to max of 16 (cycles colors)
// non-chosen color is a random color
uint8_t numBalls = SEGMENT.intensity/16 + 1;
bool hasCol2 = SEGCOLOR(2);
if (SEGENV.call == 0) {
SEGMENT.fill(hasCol2 ? BLACK : SEGCOLOR(1)); // start clean
for (int i = 0; i < maxNumBalls; i++) {
balls[i].lastBounceUpdate = strip.now;
balls[i].velocity = 20.0f * float(random16(1000, 10000))/10000.0f; // number from 1 to 10
@ -2890,14 +2873,16 @@ static uint16_t rolling_balls(void) {
float cfac = float(scale8(8, 255-SEGMENT.speed) +1)*20000.0f; // this uses the Aircoookie conversion factor for scaling time using speed slider
bool hasCol2 = SEGCOLOR(2);
if (!SEGMENT.check2) SEGMENT.fill(hasCol2 ? BLACK : SEGCOLOR(1));
if (SEGMENT.check3) SEGMENT.fade_out(250); // 2-8 pixel trails (optional)
else {
if (!SEGMENT.check2) SEGMENT.fill(hasCol2 ? BLACK : SEGCOLOR(1)); // don't fill with background color if user wants to see trails
}
for (int i = 0; i < numBalls; i++) {
float timeSinceLastUpdate = float((strip.now - balls[i].lastBounceUpdate))/cfac;
float thisHeight = balls[i].height + balls[i].velocity * timeSinceLastUpdate; // this method keeps higher resolution
// test if intensity level was increased and some balls are way off the track then put them back
if (thisHeight < -0.5f || thisHeight > 1.5f){
if (thisHeight < -0.5f || thisHeight > 1.5f) {
thisHeight = balls[i].height = (float(random16(0, 10000)) / 10000.0f); // from 0. to 1.
balls[i].lastBounceUpdate = strip.now;
}
@ -2947,7 +2932,7 @@ static uint16_t rolling_balls(void) {
return FRAMETIME;
}
static const char _data_FX_MODE_ROLLINGBALLS[] PROGMEM = "Rolling Balls@!,# of balls,,,,Collisions,Overlay;!,!,!;!;1;m12=1"; //bar
static const char _data_FX_MODE_ROLLINGBALLS[] PROGMEM = "Rolling Balls@!,# of balls,,,,Collisions,Overlay,Trails;!,!,!;!;1;m12=1"; //bar
/*
@ -3027,7 +3012,7 @@ uint16_t mode_glitter()
static const char _data_FX_MODE_GLITTER[] PROGMEM = "Glitter@!,!,,,,,Overlay;1,2,Glitter color;!;;pal=0,m12=0"; //pixels
//Solid colour background with glitter
//Solid colour background with glitter (can be replaced by Glitter)
uint16_t mode_solid_glitter()
{
SEGMENT.fill(SEGCOLOR(0));
@ -3117,10 +3102,9 @@ static const char _data_FX_MODE_POPCORN[] PROGMEM = "Popcorn@!,!,,,,,Overlay;!,!
uint16_t candle(bool multi)
{
if (multi)
{
if (multi && SEGLEN > 1) {
//allocate segment data
uint16_t dataSize = (SEGLEN -1) *3; //max. 1365 pixels (ESP8266)
uint16_t dataSize = max(1, SEGLEN -1) *3; //max. 1365 pixels (ESP8266)
if (!SEGENV.allocateData(dataSize)) return candle(false); //allocation failed
}
@ -3253,7 +3237,7 @@ uint16_t mode_starburst(void) {
if (random8((144-(SEGMENT.speed >> 1))) == 0 && stars[j].birth == 0)
{
// Pick a random color and location.
uint16_t startPos = random16(SEGLEN-1);
uint16_t startPos = (SEGLEN > 1) ? random16(SEGLEN-1) : 0;
float multiplier = (float)(random8())/255.0 * 1.0;
stars[j].color = CRGB(SEGMENT.color_wheel(random8()));
@ -3497,7 +3481,7 @@ uint16_t mode_drip(void)
uint8_t numDrops = 1 + (SEGMENT.intensity >> 6); // 255>>6 = 3
float gravity = -0.0005 - (SEGMENT.speed/50000.0);
gravity *= SEGLEN-1;
gravity *= max(1, SEGLEN-1);
int sourcedrop = 12;
for (int j=0;j<numDrops;j++) {
@ -3687,8 +3671,8 @@ uint16_t mode_percent(void) {
uint8_t percent = SEGMENT.intensity;
percent = constrain(percent, 0, 200);
uint16_t active_leds = (percent < 100) ? SEGLEN * percent / 100.0
: SEGLEN * (200 - percent) / 100.0;
uint16_t active_leds = (percent < 100) ? roundf(SEGLEN * percent / 100.0f)
: roundf(SEGLEN * (200 - percent) / 100.0f);
uint8_t size = (1 + ((SEGMENT.speed * SEGLEN) >> 11));
if (SEGMENT.speed == 255) size = 255;
@ -4679,11 +4663,6 @@ uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulma
const uint16_t rows = SEGMENT.virtualHeight();
uint16_t x, y;
// initialize on first call
if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
}
SEGMENT.fadeToBlackBy(16 + (SEGMENT.speed>>3)); // create fading trails
unsigned long t = millis()/128; // timebase
// outer stars
@ -4718,7 +4697,6 @@ uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.so
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
SEGENV.aux0 = 0; // start with red hue
}
@ -4770,12 +4748,7 @@ uint16_t mode_2Ddna(void) { // dna originally by by ldirko at https://pa
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
}
SEGMENT.fadeToBlackBy(64);
for (int i = 0; i < cols; i++) {
SEGMENT.setPixelColorXY(i, beatsin8(SEGMENT.speed/8, 0, rows-1, 0, i*4 ), ColorFromPalette(SEGPALETTE, i*5+millis()/17, beatsin8(5, 55, 255, 0, i*10), LINEARBLEND));
SEGMENT.setPixelColorXY(i, beatsin8(SEGMENT.speed/8, 0, rows-1, 0, i*4+128), ColorFromPalette(SEGPALETTE, i*5+128+millis()/17, beatsin8(5, 55, 255, 0, i*10+128), LINEARBLEND));
@ -4841,12 +4814,7 @@ uint16_t mode_2DDrift() { // By: Stepko https://editor.soulmateli
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
}
SEGMENT.fadeToBlackBy(128);
const uint16_t maxDim = MAX(cols, rows)/2;
unsigned long t = millis() / (32 - (SEGMENT.speed>>3));
unsigned long t_20 = t/20; // softhack007: pre-calculating this gives about 10% speedup
@ -4906,10 +4874,6 @@ uint16_t mode_2DFrizzles(void) { // By: Stepko https://editor.so
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
}
SEGMENT.fadeToBlackBy(16);
for (size_t i = 8; i > 0; i--) {
SEGMENT.addPixelColorXY(beatsin8(SEGMENT.speed/8 + i, 0, cols - 1),
@ -5353,12 +5317,7 @@ uint16_t mode_2DPlasmaball(void) { // By: Stepko https://edito
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
}
SEGMENT.fadeToBlackBy(SEGMENT.custom1>>2);
uint_fast32_t t = (millis() * 8) / (256 - SEGMENT.speed); // optimized to avoid float
for (int i = 0; i < cols; i++) {
uint16_t thisVal = inoise8(i * 30, t, t);
@ -5449,12 +5408,7 @@ uint16_t mode_2DPulser(void) { // By: ldirko https://edi
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
}
SEGMENT.fadeToBlackBy(8 - (SEGMENT.intensity>>5));
uint32_t a = strip.now / (18 - SEGMENT.speed / 16);
uint16_t x = (a / 14) % cols;
uint16_t y = map((sin8(a * 5) + sin8(a * 4) + sin8(a * 2)), 0, 765, rows-1, 0);
@ -5507,14 +5461,9 @@ uint16_t mode_2Dsquaredswirl(void) { // By: Mark Kriegsman. https://g
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
}
const uint8_t kBorderWidth = 2;
SEGMENT.fadeToBlackBy(24);
uint8_t blurAmount = SEGMENT.custom3>>1; // reduced resolution slider
SEGMENT.blur(blurAmount);
@ -5635,10 +5584,6 @@ uint16_t mode_2Dspaceships(void) { //// Space ships by stepko (c)05.02.21 [ht
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
}
uint32_t tb = strip.now >> 12; // every ~4s
if (tb > SEGENV.step) {
int8_t dir = ++SEGENV.aux0;
@ -5704,7 +5649,6 @@ uint16_t mode_2Dcrazybees(void) {
bee_t *bee = reinterpret_cast<bee_t*>(SEGENV.data);
if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
for (size_t i = 0; i < n; i++) {
bee[i].posX = random8(0, cols);
bee[i].posY = random8(0, rows);
@ -5713,7 +5657,7 @@ uint16_t mode_2Dcrazybees(void) {
}
if (millis() > SEGENV.step) {
SEGENV.step = millis() + (FRAMETIME * 8 / ((SEGMENT.speed>>5)+1));
SEGENV.step = millis() + (FRAMETIME * 16 / ((SEGMENT.speed>>4)+1));
SEGMENT.fadeToBlackBy(32);
@ -5776,9 +5720,9 @@ uint16_t mode_2Dghostrider(void) {
if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) {
SEGENV.aux0 = cols;
SEGENV.aux1 = rows;
SEGMENT.fill(BLACK);
random16_set_seed(strip.now);
lighter->angleSpeed = random8(0,20) - 10;
lighter->gAngle = random16();
lighter->Vspeed = 5;
lighter->gPosX = (cols/2) * 10;
lighter->gPosY = (rows/2) * 10;
@ -5786,6 +5730,7 @@ uint16_t mode_2Dghostrider(void) {
lighter->lightersPosX[i] = lighter->gPosX;
lighter->lightersPosY[i] = lighter->gPosY + i;
lighter->time[i] = i * 2;
lighter->reg[i] = false;
}
}
@ -5860,7 +5805,7 @@ uint16_t mode_2Dfloatingblobs(void) {
if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) {
SEGENV.aux0 = cols; // re-initialise if virtual size changes
SEGENV.aux1 = rows;
SEGMENT.fill(BLACK);
//SEGMENT.fill(BLACK);
for (size_t i = 0; i < MAX_BLOBS; i++) {
blob->r[i] = random8(1, cols>8 ? (cols/4) : 2);
blob->sX[i] = (float) random8(3, cols) / (float)(256 - SEGMENT.speed); // speed x
@ -5874,7 +5819,7 @@ uint16_t mode_2Dfloatingblobs(void) {
}
}
SEGMENT.fadeToBlackBy(20);
SEGMENT.fadeToBlackBy((SEGMENT.custom2>>3)+1);
// Bounce balls around
for (size_t i = 0; i < Amount; i++) {
@ -5930,7 +5875,7 @@ uint16_t mode_2Dfloatingblobs(void) {
return FRAMETIME;
}
#undef MAX_BLOBS
static const char _data_FX_MODE_2DBLOBS[] PROGMEM = "Blobs@!,# blobs,Blur;!;!;2;c1=8";
static const char _data_FX_MODE_2DBLOBS[] PROGMEM = "Blobs@!,# blobs,Blur,Trail;!;!;2;c1=8";
////////////////////////////
@ -5942,8 +5887,8 @@ uint16_t mode_2Dscrollingtext(void) {
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
int letterWidth;
int letterHeight;
int letterWidth, rotLW;
int letterHeight, rotLH;
switch (map(SEGMENT.custom2, 0, 255, 1, 5)) {
default:
case 1: letterWidth = 4; letterHeight = 6; break;
@ -5952,59 +5897,84 @@ uint16_t mode_2Dscrollingtext(void) {
case 4: letterWidth = 7; letterHeight = 9; break;
case 5: letterWidth = 5; letterHeight = 12; break;
}
const bool zero = SEGMENT.check3;
const int yoffset = map(SEGMENT.intensity, 0, 255, -rows/2, rows/2) + (rows-letterHeight)/2;
// letters are rotated
if (((SEGMENT.custom3+1)>>3) % 2) {
rotLH = letterWidth;
rotLW = letterHeight;
} else {
rotLW = letterWidth;
rotLH = letterHeight;
}
char text[WLED_MAX_SEGNAME_LEN+1] = {'\0'};
if (SEGMENT.name) for (size_t i=0,j=0; i<strlen(SEGMENT.name); i++) if (SEGMENT.name[i]>31 && SEGMENT.name[i]<128) text[j++] = SEGMENT.name[i];
const bool zero = strchr(text, '0') != nullptr;
const int numberOfLetters = strlen(text);
if (!strlen(text)
|| !strncmp_P(text,PSTR("#DATE"),5)
|| !strncmp_P(text,PSTR("#DDMM"),5)
|| !strncmp_P(text,PSTR("#MMDD"),5)
|| !strncmp_P(text,PSTR("#TIME"),5)
|| !strncmp_P(text,PSTR("#HHMM"),5)) { // fallback if empty segment name: display date and time
char sec[5];
byte AmPmHour = hour(localTime);
boolean isitAM = true;
if (useAMPM) {
if (AmPmHour > 11) { AmPmHour -= 12; isitAM = false; }
if (AmPmHour == 0) { AmPmHour = 12; }
}
if (useAMPM) sprintf_P(sec, PSTR(" %2s"), (isitAM ? "AM" : "PM"));
else sprintf_P(sec, PSTR(":%02d"), second(localTime));
if (!strncmp_P(text,PSTR("#DATE"),5)) sprintf_P(text, zero?PSTR("%02d.%02d.%04d"):PSTR("%d.%d.%d"), day(localTime), month(localTime), year(localTime));
else if (!strncmp_P(text,PSTR("#DDMM"),5)) sprintf_P(text, zero?PSTR("%02d.%02d"):PSTR("%d.%d"), day(localTime), month(localTime));
else if (!strncmp_P(text,PSTR("#MMDD"),5)) sprintf_P(text, zero?PSTR("%02d/%02d"):PSTR("%d/%d"), month(localTime), day(localTime));
else if (!strncmp_P(text,PSTR("#TIME"),5)) sprintf_P(text, zero?PSTR("%02d:%02d%s"):PSTR("%2d:%02d%s"), AmPmHour, minute(localTime), sec);
else if (!strncmp_P(text,PSTR("#HHMM"),5)) sprintf_P(text, zero?PSTR("%02d:%02d"):PSTR("%d:%02d"), AmPmHour, minute(localTime));
else sprintf_P(text, zero?PSTR("%s %02d, %04d %02d:%02d%s"):PSTR("%s %d, %d %d:%02d%s"), monthShortStr(month(localTime)), day(localTime), year(localTime), AmPmHour, minute(localTime), sec);
char sec[5];
int AmPmHour = hour(localTime);
bool isitAM = true;
if (useAMPM) {
if (AmPmHour > 11) { AmPmHour -= 12; isitAM = false; }
if (AmPmHour == 0) { AmPmHour = 12; }
sprintf_P(sec, PSTR(" %2s"), (isitAM ? "AM" : "PM"));
} else {
sprintf_P(sec, PSTR(":%02d"), second(localTime));
}
const int numberOfLetters = strlen(text);
if (SEGENV.step < millis()) {
if ((numberOfLetters * letterWidth) > cols) ++SEGENV.aux0 %= (numberOfLetters * letterWidth) + cols; // offset
else SEGENV.aux0 = (cols + (numberOfLetters * letterWidth))/2;
if (!numberOfLetters) { // fallback if empty segment name: display date and time
sprintf_P(text, PSTR("%s %d, %d %d:%02d%s"), monthShortStr(month(localTime)), day(localTime), year(localTime), AmPmHour, minute(localTime), sec);
} else {
if (!strncmp_P(text,PSTR("#DATE"),5)) sprintf_P(text, zero?PSTR("%02d.%02d.%04d"):PSTR("%d.%d.%d"), day(localTime), month(localTime), year(localTime));
else if (!strncmp_P(text,PSTR("#DDMM"),5)) sprintf_P(text, zero?PSTR("%02d.%02d") :PSTR("%d.%d"), day(localTime), month(localTime));
else if (!strncmp_P(text,PSTR("#MMDD"),5)) sprintf_P(text, zero?PSTR("%02d/%02d") :PSTR("%d/%d"), month(localTime), day(localTime));
else if (!strncmp_P(text,PSTR("#TIME"),5)) sprintf_P(text, zero?PSTR("%02d:%02d%s") :PSTR("%2d:%02d%s"), AmPmHour, minute(localTime), sec);
else if (!strncmp_P(text,PSTR("#HHMM"),5)) sprintf_P(text, zero?PSTR("%02d:%02d") :PSTR("%d:%02d"), AmPmHour, minute(localTime));
}
const unsigned long now = millis(); // reduce millis() calls
int width = (numberOfLetters * rotLW);
int yoffset = map(SEGMENT.intensity, 0, 255, -rows/2, rows/2) + (rows-rotLH)/2;
if (width <= cols) {
// scroll vertically (e.g. ^^ Way out ^^) if it fits
int speed = map(SEGMENT.speed, 0, 255, 5000, 1000);
int frac = now % speed + 1;
if (SEGMENT.intensity == 255) {
yoffset = (2 * frac * rows)/speed - rows;
} else if (SEGMENT.intensity == 0) {
yoffset = rows - (2 * frac * rows)/speed;
}
}
if (SEGENV.step < now) {
// calculate start offset
if (width > cols) {
if (SEGMENT.check3) {
if (SEGENV.aux0 == 0) SEGENV.aux0 = width + cols - 1;
else --SEGENV.aux0;
} else ++SEGENV.aux0 %= width + cols;
} else SEGENV.aux0 = (cols + width)/2;
++SEGENV.aux1 &= 0xFF; // color shift
SEGENV.step = millis() + map(SEGMENT.speed, 0, 255, 10*FRAMETIME_FIXED, 2*FRAMETIME_FIXED);
if (!SEGMENT.check2) {
for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++ )
SEGMENT.blendPixelColorXY(x, y, SEGCOLOR(1), 255 - (SEGMENT.custom1>>1));
}
SEGENV.step = now + map(SEGMENT.speed, 0, 255, 250, 50); // shift letters every ~250ms to ~50ms
}
if (!SEGMENT.check2) SEGMENT.fade_out(255 - (SEGMENT.custom1>>4)); // trail
for (int i = 0; i < numberOfLetters; i++) {
if (int(cols) - int(SEGENV.aux0) + letterWidth*(i+1) < 0) continue; // don't draw characters off-screen
int xoffset = int(cols) - int(SEGENV.aux0) + rotLW*i;
if (xoffset + rotLW < 0) continue; // don't draw characters off-screen
uint32_t col1 = SEGMENT.color_from_palette(SEGENV.aux1, false, PALETTE_SOLID_WRAP, 0);
uint32_t col2 = BLACK;
if (SEGMENT.check1 && SEGMENT.palette == 0) {
col1 = SEGCOLOR(0);
col2 = SEGCOLOR(2);
}
SEGMENT.drawCharacter(text[i], int(cols) - int(SEGENV.aux0) + letterWidth*i, yoffset, letterWidth, letterHeight, col1, col2);
SEGMENT.drawCharacter(text[i], xoffset, yoffset, letterWidth, letterHeight, col1, col2, (SEGMENT.custom3+1)>>3);
}
return FRAMETIME;
}
static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size,,Gradient,Overlay,0;!,!,Gradient;!;2;ix=128,c1=0,rev=0,mi=0,rY=0,mY=0";
static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size,Rotate,Gradient,Overlay,Reverse;!,!,Gradient;!;2;ix=128,c1=0,c3=0,rev=0,mi=0,rY=0,mY=0";
////////////////////////////
@ -6021,10 +5991,6 @@ uint16_t mode_2Ddriftrose(void) {
const float CY = (rows-rows%2)/2.f - .5f;
const float L = min(cols, rows) / 2.f;
if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
}
SEGMENT.fadeToBlackBy(32+(SEGMENT.speed>>3));
for (size_t i = 1; i < 37; i++) {
uint32_t x = (CX + (sin_t(radians(i * 10)) * (beatsin8(i, 0, L*2)-L))) * 255.f;
@ -6197,8 +6163,6 @@ uint16_t mode_2DSwirl(void) {
float volumeSmth = *(float*) um_data->u_data[0]; //ewowi: use instead of sampleAvg???
int16_t volumeRaw = *(int16_t*) um_data->u_data[1];
// printUmData();
SEGMENT.addPixelColorXY( i, j, ColorFromPalette(SEGPALETTE, (ms / 11 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 11, 200, 255);
SEGMENT.addPixelColorXY( j, i, ColorFromPalette(SEGPALETTE, (ms / 13 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 13, 200, 255);
SEGMENT.addPixelColorXY(ni,nj, ColorFromPalette(SEGPALETTE, (ms / 17 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 17, 200, 255);
@ -6221,10 +6185,6 @@ uint16_t mode_2DWaverly(void) {
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
}
um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
@ -6272,6 +6232,7 @@ typedef struct Gravity {
// * GRAVCENTER //
///////////////////////
uint16_t mode_gravcenter(void) { // Gravcenter. By Andrew Tuline.
if (SEGLEN == 1) return mode_static();
const uint16_t dataSize = sizeof(gravity);
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@ -6290,7 +6251,7 @@ uint16_t mode_gravcenter(void) { // Gravcenter. By Andrew Tuline.
float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0f;
segmentSampleAvg *= 0.125; // divide by 8, to compensate for later "sensitivty" upscaling
float mySampleAvg = mapf(segmentSampleAvg*2.0, 0, 32, 0, (float)SEGLEN/2.0); // map to pixels available in current segment
float mySampleAvg = mapf(segmentSampleAvg*2.0, 0, 32, 0, (float)SEGLEN/2.0f); // map to pixels available in current segment
uint16_t tempsamp = constrain(mySampleAvg, 0, SEGLEN/2); // Keep the sample from overflowing.
uint8_t gravity = 8 - SEGMENT.speed/32;
@ -6320,6 +6281,7 @@ static const char _data_FX_MODE_GRAVCENTER[] PROGMEM = "Gravcenter@Rate of fall,
// * GRAVCENTRIC //
///////////////////////
uint16_t mode_gravcentric(void) { // Gravcentric. By Andrew Tuline.
if (SEGLEN == 1) return mode_static();
uint16_t dataSize = sizeof(gravity);
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@ -6338,10 +6300,10 @@ uint16_t mode_gravcentric(void) { // Gravcentric. By Andrew
//SEGMENT.fade_out(240); // twice? really?
SEGMENT.fade_out(253); // 50%
float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0;
float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0f;
segmentSampleAvg *= 0.125f; // divide by 8, to compensate for later "sensitivty" upscaling
float mySampleAvg = mapf(segmentSampleAvg*2.0, 0.0f, 32.0f, 0.0f, (float)SEGLEN/2.0); // map to pixels availeable in current segment
float mySampleAvg = mapf(segmentSampleAvg*2.0, 0.0f, 32.0f, 0.0f, (float)SEGLEN/2.0f); // map to pixels availeable in current segment
int tempsamp = constrain(mySampleAvg, 0, SEGLEN/2); // Keep the sample from overflowing.
uint8_t gravity = 8 - SEGMENT.speed/32;
@ -6371,6 +6333,7 @@ static const char _data_FX_MODE_GRAVCENTRIC[] PROGMEM = "Gravcentric@Rate of fal
// * GRAVIMETER //
///////////////////////
uint16_t mode_gravimeter(void) { // Gravmeter. By Andrew Tuline.
if (SEGLEN == 1) return mode_static();
uint16_t dataSize = sizeof(gravity);
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@ -6417,6 +6380,7 @@ static const char _data_FX_MODE_GRAVIMETER[] PROGMEM = "Gravimeter@Rate of fall,
// * JUGGLES //
//////////////////////
uint16_t mode_juggles(void) { // Juggles. By Andrew Tuline.
if (SEGLEN == 1) return mode_static();
um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
@ -6440,6 +6404,7 @@ static const char _data_FX_MODE_JUGGLES[] PROGMEM = "Juggles@!,# of balls;!,!;!;
// * MATRIPIX //
//////////////////////
uint16_t mode_matripix(void) { // Matripix. By Andrew Tuline.
if (SEGLEN == 1) return mode_static();
// even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment
um_data_t *um_data;
@ -6471,6 +6436,7 @@ static const char _data_FX_MODE_MATRIPIX[] PROGMEM = "Matripix@!,Brightness;!,!;
// * MIDNOISE //
//////////////////////
uint16_t mode_midnoise(void) { // Midnoise. By Andrew Tuline.
if (SEGLEN == 1) return mode_static();
// Changing xdist to SEGENV.aux0 and ydist to SEGENV.aux1.
um_data_t *um_data;
@ -6574,6 +6540,7 @@ static const char _data_FX_MODE_NOISEMETER[] PROGMEM = "Noisemeter@Fade rate,Wid
// * PIXELWAVE //
//////////////////////
uint16_t mode_pixelwave(void) { // Pixelwave. By Andrew Tuline.
if (SEGLEN == 1) return mode_static();
// even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment
if (SEGENV.call == 0) {
@ -6649,6 +6616,7 @@ static const char _data_FX_MODE_PLASMOID[] PROGMEM = "Plasmoid@Phase,# of pixels
///////////////////////
// Andrew's crappy peak detector. If I were 40+ years younger, I'd learn signal processing.
uint16_t mode_puddlepeak(void) { // Puddlepeak. By Andrew Tuline.
if (SEGLEN == 1) return mode_static();
uint16_t size = 0;
uint8_t fadeVal = map(SEGMENT.speed,0,255, 224, 254);
@ -6692,6 +6660,7 @@ static const char _data_FX_MODE_PUDDLEPEAK[] PROGMEM = "Puddlepeak@Fade rate,Pud
// * PUDDLES //
//////////////////////
uint16_t mode_puddles(void) { // Puddles. By Andrew Tuline.
if (SEGLEN == 1) return mode_static();
uint16_t size = 0;
uint8_t fadeVal = map(SEGMENT.speed, 0, 255, 224, 254);
uint16_t pos = random16(SEGLEN); // Set a random starting position.
@ -6723,6 +6692,7 @@ static const char _data_FX_MODE_PUDDLES[] PROGMEM = "Puddles@Fade rate,Puddle si
// * PIXELS //
//////////////////////
uint16_t mode_pixels(void) { // Pixels. By Andrew Tuline.
if (SEGLEN == 1) return mode_static();
if (!SEGENV.allocateData(32*sizeof(uint8_t))) return mode_static(); //allocation failed
uint8_t *myVals = reinterpret_cast<uint8_t*>(SEGENV.data); // Used to store a pile of samples because WLED frame rate and WLED sample rate are not synchronized. Frame rate is too low.
@ -6756,6 +6726,7 @@ static const char _data_FX_MODE_PIXELS[] PROGMEM = "Pixels@Fade rate,# of pixels
// ** Blurz //
//////////////////////
uint16_t mode_blurz(void) { // Blurz. By Andrew Tuline.
if (SEGLEN == 1) return mode_static();
// even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment
um_data_t *um_data;
@ -6776,7 +6747,7 @@ uint16_t mode_blurz(void) { // Blurz. By Andrew Tuline.
SEGENV.step += FRAMETIME;
if (SEGENV.step > SPEED_FORMULA_L) {
uint16_t segLoc = random16(SEGLEN);
SEGMENT.setPixelColor(segLoc, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(2*fftResult[SEGENV.aux0%16]*240/(SEGLEN-1), false, PALETTE_SOLID_WRAP, 0), 2*fftResult[SEGENV.aux0%16]));
SEGMENT.setPixelColor(segLoc, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(2*fftResult[SEGENV.aux0%16]*240/max(1, SEGLEN-1), false, PALETTE_SOLID_WRAP, 0), 2*fftResult[SEGENV.aux0%16]));
++(SEGENV.aux0) %= 16; // make sure it doesn't cross 16
SEGENV.step = 1;
@ -6792,6 +6763,7 @@ static const char _data_FX_MODE_BLURZ[] PROGMEM = "Blurz@Fade rate,Blur;!,Color
// ** DJLight //
/////////////////////////
uint16_t mode_DJLight(void) { // Written by ??? Adapted by Will Tatam.
if (SEGLEN == 1) return mode_static();
const int mid = SEGLEN / 2;
um_data_t *um_data;
@ -6825,6 +6797,7 @@ static const char _data_FX_MODE_DJLIGHT[] PROGMEM = "DJ Light@Speed;;;1f;m12=2,s
// ** Freqmap //
////////////////////
uint16_t mode_freqmap(void) { // Map FFT_MajorPeak to SEGLEN. Would be better if a higher framerate.
if (SEGLEN == 1) return mode_static();
// Start frequency = 60 Hz and log10(60) = 1.78
// End frequency = MAX_FREQUENCY in Hz and lo10(MAX_FREQUENCY) = MAX_FREQ_LOG10
@ -6861,6 +6834,7 @@ static const char _data_FX_MODE_FREQMAP[] PROGMEM = "Freqmap@Fade rate,Starting
// ** Freqmatrix //
///////////////////////
uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Pleschung.
if (SEGLEN == 1) return mode_static();
um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
@ -6926,24 +6900,27 @@ uint16_t mode_freqpixels(void) { // Freqpixel. By Andrew Tuline.
}
float FFT_MajorPeak = *(float*)um_data->u_data[4];
float my_magnitude = *(float*)um_data->u_data[5] / 16.0f;
if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception)
if (FFT_MajorPeak < 1) FFT_MajorPeak = 1.0f; // log10(0) is "forbidden" (throws exception)
uint16_t fadeRate = 2*SEGMENT.speed - SEGMENT.speed*SEGMENT.speed/255; // Get to 255 as quick as you can.
// this code translates to speed * (2 - speed/255) which is a) speed*2 or b) speed (when speed is 255)
// and since fade_out() can only take 0-255 it will behave incorrectly when speed > 127
//uint16_t fadeRate = 2*SEGMENT.speed - SEGMENT.speed*SEGMENT.speed/255; // Get to 255 as quick as you can.
uint16_t fadeRate = SEGMENT.speed*SEGMENT.speed; // Get to 255 as quick as you can.
fadeRate = map(fadeRate, 0, 65535, 1, 255);
if (SEGENV.call == 0) SEGMENT.fill(BLACK);
int fadeoutDelay = (256 - SEGMENT.speed) / 64;
if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fade_out(fadeRate);
uint8_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(MAX_FREQ_LOG10 - 1.78f); // Scale log10 of frequency values to the 255 colour index.
if (FFT_MajorPeak < 61.0f) pixCol = 0; // handle underflow
for (int i=0; i < SEGMENT.intensity/32+1; i++) {
uint16_t locn = random16(0,SEGLEN);
uint8_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(MAX_FREQ_LOG10 - 1.78f); // Scale log10 of frequency values to the 255 colour index.
if (FFT_MajorPeak < 61.0f) pixCol = 0; // handle underflow
SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(SEGMENT.intensity+pixCol, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude));
}
return FRAMETIME;
} // mode_freqpixels()
static const char _data_FX_MODE_FREQPIXELS[] PROGMEM = "Freqpixels@Fade rate,Starting color and # of pixels;;;1f;m12=0,si=0"; // Pixels, Beatsin
static const char _data_FX_MODE_FREQPIXELS[] PROGMEM = "Freqpixels@Fade rate,Starting color and # of pixels;!,!,;!;1f;m12=0,si=0"; // Pixels, Beatsin
//////////////////////
@ -6962,6 +6939,7 @@ static const char _data_FX_MODE_FREQPIXELS[] PROGMEM = "Freqpixels@Fade rate,Sta
// As a compromise between speed and accuracy we are currently sampling with 10240Hz, from which we can then determine with a 512bin FFT our max frequency is 5120Hz.
// Depending on the music stream you have you might find it useful to change the frequency mapping.
uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschung.
if (SEGLEN == 1) return mode_static();
um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
@ -7018,7 +6996,7 @@ static const char _data_FX_MODE_FREQWAVE[] PROGMEM = "Freqwave@Speed,Sound effec
// ** Gravfreq //
///////////////////////
uint16_t mode_gravfreq(void) { // Gravfreq. By Andrew Tuline.
if (SEGLEN == 1) return mode_static();
uint16_t dataSize = sizeof(gravity);
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
Gravity* gravcen = reinterpret_cast<Gravity*>(SEGENV.data);
@ -7035,9 +7013,9 @@ uint16_t mode_gravfreq(void) { // Gravfreq. By Andrew Tuline.
SEGMENT.fade_out(250);
float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0f;
segmentSampleAvg *= 0.125; // divide by 8, to compensate for later "sensitivty" upscaling
segmentSampleAvg *= 0.125f; // divide by 8, to compensate for later "sensitivty" upscaling
float mySampleAvg = mapf(segmentSampleAvg*2.0f, 0,32, 0, (float)SEGLEN/2.0); // map to pixels availeable in current segment
float mySampleAvg = mapf(segmentSampleAvg*2.0f, 0,32, 0, (float)SEGLEN/2.0f); // map to pixels availeable in current segment
int tempsamp = constrain(mySampleAvg,0,SEGLEN/2); // Keep the sample from overflowing.
uint8_t gravity = 8 - SEGMENT.speed/32;
@ -7070,6 +7048,7 @@ static const char _data_FX_MODE_GRAVFREQ[] PROGMEM = "Gravfreq@Rate of fall,Sens
// ** Noisemove //
//////////////////////
uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuline
if (SEGLEN == 1) return mode_static();
um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
@ -7077,8 +7056,6 @@ uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuli
}
uint8_t *fftResult = (uint8_t*)um_data->u_data[2];
if (SEGENV.call == 0) SEGMENT.fill(BLACK);
//SEGMENT.fade_out(224); // Just in case something doesn't get faded.
int fadeoutDelay = (256 - SEGMENT.speed) / 96;
if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fadeToBlackBy(4+ SEGMENT.speed/4);
@ -7098,6 +7075,7 @@ static const char _data_FX_MODE_NOISEMOVE[] PROGMEM = "Noisemove@Speed of perlin
// ** Rocktaves //
//////////////////////
uint16_t mode_rocktaves(void) { // Rocktaves. Same note from each octave is same colour. By: Andrew Tuline
if (SEGLEN == 1) return mode_static();
um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
@ -7106,8 +7084,7 @@ uint16_t mode_rocktaves(void) { // Rocktaves. Same note from eac
float FFT_MajorPeak = *(float*) um_data->u_data[4];
float my_magnitude = *(float*) um_data->u_data[5] / 16.0f;
if (SEGENV.call == 0) SEGMENT.fill(BLACK);
SEGMENT.fadeToBlackBy(16); // Just in case something doesn't get faded.
SEGMENT.fadeToBlackBy(16); // Just in case something doesn't get faded.
float frTemp = FFT_MajorPeak;
uint8_t octCount = 0; // Octave counter.
@ -7122,8 +7099,8 @@ uint16_t mode_rocktaves(void) { // Rocktaves. Same note from eac
frTemp = frTemp/2;
}
frTemp -=132; // This should give us a base musical note of C3
frTemp = fabsf(frTemp * 2.1f); // Fudge factors to compress octave range starting at 0 and going to 255;
frTemp -= 132.0f; // This should give us a base musical note of C3
frTemp = fabsf(frTemp * 2.1f); // Fudge factors to compress octave range starting at 0 and going to 255;
uint16_t i = map(beatsin8(8+octCount*4, 0, 255, 0, octCount*8), 0, 255, 0, SEGLEN-1);
i = constrain(i, 0, SEGLEN-1);
@ -7139,7 +7116,7 @@ static const char _data_FX_MODE_ROCKTAVES[] PROGMEM = "Rocktaves@;!,!;!;1f;m12=1
///////////////////////
// Combines peak detection with FFT_MajorPeak and FFT_Magnitude.
uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tuline
if (SEGENV.call == 0) SEGMENT.fill(BLACK);
if (SEGLEN == 1) return mode_static();
um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
@ -7214,7 +7191,6 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma.
rippleTime = true;
}
if (SEGENV.call == 0) SEGMENT.fill(BLACK);
int fadeoutDelay = (256 - SEGMENT.speed) / 64;
if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fadeToBlackBy(SEGMENT.speed);
@ -7516,6 +7492,7 @@ uint16_t mode_2Dsoap() {
int amplitude;
int8_t shiftX = 0; //(SEGMENT.custom1 - 128) / 4;
int8_t shiftY = 0; //(SEGMENT.custom2 - 128) / 4;
CRGB ledsbuff[MAX(cols,rows)];
amplitude = (cols >= 16) ? (cols-8)/8 : 1;
for (int y = 0; y < rows; y++) {
@ -7536,9 +7513,9 @@ uint16_t mode_2Dsoap() {
CRGB PixelB = CRGB::Black;
if ((zF >= 0) && (zF < cols)) PixelB = SEGMENT.getPixelColorXY(zF, y);
else PixelB = ColorFromPalette(SEGPALETTE, ~noise3d[XY(abs(zF),y)]*3);
CRGB pix = (PixelA.nscale8(ease8InOutApprox(255 - fraction))) + (PixelB.nscale8(ease8InOutApprox(fraction)));
SEGMENT.setPixelColorXY(x, y, pix);
ledsbuff[x] = (PixelA.nscale8(ease8InOutApprox(255 - fraction))) + (PixelB.nscale8(ease8InOutApprox(fraction)));
}
for (int x = 0; x < cols; x++) SEGMENT.setPixelColorXY(x, y, ledsbuff[x]);
}
amplitude = (rows >= 16) ? (rows-8)/8 : 1;
@ -7560,9 +7537,9 @@ uint16_t mode_2Dsoap() {
CRGB PixelB = CRGB::Black;
if ((zF >= 0) && (zF < rows)) PixelB = SEGMENT.getPixelColorXY(x, zF);
else PixelB = ColorFromPalette(SEGPALETTE, ~noise3d[XY(x,abs(zF))]*3);
CRGB pix = (PixelA.nscale8(ease8InOutApprox(255 - fraction))) + (PixelB.nscale8(ease8InOutApprox(fraction)));
SEGMENT.setPixelColorXY(x, y, pix);
ledsbuff[y] = (PixelA.nscale8(ease8InOutApprox(255 - fraction))) + (PixelB.nscale8(ease8InOutApprox(fraction)));
}
for (int y = 0; y < rows; y++) SEGMENT.setPixelColorXY(x, y, ledsbuff[y]);
}
return FRAMETIME;

View File

@ -381,6 +381,26 @@ typedef struct Segment {
byte *data; // effect data pointer
static uint16_t maxWidth, maxHeight; // these define matrix width & height (max. segment dimensions)
typedef struct TemporarySegmentData {
uint16_t _optionsT;
uint32_t _colorT[NUM_COLORS];
uint8_t _speedT;
uint8_t _intensityT;
uint8_t _custom1T, _custom2T; // custom FX parameters/sliders
struct {
uint8_t _custom3T : 5; // reduced range slider (0-31)
bool _check1T : 1; // checkmark 1
bool _check2T : 1; // checkmark 2
bool _check3T : 1; // checkmark 3
};
uint16_t _aux0T;
uint16_t _aux1T;
uint32_t _stepT;
uint32_t _callT;
uint8_t *_dataT;
uint16_t _dataLenT;
} tmpsegd_t;
private:
union {
uint8_t _capabilities;
@ -395,39 +415,34 @@ typedef struct Segment {
uint16_t _dataLen;
static uint16_t _usedSegmentData;
// perhaps this should be per segment, not static
static CRGBPalette16 _randomPalette; // actual random palette
static CRGBPalette16 _newRandomPalette; // target random palette
static unsigned long _lastPaletteChange; // last random palette change time in millis()
#ifndef WLED_DISABLE_MODE_BLEND
static bool _modeBlend; // mode/effect blending semaphore
#endif
// transition data, valid only if transitional==true, holds values during transition (72 bytes)
struct Transition {
#ifndef WLED_DISABLE_MODE_BLEND
tmpsegd_t _segT; // previous segment environment
uint8_t _modeT; // previous mode/effect
#else
uint32_t _colorT[NUM_COLORS];
#endif
uint8_t _briT; // temporary brightness
uint8_t _cctT; // temporary CCT
CRGBPalette16 _palT; // temporary palette
uint8_t _prevPaletteBlends; // number of previous palette blends (there are max 255 belnds possible)
uint8_t _modeP; // previous mode/effect
//uint16_t _aux0, _aux1; // previous mode/effect runtime data
//uint32_t _step, _call; // previous mode/effect runtime data
//byte *_data; // previous mode/effect runtime data
unsigned long _start; // must accommodate millis()
unsigned long _start; // must accommodate millis()
uint16_t _dur;
Transition(uint16_t dur=750)
: _briT(255)
, _cctT(127)
, _palT(CRGBPalette16(CRGB::Black))
: _palT(CRGBPalette16(CRGB::Black))
, _prevPaletteBlends(0)
, _modeP(FX_MODE_STATIC)
, _start(millis())
, _dur(dur)
{}
Transition(uint16_t d, uint8_t b, uint8_t c, const uint32_t *o)
: _briT(b)
, _cctT(c)
, _palT(CRGBPalette16(CRGB::Black))
, _prevPaletteBlends(0)
, _modeP(FX_MODE_STATIC)
, _start(millis())
, _dur(d)
{
for (size_t i=0; i<NUM_COLORS; i++) _colorT[i] = o[i];
}
} *_t;
public:
@ -466,6 +481,9 @@ typedef struct Segment {
_t(nullptr)
{
//refreshLightCapabilities();
#ifdef WLED_DEBUG
//Serial.printf("-- Creating segment: %p\n", this);
#endif
}
Segment(uint16_t sStartX, uint16_t sStopX, uint16_t sStartY, uint16_t sStopY) : Segment(sStartX, sStopX) {
@ -477,14 +495,14 @@ typedef struct Segment {
Segment(Segment &&orig) noexcept; // move constructor
~Segment() {
//#ifdef WLED_DEBUG
//Serial.print(F("Destroying segment:"));
#ifdef WLED_DEBUG
//Serial.printf("-- Destroying segment: %p\n", this);
//if (name) Serial.printf(" %s (%p)", name, name);
//if (data) Serial.printf(" %d (%p)", (int)_dataLen, data);
//Serial.println();
//#endif
#endif
if (name) { delete[] name; name = nullptr; }
if (_t) { transitional = false; delete _t; _t = nullptr; }
stopTransition();
deallocateData();
}
@ -510,6 +528,10 @@ typedef struct Segment {
static uint16_t getUsedSegmentData(void) { return _usedSegmentData; }
static void addUsedSegmentData(int len) { _usedSegmentData += len; }
#ifndef WLED_DISABLE_MODE_BLEND
static void modeBlend(bool blend) { _modeBlend = blend; }
#endif
static void handleRandomPalette();
void setUp(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1, uint8_t segId = 255);
bool setColor(uint8_t slot, uint32_t c); //returns true if changed
@ -536,7 +558,12 @@ typedef struct Segment {
// transition functions
void startTransition(uint16_t dur); // transition has to start before actual segment values change
void stopTransition(void);
void handleTransition(void);
#ifndef WLED_DISABLE_MODE_BLEND
void swapSegenv(tmpsegd_t &tmpSegD);
void restoreSegenv(tmpsegd_t &tmpSegD);
#endif
uint16_t progress(void); //transition progression between 0-65535
uint8_t currentBri(uint8_t briNew, bool useCct = false);
uint8_t currentMode(uint8_t modeNew);
@ -598,9 +625,9 @@ typedef struct Segment {
void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c);
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c);
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0)); } // automatic inline
void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0);
void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0, uint8_t rotate = 0);
void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0)); } // automatic inline
void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.b,0)); } // automatic inline
void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2, uint8_t rotate = 0) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.b,0), rotate); } // automatic inline
void wu_pixel(uint32_t x, uint32_t y, CRGB c);
void blur1d(fract8 blur_amount); // blur all rows in 1 dimension
void blur2d(fract8 blur_amount) { blur(blur_amount); }

View File

@ -216,23 +216,29 @@ void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col)
y *= groupLength(); // expand to physical pixels
if (x >= width() || y >= height()) return; // if pixel would fall out of segment just exit
uint32_t tmpCol = col;
for (int j = 0; j < grouping; j++) { // groupping vertically
for (int g = 0; g < grouping; g++) { // groupping horizontally
uint16_t xX = (x+g), yY = (y+j);
if (xX >= width() || yY >= height()) continue; // we have reached one dimension's end
strip.setPixelColorXY(start + xX, startY + yY, col);
#ifndef WLED_DISABLE_MODE_BLEND
// if blending modes, blend with underlying pixel
if (_modeBlend) tmpCol = color_blend(strip.getPixelColorXY(start + xX, startY + yY), col, 0xFFFFU - progress(), true);
#endif
strip.setPixelColorXY(start + xX, startY + yY, tmpCol);
if (mirror) { //set the corresponding horizontally mirrored pixel
if (transpose) strip.setPixelColorXY(start + xX, startY + height() - yY - 1, col);
else strip.setPixelColorXY(start + width() - xX - 1, startY + yY, col);
if (transpose) strip.setPixelColorXY(start + xX, startY + height() - yY - 1, tmpCol);
else strip.setPixelColorXY(start + width() - xX - 1, startY + yY, tmpCol);
}
if (mirror_y) { //set the corresponding vertically mirrored pixel
if (transpose) strip.setPixelColorXY(start + width() - xX - 1, startY + yY, col);
else strip.setPixelColorXY(start + xX, startY + height() - yY - 1, col);
if (transpose) strip.setPixelColorXY(start + width() - xX - 1, startY + yY, tmpCol);
else strip.setPixelColorXY(start + xX, startY + height() - yY - 1, tmpCol);
}
if (mirror_y && mirror) { //set the corresponding vertically AND horizontally mirrored pixel
strip.setPixelColorXY(width() - xX - 1, height() - yY - 1, col);
strip.setPixelColorXY(width() - xX - 1, height() - yY - 1, tmpCol);
}
}
}
@ -565,7 +571,7 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3
// draws a raster font character on canvas
// only supports: 4x6=24, 5x8=40, 5x12=60, 6x8=48 and 7x9=63 fonts ATM
void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2) {
void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2, uint8_t rotate) {
if (!isActive()) return; // not active
if (chr < 32 || chr > 126) return; // only ASCII 32-126 supported
chr -= 32; // align with font table entries
@ -578,9 +584,6 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w,
//if (w<5 || w>6 || h!=8) return;
for (int i = 0; i<h; i++) { // character height
int16_t y0 = y + i;
if (y0 < 0) continue; // drawing off-screen
if (y0 >= rows) break; // drawing off-screen
uint8_t bits = 0;
switch (font) {
case 24: bits = pgm_read_byte_near(&console_font_4x6[(chr * h) + i]); break; // 5x8 font
@ -592,8 +595,15 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w,
}
col = ColorFromPalette(grad, (i+1)*255/h, 255, NOBLEND);
for (int j = 0; j<w; j++) { // character width
int16_t x0 = x + (w-1) - j;
if ((x0 >= 0 || x0 < cols) && ((bits>>(j+(8-w))) & 0x01)) { // bit set & drawing on-screen
int x0, y0;
switch (rotate) {
case 3: x0 = x + (h-1) - i; y0 = y + (w-1) - j; break;
case 2: x0 = x + j; y0 = y + (h-1) - i; break;
case 1: x0 = x + i; y0 = y + j; break;
default: x0 = x + (w-1) - j; y0 = y + i; break;
}
if (x0 < 0 || x0 >= cols || y0 < 0 || y0 >= rows) continue; // drawing off-screen
if (((bits>>(j+(8-w))) & 0x01)) { // bit set
setPixelColorXY(x0, y0, col);
}
}

View File

@ -77,9 +77,17 @@ uint16_t Segment::_usedSegmentData = 0U; // amount of RAM all segments use for t
uint16_t Segment::maxWidth = DEFAULT_LED_COUNT;
uint16_t Segment::maxHeight = 1;
CRGBPalette16 Segment::_randomPalette = CRGBPalette16(DEFAULT_COLOR);
CRGBPalette16 Segment::_newRandomPalette = CRGBPalette16(DEFAULT_COLOR);
unsigned long Segment::_lastPaletteChange = 0; // perhaps it should be per segment
#ifndef WLED_DISABLE_MODE_BLEND
bool Segment::_modeBlend = false;
#endif
// copy constructor
Segment::Segment(const Segment &orig) {
//DEBUG_PRINTLN(F("-- Copy segment constructor --"));
//DEBUG_PRINTF("-- Copy segment constructor: %p -> %p\n", &orig, this);
memcpy((void*)this, (void*)&orig, sizeof(Segment));
transitional = false; // copied segment cannot be in transition
name = nullptr;
@ -88,12 +96,12 @@ Segment::Segment(const Segment &orig) {
_t = nullptr;
if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); }
//if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); }
//if (orig._t) { _t = new Transition(orig._t->_dur); }
}
// move constructor
Segment::Segment(Segment &&orig) noexcept {
//DEBUG_PRINTLN(F("-- Move segment constructor --"));
//DEBUG_PRINTF("-- Move segment constructor: %p -> %p\n", &orig, this);
memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.transitional = false; // old segment cannot be in transition any more
orig.name = nullptr;
@ -104,12 +112,17 @@ Segment::Segment(Segment &&orig) noexcept {
// copy assignment
Segment& Segment::operator= (const Segment &orig) {
//DEBUG_PRINTLN(F("-- Copying segment --"));
//DEBUG_PRINTF("-- Copying segment: %p -> %p\n", &orig, this);
if (this != &orig) {
// clean destination
transitional = false; // copied segment cannot be in transition
if (name) delete[] name;
if (_t) delete _t;
if (_t) {
#ifndef WLED_DISABLE_MODE_BLEND
if (_t->_segT._dataT) free(_t->_segT._dataT);
#endif
delete _t;
}
deallocateData();
// copy source
memcpy((void*)this, (void*)&orig, sizeof(Segment));
@ -129,12 +142,18 @@ Segment& Segment::operator= (const Segment &orig) {
// move assignment
Segment& Segment::operator= (Segment &&orig) noexcept {
//DEBUG_PRINTLN(F("-- Moving segment --"));
//DEBUG_PRINTF("-- Moving segment: %p -> %p\n", &orig, this);
if (this != &orig) {
transitional = false; // just temporary
if (name) { delete[] name; name = nullptr; } // free old name
deallocateData(); // free old runtime data
if (_t) { delete _t; _t = nullptr; }
if (_t) {
#ifndef WLED_DISABLE_MODE_BLEND
if (_t->_segT._dataT) free(_t->_segT._dataT);
#endif
delete _t;
_t = nullptr;
}
memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.transitional = false; // old segment cannot be in transition
orig.name = nullptr;
@ -147,12 +166,18 @@ Segment& Segment::operator= (Segment &&orig) noexcept {
bool Segment::allocateData(size_t len) {
if (data && _dataLen == len) return true; //already allocated
//DEBUG_PRINTF("-- Allocating data (%d): %p\n", len, this);
deallocateData();
if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) return false; //not enough memory
if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) {
// not enough memory
DEBUG_PRINTF("!!! Effect RAM depleted: %d/%d !!!\n", len, Segment::getUsedSegmentData());
return false;
}
// do not use SPI RAM on ESP32 since it is slow
data = (byte*) malloc(len);
if (!data) return false; //allocation failed
if (!data) { DEBUG_PRINTLN(F("!!! Allocation failed. !!!")); return false; } //allocation failed
Segment::addUsedSegmentData(len);
//DEBUG_PRINTF("--- Allocated data (%p): %d/%d -> %p\n", this, len, Segment::getUsedSegmentData(), data);
_dataLen = len;
memset(data, 0, len);
return true;
@ -160,9 +185,11 @@ bool Segment::allocateData(size_t len) {
void Segment::deallocateData() {
if (!data) return;
//DEBUG_PRINTF("--- Released data (%p): %d/%d -> %p\n", this, _dataLen, Segment::getUsedSegmentData(), data);
free(data);
data = nullptr;
Segment::addUsedSegmentData(-_dataLen);
// WARNING it looks like we have a memory leak somewhere
Segment::addUsedSegmentData(_dataLen <= Segment::getUsedSegmentData() ? -_dataLen : -Segment::getUsedSegmentData());
_dataLen = 0;
}
@ -175,17 +202,13 @@ void Segment::deallocateData() {
*/
void Segment::resetIfRequired() {
if (!reset) return;
//DEBUG_PRINTF("-- Segment reset: %p\n", this);
deallocateData();
next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
reset = false;
}
CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
static unsigned long _lastPaletteChange = 0; // perhaps it should be per segment
static CRGBPalette16 randomPalette = CRGBPalette16(DEFAULT_COLOR);
static CRGBPalette16 prevRandomPalette = CRGBPalette16(CRGB(BLACK));
byte tcp[72];
if (pal < 245 && pal > GRADIENT_PALETTE_COUNT+13) pal = 0;
if (pal > 245 && (strip.customPalettes.size() == 0 || 255U-pal > strip.customPalettes.size()-1)) pal = 0;
//default palette. Differs depending on effect
@ -206,27 +229,18 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
case 0: //default palette. Exceptions for specific effects above
targetPalette = PartyColors_p; break;
case 1: {//periodically replace palette with a random one. Transition palette change in 500ms
uint32_t timeSinceLastChange = millis() - _lastPaletteChange;
unsigned long timeSinceLastChange = millis() - _lastPaletteChange;
if (timeSinceLastChange > randomPaletteChangeTime * 1000U) {
prevRandomPalette = randomPalette;
randomPalette = CRGBPalette16(
_randomPalette = _newRandomPalette;
_newRandomPalette = CRGBPalette16(
CHSV(random8(), random8(160, 255), random8(128, 255)),
CHSV(random8(), random8(160, 255), random8(128, 255)),
CHSV(random8(), random8(160, 255), random8(128, 255)),
CHSV(random8(), random8(160, 255), random8(128, 255)));
_lastPaletteChange = millis();
timeSinceLastChange = 0;
}
if (timeSinceLastChange <= 250) {
targetPalette = prevRandomPalette;
// there needs to be 255 palette blends (48) for full blend but that is too resource intensive
// so 128 is a compromise (we need to perform full blend of the two palettes as each segment can have random
// palette selected but only 2 static palettes are used)
size_t noOfBlends = ((128U * timeSinceLastChange) / 250U);
for (size_t i=0; i<noOfBlends; i++) nblendPaletteTowardPalette(targetPalette, randomPalette, 48);
} else {
targetPalette = randomPalette;
handleRandomPalette(); // do a 1st pass of blend
}
targetPalette = _randomPalette;
break;}
case 2: {//primary color only
CRGB prim = gamma32(colors[0]);
@ -268,6 +282,7 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
if (pal>245) {
targetPalette = strip.customPalettes[255-pal]; // we checked bounds above
} else {
byte tcp[72];
memcpy_P(tcp, (byte*)pgm_read_dword(&(gGradientPalettes[pal-13])), 72);
targetPalette.loadDynamicGradientPalette(tcp);
}
@ -278,11 +293,8 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
void Segment::startTransition(uint16_t dur) {
if (!dur) {
transitional = false;
if (_t) {
delete _t;
_t = nullptr;
}
if (_t) _t->_dur = dur; // this will stop transition in next handleTransisiton()
else transitional = false;
return;
}
if (transitional && _t) return; // already in transition no need to store anything
@ -291,44 +303,165 @@ void Segment::startTransition(uint16_t dur) {
_t = new Transition(dur); // no previous transition running
if (!_t) return; // failed to allocate data
//DEBUG_PRINTF("-- Started transition: %p\n", this);
CRGBPalette16 _palT = CRGBPalette16(DEFAULT_COLOR); loadPalette(_palT, palette);
_t->_briT = on ? opacity : 0;
_t->_cctT = cct;
_t->_palT = _palT;
_t->_modeP = mode;
_t->_palT = _palT;
_t->_briT = on ? opacity : 0;
_t->_cctT = cct;
#ifndef WLED_DISABLE_MODE_BLEND
swapSegenv(_t->_segT);
_t->_modeT = mode;
_t->_segT._optionsT |= 0b0000000001000000; // mark old segment transitional
_t->_segT._dataLenT = 0;
_t->_segT._dataT = nullptr;
if (_dataLen > 0 && data) {
_t->_segT._dataT = (byte *)malloc(_dataLen);
if (_t->_segT._dataT) {
//DEBUG_PRINTF("-- Allocated duplicate data (%d): %p\n", _dataLen, _t->_segT._dataT);
memcpy(_t->_segT._dataT, data, _dataLen);
_t->_segT._dataLenT = _dataLen;
}
}
#else
for (size_t i=0; i<NUM_COLORS; i++) _t->_colorT[i] = colors[i];
#endif
transitional = true; // setOption(SEG_OPTION_TRANSITIONAL, true);
}
void Segment::stopTransition() {
if (!transitional) return;
transitional = false; // finish transitioning segment
//DEBUG_PRINTF("-- Stopping transition: %p\n", this);
if (_t) {
#ifndef WLED_DISABLE_MODE_BLEND
if (_t->_segT._dataT && _t->_segT._dataLenT > 0) {
//DEBUG_PRINTF("-- Released duplicate data (%d): %p\n", _t->_segT._dataLenT, _t->_segT._dataT);
free(_t->_segT._dataT);
_t->_segT._dataT = nullptr;
}
#endif
delete _t;
_t = nullptr;
}
}
void Segment::handleTransition() {
if (!transitional) return;
uint16_t _progress = progress();
if (_progress == 0xFFFFU) stopTransition();
}
// transition progression between 0-65535
uint16_t Segment::progress() {
if (!transitional || !_t) return 0xFFFFU;
unsigned long timeNow = millis();
if (timeNow - _t->_start > _t->_dur || _t->_dur == 0) return 0xFFFFU;
return (timeNow - _t->_start) * 0xFFFFU / _t->_dur;
if (transitional && _t) {
unsigned long timeNow = millis();
if (_t->_dur > 0 && timeNow - _t->_start < _t->_dur) return (timeNow - _t->_start) * 0xFFFFU / _t->_dur;
}
return 0xFFFFU;
}
#ifndef WLED_DISABLE_MODE_BLEND
void Segment::swapSegenv(tmpsegd_t &tmpSeg) {
if (!_t) return;
//DEBUG_PRINTF("-- Saving temp seg: %p (%p)\n", this, tmpSeg);
tmpSeg._optionsT = options;
for (size_t i=0; i<NUM_COLORS; i++) tmpSeg._colorT[i] = colors[i];
tmpSeg._speedT = speed;
tmpSeg._intensityT = intensity;
tmpSeg._custom1T = custom1;
tmpSeg._custom2T = custom2;
tmpSeg._custom3T = custom3;
tmpSeg._check1T = check1;
tmpSeg._check2T = check2;
tmpSeg._check3T = check3;
tmpSeg._aux0T = aux0;
tmpSeg._aux1T = aux1;
tmpSeg._stepT = step;
tmpSeg._callT = call;
tmpSeg._dataT = data;
tmpSeg._dataLenT = _dataLen;
if (&tmpSeg != &(_t->_segT)) {
// swap SEGENV with transitional data
options = _t->_segT._optionsT;
for (size_t i=0; i<NUM_COLORS; i++) colors[i] = _t->_segT._colorT[i];
speed = _t->_segT._speedT;
intensity = _t->_segT._intensityT;
custom1 = _t->_segT._custom1T;
custom2 = _t->_segT._custom2T;
custom3 = _t->_segT._custom3T;
check1 = _t->_segT._check1T;
check2 = _t->_segT._check2T;
check3 = _t->_segT._check3T;
aux0 = _t->_segT._aux0T;
aux1 = _t->_segT._aux1T;
step = _t->_segT._stepT;
call = _t->_segT._callT;
data = _t->_segT._dataT;
_dataLen = _t->_segT._dataLenT;
}
//DEBUG_PRINTF("-- temp seg data: %p (%d,%p)\n", this, _dataLen, data);
}
void Segment::restoreSegenv(tmpsegd_t &tmpSeg) {
//DEBUG_PRINTF("-- Restoring temp seg: %p (%p)\n", this, tmpSeg);
if (_t && &(_t->_segT) != &tmpSeg) {
// update possibly changed variables to keep old effect running correctly
_t->_segT._aux0T = aux0;
_t->_segT._aux1T = aux1;
_t->_segT._stepT = step;
_t->_segT._callT = call;
//if (_t->_segT._dataT != data) DEBUG_PRINTF("--- data re-allocated: (%p) %p -> %p\n", this, _t->_segT._dataT, data);
_t->_segT._dataT = data; // sometimes memory gets re-allocated (!! INVESTIGATE WHY !!)
_t->_segT._dataLenT = _dataLen; // sometimes memory gets re-allocated (!! INVESTIGATE WHY !!)
}
options = tmpSeg._optionsT;
for (size_t i=0; i<NUM_COLORS; i++) colors[i] = tmpSeg._colorT[i];
speed = tmpSeg._speedT;
intensity = tmpSeg._intensityT;
custom1 = tmpSeg._custom1T;
custom2 = tmpSeg._custom2T;
custom3 = tmpSeg._custom3T;
check1 = tmpSeg._check1T;
check2 = tmpSeg._check2T;
check3 = tmpSeg._check3T;
aux0 = tmpSeg._aux0T;
aux1 = tmpSeg._aux1T;
step = tmpSeg._stepT;
call = tmpSeg._callT;
data = tmpSeg._dataT;
_dataLen = tmpSeg._dataLenT;
//DEBUG_PRINTF("-- temp seg data: %p (%d,%p)\n", this, _dataLen, data);
}
#endif
uint8_t Segment::currentBri(uint8_t briNew, bool useCct) {
uint32_t prog = progress();
if (transitional && _t && prog < 0xFFFFU) {
if (prog < 0xFFFFU) {
if (useCct) return ((briNew * prog) + _t->_cctT * (0xFFFFU - prog)) >> 16;
else return ((briNew * prog) + _t->_briT * (0xFFFFU - prog)) >> 16;
} else {
return briNew;
}
return briNew;
}
uint8_t Segment::currentMode(uint8_t newMode) {
return (progress()>32767U) ? newMode : _t->_modeP; // change effect in the middle of transition
#ifndef WLED_DISABLE_MODE_BLEND
uint16_t prog = progress(); // implicit check for transitional & _t in progress()
if (prog < 0xFFFFU) return _t->_modeT;
#endif
return newMode;
}
uint32_t Segment::currentColor(uint8_t slot, uint32_t colorNew) {
#ifndef WLED_DISABLE_MODE_BLEND
return transitional && _t ? color_blend(_t->_segT._colorT[slot], colorNew, progress(), true) : colorNew;
#else
return transitional && _t ? color_blend(_t->_colorT[slot], colorNew, progress(), true) : colorNew;
#endif
}
CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
loadPalette(targetPalette, pal);
if (transitional && _t && progress() < 0xFFFFU) {
if (progress() < 0xFFFFU) {
// blend palettes
// there are about 255 blend passes of 48 "blends" to completely blend two palettes (in _dur time)
// minimum blend time is 100ms maximum is 65535ms
@ -340,17 +473,11 @@ CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal
return targetPalette;
}
void Segment::handleTransition() {
if (!transitional) return;
uint16_t _progress = progress();
if (_progress == 0xFFFFU) transitional = false; // finish transitioning segment
if (_t) { // thanks to @nXm AKA https://github.com/NMeirer
if (_progress >= 32767U && _t->_modeP != mode) markForReset();
if (_progress == 0xFFFFU) {
delete _t;
_t = nullptr;
}
}
// relies on WS2812FX::service() to call it max every 8ms or more (MIN_SHOW_DELAY)
void Segment::handleRandomPalette() {
// just do a blend; if the palettes are identical it will just compare 48 bytes (same as _randomPalette == _newRandomPalette)
// this will slowly blend _newRandomPalette into _randomPalette every 15ms or 8ms (depending on MIN_SHOW_DELAY)
nblendPaletteTowardPalette(_randomPalette, _newRandomPalette, 48);
}
// segId is given when called from network callback, changes are queued if that segment is currently in its effect function
@ -428,6 +555,7 @@ void Segment::setCCT(uint16_t k) {
void Segment::setOpacity(uint8_t o) {
if (opacity == o) return;
if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change
DEBUG_PRINT(F("-- Setting opacity: ")); DEBUG_PRINTLN(o);
opacity = o;
stateChanged = true; // send UDP/WS broadcast
}
@ -446,7 +574,6 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) {
if (fx != mode) {
if (fadeTransition) startTransition(strip.getTransition()); // set effect transitions
mode = fx;
// load default values from effect string
if (loadDefaults) {
int16_t sOpt;
@ -466,6 +593,7 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) {
sOpt = extractModeDefaults(fx, "mY"); if (sOpt >= 0) mirror_y = (bool)sOpt; // NOTE: setting this option is a risky business
sOpt = extractModeDefaults(fx, "pal"); if (sOpt >= 0) setPalette(sOpt); //else setPalette(0);
}
markForReset();
stateChanged = true; // send UDP/WS broadcast
}
}
@ -626,6 +754,7 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
}
i += start; // starting pixel in a group
uint32_t tmpCol = col;
// set all the pixels in the group
for (int j = 0; j < grouping; j++) {
uint16_t indexSet = i + ((reverse) ? -j : j);
@ -634,11 +763,17 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
uint16_t indexMir = stop - indexSet + start - 1;
indexMir += offset; // offset/phase
if (indexMir >= stop) indexMir -= len; // wrap
strip.setPixelColor(indexMir, col);
#ifndef WLED_DISABLE_MODE_BLEND
if (_modeBlend) tmpCol = color_blend(strip.getPixelColor(indexMir), col, 0xFFFFU - progress(), true);
#endif
strip.setPixelColor(indexMir, tmpCol);
}
indexSet += offset; // offset/phase
if (indexSet >= stop) indexSet -= len; // wrap
strip.setPixelColor(indexSet, col);
#ifndef WLED_DISABLE_MODE_BLEND
if (_modeBlend) tmpCol = color_blend(strip.getPixelColor(indexSet), col, 0xFFFFU - progress(), true);
#endif
strip.setPixelColor(indexSet, tmpCol);
}
}
}
@ -844,7 +979,7 @@ void Segment::fade_out(uint8_t rate) {
const uint16_t rows = virtualHeight(); // will be 1 for 1D
rate = (255-rate) >> 1;
float mappedRate = float(rate) +1.1;
float mappedRate = float(rate) +1.1f;
uint32_t color = colors[1]; // SEGCOLOR(1); // target color
int w2 = W(color);
@ -1073,14 +1208,17 @@ void WS2812FX::service() {
_isServicing = true;
_segment_index = 0;
Segment::handleRandomPalette(); // move it into for loop when each segment has individual random palette
for (segment &seg : _segments) {
// process transition (mode changes in the middle of transition)
seg.handleTransition();
// reset the segment runtime data if needed
seg.resetIfRequired();
if (!seg.isActive()) continue;
// last condition ensures all solid segments are updated at the same time
if (seg.isActive() && (nowUp > seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC)))
if (nowUp > seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC))
{
doShow = true;
uint16_t delay = FRAMETIME;
@ -1095,10 +1233,25 @@ void WS2812FX::service() {
if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(seg.cct, true), correctWB);
for (uint8_t c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]);
// effect blending (execute previous effect)
// actual code may be a bit more involved as effects have runtime data including allocated memory
//if (seg.transitional && seg._modeP) (*_mode[seg._modeP])(progress());
delay = (*_mode[seg.currentMode(seg.mode)])();
// Effect blending
// When two effects are being blended, each may have different segment data, this
// data needs to be saved first and then restored before running previous/transitional mode.
// The blending will largely depend on the effect behaviour since actual output (LEDs) may be
// overwritten by later effect. To enable seamless blending for every effect, additional LED buffer
// would need to be allocated for each effect and then blended together for each pixel.
[[maybe_unused]] uint8_t tmpMode = seg.currentMode(seg.mode); // this will return old mode while in transition
delay = (*_mode[seg.mode])(); // run new/current mode
#ifndef WLED_DISABLE_MODE_BLEND
if (seg.mode != tmpMode) {
Segment::tmpsegd_t _tmpSegData;
Segment::modeBlend(true); // set semaphore
seg.swapSegenv(_tmpSegData); // temporarily store new mode state (and swap it with transitional state)
uint16_t d2 = (*_mode[tmpMode])(); // run old mode
seg.restoreSegenv(_tmpSegData); // restore mode state (will also update transitional state)
delay = MIN(delay,d2); // use shortest delay
Segment::modeBlend(false); // unset semaphore
}
#endif
if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.call++;
if (seg.transitional && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition
}
@ -1224,19 +1377,9 @@ void WS2812FX::show(void) {
show_callback callback = _callback;
if (callback) callback();
#ifdef WLED_DEBUG
static unsigned long sumMicros = 0, sumCurrent = 0;
static size_t calls = 0;
unsigned long microsStart = micros();
#endif
uint8_t newBri = estimateCurrentAndLimitBri();
busses.setBrightness(newBri); // "repaints" all pixels if brightness changed
#ifdef WLED_DEBUG
sumCurrent += micros() - microsStart;
#endif
// some buses send asynchronously and this method will return before
// all of the data has been sent.
// See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods
@ -1247,15 +1390,6 @@ void WS2812FX::show(void) {
// or async show has a separate buffer (ESP32 RMT and I2S are ok)
if (newBri < _brightness) busses.setBrightness(_brightness);
#ifdef WLED_DEBUG
sumMicros += micros() - microsStart;
if (++calls == 100) {
DEBUG_PRINTF("%d show calls: %lu[us] avg: %lu[us] (current: %lu[us] avg: %lu[us])\n", calls, sumMicros, sumMicros/calls, sumCurrent, sumCurrent/calls);
sumMicros = sumCurrent = 0;
calls = 0;
}
#endif
unsigned long now = millis();
size_t diff = now - _lastShow;
size_t fpsCurr = 200;
@ -1292,9 +1426,7 @@ void WS2812FX::setMode(uint8_t segid, uint8_t m) {
if (m >= getModeCount()) m = getModeCount() - 1;
if (_segments[segid].mode != m) {
_segments[segid].startTransition(_transitionDur); // set effect transitions
//_segments[segid].markForReset();
_segments[segid].mode = m;
_segments[segid].setMode(m); // do not load defaults
}
}

View File

@ -144,10 +144,14 @@ void BusDigital::show() {
c = RGBW32(_data[offset],_data[offset+1],_data[offset+2],(Bus::hasWhite(_type)?_data[offset+3]:0));
}
uint16_t pix = i;
if (_reversed) pix = _len - pix -1;
else pix += _skip;
if (_reversed) pix = _len - pix -1;
pix += _skip;
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co);
}
#if !defined(STATUSLED) || STATUSLED>=0
if (_skip) PolyBus::setPixelColor(_busPtr, _iType, 0, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black
#endif
for (int i=1; i<_skip; i++) PolyBus::setPixelColor(_busPtr, _iType, i, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black
}
PolyBus::show(_busPtr, _iType, !_buffering); // faster if buffer consistency is not important
}
@ -206,8 +210,8 @@ void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) {
}
if (Bus::hasWhite(_type)) _data[offset] = W(c);
} else {
if (_reversed) pix = _len - pix -1;
else pix += _skip;
if (_reversed) pix = _len - pix -1;
pix += _skip;
uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder);
if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs
uint16_t pOld = pix;
@ -237,8 +241,8 @@ uint32_t BusDigital::getPixelColor(uint16_t pix) {
}
return c;
} else {
if (_reversed) pix = _len - pix -1;
else pix += _skip;
if (_reversed) pix = _len - pix -1;
pix += _skip;
uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder);
uint32_t c = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, (_type==TYPE_WS2812_1CH_X3) ? IC_INDEX_WS2812_1CH_3X(pix) : pix, co),_bri);
if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs

View File

@ -21,6 +21,7 @@ void shortPressAction(uint8_t b)
case 1: ++effectCurrent %= strip.getModeCount(); stateChanged = true; colorUpdated(CALL_MODE_BUTTON); break;
}
} else {
unloadPlaylist(); // applying a preset unloads the playlist
applyPreset(macroButton[b], CALL_MODE_BUTTON_PRESET);
}
@ -42,6 +43,7 @@ void longPressAction(uint8_t b)
case 1: bri += 8; stateUpdated(CALL_MODE_BUTTON); buttonPressedTime[b] = millis(); break; // repeatable action
}
} else {
unloadPlaylist(); // applying a preset unloads the playlist
applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET);
}
@ -63,6 +65,7 @@ void doublePressAction(uint8_t b)
case 1: ++effectPalette %= strip.getPaletteCount(); colorUpdated(CALL_MODE_BUTTON); break;
}
} else {
unloadPlaylist(); // applying a preset unloads the playlist
applyPreset(macroDoublePress[b], CALL_MODE_BUTTON_PRESET);
}
@ -262,7 +265,7 @@ void handleButton()
shortPressAction(b);
buttonPressedBefore[b] = true;
buttonPressedTime[b] = now; // continually update (for debouncing to work in release handler)
return;
continue;
}
if (!buttonPressedBefore[b]) buttonPressedTime[b] = now;
@ -283,7 +286,7 @@ void handleButton()
// released after rising-edge short press action
if (macroButton[b] && macroButton[b] == macroLongPress[b] && macroButton[b] == macroDoublePress[b]) {
if (dur > WLED_DEBOUNCE_THRESHOLD) buttonPressedBefore[b] = false; // debounce, blocks button for 50 ms once it has been released
return;
continue;
}
if (dur < WLED_DEBOUNCE_THRESHOLD) {buttonPressedBefore[b] = false; continue;} // too short "press", debounce

View File

@ -442,14 +442,14 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
#ifdef WLED_ENABLE_MQTT
JsonObject if_mqtt = interfaces["mqtt"];
CJSON(mqttEnabled, if_mqtt["en"]);
getStringFromJson(mqttServer, if_mqtt[F("broker")], 33);
getStringFromJson(mqttServer, if_mqtt[F("broker")], MQTT_MAX_SERVER_LEN+1);
CJSON(mqttPort, if_mqtt["port"]); // 1883
getStringFromJson(mqttUser, if_mqtt[F("user")], 41);
getStringFromJson(mqttPass, if_mqtt["psk"], 65); //normally not present due to security
getStringFromJson(mqttClientID, if_mqtt[F("cid")], 41);
getStringFromJson(mqttDeviceTopic, if_mqtt[F("topics")][F("device")], 33); // "wled/test"
getStringFromJson(mqttGroupTopic, if_mqtt[F("topics")][F("group")], 33); // ""
getStringFromJson(mqttDeviceTopic, if_mqtt[F("topics")][F("device")], MQTT_MAX_TOPIC_LEN+1); // "wled/test"
getStringFromJson(mqttGroupTopic, if_mqtt[F("topics")][F("group")], MQTT_MAX_TOPIC_LEN+1); // ""
CJSON(retainMqttMsg, if_mqtt[F("rtn")]);
#endif

View File

@ -147,8 +147,9 @@
#define USERMOD_ID_SD_CARD 37 //Usermod "usermod_sd_card.h"
#define USERMOD_ID_PWM_OUTPUTS 38 //Usermod "usermod_pwm_outputs.h
#define USERMOD_ID_SHT 39 //Usermod "usermod_sht.h
#define USERMOD_ID_KLIPPER 40 // Usermod Klipper percentage
#define USERMOD_ID_KLIPPER 40 //Usermod Klipper percentage
#define USERMOD_ID_WIREGUARD 41 //Usermod "wireguard.h"
#define USERMOD_ID_INTERNAL_TEMPERATURE 42 //Usermod "usermod_internal_temperature.h"
//Access point behavior
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot

View File

@ -683,6 +683,7 @@ ${i.opt&0x100?inforow("Debug","<button class=\"btn btn-xs\" onclick=\"requestJso
${inforow("Build",i.vid)}
${inforow("Signal strength",i.wifi.signal +"% ("+ i.wifi.rssi, " dBm)")}
${inforow("Uptime",getRuntimeStr(i.uptime))}
${inforow("Time",i.time)}
${inforow("Free heap",heap," kB")}
${i.psram?inforow("Free PSRAM",(i.psram/1024).toFixed(1)," kB"):""}
${inforow("Estimated current",pwru)}

View File

@ -523,6 +523,7 @@ ${urows}
${inforow("Build",i.vid)}
${inforow("Signal strength",i.wifi.signal +"% ("+ i.wifi.rssi, " dBm)")}
${inforow("Uptime",getRuntimeStr(i.uptime))}
${inforow("Time",i.time)}
${inforow("Free heap",heap," kB")}
${i.psram?inforow("Free PSRAM",(i.psram/1024).toFixed(1)," kB"):""}
${inforow("Estimated current",pwru)}

View File

@ -1754,7 +1754,7 @@ const uint8_t PAGE_settings_sec[] PROGMEM = {
0xb7, 0x4d, 0x92, 0x26, 0x11, 0xda, 0xcf, 0x48, 0x28, 0xb8, 0x59, 0x35, 0x09, 0x71, 0x75, 0xef,
0x9d, 0x48, 0x9d, 0x28, 0xa5, 0xe6, 0x52, 0xf4, 0xa8, 0x07, 0xef, 0xa1, 0x4e, 0xc7, 0x7a, 0x46,
0x9f, 0xbb, 0x3e, 0x4d, 0x72, 0x94, 0x48, 0x3e, 0xa4, 0x61, 0x0a, 0x82, 0x0e, 0xbb, 0x17, 0x9a,
0x68, 0x86, 0xf5, 0xc3, 0xfd, 0x97, 0x61, 0x7f, 0x6f, 0xf2, 0x62, 0xcd, 0x55, 0x7f, 0x46, 0xc2,
0x68, 0x86, 0xf5, 0xc3, 0xfd, 0x97, 0x61, 0x7f, 0x6f, 0xf2, 0x72, 0xcd, 0x55, 0x7f, 0x46, 0xc2,
0x52, 0xce, 0x65, 0xcf, 0x5d, 0xdb, 0xa8, 0x83, 0x55, 0xda, 0xec, 0x81, 0x0d, 0xf7, 0x12, 0x2d,
0x52, 0x49, 0x41, 0xda, 0x79, 0x28, 0x7f, 0x7b, 0x29, 0xaa, 0xb5, 0xa0, 0x0b, 0x28, 0xf8, 0x4f,
0x8a, 0x9a, 0x46, 0x4d, 0x29, 0x12, 0x09, 0xce, 0xb1, 0xa8, 0x08, 0xf3, 0x75, 0xea, 0x76, 0x4e,
@ -1768,7 +1768,7 @@ const uint8_t PAGE_settings_sec[] PROGMEM = {
0x19, 0xc6, 0xa0, 0x40, 0x82, 0x50, 0x71, 0x5d, 0x2e, 0x5a, 0x26, 0x30, 0xb2, 0xe4, 0x94, 0xb3,
0xee, 0x93, 0x02, 0x73, 0x5f, 0x09, 0x88, 0xea, 0x69, 0x49, 0xcd, 0xab, 0x94, 0x27, 0xc4, 0x12,
0xf5, 0x67, 0x98, 0xdf, 0x6d, 0x64, 0xbf, 0xef, 0x63, 0x3b, 0x7f, 0xaa, 0x91, 0xed, 0x51, 0xb5,
0xc1, 0x0f, 0xf5, 0xef, 0xd4, 0xcc, 0xd3, 0x77, 0xe0, 0xff, 0x02, 0xe8, 0x93, 0xa8, 0x82, 0x17,
0xc1, 0x0f, 0xf5, 0xef, 0xd4, 0xcc, 0xd3, 0x77, 0xe0, 0xff, 0x02, 0x6f, 0x8b, 0xcf, 0x3d, 0x17,
0x16, 0x00, 0x00
};

View File

@ -7,9 +7,9 @@
*/
// Autogenerated from wled00/data/simple.htm, do not edit!!
const uint16_t PAGE_simple_L = 18061;
const uint16_t PAGE_simple_L = 18069;
const uint8_t PAGE_simple[] PROGMEM = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0xcd, 0x7d, 0x69, 0x77, 0xe2, 0xba,
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0xcd, 0x7d, 0x69, 0x77, 0xe2, 0xba,
0xb2, 0xe8, 0xf7, 0xfc, 0x0a, 0xda, 0x7d, 0x4e, 0x1a, 0x1f, 0x1c, 0x30, 0xf3, 0xd4, 0x24, 0x87,
0x90, 0x90, 0x79, 0x24, 0x73, 0xbf, 0x5e, 0xb7, 0x3d, 0x08, 0x70, 0x62, 0x6c, 0xc7, 0x36, 0x53,
0x68, 0xee, 0x5a, 0xef, 0xbf, 0xbd, 0x1f, 0xf6, 0xaa, 0x24, 0xdb, 0xd8, 0x06, 0x92, 0xf4, 0xde,
@ -823,319 +823,320 @@ const uint8_t PAGE_simple[] PROGMEM = {
0x2a, 0x1f, 0xcd, 0xea, 0x80, 0xda, 0x2d, 0xe9, 0x09, 0x20, 0x09, 0xca, 0x82, 0xb1, 0xd0, 0x58,
0xeb, 0x6a, 0x69, 0x87, 0xa6, 0xa7, 0xb8, 0x7f, 0x26, 0x92, 0xe8, 0x43, 0xa5, 0x69, 0x36, 0x28,
0xe9, 0x02, 0x97, 0x50, 0x77, 0x07, 0x60, 0xea, 0x47, 0xc1, 0xdc, 0x5a, 0x68, 0x2e, 0x72, 0x42,
0xcc, 0x74, 0x4c, 0x0f, 0x69, 0x3a, 0x1f, 0x2b, 0xdd, 0x06, 0x2a, 0x49, 0x20, 0x99, 0x00, 0xd1,
0x00, 0xc0, 0x97, 0x5d, 0x0f, 0x1c, 0x01, 0x4b, 0xc2, 0x96, 0x06, 0x3b, 0xd1, 0x82, 0x97, 0x9d,
0xeb, 0xe6, 0x19, 0x27, 0x24, 0xbd, 0xdc, 0x4c, 0x56, 0xcc, 0x15, 0xf8, 0x10, 0x61, 0x79, 0x10,
0x90, 0x07, 0x44, 0x5a, 0xd9, 0x87, 0xd5, 0x37, 0x40, 0xb2, 0x4a, 0x78, 0x21, 0x50, 0x9c, 0x60,
0xc4, 0x3a, 0xd2, 0x04, 0x34, 0x82, 0xfa, 0x9b, 0x68, 0x5f, 0x76, 0x70, 0xe4, 0x94, 0x32, 0xbb,
0x96, 0x13, 0x2b, 0x75, 0xd6, 0x6c, 0x25, 0x24, 0x55, 0x05, 0x7e, 0xe5, 0x60, 0xa9, 0x81, 0xa4,
0xc4, 0xc7, 0x03, 0x26, 0xba, 0x33, 0x75, 0x40, 0x81, 0xc6, 0x7c, 0x58, 0x3f, 0xc3, 0x14, 0xba,
0x91, 0xe9, 0xa3, 0x9b, 0xc2, 0xee, 0x21, 0x16, 0x43, 0x14, 0x9a, 0x05, 0x83, 0x97, 0x15, 0xcc,
0xb0, 0x42, 0x40, 0xad, 0xff, 0x5c, 0x42, 0xea, 0xbe, 0x31, 0xd2, 0x6c, 0xd3, 0x18, 0xd0, 0xae,
0x93, 0x34, 0x06, 0x29, 0x53, 0x43, 0x17, 0xc3, 0x35, 0x6c, 0x02, 0x8f, 0x74, 0x6a, 0xf4, 0xb1,
0x66, 0xa5, 0x38, 0x56, 0x19, 0x6c, 0x45, 0x4a, 0x03, 0xbf, 0x98, 0x37, 0xea, 0x65, 0x14, 0x65,
0x31, 0xcb, 0x8b, 0xd8, 0x3f, 0xf6, 0x14, 0x5e, 0xc8, 0xb8, 0x50, 0x82, 0xb0, 0xb0, 0x24, 0x5a,
0x32, 0x3d, 0xa6, 0x54, 0x30, 0x29, 0x1d, 0x0f, 0x0e, 0xf3, 0x62, 0xc2, 0xfc, 0x1a, 0xdb, 0x59,
0x7e, 0x11, 0xe8, 0x21, 0xd6, 0xcd, 0xef, 0x7e, 0xc6, 0xe6, 0xa6, 0xf9, 0xbd, 0x50, 0x37, 0x17,
0x9e, 0x57, 0x0a, 0xf9, 0x87, 0xf9, 0x93, 0xca, 0x21, 0x23, 0x24, 0x87, 0xd2, 0x40, 0xa5, 0x75,
0x2f, 0xca, 0xed, 0x87, 0xf1, 0xb3, 0xe1, 0xa4, 0x4d, 0xf4, 0x57, 0x36, 0xc2, 0x0d, 0x53, 0x4f,
0x7c, 0xa8, 0x1f, 0x46, 0x0a, 0x08, 0xc1, 0xd8, 0xc6, 0xce, 0x60, 0x16, 0xf6, 0xc9, 0x5b, 0x2e,
0x11, 0xc1, 0xe4, 0x1f, 0x10, 0xfa, 0xc7, 0xcc, 0x41, 0x63, 0x98, 0xfa, 0x5a, 0xe1, 0x69, 0x07,
0xfe, 0x6a, 0xfe, 0x19, 0x30, 0xc0, 0x30, 0x9a, 0xde, 0x28, 0x24, 0x00, 0xa3, 0xec, 0x2b, 0xf1,
0x9d, 0x55, 0xf5, 0xcf, 0x47, 0xd0, 0x1b, 0x2c, 0xe9, 0x9d, 0xa9, 0x1c, 0xe6, 0x26, 0x36, 0x0d,
0xd9, 0xb1, 0xea, 0xf8, 0x14, 0x89, 0x85, 0xa7, 0xe5, 0x64, 0x73, 0xc2, 0xf9, 0xe7, 0xc7, 0xfe,
0x31, 0x33, 0xe6, 0x8e, 0x17, 0x32, 0x1d, 0x1c, 0x65, 0xc1, 0xde, 0x26, 0x31, 0x87, 0xe7, 0x12,
0xd8, 0x31, 0x48, 0xd9, 0xe1, 0xbc, 0xcb, 0x34, 0xa9, 0x68, 0xa3, 0x4d, 0x44, 0xe4, 0x5b, 0x70,
0x77, 0x26, 0xed, 0x04, 0xe7, 0x47, 0x82, 0x62, 0x47, 0x33, 0xb4, 0xa7, 0xf4, 0x31, 0x76, 0x3b,
0x0b, 0xeb, 0xde, 0xda, 0xc3, 0x3b, 0x09, 0xe0, 0xc9, 0xd0, 0xfe, 0x02, 0xf1, 0x3b, 0x9c, 0xa4,
0xb8, 0x1c, 0x53, 0x8d, 0x42, 0xfd, 0x87, 0x62, 0x51, 0x49, 0x0e, 0xdd, 0xbf, 0x1c, 0xdb, 0xfe,
0x08, 0x02, 0xe5, 0x0a, 0x70, 0x3a, 0x8f, 0xde, 0x3a, 0x44, 0x3b, 0x10, 0x9c, 0xa6, 0x63, 0x47,
0xa5, 0x18, 0x4c, 0x7a, 0x93, 0xcb, 0xea, 0x23, 0x41, 0x58, 0x29, 0x72, 0xc4, 0x8a, 0xd5, 0x90,
0x97, 0xee, 0x7e, 0x01, 0x50, 0x78, 0xfd, 0x8b, 0xd7, 0x8d, 0xbf, 0x7f, 0x03, 0x0c, 0x8e, 0x00,
0x5a, 0x81, 0xc1, 0x67, 0xbc, 0x4e, 0x7c, 0x74, 0x36, 0x88, 0x8e, 0x6f, 0xf1, 0xf4, 0xa9, 0x03,
0x42, 0x74, 0xca, 0x3c, 0x5a, 0x63, 0x8a, 0x09, 0xd3, 0x48, 0xfc, 0xc3, 0x86, 0x91, 0x05, 0x1c,
0xe8, 0x79, 0x9e, 0xf7, 0x3e, 0x58, 0x5a, 0x06, 0x2c, 0x2d, 0x74, 0xe1, 0x87, 0x07, 0x8b, 0x60,
0x7e, 0x2d, 0xb0, 0xf5, 0x2b, 0x6c, 0xa4, 0xca, 0x38, 0x60, 0x6a, 0xef, 0x8c, 0x35, 0xba, 0x33,
0xc3, 0xcf, 0x14, 0xdc, 0xf8, 0xcb, 0xd5, 0xe8, 0x57, 0x3e, 0x57, 0xf3, 0xfc, 0x34, 0xfb, 0x9d,
0xcb, 0x7c, 0x8e, 0xab, 0xd3, 0xd4, 0x2c, 0xcb, 0xac, 0x84, 0x33, 0x2b, 0xb9, 0x52, 0x89, 0x9b,
0x7b, 0xbf, 0x77, 0x42, 0x5e, 0x12, 0xd9, 0x88, 0xb8, 0x5a, 0x38, 0xdc, 0x4c, 0xa4, 0x7b, 0x96,
0x98, 0xbe, 0x43, 0xd2, 0x9a, 0x55, 0x63, 0xcf, 0xcb, 0x4c, 0x89, 0xc5, 0xfe, 0xd2, 0xe0, 0x3e,
0xc6, 0x46, 0x40, 0xb9, 0x70, 0xf0, 0x03, 0x47, 0x8d, 0xfb, 0x69, 0x69, 0x1a, 0xdf, 0xcc, 0xcf,
0xbc, 0x87, 0xa8, 0x91, 0x40, 0x81, 0xd2, 0x0d, 0x53, 0x9d, 0xb4, 0xcc, 0x01, 0xbe, 0x03, 0x05,
0x6b, 0x48, 0x28, 0x86, 0x02, 0xfc, 0xe9, 0x00, 0x49, 0xff, 0xee, 0xd7, 0xf7, 0xf6, 0x40, 0x74,
0x9f, 0x37, 0x49, 0x0d, 0x2f, 0xe7, 0x87, 0xfe, 0x13, 0x1b, 0x94, 0x58, 0x75, 0x67, 0x21, 0xf7,
0x57, 0xd9, 0x1c, 0xc0, 0x7a, 0x43, 0xd4, 0x2f, 0xc1, 0x08, 0xe7, 0x91, 0x90, 0x68, 0xef, 0x02,
0x29, 0xcf, 0xd5, 0xf5, 0x8d, 0xc6, 0x05, 0x67, 0x32, 0x5e, 0xc9, 0x6f, 0xcc, 0xc2, 0x60, 0x58,
0x93, 0xf8, 0x90, 0x95, 0x21, 0x80, 0x56, 0xcc, 0xa6, 0x4b, 0xa2, 0xdb, 0xd5, 0x90, 0x87, 0xc2,
0x5e, 0x83, 0xd2, 0x62, 0xa3, 0x21, 0xa1, 0x4c, 0xdf, 0xe1, 0xce, 0x33, 0xa0, 0x10, 0xd1, 0xe7,
0x39, 0x2e, 0xb4, 0x5f, 0xc0, 0x0b, 0x41, 0xb5, 0x9d, 0x03, 0x32, 0x54, 0xe5, 0xbb, 0xb8, 0x63,
0xa6, 0x1a, 0x5c, 0x24, 0x78, 0x3e, 0xa1, 0xe1, 0x8d, 0x74, 0x0e, 0x0a, 0x0b, 0x35, 0xcd, 0xd5,
0x00, 0x10, 0x6a, 0x19, 0x58, 0xec, 0xdc, 0x4c, 0x98, 0x60, 0x67, 0xda, 0x8b, 0xc8, 0xf0, 0x44,
0x17, 0x65, 0x56, 0x1a, 0x37, 0x50, 0x91, 0x9d, 0xb2, 0x57, 0x8a, 0xf9, 0x4c, 0x04, 0x5f, 0xb4,
0x4a, 0xd7, 0x28, 0xac, 0x96, 0x39, 0xfd, 0x0a, 0xc4, 0x57, 0x8b, 0x49, 0xdd, 0x00, 0x50, 0x0d,
0x85, 0x18, 0x45, 0xe4, 0x9c, 0x12, 0x7d, 0x54, 0x54, 0x19, 0x11, 0x4a, 0x37, 0xa3, 0xf6, 0xb5,
0x17, 0x0d, 0xbe, 0xd2, 0xb0, 0x66, 0xb1, 0xee, 0x9f, 0xb2, 0xab, 0x03, 0xef, 0xda, 0x50, 0x57,
0xe9, 0x46, 0x23, 0xc2, 0x4e, 0x20, 0x70, 0x1a, 0x60, 0xce, 0x9c, 0xec, 0x9f, 0x75, 0x32, 0x45,
0x88, 0x35, 0x50, 0xf5, 0xc9, 0x5f, 0x75, 0x32, 0x2d, 0x2f, 0x83, 0xc0, 0x53, 0xe1, 0x99, 0x13,
0xd4, 0xb3, 0x21, 0x50, 0x09, 0x4d, 0x7c, 0x03, 0x9f, 0x12, 0x34, 0xb3, 0xe3, 0x70, 0x8b, 0x93,
0x84, 0x37, 0x38, 0xc9, 0x0f, 0xf7, 0x67, 0x63, 0xa6, 0xa9, 0x35, 0xe2, 0xef, 0x61, 0x02, 0xee,
0xd9, 0x0f, 0x30, 0x63, 0x11, 0xc6, 0x67, 0x97, 0x0d, 0xa0, 0xc4, 0xf7, 0x20, 0x20, 0x3c, 0x91,
0x41, 0xe2, 0xe8, 0x89, 0x32, 0xf4, 0x28, 0x88, 0x80, 0xef, 0xa0, 0x27, 0x8c, 0x35, 0x91, 0xf0,
0xc6, 0x22, 0x6a, 0xde, 0xb8, 0xb7, 0x48, 0x9b, 0xa0, 0xf7, 0x8c, 0x5e, 0x74, 0x93, 0xdc, 0x75,
0xe7, 0x6e, 0x8f, 0xe3, 0xbf, 0x8b, 0xfe, 0x2a, 0x5f, 0x51, 0xe4, 0xdf, 0xe8, 0x16, 0xc5, 0x01,
0x9b, 0xdb, 0xe2, 0x8e, 0x13, 0x2a, 0xe1, 0x0c, 0x65, 0x50, 0x59, 0x93, 0x26, 0x5f, 0x33, 0x1b,
0xd5, 0x6a, 0x15, 0x05, 0x7d, 0x0f, 0xf7, 0xba, 0x00, 0x6d, 0x48, 0xde, 0xe8, 0xcb, 0x3c, 0x74,
0x07, 0x7a, 0x92, 0xeb, 0x4e, 0x80, 0xea, 0xb0, 0x9a, 0xa6, 0x0a, 0x4b, 0xd5, 0x45, 0xd0, 0xde,
0x85, 0xc5, 0x31, 0x39, 0xdc, 0x06, 0xc1, 0xff, 0x41, 0xff, 0xa3, 0x34, 0xe9, 0x9f, 0x1f, 0x7c,
0x5f, 0x85, 0x5a, 0x78, 0xd1, 0x7c, 0xb3, 0x0f, 0xe7, 0xe9, 0xff, 0x93, 0x29, 0xf2, 0xb6, 0xcb,
0x71, 0x7e, 0x02, 0xf5, 0xce, 0xef, 0x0f, 0x53, 0xcf, 0x82, 0xfe, 0xa0, 0x66, 0xb6, 0x16, 0x8d,
0x9e, 0xaf, 0x12, 0x71, 0x69, 0x7a, 0xb8, 0x34, 0x19, 0x2e, 0x85, 0xd0, 0x79, 0x35, 0x4e, 0xf8,
0xf6, 0xde, 0x11, 0x35, 0xe0, 0x71, 0xcc, 0xd8, 0xf6, 0xcf, 0xf8, 0xad, 0x46, 0x6c, 0x64, 0x77,
0x37, 0x08, 0x84, 0x59, 0x15, 0xc7, 0xfb, 0xd5, 0x83, 0x94, 0xa0, 0xb7, 0x1f, 0x73, 0xef, 0xa2,
0x7a, 0xc6, 0x86, 0x8c, 0xb8, 0x4d, 0xa3, 0x9f, 0x0b, 0x7a, 0x8d, 0xe3, 0x70, 0x58, 0x4a, 0x3c,
0x90, 0x37, 0xe8, 0x3a, 0x5f, 0xc7, 0x9d, 0x23, 0x87, 0x6d, 0x41, 0x22, 0x72, 0xbc, 0x7e, 0xb5,
0x1c, 0x07, 0xa8, 0x2f, 0x1a, 0xf6, 0x13, 0xce, 0x63, 0xdb, 0x1f, 0xe1, 0x13, 0x23, 0x8b, 0x6d,
0xb4, 0x45, 0xda, 0x0f, 0x42, 0xe5, 0xcb, 0x17, 0xd7, 0xdf, 0x16, 0xf1, 0x2f, 0x58, 0x48, 0xb0,
0x20, 0xe3, 0x6c, 0x03, 0x24, 0x11, 0x1b, 0x04, 0xba, 0x00, 0x80, 0x28, 0x1a, 0xe8, 0x8e, 0x12,
0xa2, 0xa1, 0x16, 0x98, 0xce, 0x7b, 0xf9, 0x90, 0xdb, 0x00, 0xcd, 0x86, 0xe7, 0xa3, 0xbe, 0x21,
0x86, 0x43, 0xb1, 0x4e, 0xbe, 0xfb, 0xf0, 0xea, 0x04, 0x91, 0x42, 0x0d, 0x61, 0xd0, 0xc5, 0x5d,
0xec, 0x09, 0x5d, 0xbd, 0x82, 0x2e, 0x48, 0x82, 0xdd, 0xf8, 0x92, 0xc5, 0x8e, 0x45, 0xdb, 0x71,
0x78, 0xde, 0x6e, 0xa0, 0x97, 0x28, 0x03, 0x2d, 0xfc, 0x0b, 0x6c, 0x18, 0x81, 0x39, 0x8a, 0x04,
0x1d, 0xbe, 0x72, 0x3f, 0x05, 0x09, 0xbe, 0xf2, 0x3f, 0x69, 0x18, 0x50, 0x02, 0xe3, 0x94, 0x6c,
0x90, 0xf7, 0x0e, 0x6f, 0x60, 0x7f, 0x3c, 0xf3, 0x9c, 0xde, 0x01, 0x85, 0x9e, 0xe9, 0x15, 0x69,
0xd2, 0x72, 0x5a, 0x00, 0x2a, 0x38, 0x83, 0xe3, 0x53, 0x03, 0x36, 0xbb, 0x95, 0xad, 0x43, 0xfb,
0x7e, 0x0e, 0x0c, 0x00, 0x31, 0xa3, 0x47, 0x53, 0xb2, 0xb4, 0x57, 0xe1, 0x94, 0xdc, 0xcf, 0xf9,
0x17, 0x40, 0x6b, 0xc3, 0x06, 0x84, 0xd9, 0x0d, 0x92, 0xf1, 0xf1, 0x81, 0xe3, 0x09, 0x7c, 0x5a,
0xbf, 0x30, 0xdc, 0x01, 0xf5, 0x26, 0x01, 0x6d, 0x69, 0x01, 0xcd, 0x6c, 0x1e, 0x44, 0x9b, 0x3d,
0xff, 0xe7, 0x2f, 0xde, 0x53, 0x76, 0x7e, 0x85, 0x2e, 0xb0, 0x4a, 0xac, 0xbd, 0xc1, 0x0a, 0x63,
0xe0, 0x9e, 0x4d, 0x8d, 0x46, 0x1b, 0xd4, 0x7f, 0x45, 0xa9, 0x85, 0x2e, 0xb3, 0x0b, 0xfa, 0xb6,
0x97, 0x60, 0xa1, 0x51, 0xa5, 0xc7, 0xdf, 0x36, 0x64, 0x6f, 0x82, 0x59, 0xa8, 0xa3, 0x64, 0xce,
0xb6, 0x06, 0x41, 0x77, 0xa4, 0x39, 0xe1, 0x5d, 0xc1, 0x95, 0xcb, 0x96, 0xee, 0x44, 0x0a, 0x0e,
0x3a, 0x5a, 0x38, 0x01, 0x7d, 0x20, 0x80, 0x8c, 0xf0, 0xbe, 0x64, 0xdc, 0x65, 0x87, 0x83, 0x0d,
0x1d, 0x5d, 0xc5, 0xb6, 0xbc, 0x9f, 0xd0, 0x60, 0x83, 0x79, 0x1b, 0x40, 0xa3, 0x69, 0x31, 0x8d,
0x06, 0x25, 0xbe, 0xef, 0xbe, 0x8b, 0x59, 0x1b, 0xa1, 0xbb, 0xd0, 0xb9, 0x65, 0xe3, 0x65, 0xe1,
0x9c, 0xa3, 0x6a, 0x35, 0xee, 0x74, 0x32, 0xcd, 0x79, 0x61, 0xc3, 0x78, 0x3a, 0x34, 0x4e, 0xc1,
0x42, 0x2f, 0x0e, 0x06, 0x1b, 0xd6, 0x72, 0xd9, 0x42, 0x43, 0xbf, 0xfb, 0x97, 0x46, 0x60, 0xc4,
0x92, 0x74, 0x5f, 0x72, 0x9a, 0xae, 0x6b, 0x6b, 0xa0, 0x45, 0x91, 0x24, 0x07, 0x0a, 0x3f, 0xc7,
0x83, 0xd2, 0x29, 0xf9, 0x49, 0xd4, 0xe5, 0xc5, 0xee, 0x68, 0xa9, 0x01, 0xd9, 0x09, 0x66, 0x83,
0x99, 0xe4, 0x34, 0x25, 0xe3, 0xd6, 0x93, 0xe6, 0xc2, 0x1e, 0x85, 0x65, 0xfe, 0xbd, 0x28, 0x32,
0xf5, 0x28, 0xe7, 0xbb, 0xb1, 0x7e, 0xad, 0x7e, 0x55, 0x62, 0xe8, 0x85, 0x04, 0x74, 0x5c, 0xff,
0x14, 0x42, 0xf7, 0x51, 0xb3, 0x14, 0xfe, 0x57, 0x1d, 0xa3, 0xe3, 0x50, 0x35, 0x42, 0xdd, 0x61,
0xdd, 0x81, 0x92, 0x98, 0x85, 0x81, 0xa1, 0x8e, 0x4b, 0x11, 0x18, 0x0d, 0xc7, 0x73, 0xd7, 0x46,
0x00, 0xae, 0x83, 0x1d, 0xc4, 0x4e, 0x32, 0xcb, 0x83, 0x82, 0x44, 0x07, 0x6f, 0xd2, 0x08, 0xbb,
0x3d, 0xbd, 0x8b, 0x6b, 0x42, 0xa4, 0x1a, 0x8e, 0x19, 0xa1, 0xe1, 0xf4, 0x69, 0x76, 0x32, 0xf1,
0xaf, 0xb6, 0x19, 0x8a, 0xce, 0x60, 0xa0, 0x93, 0x5c, 0xfc, 0x4d, 0x8a, 0xe1, 0xbd, 0xce, 0x85,
0x1b, 0xd6, 0xdf, 0x71, 0xa5, 0x67, 0x72, 0x38, 0xea, 0x5d, 0xfe, 0x50, 0xba, 0xae, 0x0a, 0x05,
0xf1, 0x4e, 0xc9, 0xe1, 0x8e, 0x6d, 0x00, 0x0c, 0x37, 0x0a, 0xfe, 0x26, 0x30, 0x20, 0xc3, 0xc8,
0x59, 0xbb, 0x6d, 0xd1, 0x27, 0x47, 0x6a, 0x89, 0x59, 0xff, 0x98, 0x45, 0xb2, 0xe7, 0xe6, 0x2f,
0xbe, 0x0e, 0xa6, 0x9b, 0x1b, 0x82, 0x2a, 0xa9, 0x6a, 0x18, 0xa4, 0x90, 0x5c, 0x57, 0xf7, 0x55,
0x97, 0xc1, 0x9e, 0x7b, 0xb7, 0xf2, 0x3c, 0x8e, 0x42, 0xdc, 0xa8, 0x61, 0x61, 0x22, 0xe1, 0xfb,
0x83, 0xc3, 0xc1, 0x22, 0x78, 0x2c, 0x91, 0x1a, 0xfd, 0xda, 0x88, 0xd0, 0x38, 0x0b, 0x86, 0x70,
0x6a, 0x74, 0xe9, 0x6c, 0xab, 0x9c, 0x7e, 0x79, 0x78, 0xe1, 0x83, 0x58, 0x3b, 0x86, 0xaf, 0x70,
0x1e, 0xc5, 0x99, 0x5f, 0x0d, 0x51, 0x07, 0x0c, 0xa5, 0x11, 0x3a, 0x9f, 0x08, 0x64, 0x04, 0xe6,
0xac, 0x6c, 0x13, 0xe9, 0x65, 0xee, 0x2b, 0x05, 0x78, 0xcf, 0x44, 0x58, 0x27, 0xf8, 0xb6, 0xf6,
0xce, 0x09, 0x7a, 0x38, 0xff, 0x5b, 0x4a, 0x0f, 0x02, 0x19, 0xd0, 0x8e, 0x44, 0x2d, 0xb3, 0x21,
0xb2, 0x0d, 0xc0, 0xcd, 0x4d, 0xb2, 0xd4, 0x51, 0x6f, 0x52, 0x57, 0x74, 0x92, 0xac, 0xea, 0x64,
0x7b, 0x12, 0xee, 0xa3, 0xa7, 0xa2, 0x86, 0x1b, 0x8c, 0xa8, 0xa9, 0xa0, 0x49, 0x35, 0x28, 0x8f,
0x68, 0xd0, 0x1d, 0x7e, 0x4f, 0x7f, 0xfc, 0xdc, 0x88, 0x82, 0xab, 0x3a, 0xbe, 0xa5, 0xa2, 0x0d,
0x84, 0xd4, 0xd4, 0x79, 0xdc, 0xb2, 0x0f, 0x5d, 0x81, 0xcb, 0xfb, 0x5b, 0x71, 0x4b, 0xb9, 0xec,
0x22, 0x9a, 0xf5, 0xf9, 0x8b, 0xbb, 0x55, 0x78, 0xaf, 0xcf, 0xec, 0xe6, 0xb0, 0xf8, 0x19, 0x26,
0xb6, 0xcd, 0x10, 0x3f, 0xc8, 0xc4, 0x2a, 0xe0, 0x75, 0x5b, 0xf1, 0xf2, 0x8a, 0xe2, 0xae, 0x2c,
0xfc, 0xb2, 0x12, 0x3a, 0x2d, 0x4d, 0x4b, 0xd5, 0xbc, 0x4a, 0xe1, 0xbd, 0x97, 0x98, 0x3a, 0xe8,
0x67, 0x75, 0x58, 0x8c, 0x70, 0xd2, 0x67, 0xc8, 0xf1, 0x23, 0x41, 0xc6, 0xc7, 0x27, 0xbf, 0x18,
0x41, 0x44, 0x88, 0x81, 0x52, 0x41, 0xb4, 0x77, 0x4e, 0x6c, 0x24, 0xa1, 0x40, 0x90, 0x81, 0x75,
0x19, 0x96, 0xd8, 0x09, 0x82, 0xbb, 0x66, 0xc6, 0x0e, 0x28, 0xec, 0xf8, 0xe0, 0x87, 0xc7, 0xec,
0x70, 0xff, 0x0d, 0x75, 0x13, 0x18, 0xec, 0x4d, 0xf3, 0xf9, 0x98, 0x0a, 0x4f, 0xf5, 0xb6, 0x15,
0xa5, 0x69, 0xb2, 0xc1, 0x42, 0x2d, 0x44, 0x61, 0x66, 0x0c, 0x07, 0xc4, 0xd6, 0x94, 0xda, 0x17,
0x71, 0xce, 0xd7, 0x82, 0xad, 0xd0, 0x45, 0x6f, 0xfc, 0xf8, 0x61, 0x14, 0x87, 0x63, 0xc7, 0x53,
0x28, 0xeb, 0x4c, 0x5d, 0xc2, 0x08, 0xf4, 0xc0, 0x6a, 0x1e, 0x03, 0xa7, 0x5b, 0x04, 0x80, 0xa1,
0x1b, 0x02, 0xac, 0x9e, 0x31, 0x8d, 0x95, 0x1a, 0x63, 0x54, 0x31, 0x97, 0x5a, 0xba, 0xf3, 0xda,
0x0f, 0xac, 0xa6, 0x95, 0xeb, 0x49, 0x0c, 0x71, 0x23, 0xe3, 0x44, 0x10, 0x05, 0x87, 0x61, 0xfb,
0x69, 0x74, 0x54, 0x3b, 0x0e, 0x46, 0xff, 0xfb, 0x27, 0x0c, 0xdc, 0x70, 0xb8, 0x09, 0xa1, 0x5a,
0x37, 0xcf, 0xd4, 0x5d, 0xea, 0x69, 0xe7, 0x67, 0x91, 0x78, 0xbf, 0xd0, 0xe6, 0x31, 0x2f, 0x84,
0x7e, 0x2c, 0xa2, 0xe9, 0xbc, 0x43, 0xcb, 0x41, 0x58, 0xef, 0xb2, 0x51, 0xfe, 0x67, 0x11, 0x8b,
0x3a, 0xcf, 0x79, 0x5a, 0x32, 0x98, 0x02, 0x60, 0xbc, 0xd7, 0xcd, 0x9d, 0xc0, 0x90, 0x6f, 0x98,
0x42, 0x68, 0x43, 0xcf, 0x3b, 0xa8, 0xbd, 0xb9, 0x19, 0xd9, 0xfc, 0x02, 0xc5, 0x00, 0xcc, 0xcf,
0xc0, 0xf4, 0xc7, 0x08, 0x41, 0x1a, 0x20, 0x98, 0xc4, 0x63, 0x11, 0xf0, 0xbd, 0xe3, 0x7d, 0xd7,
0x5c, 0x60, 0xc6, 0xc2, 0x18, 0xdd, 0xd8, 0x34, 0x48, 0x90, 0x22, 0xe8, 0x8f, 0x83, 0x2b, 0xff,
0x30, 0xb0, 0xd0, 0x6b, 0xd0, 0xb4, 0x88, 0x41, 0xdb, 0x1b, 0xa3, 0x5f, 0x19, 0xa3, 0x8d, 0x66,
0xdf, 0x46, 0xdf, 0x6a, 0xae, 0x3d, 0x04, 0x6d, 0x12, 0x17, 0xd7, 0xab, 0x73, 0x4a, 0x7a, 0x92,
0x8e, 0x11, 0xf0, 0x4b, 0xf8, 0x9c, 0x87, 0x8d, 0x32, 0x7f, 0x70, 0x7e, 0x84, 0x28, 0x4e, 0x24,
0xf1, 0xc8, 0x0c, 0xad, 0x05, 0x3c, 0xd2, 0x4e, 0xd0, 0x53, 0x1f, 0xe7, 0x4e, 0x4c, 0xa3, 0x80,
0x3c, 0xd9, 0xd6, 0x04, 0x43, 0x6f, 0xa2, 0x0f, 0x50, 0xa7, 0x2e, 0x7d, 0x7d, 0x6f, 0x68, 0xb3,
0x5f, 0xea, 0xd0, 0x86, 0x9f, 0x37, 0x92, 0xf7, 0xd3, 0x65, 0x45, 0xdb, 0x92, 0x4a, 0x58, 0x02,
0xbe, 0xb4, 0x9d, 0xee, 0x62, 0x76, 0x60, 0x0c, 0x90, 0x34, 0x54, 0x2d, 0x83, 0x8e, 0x27, 0x76,
0xbc, 0x1d, 0x34, 0x11, 0xfd, 0xbb, 0xb8, 0x83, 0x5b, 0x45, 0x35, 0x7c, 0xc6, 0xa3, 0xea, 0xa0,
0xaf, 0x04, 0xaf, 0x3c, 0xca, 0x64, 0x45, 0x6f, 0xca, 0x45, 0x30, 0xfd, 0xc4, 0xfa, 0x8a, 0x7d,
0x90, 0x98, 0x12, 0xb0, 0xbc, 0x01, 0xc2, 0xcc, 0x49, 0xba, 0x9b, 0x88, 0x1b, 0x18, 0xc8, 0x30,
0x08, 0xc8, 0x33, 0x13, 0xb4, 0x31, 0x26, 0x2b, 0x9c, 0x54, 0x6a, 0xee, 0xeb, 0x65, 0xde, 0x16,
0x07, 0x22, 0xcb, 0xf0, 0x43, 0xb1, 0x42, 0xae, 0xa8, 0x73, 0x33, 0xe1, 0x37, 0xed, 0x79, 0x9f,
0xe8, 0x72, 0x5f, 0x08, 0xec, 0x7a, 0x60, 0xbc, 0x34, 0x0c, 0x3c, 0x7a, 0xeb, 0x6d, 0x67, 0xae,
0x88, 0xe8, 0x0e, 0xd9, 0x79, 0x7e, 0x9c, 0x3b, 0x58, 0x48, 0x64, 0x1b, 0xad, 0xbe, 0xad, 0x2d,
0xcf, 0xb1, 0x29, 0xd8, 0x82, 0x26, 0x28, 0x75, 0x89, 0x01, 0xf3, 0x6c, 0x26, 0x7b, 0xf1, 0x0b,
0xec, 0x25, 0x6d, 0xf1, 0x0b, 0x6c, 0x3a, 0xc6, 0xf9, 0xf1, 0x00, 0xc3, 0x22, 0x19, 0xc3, 0xd7,
0xf1, 0xe1, 0xbd, 0x58, 0x71, 0x2e, 0x25, 0xa5, 0x80, 0xad, 0xa4, 0x6c, 0xfa, 0xa9, 0xe1, 0x06,
0xd5, 0x02, 0x16, 0xbb, 0xd0, 0x00, 0x20, 0x2c, 0x94, 0x6c, 0x05, 0x23, 0x61, 0x17, 0x51, 0xe5,
0x78, 0x77, 0x01, 0x8f, 0x12, 0x3d, 0x44, 0x4c, 0xf7, 0x01, 0x29, 0x79, 0xf5, 0xb1, 0xd0, 0x4f,
0xc1, 0xc0, 0xed, 0xe9, 0xcd, 0x4d, 0xfa, 0xb5, 0x4d, 0x65, 0x70, 0xa8, 0x4e, 0x33, 0xa8, 0x43,
0xf3, 0x79, 0x61, 0x59, 0x38, 0x06, 0xf9, 0xce, 0x44, 0x58, 0x2d, 0x1a, 0x83, 0x12, 0xda, 0x44,
0x20, 0x69, 0x1a, 0x31, 0xbb, 0xb9, 0x29, 0x82, 0xb9, 0xc1, 0x9e, 0x19, 0x6a, 0x55, 0x74, 0xad,
0xf8, 0xde, 0x74, 0x3f, 0x83, 0xb9, 0xcd, 0xc5, 0x1a, 0xbd, 0x44, 0xd8, 0x77, 0x3a, 0x0e, 0xd0,
0x65, 0x9f, 0xe8, 0x06, 0x7b, 0x86, 0x5f, 0x38, 0x46, 0x37, 0x9e, 0x93, 0x3d, 0x8b, 0xa5, 0x71,
0x5f, 0x9c, 0x18, 0xe6, 0xb0, 0xd7, 0x4f, 0xd0, 0xb7, 0x3e, 0x61, 0x28, 0xa0, 0x23, 0x8d, 0x48,
0x82, 0x05, 0xd0, 0xc5, 0xaa, 0xe4, 0x6a, 0xc1, 0xed, 0x83, 0xb4, 0x05, 0xcf, 0x61, 0x1b, 0x29,
0x93, 0xc7, 0x32, 0x67, 0x9a, 0xe3, 0xe0, 0x3b, 0x32, 0x8e, 0xae, 0xa9, 0x8f, 0x33, 0x56, 0xa4,
0x8a, 0x45, 0x9a, 0xa1, 0x9e, 0x25, 0xe8, 0x30, 0x12, 0x60, 0x4b, 0x25, 0x4c, 0x05, 0x16, 0x1a,
0xfa, 0x8a, 0xe7, 0x21, 0xea, 0xa5, 0xbc, 0x82, 0x6e, 0x48, 0xd2, 0x82, 0x29, 0xae, 0x06, 0x3f,
0x54, 0x1a, 0x32, 0x14, 0xbe, 0x8a, 0xc2, 0x00, 0x11, 0xa0, 0x87, 0x6f, 0xaa, 0x30, 0xd2, 0xdd,
0x49, 0x38, 0x7a, 0x08, 0xf1, 0x17, 0xe2, 0x44, 0xd9, 0x7a, 0x88, 0xed, 0x2c, 0x02, 0xf6, 0x43,
0x81, 0x8e, 0x7f, 0xce, 0x3c, 0xbf, 0x90, 0xdf, 0xbf, 0x83, 0x26, 0xf8, 0x59, 0x48, 0xbe, 0xfc,
0xfe, 0x1d, 0x16, 0x3d, 0xe1, 0x60, 0xf2, 0x68, 0x28, 0x96, 0x90, 0x27, 0x79, 0xde, 0x77, 0xa3,
0x51, 0x46, 0x6c, 0x36, 0x3e, 0x8e, 0x0d, 0x47, 0x07, 0xd3, 0x0e, 0x67, 0x81, 0x00, 0xe5, 0x98,
0x67, 0x1a, 0x4f, 0xd8, 0x27, 0x31, 0xf4, 0x18, 0x79, 0x14, 0xb4, 0x0d, 0x06, 0x57, 0x83, 0x79,
0x9d, 0x71, 0xa7, 0x3c, 0x1c, 0x21, 0x8d, 0x32, 0x2e, 0x6d, 0x98, 0xe3, 0x24, 0x8f, 0xd1, 0x14,
0xd4, 0x18, 0x88, 0x05, 0xb7, 0xa2, 0xd8, 0xf5, 0x42, 0xa7, 0xb2, 0x78, 0x57, 0x42, 0x12, 0xaf,
0x12, 0x81, 0x85, 0x64, 0xee, 0xf8, 0xdc, 0x1e, 0x46, 0xf7, 0x6d, 0xc6, 0x8d, 0x38, 0xc6, 0xf0,
0xbf, 0xf1, 0x35, 0x8c, 0x11, 0xc5, 0x96, 0x23, 0x62, 0x23, 0x84, 0x65, 0x21, 0x87, 0x6e, 0x90,
0x55, 0x7e, 0x76, 0x47, 0x0b, 0x39, 0xd9, 0x1d, 0x01, 0x0f, 0xe0, 0x11, 0xbc, 0x4b, 0x03, 0xa3,
0xd6, 0xd1, 0xd0, 0xdf, 0xc2, 0xcd, 0x09, 0x18, 0x24, 0x1e, 0x5f, 0xd2, 0x98, 0xe2, 0x40, 0x2b,
0xd6, 0xf1, 0x72, 0x59, 0x1b, 0x99, 0xf3, 0xed, 0x4d, 0x7b, 0xab, 0xc2, 0xcd, 0x05, 0x7c, 0x99,
0x4b, 0xcd, 0xfd, 0x8b, 0xa1, 0x6f, 0xc9, 0xff, 0x61, 0xa5, 0xa1, 0xc7, 0xe3, 0xa5, 0x08, 0x3b,
0xa8, 0x9c, 0x2b, 0x0a, 0x28, 0x3c, 0x74, 0x0a, 0x35, 0xaa, 0x1c, 0x2c, 0x94, 0x07, 0x96, 0xf2,
0xb1, 0x06, 0xe1, 0x57, 0xe0, 0xf9, 0x90, 0xf6, 0x40, 0x3c, 0xed, 0xc1, 0xfb, 0xae, 0x91, 0xa8,
0x98, 0xe6, 0x6b, 0xe1, 0x15, 0x88, 0x17, 0x4d, 0xc0, 0x2a, 0x71, 0x2c, 0xb0, 0x26, 0x08, 0x3b,
0x24, 0xf0, 0xd7, 0xf6, 0x1f, 0x96, 0x2d, 0x7e, 0xef, 0x4d, 0x32, 0x91, 0x43, 0x33, 0x33, 0xd3,
0xa8, 0xb1, 0xcb, 0x6b, 0xf0, 0x33, 0x1c, 0x06, 0x19, 0xbe, 0xea, 0x69, 0xe6, 0xdd, 0x66, 0x03,
0xf6, 0x69, 0xf8, 0x32, 0x1f, 0x21, 0xe9, 0xdf, 0x77, 0xc3, 0xbe, 0xf9, 0xd8, 0xed, 0x1e, 0xde,
0xa9, 0x59, 0xbc, 0x23, 0xcc, 0x9f, 0x86, 0xe0, 0x7a, 0xf7, 0x06, 0xab, 0xb2, 0xc3, 0x85, 0xae,
0x79, 0xc7, 0xb7, 0x24, 0x01, 0xc1, 0x85, 0x52, 0xf0, 0x95, 0x64, 0xbc, 0x67, 0x6b, 0xf8, 0x6f,
0xab, 0x89, 0x1a, 0xb8, 0x0c, 0xc8, 0xc2, 0xc4, 0x8d, 0x0f, 0xc0, 0xdf, 0x69, 0xf2, 0xa7, 0x2b,
0x3c, 0x2c, 0xec, 0xbf, 0x77, 0x4b, 0x8f, 0xf7, 0xc0, 0xb3, 0x63, 0x5a, 0xfe, 0xf8, 0x58, 0x78,
0x17, 0xdb, 0x8f, 0x5a, 0x31, 0x00, 0x5a, 0xec, 0x8f, 0x47, 0x70, 0xee, 0xc1, 0x0b, 0x8f, 0xc2,
0x83, 0xb4, 0x72, 0x18, 0xec, 0x3d, 0x31, 0x7e, 0x88, 0xa5, 0x57, 0x04, 0xf8, 0x52, 0x08, 0x64,
0xeb, 0x32, 0x0c, 0xaf, 0xbe, 0x36, 0x07, 0x59, 0x17, 0xae, 0x68, 0x06, 0x83, 0x75, 0xca, 0xbb,
0x33, 0x3f, 0x6e, 0xc3, 0x91, 0xb8, 0x05, 0x47, 0x0b, 0x8f, 0x56, 0xda, 0x7b, 0xab, 0xcb, 0xfa,
0x97, 0xaf, 0x7f, 0x54, 0xda, 0xdf, 0xc8, 0x60, 0xbf, 0xfc, 0x81, 0xb1, 0x8b, 0x8f, 0x63, 0x75,
0xeb, 0xeb, 0xb3, 0x1a, 0x71, 0x93, 0x33, 0x8a, 0xc1, 0x50, 0x40, 0x8c, 0xef, 0x80, 0x21, 0x73,
0xc5, 0x81, 0xac, 0x5f, 0x7c, 0xda, 0x0b, 0xcd, 0xa8, 0x2f, 0x72, 0xcc, 0xec, 0xaf, 0x15, 0xf0,
0x97, 0x0d, 0x60, 0xaf, 0x74, 0xee, 0xd7, 0xe7, 0x7b, 0xe3, 0x85, 0x87, 0x90, 0xd8, 0x3a, 0x04,
0xbd, 0xb2, 0x46, 0x37, 0x8e, 0x50, 0xae, 0xd6, 0x16, 0x5b, 0xfd, 0x04, 0x25, 0xef, 0xa2, 0x8f,
0xf3, 0x79, 0xf4, 0x4a, 0x8d, 0xc8, 0x75, 0x9f, 0xe1, 0x13, 0x0c, 0xe1, 0x6b, 0x1f, 0xbd, 0xce,
0xd5, 0x17, 0xdd, 0x5b, 0x20, 0x3b, 0x48, 0x5b, 0x39, 0x87, 0xa1, 0xfb, 0x3e, 0xe3, 0x23, 0xf4,
0xb0, 0xb0, 0x02, 0xd2, 0xe6, 0xe6, 0xaa, 0x1e, 0xd0, 0x17, 0xcd, 0x81, 0x12, 0x66, 0xde, 0x69,
0x64, 0x9c, 0x9c, 0xc9, 0xa4, 0x2f, 0x8d, 0x34, 0xd3, 0xae, 0x71, 0xce, 0xc0, 0x34, 0x31, 0x50,
0x8d, 0x02, 0xaa, 0xf9, 0xd7, 0x00, 0xc7, 0xc6, 0x19, 0xbe, 0x3a, 0x35, 0x3c, 0xcc, 0xe5, 0x0e,
0xfe, 0x85, 0x51, 0xae, 0xc0, 0xd5, 0xe7, 0x87, 0x19, 0xed, 0xc1, 0xdf, 0x18, 0xe5, 0x22, 0xf0,
0x66, 0x3d, 0x6d, 0x00, 0xb7, 0xfe, 0xe2, 0xc7, 0xf3, 0x90, 0x9f, 0xf3, 0xe5, 0xea, 0x18, 0x30,
0xb3, 0xbe, 0x3a, 0x58, 0x61, 0xb5, 0x40, 0x43, 0x0f, 0xd3, 0x18, 0x0d, 0x27, 0x09, 0x3c, 0xc5,
0x51, 0xa8, 0xde, 0x95, 0xa3, 0xa4, 0x21, 0xf2, 0xb3, 0xd8, 0x4c, 0x08, 0xcb, 0xed, 0x74, 0x27,
0xb5, 0xf0, 0xb9, 0xb2, 0xee, 0x44, 0x25, 0x5d, 0x74, 0x71, 0xc4, 0x80, 0xfa, 0x17, 0x62, 0xfa,
0x3a, 0x60, 0x9c, 0x94, 0x57, 0x40, 0x86, 0x59, 0xaa, 0x91, 0x38, 0x1c, 0xfa, 0x7e, 0xa8, 0xe8,
0x70, 0x97, 0x06, 0xb9, 0x6c, 0xbb, 0xf2, 0x71, 0xc4, 0xb1, 0xbb, 0x9c, 0x57, 0xa0, 0xcd, 0x99,
0xac, 0x84, 0x15, 0x31, 0x35, 0x96, 0x30, 0x16, 0xba, 0xc9, 0x78, 0xd5, 0x4c, 0xac, 0x06, 0xb9,
0x64, 0x9f, 0x2c, 0x81, 0xc5, 0x5b, 0xee, 0xe2, 0x73, 0x8b, 0x77, 0xa2, 0x91, 0x38, 0x6e, 0x59,
0x20, 0x76, 0xc0, 0xf0, 0x66, 0x68, 0x3b, 0xcf, 0xeb, 0x91, 0xe3, 0x60, 0xb8, 0xbb, 0x08, 0x26,
0x3b, 0xd5, 0x54, 0x43, 0xea, 0x87, 0xff, 0xce, 0x3d, 0xcb, 0x3f, 0x83, 0xe6, 0x1f, 0x4e, 0xf3,
0x82, 0x13, 0x69, 0x5c, 0x62, 0x64, 0x6e, 0xdc, 0x48, 0xdb, 0x81, 0xbd, 0x87, 0xf1, 0x47, 0x78,
0x5d, 0x1d, 0xa9, 0x2f, 0x98, 0xee, 0x7a, 0xfb, 0x76, 0x69, 0x1f, 0x13, 0xb7, 0x2f, 0x57, 0x38,
0xe7, 0xe9, 0x3b, 0xe4, 0x39, 0xbe, 0x1e, 0xcb, 0xa6, 0x8e, 0x72, 0x2f, 0x4f, 0x40, 0x04, 0x50,
0x91, 0x96, 0x74, 0xd7, 0x9a, 0xb4, 0x11, 0xb3, 0x71, 0xc9, 0x0c, 0x25, 0x3f, 0x3f, 0xf4, 0x51,
0x52, 0x0b, 0x07, 0x95, 0xbd, 0x3e, 0xf4, 0x7d, 0x11, 0x41, 0xd0, 0xf2, 0x76, 0xaf, 0xe8, 0xbb,
0x72, 0x1a, 0xb8, 0x7d, 0x05, 0x04, 0x3e, 0xeb, 0xd7, 0x44, 0xc1, 0x81, 0xbf, 0x11, 0xbe, 0x61,
0x15, 0xb4, 0xb9, 0xc8, 0xa9, 0xca, 0x20, 0xd2, 0xd9, 0xdb, 0x22, 0x4d, 0x15, 0x45, 0xbe, 0xae,
0x9a, 0x33, 0x92, 0xee, 0x87, 0x8b, 0xe5, 0x4b, 0xb1, 0x72, 0xfc, 0x1c, 0x7a, 0xab, 0x93, 0x24,
0x4d, 0x94, 0x64, 0x0c, 0x73, 0xee, 0xd3, 0xbb, 0xd6, 0xfa, 0xb8, 0xb9, 0xc5, 0xd7, 0x59, 0xe7,
0x20, 0x71, 0xbe, 0x40, 0x08, 0xa1, 0xd8, 0xf1, 0x5f, 0xe0, 0xb0, 0xb4, 0x23, 0x13, 0x8c, 0xce,
0x63, 0xb0, 0xec, 0x18, 0x38, 0xbb, 0x49, 0x0c, 0x84, 0x79, 0x3d, 0x84, 0xb3, 0xeb, 0x90, 0x17,
0xc8, 0x0e, 0x23, 0xf3, 0x20, 0x94, 0xd1, 0x8b, 0x78, 0x8e, 0xc2, 0x7e, 0xa3, 0x7a, 0x18, 0x74,
0x9f, 0x4c, 0x3a, 0xd4, 0x02, 0xf2, 0xdc, 0xe3, 0xf0, 0xe4, 0x47, 0x97, 0xbb, 0x11, 0xcf, 0x40,
0xdd, 0xc5, 0x00, 0x71, 0xdc, 0x64, 0xea, 0x78, 0xa5, 0x4a, 0x91, 0x03, 0xca, 0xfe, 0x4b, 0x78,
0xf0, 0x88, 0x32, 0x6b, 0x2a, 0xdc, 0x0c, 0x4d, 0xf1, 0x7c, 0xcb, 0x30, 0x25, 0x91, 0x1e, 0x0c,
0x41, 0xea, 0x46, 0x52, 0x1c, 0xc9, 0x1d, 0xda, 0xd4, 0xe4, 0xf1, 0x66, 0x4d, 0x30, 0x1a, 0x9a,
0x6d, 0xa6, 0x5b, 0xac, 0xbc, 0x33, 0xba, 0x31, 0xf1, 0x55, 0x20, 0x0e, 0xee, 0x7c, 0x7b, 0xee,
0x11, 0x23, 0xcd, 0x5c, 0x23, 0x46, 0xba, 0xe7, 0x7d, 0xcb, 0xb8, 0x5a, 0xa0, 0x2f, 0x06, 0x99,
0xf8, 0x3b, 0x68, 0x1d, 0x4d, 0xd6, 0xe9, 0x48, 0x57, 0xdf, 0xb2, 0xb2, 0x66, 0x0f, 0x12, 0x6f,
0x58, 0x4a, 0x88, 0xff, 0x14, 0xe8, 0x7e, 0x2e, 0xff, 0x2b, 0x8c, 0xd6, 0x93, 0x00, 0xad, 0xe1,
0xee, 0xbf, 0x10, 0x7d, 0xa4, 0x19, 0x51, 0xa6, 0xe0, 0x93, 0x80, 0xc7, 0x14, 0xd0, 0x7e, 0x0a,
0x46, 0x84, 0x5e, 0x32, 0x97, 0xc1, 0xd9, 0x16, 0x77, 0x22, 0x88, 0x20, 0x78, 0x6e, 0xa3, 0x16,
0x4b, 0x6a, 0xb1, 0xb7, 0x8b, 0x24, 0x39, 0xc0, 0x04, 0x8c, 0x15, 0xfe, 0xc2, 0xe4, 0xe4, 0xbd,
0xea, 0x68, 0xf6, 0x61, 0xa5, 0xa5, 0x9b, 0xfa, 0x16, 0x53, 0xe8, 0xb3, 0xc0, 0x08, 0xd0, 0x93,
0x65, 0xa0, 0xc9, 0x19, 0x1b, 0x6b, 0x6d, 0x1d, 0xac, 0x00, 0x41, 0xf3, 0x18, 0x30, 0xfa, 0x2a,
0x97, 0xd9, 0x02, 0x33, 0x5e, 0x88, 0xc0, 0x2a, 0x22, 0xc7, 0x80, 0x81, 0x55, 0x34, 0x1e, 0x4b,
0xdf, 0x5d, 0x34, 0xf5, 0x6b, 0xcd, 0x4a, 0x0b, 0x12, 0x23, 0xda, 0x68, 0x9c, 0x31, 0x66, 0x81,
0x75, 0x6c, 0x6e, 0x46, 0xaf, 0x68, 0x68, 0x34, 0x5c, 0xb6, 0x0a, 0x56, 0x73, 0xb4, 0xcd, 0xcd,
0x0f, 0xb1, 0x4d, 0xe3, 0x21, 0xde, 0x05, 0x12, 0x5f, 0xf1, 0x6c, 0x99, 0x09, 0xb9, 0x2f, 0xd8,
0x9d, 0x64, 0x78, 0x29, 0x36, 0x56, 0x49, 0xb0, 0x80, 0x91, 0x06, 0xc1, 0x2a, 0x31, 0x78, 0x02,
0xac, 0x3c, 0x2a, 0x02, 0x21, 0xa5, 0xf6, 0xe3, 0x87, 0x09, 0xac, 0xc3, 0x04, 0x2e, 0x61, 0xa6,
0x65, 0x21, 0x0c, 0xfc, 0xa7, 0xf0, 0x03, 0xff, 0x07, 0xf5, 0xa6, 0x9e, 0xa5, 0x4d, 0xc7, 0x4c,
0x83, 0xd5, 0x16, 0x0b, 0xd8, 0xed, 0xe8, 0x23, 0xe9, 0xa5, 0xbb, 0x13, 0xd0, 0x54, 0x04, 0xc0,
0x21, 0x02, 0xdb, 0x89, 0x36, 0x09, 0x70, 0xdf, 0x6b, 0x15, 0x9a, 0xac, 0xe5, 0x58, 0x45, 0x7a,
0x49, 0x52, 0xac, 0xea, 0x7b, 0xb5, 0x51, 0x19, 0x8b, 0x08, 0x46, 0x27, 0xa6, 0xa8, 0x78, 0xef,
0x92, 0x59, 0xa9, 0x9c, 0x29, 0x8a, 0x1b, 0xd6, 0x9a, 0xe6, 0xde, 0xf1, 0xbb, 0xbe, 0x12, 0x96,
0x2e, 0xe8, 0xf9, 0x3f, 0x38, 0xc4, 0x0b, 0x0c, 0xd8, 0x6e, 0x0e, 0xfe, 0x4e, 0x06, 0x57, 0xc7,
0x78, 0x97, 0x76, 0xe2, 0x05, 0xa3, 0x8b, 0x3b, 0x8a, 0x33, 0x18, 0xa4, 0x9b, 0x19, 0x6b, 0x2f,
0xa0, 0xf9, 0xcc, 0x3d, 0xc5, 0xc4, 0x86, 0x29, 0x62, 0xa7, 0xfe, 0x7c, 0x93, 0x38, 0x99, 0xec,
0x2b, 0xa9, 0x46, 0xb6, 0xc2, 0x6f, 0xe7, 0xe9, 0xa1, 0x78, 0x6c, 0x96, 0x17, 0xfa, 0xca, 0x76,
0xce, 0xff, 0x99, 0x17, 0x4b, 0x80, 0xd1, 0x42, 0xa1, 0xd1, 0xe8, 0x2b, 0x34, 0x25, 0xd5, 0xc8,
0x63, 0x8a, 0x58, 0x09, 0xa5, 0x00, 0x00, 0xc6, 0x99, 0xd8, 0x2d, 0xca, 0xfe, 0xf4, 0xd0, 0xd9,
0x6f, 0xfc, 0xea, 0x3b, 0x78, 0x81, 0x54, 0x5f, 0x99, 0xe3, 0x6d, 0x5c, 0xc8, 0xcb, 0x8a, 0x60,
0x17, 0xff, 0xe2, 0xe7, 0x42, 0x35, 0x2b, 0x32, 0x72, 0x51, 0x8c, 0xae, 0x1d, 0xf1, 0x15, 0x2e,
0xae, 0x76, 0xa6, 0x7b, 0x13, 0x98, 0x1f, 0xd1, 0xfd, 0x83, 0xdb, 0xa0, 0x83, 0xa3, 0xe5, 0x24,
0xd2, 0x26, 0xf7, 0x15, 0x5f, 0x6e, 0x27, 0x90, 0xd0, 0x7e, 0x2d, 0x47, 0x5f, 0xd4, 0x6a, 0x0f,
0x12, 0xec, 0xaa, 0x68, 0x8e, 0x7a, 0xe7, 0x93, 0xac, 0x65, 0x58, 0xa7, 0x6b, 0xde, 0x0d, 0x4b,
0x1b, 0xe2, 0x56, 0x9f, 0x08, 0x8e, 0x9c, 0x13, 0x8d, 0x47, 0x88, 0x79, 0x7b, 0x76, 0x1f, 0x5d,
0x2d, 0x04, 0xb0, 0x26, 0xde, 0xcd, 0x42, 0xbc, 0x6b, 0x4f, 0x11, 0x4c, 0x32, 0xb2, 0xd7, 0xe6,
0xb2, 0xd0, 0x80, 0x11, 0x6e, 0x67, 0x07, 0xbb, 0x45, 0x78, 0xf6, 0xc7, 0x3f, 0x0b, 0x1e, 0x6a,
0xb4, 0x81, 0x07, 0xe8, 0xe9, 0xb0, 0xd8, 0xa9, 0x4e, 0x7e, 0xee, 0xdd, 0xd5, 0xc3, 0xcf, 0xe6,
0x91, 0x72, 0xb3, 0x39, 0x9e, 0xee, 0x89, 0x8c, 0x43, 0x14, 0xbc, 0x3b, 0x57, 0xd6, 0xdd, 0x1f,
0x49, 0x7b, 0x1a, 0x3f, 0x92, 0x3f, 0xb3, 0x6a, 0x91, 0x9b, 0x94, 0xa1, 0x63, 0xb5, 0x70, 0x2f,
0xe7, 0x18, 0xcd, 0xb8, 0xea, 0x90, 0x75, 0xe4, 0x86, 0x8a, 0x28, 0x42, 0x71, 0x13, 0x76, 0xcd,
0x19, 0xeb, 0xc9, 0x8e, 0x85, 0xc2, 0x12, 0xb4, 0xd1, 0x58, 0x8c, 0xf0, 0xdf, 0x70, 0x61, 0xfe,
0x05, 0xdf, 0xa5, 0x09, 0x88, 0x8a, 0xe0, 0x73, 0xf9, 0xe6, 0x83, 0x30, 0x52, 0xcc, 0xb4, 0x05,
0x30, 0xbe, 0x9b, 0xe9, 0xc1, 0x4e, 0xfc, 0x7a, 0x93, 0xa5, 0xc1, 0xa7, 0xb2, 0x78, 0xbd, 0xad,
0x00, 0x6a, 0x5c, 0xcd, 0x4d, 0x46, 0x6f, 0x5b, 0x5a, 0x3a, 0xc9, 0x1b, 0x66, 0x32, 0xf4, 0xad,
0x0a, 0xb8, 0x9d, 0xb7, 0xb8, 0x40, 0x60, 0xbd, 0xf2, 0x11, 0x18, 0xb8, 0x78, 0x64, 0xc6, 0x8b,
0x12, 0x8a, 0xbb, 0x38, 0x82, 0x80, 0x7d, 0x5c, 0x6f, 0x2e, 0xbf, 0x72, 0xff, 0x7d, 0x11, 0xa8,
0xa9, 0x61, 0x60, 0xc9, 0x77, 0xff, 0x52, 0xae, 0xba, 0xb6, 0x38, 0x19, 0x64, 0xfe, 0xd0, 0xf0,
0xc6, 0x25, 0x67, 0x65, 0x58, 0x26, 0x8d, 0x1c, 0xf3, 0x82, 0x2a, 0x6e, 0xa0, 0xb7, 0xa0, 0xdf,
0xdd, 0xe2, 0x9b, 0xb5, 0x5b, 0x12, 0x5e, 0xbf, 0x52, 0x77, 0x62, 0xfd, 0x35, 0x82, 0x30, 0x0d,
0xaf, 0xd3, 0xd1, 0xf2, 0xfc, 0xf6, 0x56, 0x96, 0x3a, 0xb9, 0xd8, 0xde, 0xfe, 0x3c, 0x7a, 0xc5,
0x8a, 0xc1, 0xe2, 0x9d, 0x56, 0x99, 0xf8, 0x75, 0x3f, 0xd2, 0x0c, 0xc3, 0x44, 0x35, 0x73, 0xe8,
0x44, 0xb1, 0xe6, 0xeb, 0x47, 0xf4, 0x44, 0x6f, 0x9a, 0xbe, 0xc8, 0x22, 0x89, 0x37, 0xe8, 0x21,
0x10, 0x9c, 0x1d, 0x7a, 0x43, 0x4f, 0x12, 0xb5, 0x2a, 0xf6, 0xc4, 0xae, 0x4e, 0xe2, 0xc2, 0x71,
0xe0, 0x43, 0xf6, 0xfe, 0x8b, 0x85, 0x9a, 0xbd, 0x64, 0x9c, 0xca, 0xfa, 0xd0, 0x4e, 0xae, 0xdc,
0xd3, 0x5a, 0xce, 0x09, 0xdb, 0x8b, 0x2c, 0x77, 0xce, 0x82, 0x3f, 0xff, 0xab, 0xb5, 0xe2, 0x22,
0xdb, 0xc5, 0x4b, 0x88, 0x79, 0xe1, 0xbc, 0x91, 0xa5, 0x61, 0x04, 0x63, 0x41, 0xa3, 0x7d, 0x69,
0x88, 0xc2, 0x44, 0x64, 0x0e, 0x76, 0xe6, 0xbf, 0xe8, 0xd0, 0x1b, 0xc3, 0xd1, 0xdd, 0x14, 0xe1,
0xc7, 0x43, 0x83, 0xed, 0x51, 0x04, 0xd1, 0x11, 0x69, 0x76, 0x58, 0x47, 0xbd, 0xa1, 0xd7, 0x11,
0x39, 0x3b, 0xf1, 0x84, 0x1f, 0xe2, 0xcf, 0x5a, 0xe8, 0x38, 0x48, 0x5f, 0x72, 0x8e, 0x6c, 0x93,
0x46, 0xc4, 0xd1, 0xe3, 0xe9, 0x91, 0x58, 0x23, 0xb2, 0xb4, 0x19, 0xcb, 0x22, 0x37, 0x16, 0x67,
0x2e, 0xa1, 0x2e, 0xa0, 0xd3, 0xdb, 0x9f, 0x16, 0xe7, 0xfe, 0x46, 0x75, 0x98, 0x2d, 0x2b, 0x2f,
0xc9, 0x50, 0xc8, 0xa0, 0x17, 0xd5, 0x16, 0xd8, 0x89, 0xf4, 0x40, 0x6b, 0x24, 0xd4, 0xcd, 0xbf,
0x60, 0x3a, 0x28, 0x02, 0xb3, 0xec, 0x61, 0xca, 0x49, 0x06, 0x2f, 0x85, 0xe3, 0x7f, 0xff, 0x0e,
0x77, 0xdd, 0x8d, 0xfd, 0x36, 0xf1, 0x02, 0x3e, 0x40, 0xa0, 0x8f, 0x1e, 0x80, 0x86, 0xca, 0xfb,
0x43, 0x80, 0x4c, 0x2f, 0x24, 0x2d, 0xf4, 0xbe, 0x67, 0xfe, 0x07, 0xc3, 0xfc, 0x4f, 0xcf, 0x61,
0x74, 0x63, 0x5a, 0xc2, 0x7f, 0xb5, 0x56, 0xc5, 0xd1, 0x79, 0x5e, 0xa3, 0x2f, 0x49, 0x7f, 0x3e,
0x44, 0x3e, 0x4c, 0x52, 0xd4, 0x2c, 0x66, 0x36, 0x27, 0x2b, 0xe0, 0x8f, 0x7e, 0xa9, 0x33, 0x66,
0xc3, 0xdd, 0x9a, 0x88, 0xfe, 0x8d, 0x3e, 0x94, 0x45, 0x99, 0x78, 0x83, 0x4f, 0x2a, 0xe9, 0xfc,
0xcb, 0xcc, 0x8c, 0x17, 0xe7, 0x2f, 0x73, 0x7c, 0x5d, 0xfc, 0xd2, 0xc0, 0xeb, 0xa2, 0x58, 0x1f,
0xb7, 0x45, 0x60, 0x86, 0xdf, 0x45, 0x3e, 0x48, 0xf8, 0x8e, 0x09, 0xdb, 0x98, 0x60, 0x6c, 0xa7,
0xb3, 0xb9, 0xcd, 0xcd, 0x4f, 0x8d, 0xaf, 0xd1, 0xf0, 0xd0, 0x01, 0x70, 0x60, 0xa8, 0xcb, 0x57,
0x7a, 0x73, 0x5b, 0x5b, 0x1a, 0xe7, 0x51, 0xe4, 0x56, 0xc3, 0xc1, 0xbe, 0x65, 0xb7, 0x0c, 0x3f,
0xd0, 0x49, 0xc2, 0x45, 0xf8, 0xe2, 0x78, 0x5d, 0x00, 0x81, 0xb2, 0x0e, 0x46, 0x17, 0x0f, 0x85,
0x7e, 0x16, 0x99, 0xb8, 0xb7, 0xe6, 0x91, 0x7e, 0x88, 0x51, 0xb0, 0xfb, 0xbf, 0xc2, 0x9a, 0x86,
0x6b, 0x5a, 0x9c, 0x8f, 0xca, 0x43, 0x82, 0x81, 0xc0, 0xf5, 0xe0, 0x32, 0x66, 0x3c, 0x62, 0x9b,
0xe2, 0xa2, 0x17, 0x34, 0x5b, 0x90, 0xb6, 0x95, 0x8c, 0xa9, 0xad, 0xfe, 0xfb, 0xd8, 0x63, 0x7a,
0xeb, 0x8e, 0x58, 0x8b, 0x65, 0xb3, 0x76, 0x00, 0x67, 0x7c, 0x0c, 0x2e, 0xbd, 0xa8, 0x14, 0x6f,
0x84, 0x0e, 0x53, 0x40, 0x70, 0x07, 0x21, 0x11, 0xd2, 0xe9, 0xb4, 0xcb, 0x74, 0x25, 0xdf, 0x99,
0xe2, 0x2b, 0x08, 0xa4, 0xce, 0x78, 0x03, 0xbd, 0xbb, 0xd1, 0x3b, 0xc6, 0x00, 0xe5, 0xfc, 0x3b,
0xca, 0x37, 0x37, 0xd9, 0x93, 0xc9, 0x2f, 0x4e, 0x53, 0x27, 0xf0, 0xa0, 0x4f, 0xc2, 0xe4, 0xbd,
0x1c, 0x58, 0x8d, 0xfc, 0x0e, 0x5d, 0x94, 0xbf, 0x7f, 0x47, 0x25, 0x1d, 0x11, 0x66, 0x90, 0x5a,
0x9b, 0xa1, 0x02, 0x1c, 0xea, 0x0d, 0xa4, 0x09, 0xb4, 0x16, 0x5f, 0x5b, 0x59, 0x1e, 0xf3, 0xe6,
0x81, 0xe6, 0xb6, 0x34, 0x8c, 0xb9, 0x77, 0x0b, 0x9b, 0xa7, 0x92, 0x2d, 0xdf, 0x81, 0x06, 0x4a,
0x19, 0x94, 0xe0, 0xe8, 0x6d, 0x6d, 0xf4, 0xda, 0x36, 0x98, 0xf3, 0xe5, 0x52, 0x03, 0x13, 0x4f,
0x93, 0xa2, 0x73, 0x96, 0x72, 0xb4, 0xf5, 0x05, 0xc3, 0x17, 0xb0, 0xbd, 0x5f, 0x92, 0x82, 0x04,
0xe1, 0xcd, 0x09, 0xb8, 0xfa, 0x3e, 0x28, 0x37, 0xb4, 0x3e, 0x2a, 0xb6, 0xb8, 0xcb, 0xcd, 0x2f,
0xb7, 0xf1, 0x3d, 0xe3, 0xbd, 0xb5, 0x22, 0xf1, 0x3d, 0x83, 0x7b, 0xab, 0xf8, 0xdd, 0x77, 0x07,
0xfa, 0x76, 0xe2, 0xff, 0x02, 0x56, 0xec, 0xce, 0xa9, 0xe8, 0xc4, 0x00, 0x00
0xcc, 0x74, 0x4c, 0x0f, 0x69, 0x3a, 0x1f, 0x2b, 0x7d, 0x43, 0xcb, 0x92, 0x34, 0xcd, 0x8b, 0x66,
0xb5, 0x81, 0x80, 0x12, 0x48, 0x41, 0x40, 0x4f, 0xd0, 0xd6, 0xcb, 0xae, 0xd7, 0x12, 0x01, 0x23,
0xc3, 0x96, 0x06, 0x3b, 0xd1, 0x82, 0x97, 0x9d, 0xeb, 0xe6, 0x19, 0x27, 0x24, 0xbd, 0xdc, 0x4c,
0x56, 0xcc, 0x15, 0xf8, 0x10, 0xcd, 0x79, 0x10, 0x90, 0x3d, 0x44, 0x5a, 0xd9, 0x87, 0x85, 0x39,
0x40, 0x8a, 0x4b, 0x78, 0xd1, 0x51, 0x9c, 0x60, 0xc4, 0x3a, 0xd2, 0x04, 0x0c, 0x83, 0x66, 0x9c,
0x68, 0x5f, 0x76, 0xb0, 0xab, 0x94, 0x68, 0xbb, 0x96, 0x13, 0x2b, 0x75, 0xd6, 0x6c, 0x25, 0x24,
0x55, 0x05, 0x56, 0xe6, 0x60, 0xa9, 0x81, 0xa4, 0xc4, 0xc7, 0x03, 0xd6, 0xbb, 0x33, 0x75, 0x40,
0xb7, 0xc6, 0x7c, 0x58, 0x5a, 0xc3, 0x14, 0x7a, 0x98, 0xe9, 0xa3, 0x9b, 0xc2, 0xee, 0x21, 0x82,
0x43, 0xc4, 0x9b, 0x05, 0x5b, 0x98, 0x15, 0xcc, 0xb0, 0x42, 0x40, 0xc8, 0xff, 0x5c, 0xc2, 0xf7,
0xbe, 0x31, 0xd2, 0x6c, 0xd3, 0x18, 0xd0, 0xae, 0x93, 0x34, 0xc6, 0x2f, 0x53, 0x1b, 0x18, 0x23,
0x39, 0x6c, 0x02, 0x8f, 0x74, 0xd6, 0xf4, 0xb1, 0x66, 0xa5, 0x38, 0x56, 0x19, 0xcc, 0x48, 0x4a,
0x1e, 0xbf, 0x98, 0xa3, 0xea, 0x65, 0x14, 0xe5, 0x3e, 0xcb, 0xeb, 0xdb, 0x3f, 0x11, 0x15, 0x5e,
0xe3, 0xb8, 0x86, 0x82, 0x88, 0xb1, 0x24, 0x1a, 0x39, 0x3d, 0xa6, 0x6f, 0x30, 0x01, 0x1e, 0x8f,
0x1b, 0xf3, 0xc2, 0xc5, 0xfc, 0x1a, 0xdb, 0x59, 0x7e, 0x11, 0x03, 0x22, 0xd6, 0xcd, 0xef, 0x7e,
0xc6, 0xe6, 0xa6, 0xf9, 0xbd, 0x50, 0x37, 0x17, 0x4e, 0x59, 0x0a, 0xf9, 0x87, 0xf9, 0x93, 0x8a,
0x28, 0x23, 0x24, 0xa2, 0xd2, 0x40, 0xc0, 0x75, 0x2f, 0x00, 0xee, 0x87, 0xf1, 0xb3, 0xe1, 0xa4,
0x4d, 0x74, 0x65, 0x36, 0xc2, 0x0d, 0x53, 0x27, 0x7d, 0xa8, 0x1f, 0x46, 0x0a, 0x08, 0xc1, 0xd8,
0xc6, 0xce, 0x60, 0x16, 0xf6, 0xc9, 0x5b, 0x49, 0x11, 0x99, 0xe5, 0x9f, 0x1d, 0xfa, 0xc7, 0xcc,
0x41, 0x3b, 0x99, 0xba, 0x61, 0xe1, 0x69, 0x07, 0xfe, 0x6a, 0xfe, 0xf1, 0x30, 0xc0, 0x30, 0x5a,
0xe5, 0x28, 0x3f, 0x00, 0xa3, 0xec, 0x2b, 0xf1, 0x9d, 0x55, 0xf5, 0x8f, 0x4e, 0xd0, 0xcb, 0x2d,
0xe9, 0x75, 0xaa, 0x1c, 0xe6, 0x26, 0x36, 0x0d, 0xd9, 0xb1, 0xea, 0xf8, 0x14, 0x09, 0x93, 0xa7,
0xe5, 0x64, 0x73, 0xc2, 0xf9, 0x47, 0xcb, 0xfe, 0x31, 0x33, 0xe6, 0x8e, 0x17, 0x4d, 0x1d, 0x9c,
0x72, 0xc1, 0xde, 0x26, 0x31, 0x87, 0xe7, 0x12, 0xd8, 0x31, 0x48, 0xd9, 0xe1, 0xbc, 0x7b, 0x36,
0xa9, 0xd4, 0xa3, 0x4d, 0x44, 0x44, 0x5f, 0x70, 0xad, 0x26, 0xed, 0x04, 0xe7, 0x07, 0x89, 0x62,
0x47, 0x33, 0xb4, 0xa7, 0xf4, 0x31, 0x76, 0x71, 0x0b, 0xeb, 0xde, 0xda, 0x73, 0x3d, 0x09, 0x60,
0xd7, 0xd0, 0xfe, 0x02, 0xf1, 0x3b, 0x9c, 0xa4, 0xb8, 0x1c, 0xd3, 0x9a, 0x42, 0xfd, 0x87, 0x62,
0x51, 0x21, 0x0f, 0xdd, 0xbf, 0x1c, 0xdb, 0xfe, 0x08, 0x02, 0xbd, 0x0b, 0x70, 0x3a, 0x8f, 0x5e,
0x48, 0x44, 0x3b, 0x10, 0x1c, 0xb4, 0x63, 0xa7, 0xa8, 0x18, 0x4c, 0x7a, 0xc9, 0xcb, 0xea, 0xd3,
0x42, 0x58, 0x29, 0x72, 0xfa, 0x8a, 0xd5, 0x90, 0x97, 0xae, 0x85, 0x01, 0x50, 0x78, 0x33, 0x8c,
0xd7, 0x8d, 0xbf, 0x7f, 0x39, 0x0c, 0x8e, 0x00, 0x5a, 0x81, 0xc1, 0x67, 0xbc, 0x4e, 0x7c, 0x74,
0x6c, 0x88, 0x8e, 0x6f, 0xf1, 0xf4, 0xa9, 0xb3, 0x43, 0x74, 0xca, 0x3c, 0x5a, 0x63, 0x3a, 0x0b,
0x53, 0x56, 0xfc, 0x73, 0x88, 0x91, 0x05, 0x1c, 0xa8, 0x80, 0x9e, 0x63, 0x3f, 0x58, 0x5a, 0x06,
0x2c, 0x2d, 0xf4, 0xee, 0x87, 0x07, 0x8b, 0x60, 0x7e, 0x2d, 0xb0, 0xf5, 0x2b, 0x6c, 0xbf, 0xca,
0x38, 0x60, 0x6a, 0x0a, 0x8d, 0x35, 0xba, 0x69, 0xc3, 0xcf, 0x14, 0xdc, 0x13, 0xcc, 0xd5, 0xe8,
0x57, 0x3e, 0x57, 0xf3, 0x5c, 0x38, 0xfb, 0x9d, 0xcb, 0x7c, 0x8e, 0xab, 0xd3, 0xd4, 0x2c, 0xcb,
0xac, 0x84, 0x33, 0x2b, 0xb9, 0x52, 0x89, 0x9b, 0x7b, 0xbf, 0x77, 0x42, 0x0e, 0x14, 0xd9, 0x88,
0x78, 0x61, 0x38, 0xdc, 0x67, 0xa4, 0xdb, 0x99, 0x98, 0xbe, 0x43, 0xd2, 0x9a, 0x55, 0x63, 0xcf,
0xcb, 0x4c, 0x89, 0x85, 0x05, 0xd3, 0xb8, 0x3f, 0xc6, 0x46, 0x40, 0xef, 0x70, 0xf0, 0x03, 0x47,
0x8d, 0x5b, 0x6d, 0x69, 0x1a, 0xfa, 0xcc, 0xcf, 0xbc, 0x87, 0xa8, 0xfd, 0x40, 0x81, 0xd2, 0xbd,
0x54, 0x9d, 0xb4, 0xcc, 0x01, 0xbe, 0x1e, 0x05, 0x6b, 0x48, 0x28, 0xa1, 0x02, 0xfc, 0xe9, 0x00,
0x49, 0xff, 0xee, 0xd7, 0xf7, 0xb6, 0x47, 0x74, 0x9f, 0x37, 0x49, 0x0d, 0x2f, 0xe7, 0x87, 0xfe,
0x13, 0x1b, 0x94, 0x58, 0x75, 0x67, 0xa1, 0x12, 0xac, 0x32, 0x47, 0x80, 0xf5, 0x86, 0xa8, 0x5f,
0x82, 0x11, 0xce, 0x23, 0xd1, 0xd2, 0xde, 0xdd, 0x52, 0x9e, 0x17, 0xec, 0x1b, 0x0d, 0x19, 0xce,
0x64, 0xbc, 0x92, 0xdf, 0x98, 0xf1, 0xc1, 0xb0, 0x26, 0xf1, 0x21, 0x03, 0x44, 0x00, 0x85, 0x99,
0x4d, 0x97, 0x44, 0x77, 0xb2, 0x21, 0x0f, 0xf5, 0x00, 0x0d, 0x4a, 0x8b, 0x8d, 0x86, 0x84, 0xe2,
0x7e, 0x87, 0x3b, 0xcf, 0x80, 0xae, 0x44, 0x9f, 0xe7, 0xb8, 0xd0, 0x7e, 0x01, 0x2f, 0x04, 0xad,
0x77, 0x0e, 0xc8, 0x50, 0x95, 0xef, 0xe2, 0x8e, 0x99, 0x6a, 0x70, 0x91, 0xb8, 0xfa, 0x84, 0x86,
0x97, 0xd5, 0x39, 0x28, 0x2c, 0xd4, 0x34, 0x57, 0x03, 0x40, 0xa8, 0x80, 0x60, 0xb1, 0x73, 0x33,
0x61, 0x82, 0x09, 0x6a, 0x2f, 0x82, 0xc6, 0x13, 0x5d, 0x94, 0x59, 0x69, 0xdc, 0x5b, 0x45, 0x76,
0xca, 0xde, 0x36, 0xe6, 0x33, 0x11, 0x7c, 0x07, 0x2b, 0x5d, 0xa3, 0xb0, 0x5a, 0xe6, 0xf4, 0x2b,
0x10, 0x5f, 0x2d, 0x26, 0x75, 0x03, 0x40, 0x35, 0x14, 0x62, 0x14, 0x91, 0x73, 0x4a, 0xf4, 0x51,
0x51, 0x65, 0x44, 0x28, 0xdd, 0x8c, 0x9a, 0xde, 0x5e, 0xa0, 0xf8, 0x4a, 0x9b, 0x9b, 0x85, 0xc1,
0x7f, 0xca, 0xe4, 0x0e, 0x1c, 0x6f, 0x43, 0x5d, 0xa5, 0x7b, 0x90, 0x08, 0x3b, 0x81, 0xc0, 0x69,
0xec, 0x39, 0xf3, 0xbf, 0x7f, 0xd6, 0xff, 0x14, 0x21, 0xd6, 0xc0, 0x0a, 0x20, 0x7f, 0xd5, 0xff,
0xb4, 0xbc, 0x0c, 0x02, 0x27, 0x86, 0x67, 0x69, 0x50, 0xa7, 0x87, 0x40, 0x25, 0x34, 0xf1, 0x6d,
0x7f, 0x4a, 0xd0, 0xcc, 0xc4, 0xc3, 0xdd, 0x4f, 0x12, 0xde, 0xfb, 0x24, 0x3f, 0xdc, 0x9f, 0x8d,
0x99, 0xa6, 0xd6, 0x88, 0xbf, 0xbd, 0x09, 0xb8, 0x67, 0x3f, 0xc0, 0xc2, 0x45, 0x18, 0x9f, 0x5d,
0x36, 0x80, 0x12, 0xdf, 0xb9, 0x80, 0xf0, 0x44, 0x06, 0x89, 0xa3, 0x87, 0xcd, 0xd0, 0xd9, 0x20,
0x02, 0xbe, 0x83, 0x9e, 0x30, 0xd6, 0x44, 0xc2, 0x7b, 0x8e, 0xa8, 0x94, 0xe3, 0xb6, 0x23, 0x6d,
0x82, 0x5e, 0x41, 0x7a, 0xd1, 0x4d, 0x72, 0xd7, 0x9d, 0xbb, 0x3d, 0x8e, 0xff, 0x2e, 0xfa, 0xab,
0x7c, 0x45, 0x91, 0x7f, 0xa3, 0xc7, 0x14, 0x07, 0x6c, 0x6e, 0x8b, 0x3b, 0x4e, 0xa8, 0x84, 0x33,
0x94, 0x41, 0x9b, 0x4d, 0x9a, 0x7c, 0xcd, 0x6c, 0x54, 0xab, 0x55, 0x14, 0xf4, 0x3d, 0xdc, 0x06,
0x03, 0xb4, 0x21, 0x79, 0xa3, 0x9b, 0xf3, 0xd0, 0x1d, 0xe8, 0x49, 0xae, 0x3b, 0x01, 0xaa, 0xc3,
0x6a, 0x9a, 0x2a, 0x2c, 0x55, 0x17, 0x41, 0xb1, 0x17, 0x16, 0x27, 0xe8, 0x70, 0x87, 0x04, 0xff,
0x07, 0xfd, 0x8f, 0xd2, 0xa4, 0x7f, 0xb4, 0xf0, 0x7d, 0x15, 0x6a, 0xe1, 0x60, 0xf3, 0x2d, 0x42,
0x9c, 0xa7, 0xff, 0x4f, 0xa6, 0xc8, 0xdb, 0x49, 0xc7, 0xf9, 0x09, 0xd4, 0x3b, 0xbf, 0x3f, 0x4c,
0x3d, 0x0b, 0xfa, 0x83, 0x9a, 0xd9, 0x5a, 0x34, 0x7a, 0x6e, 0x4c, 0xc4, 0xa5, 0xe9, 0xe1, 0xd2,
0x64, 0xb8, 0x14, 0x42, 0x47, 0xd9, 0x38, 0xe1, 0xdb, 0x7b, 0xa7, 0xd7, 0x80, 0xc7, 0x31, 0x3b,
0xdc, 0x3f, 0xfe, 0xb7, 0x1a, 0xb1, 0x91, 0x8d, 0xdf, 0x20, 0x46, 0x66, 0x55, 0x88, 0xef, 0x57,
0x0f, 0x52, 0x82, 0x5e, 0x8c, 0xcc, 0xbd, 0x8b, 0xea, 0x19, 0x1b, 0x32, 0xe2, 0x36, 0x8d, 0x2e,
0x30, 0xe8, 0x35, 0x8e, 0xc3, 0x61, 0x29, 0xf1, 0x18, 0xdf, 0xa0, 0xeb, 0x7c, 0x1d, 0x37, 0x95,
0x1c, 0xb6, 0x3b, 0x89, 0xc8, 0xf1, 0xfa, 0xd5, 0x72, 0x1c, 0xa0, 0xbe, 0x68, 0x44, 0x50, 0x38,
0x8f, 0xed, 0x8c, 0x84, 0x0f, 0x93, 0x2c, 0x76, 0xd8, 0x16, 0x69, 0x3f, 0x08, 0x95, 0x2f, 0x5f,
0x5c, 0x7f, 0xc7, 0xc4, 0xbf, 0x7b, 0x21, 0xc1, 0xe2, 0x8f, 0xb3, 0x0d, 0x90, 0x44, 0x6c, 0x10,
0xe8, 0x1d, 0x00, 0xa2, 0x68, 0xa0, 0xa7, 0x4a, 0x88, 0x46, 0x61, 0x60, 0x3a, 0xef, 0xe5, 0x43,
0x6e, 0x03, 0x34, 0x1b, 0x9e, 0x8f, 0xba, 0x8d, 0x18, 0x0e, 0xc5, 0x3a, 0xf9, 0xee, 0xc3, 0xab,
0x13, 0x44, 0x0a, 0xb5, 0x91, 0x41, 0x17, 0x77, 0xb1, 0x27, 0x74, 0xf5, 0x0a, 0xba, 0x20, 0x09,
0x76, 0xe3, 0x4b, 0x16, 0x3b, 0x16, 0x6d, 0xc7, 0xe1, 0x79, 0xbb, 0x81, 0x0e, 0xa4, 0x0c, 0xb4,
0xf0, 0x2f, 0xb0, 0x61, 0x04, 0xe6, 0x43, 0x12, 0x74, 0xf8, 0xca, 0xfd, 0x14, 0x24, 0xf8, 0xca,
0xff, 0xa4, 0x11, 0x42, 0x09, 0x0c, 0x61, 0xb2, 0x41, 0xde, 0x3b, 0xbc, 0x81, 0xfd, 0xf1, 0x2c,
0x77, 0x7a, 0x3d, 0x14, 0x3a, 0xad, 0x57, 0xa4, 0x49, 0xcb, 0x69, 0x01, 0xa8, 0xe0, 0x78, 0x8e,
0x4f, 0x0d, 0xd8, 0xec, 0x56, 0xb6, 0x0e, 0xed, 0xfb, 0x39, 0x30, 0x00, 0xc4, 0x8c, 0x1e, 0x4d,
0xc9, 0xd2, 0x5e, 0x85, 0x53, 0x72, 0x3f, 0xe7, 0x5f, 0x00, 0xad, 0x0d, 0x1b, 0x10, 0x66, 0x37,
0x48, 0xc6, 0xc7, 0x07, 0x8e, 0x27, 0x70, 0x77, 0xfd, 0xc2, 0x48, 0x08, 0xd4, 0x9b, 0x04, 0x34,
0xb3, 0x05, 0xb4, 0xc0, 0x79, 0x10, 0x6d, 0xf6, 0xfc, 0x9f, 0xbf, 0x78, 0x4f, 0xd9, 0xf9, 0x15,
0xba, 0xdb, 0x2a, 0xb1, 0xf6, 0x72, 0x2b, 0x0c, 0x8f, 0x7b, 0x36, 0x35, 0x1a, 0x88, 0x50, 0xff,
0x15, 0xa5, 0x16, 0xba, 0xcc, 0x2e, 0xe8, 0x8b, 0x60, 0x82, 0x85, 0x46, 0x95, 0x1e, 0x7f, 0x47,
0x91, 0xbd, 0x24, 0x66, 0xa1, 0x8e, 0x92, 0x39, 0xdb, 0x35, 0x04, 0xdd, 0x91, 0xe6, 0x84, 0x37,
0x0c, 0x57, 0x2e, 0x5b, 0xba, 0x49, 0x29, 0x38, 0xe8, 0x83, 0xe1, 0x04, 0x74, 0x8f, 0x00, 0x32,
0xc2, 0x5b, 0x96, 0x71, 0x6f, 0x1e, 0x0e, 0x36, 0x74, 0xaa, 0x15, 0xdb, 0xf2, 0x7e, 0x42, 0x83,
0x0d, 0xe6, 0x88, 0x00, 0x8d, 0xa6, 0xc5, 0x34, 0x1a, 0x94, 0xf8, 0xbe, 0x67, 0x2f, 0x66, 0x6d,
0x84, 0xae, 0x49, 0xe7, 0x96, 0x8d, 0x97, 0x85, 0xdf, 0x8e, 0xaa, 0xd5, 0xb8, 0x09, 0xca, 0x34,
0xe7, 0x85, 0x0d, 0xe3, 0xe9, 0xd0, 0x38, 0x05, 0x0b, 0xbd, 0x38, 0x18, 0x6c, 0x58, 0xcb, 0x65,
0x0b, 0x0d, 0x5d, 0xf2, 0x5f, 0x1a, 0x81, 0x11, 0x4b, 0xd2, 0x7d, 0xc9, 0x69, 0xba, 0xae, 0xad,
0x81, 0x16, 0x45, 0x92, 0x1c, 0x28, 0xfc, 0x1c, 0x0f, 0x4a, 0xa7, 0xe4, 0x27, 0x51, 0x6f, 0x18,
0xbb, 0xbe, 0xa5, 0x06, 0x64, 0x27, 0x98, 0x0d, 0x66, 0x92, 0xd3, 0x94, 0x8c, 0x5b, 0x4f, 0x9a,
0x0b, 0x7b, 0x14, 0x96, 0xf9, 0xf7, 0xa2, 0xc8, 0xd4, 0xa3, 0x9c, 0xef, 0xe1, 0xfa, 0xb5, 0xfa,
0x2d, 0x8a, 0xa1, 0x77, 0x15, 0xd0, 0x71, 0xfd, 0x53, 0x08, 0x5d, 0x55, 0xcd, 0x52, 0xf8, 0x5f,
0x75, 0x0c, 0x9c, 0x43, 0xd5, 0x08, 0x75, 0x87, 0x75, 0x67, 0x4d, 0x62, 0x16, 0x06, 0x46, 0x41,
0x2e, 0x05, 0x67, 0x34, 0x1c, 0xcf, 0x93, 0x1b, 0x01, 0xb8, 0x0e, 0x76, 0x10, 0x56, 0xc9, 0x2c,
0x0f, 0x0a, 0x12, 0x7d, 0xbf, 0x49, 0x23, 0xec, 0x11, 0xf5, 0xee, 0xb4, 0x09, 0x91, 0x6a, 0x38,
0x9c, 0x84, 0x46, 0xda, 0xa7, 0xd9, 0xa1, 0xc5, 0xbf, 0xda, 0x66, 0x28, 0x70, 0x83, 0x81, 0x4e,
0x72, 0xf1, 0x97, 0x2c, 0x86, 0xb7, 0x41, 0x17, 0x1e, 0x5a, 0x7f, 0x33, 0x96, 0x1e, 0xd7, 0xe1,
0xa8, 0xe3, 0xf9, 0x43, 0xe9, 0xba, 0x2a, 0x4a, 0xc4, 0x3b, 0x40, 0x87, 0x9b, 0xb9, 0x01, 0x30,
0xdc, 0x43, 0xf8, 0x9b, 0xc0, 0x80, 0x0c, 0x23, 0xc7, 0xf0, 0xb6, 0x45, 0x9f, 0x1c, 0xa9, 0x25,
0x66, 0xfd, 0x63, 0x16, 0xc9, 0x9e, 0x9b, 0xbf, 0xf8, 0x3a, 0x98, 0x6e, 0x6e, 0x08, 0xaa, 0xa4,
0xaa, 0x61, 0x90, 0x42, 0x72, 0x5d, 0xdd, 0x57, 0x5d, 0x06, 0x7b, 0xee, 0xdd, 0xca, 0xf3, 0x38,
0x0a, 0x71, 0x0f, 0x87, 0x45, 0x90, 0x84, 0xaf, 0x16, 0x0e, 0xc7, 0x91, 0xe0, 0x89, 0x45, 0x6a,
0xf4, 0x6b, 0x23, 0x42, 0x43, 0x30, 0x18, 0xc2, 0xa9, 0xd1, 0xa5, 0xb3, 0x5d, 0x74, 0xfa, 0xe5,
0xe1, 0x85, 0x0f, 0xc2, 0xf0, 0x18, 0xbe, 0xc2, 0x79, 0x14, 0x67, 0x7e, 0x35, 0x44, 0x1d, 0x30,
0x94, 0x46, 0xe8, 0xe8, 0x22, 0x90, 0x11, 0x98, 0xb3, 0xb2, 0x4d, 0xa4, 0x97, 0xb9, 0xaf, 0x14,
0xe0, 0x15, 0x14, 0x61, 0x9d, 0xe0, 0xdb, 0xda, 0xeb, 0x28, 0xe8, 0xb9, 0xfd, 0x6f, 0x29, 0x3d,
0x88, 0x71, 0x40, 0x3b, 0x12, 0xb5, 0xcc, 0x86, 0xc8, 0xf6, 0x06, 0x37, 0x37, 0xc9, 0x52, 0x47,
0xbd, 0x49, 0x5d, 0xd1, 0x49, 0xb2, 0xaa, 0x93, 0xed, 0x49, 0xb8, 0x8f, 0x9e, 0x8a, 0x1a, 0x6e,
0x30, 0xa2, 0xa6, 0x82, 0x26, 0xd5, 0xa0, 0x3c, 0xa2, 0x41, 0x37, 0xff, 0x3d, 0xfd, 0xf1, 0x73,
0x23, 0x0a, 0x6e, 0xf1, 0xf8, 0x96, 0x8a, 0x36, 0x10, 0x52, 0x53, 0xe7, 0x71, 0xcb, 0x3e, 0x74,
0x3b, 0x2e, 0xef, 0xef, 0xd2, 0x2d, 0xe5, 0xb2, 0x3b, 0x6a, 0xd6, 0xe7, 0x2f, 0xae, 0x5d, 0xe1,
0xbd, 0x3e, 0xb3, 0x4b, 0xc5, 0xe2, 0xc7, 0x9b, 0xd8, 0x0e, 0x44, 0xfc, 0x8c, 0x13, 0xab, 0x80,
0x37, 0x71, 0xc5, 0xcb, 0x2b, 0x8a, 0xbb, 0xb2, 0xf0, 0xcb, 0x4a, 0xe8, 0xb4, 0x34, 0x2d, 0x55,
0xf3, 0x2a, 0x85, 0xb7, 0x65, 0x62, 0xea, 0xa0, 0x9f, 0xd5, 0x61, 0xe1, 0xc3, 0x49, 0x9f, 0x21,
0xc7, 0x4f, 0x0b, 0x19, 0x1f, 0x1f, 0x0a, 0x63, 0x04, 0x11, 0x21, 0x06, 0x4a, 0x05, 0xd1, 0xde,
0x39, 0xb1, 0x91, 0x84, 0x62, 0x44, 0x06, 0xd6, 0x65, 0x58, 0x62, 0x27, 0x08, 0x6e, 0xa8, 0x19,
0x3b, 0xa0, 0xb0, 0xe3, 0x83, 0x1f, 0x39, 0xb3, 0xc3, 0xfd, 0x37, 0xd4, 0x4d, 0x60, 0x1c, 0x38,
0xcd, 0xe7, 0x63, 0x2a, 0x3c, 0xd5, 0xdb, 0x56, 0x94, 0xa6, 0xc9, 0x06, 0x8b, 0xc2, 0x10, 0x85,
0x99, 0x31, 0x1c, 0x10, 0x5b, 0x53, 0x6a, 0x5f, 0xc4, 0x39, 0x5f, 0x0b, 0x76, 0x49, 0x17, 0xbd,
0xf1, 0x43, 0x8b, 0x51, 0x1c, 0x8e, 0x1d, 0x4f, 0xa1, 0xac, 0x33, 0x75, 0x09, 0x83, 0xd3, 0x03,
0xab, 0x79, 0x0c, 0x9c, 0x6e, 0x11, 0x1b, 0x86, 0x6e, 0x08, 0xb0, 0x7a, 0xc6, 0x34, 0x8c, 0x6a,
0x8c, 0x01, 0xc7, 0x5c, 0x6a, 0xe9, 0x3a, 0x6c, 0x3f, 0xe6, 0x9a, 0x56, 0xae, 0x27, 0x31, 0xfa,
0x8d, 0x8c, 0x13, 0x41, 0x80, 0x1c, 0x46, 0xf4, 0xa7, 0xd1, 0x51, 0xed, 0x38, 0x78, 0x30, 0xc0,
0x3f, 0x7c, 0xe0, 0x86, 0x23, 0x51, 0x08, 0xd5, 0xba, 0x79, 0xa6, 0xee, 0x52, 0x4f, 0x3b, 0x3f,
0x8b, 0x84, 0x02, 0x86, 0xf6, 0x95, 0x79, 0x21, 0xf4, 0x63, 0x11, 0x68, 0xe7, 0x9d, 0x67, 0x0e,
0x22, 0x7e, 0x97, 0x8d, 0xf2, 0x3f, 0x0b, 0x66, 0xd4, 0x79, 0xce, 0xd3, 0x92, 0xc1, 0x14, 0x00,
0xe3, 0xbd, 0x6e, 0xee, 0x04, 0x86, 0x7c, 0xc3, 0x14, 0x42, 0x7b, 0x7d, 0xde, 0x19, 0xee, 0xcd,
0xcd, 0xc8, 0xbe, 0x18, 0x28, 0x06, 0x60, 0x7e, 0x06, 0xa6, 0x3f, 0x06, 0x0f, 0xd2, 0xd8, 0xc1,
0x24, 0x9e, 0x98, 0x80, 0xef, 0x1d, 0xef, 0xbb, 0xe6, 0x02, 0x33, 0x16, 0xc6, 0xe8, 0xc6, 0xa6,
0xf1, 0x83, 0x14, 0x41, 0x7f, 0x1c, 0x77, 0xf9, 0x87, 0x31, 0x87, 0x5e, 0x83, 0xa6, 0x45, 0x0c,
0xda, 0xde, 0x18, 0xfd, 0xca, 0x18, 0x88, 0x34, 0xfb, 0x36, 0xfa, 0x56, 0x73, 0xed, 0x21, 0x68,
0x93, 0xb8, 0xb8, 0x5e, 0x9d, 0x53, 0xd2, 0x93, 0x74, 0x0c, 0x8e, 0x5f, 0xc2, 0xe7, 0x3c, 0x6c,
0x94, 0xf9, 0x83, 0xf3, 0x83, 0x47, 0x71, 0x22, 0x89, 0x47, 0x66, 0x68, 0x2d, 0xe0, 0x69, 0x77,
0x82, 0x9e, 0xfa, 0x38, 0x77, 0x62, 0x1a, 0x05, 0xe4, 0xc9, 0xb6, 0x26, 0x18, 0x7a, 0x13, 0x7d,
0x80, 0x3a, 0x75, 0xe9, 0xeb, 0x7b, 0x43, 0x9b, 0xfd, 0x52, 0x87, 0x36, 0xfc, 0xbc, 0x91, 0xbc,
0x9f, 0x2e, 0x2b, 0xda, 0x96, 0x54, 0xc2, 0x12, 0xf0, 0x7d, 0xee, 0x74, 0x83, 0xb3, 0x03, 0x63,
0x80, 0xa4, 0xa1, 0x6a, 0x19, 0x74, 0x3c, 0xb1, 0x93, 0xef, 0xa0, 0x89, 0xe8, 0xdf, 0xc5, 0x1d,
0xdc, 0x2a, 0xaa, 0xe1, 0x33, 0x9e, 0x62, 0x07, 0x7d, 0x25, 0x78, 0x1b, 0x52, 0x26, 0x2b, 0x7a,
0x53, 0x2e, 0x82, 0xe9, 0x27, 0xd6, 0x57, 0xec, 0x83, 0xc4, 0x94, 0x80, 0xe5, 0x0d, 0x10, 0x66,
0x4e, 0xd2, 0x8d, 0x46, 0xdc, 0xc0, 0x40, 0x86, 0x41, 0x40, 0x9e, 0x99, 0xa0, 0x8d, 0x31, 0x59,
0xe1, 0xa4, 0x52, 0x73, 0x5f, 0x2f, 0xf3, 0xb6, 0x38, 0x10, 0x59, 0x86, 0x1f, 0xa5, 0x15, 0x72,
0x45, 0x9d, 0x9b, 0x09, 0xbf, 0x69, 0xcf, 0xfb, 0x44, 0x97, 0xfb, 0x42, 0x60, 0xd7, 0x03, 0xe3,
0xa5, 0x61, 0xe0, 0xa9, 0x5c, 0x6f, 0xa7, 0x73, 0x45, 0xb0, 0x77, 0xc8, 0xce, 0xf3, 0x43, 0xe0,
0xc1, 0x42, 0x22, 0xdb, 0x68, 0xf5, 0x6d, 0x6d, 0x79, 0x8e, 0x4d, 0xc1, 0x16, 0x34, 0x41, 0xa9,
0x4b, 0x0c, 0x98, 0x67, 0x33, 0xd9, 0x8b, 0x5f, 0x60, 0x2f, 0x69, 0x8b, 0x5f, 0x60, 0xd3, 0x31,
0xce, 0x8f, 0x67, 0x1b, 0x16, 0xc9, 0x18, 0xd9, 0x8e, 0x0f, 0xef, 0x85, 0x91, 0x73, 0x29, 0x29,
0x05, 0x6c, 0x25, 0x65, 0xd3, 0x4f, 0x0d, 0x37, 0xa8, 0x16, 0xb0, 0xd8, 0x5d, 0x07, 0x00, 0x61,
0xa1, 0x64, 0x2b, 0x18, 0x24, 0xbb, 0x08, 0x38, 0xc7, 0x6b, 0x0d, 0x78, 0x94, 0xe8, 0x21, 0x62,
0xba, 0x0f, 0x48, 0xc9, 0xab, 0x8f, 0x85, 0x7e, 0x0a, 0x06, 0xee, 0x5c, 0x6f, 0x6e, 0xd2, 0xaf,
0x6d, 0x2a, 0x83, 0x43, 0x75, 0x9a, 0x41, 0x1d, 0x9a, 0xcf, 0x0b, 0xcb, 0xc2, 0x31, 0xc8, 0x77,
0x26, 0xc2, 0x6a, 0xd1, 0x18, 0x94, 0xd0, 0x26, 0x02, 0x49, 0xd3, 0x60, 0xda, 0xcd, 0x4d, 0x11,
0xcc, 0x0d, 0xf6, 0xcc, 0x50, 0xab, 0xa2, 0x6b, 0xc5, 0xf7, 0xa6, 0xfb, 0x19, 0xcc, 0x6d, 0x2e,
0xd6, 0xe8, 0xfd, 0xc2, 0xbe, 0xd3, 0x71, 0x80, 0x2e, 0xfb, 0x44, 0x37, 0xd8, 0x33, 0xfc, 0xc2,
0x31, 0xba, 0xf1, 0x9c, 0xec, 0x59, 0x2c, 0x8d, 0x5b, 0xe6, 0xc4, 0x30, 0x87, 0xbd, 0x7e, 0x82,
0xbe, 0x10, 0x0a, 0xa3, 0x04, 0x1d, 0x69, 0x44, 0x12, 0x2c, 0xb6, 0x2e, 0x56, 0x25, 0x57, 0x0b,
0x2e, 0x26, 0xa4, 0x2d, 0x78, 0x0e, 0xdb, 0x48, 0x99, 0x3c, 0x96, 0x39, 0xd3, 0x1c, 0x07, 0x5f,
0x9f, 0x71, 0x74, 0x4d, 0x7d, 0x9c, 0xb1, 0x22, 0x55, 0x2c, 0xd2, 0x0c, 0xf5, 0x2c, 0x41, 0x87,
0x91, 0x00, 0x5b, 0x2a, 0x61, 0x2a, 0xb0, 0xd0, 0xd0, 0x57, 0x3c, 0x0f, 0x51, 0x2f, 0xe5, 0x15,
0x74, 0x43, 0x92, 0x16, 0x4c, 0x71, 0x35, 0xf8, 0xa1, 0xd2, 0x68, 0xa2, 0xf0, 0x2d, 0x15, 0x06,
0x88, 0x00, 0x3d, 0x7c, 0x89, 0x85, 0x91, 0xee, 0x4e, 0xc2, 0x81, 0x45, 0x88, 0xbf, 0x10, 0x27,
0xca, 0xd6, 0x43, 0x6c, 0x67, 0x11, 0xcb, 0x1f, 0x8a, 0x81, 0xfc, 0x73, 0xe6, 0xf9, 0x85, 0xfc,
0xfe, 0x1d, 0x34, 0xc1, 0xcf, 0x42, 0xf2, 0xe5, 0xf7, 0xef, 0xb0, 0xe8, 0x09, 0xc7, 0x99, 0x47,
0xa3, 0xb4, 0x84, 0x3c, 0xc9, 0xf3, 0xbe, 0x1b, 0x8d, 0x32, 0x62, 0xb3, 0xf1, 0x71, 0xd8, 0x38,
0x3a, 0x98, 0x76, 0x38, 0x0b, 0x04, 0x28, 0xc7, 0x3c, 0xd3, 0x78, 0xf8, 0x3e, 0x89, 0x51, 0xc9,
0xc8, 0xa3, 0xa0, 0x6d, 0x30, 0xb8, 0x1a, 0xcc, 0xeb, 0x8c, 0x1b, 0xe5, 0xe1, 0xe0, 0x69, 0x94,
0x71, 0x69, 0xc3, 0x1c, 0x27, 0x79, 0x0c, 0xb4, 0xa0, 0xc6, 0x40, 0x2c, 0xee, 0x15, 0xc5, 0xae,
0x17, 0x55, 0x95, 0xc5, 0x6b, 0x14, 0x92, 0x78, 0xcb, 0x08, 0x2c, 0x24, 0x73, 0xc7, 0xe7, 0xf6,
0x30, 0xba, 0x6f, 0x33, 0x6e, 0xc4, 0x31, 0x86, 0xff, 0x8d, 0xaf, 0x61, 0xf8, 0x28, 0xb6, 0x1c,
0x11, 0x1b, 0x21, 0x2c, 0x0b, 0x39, 0x74, 0x83, 0xac, 0xf2, 0xb3, 0x3b, 0x5a, 0xc8, 0xc9, 0xee,
0x08, 0x78, 0x36, 0x8f, 0xe0, 0x35, 0x1b, 0x18, 0xd0, 0x8e, 0x86, 0xfe, 0x16, 0x6e, 0x4e, 0xc0,
0x20, 0xf1, 0x64, 0x93, 0xc6, 0x14, 0x07, 0x5a, 0xb1, 0x8e, 0xf7, 0xce, 0xda, 0xc8, 0x9c, 0x6f,
0x6f, 0xda, 0x5b, 0x15, 0x6e, 0x2e, 0xe0, 0x7b, 0x5e, 0x6a, 0xee, 0x5f, 0x8c, 0x8a, 0x4b, 0xfe,
0x0f, 0x2b, 0x0d, 0x3d, 0x1e, 0xef, 0x4b, 0xd8, 0x41, 0xe5, 0x5c, 0x51, 0x40, 0xe1, 0xa1, 0x53,
0xa8, 0x51, 0xe5, 0x60, 0xa1, 0x3c, 0xb0, 0x94, 0x8f, 0x35, 0x08, 0xbf, 0x02, 0xcf, 0x87, 0xb4,
0x07, 0xe2, 0x69, 0x0f, 0xde, 0x77, 0x8d, 0x44, 0xc5, 0x34, 0x5f, 0x0b, 0xaf, 0x40, 0xbc, 0x83,
0x02, 0x56, 0x89, 0x63, 0x81, 0x35, 0x41, 0xd8, 0xf9, 0x81, 0xbf, 0xb6, 0xff, 0xb0, 0x6c, 0xf1,
0x7b, 0x2f, 0x99, 0x89, 0x9c, 0xa7, 0x99, 0x99, 0x46, 0x8d, 0xdd, 0x6b, 0x83, 0x9f, 0xe1, 0x08,
0xc9, 0xf0, 0x2d, 0x50, 0x33, 0xef, 0xa2, 0x1b, 0xb0, 0x4f, 0xc3, 0xf7, 0xfc, 0x08, 0x49, 0xff,
0x2a, 0x1c, 0xf6, 0xcd, 0xc7, 0x2e, 0xfe, 0xf0, 0x0e, 0xd4, 0xe2, 0xf5, 0x61, 0xfe, 0x34, 0x04,
0x37, 0xbf, 0x37, 0x58, 0x95, 0x1d, 0x2e, 0x74, 0x03, 0x3c, 0xbe, 0x40, 0x09, 0x08, 0x2e, 0x94,
0x82, 0x6f, 0x2b, 0xe3, 0x3d, 0x5b, 0xc3, 0x7f, 0x91, 0x4d, 0xd4, 0xc0, 0x65, 0x40, 0x16, 0x26,
0x6e, 0x7c, 0x00, 0xfe, 0x4e, 0x93, 0x3f, 0x5d, 0xe1, 0x61, 0x61, 0xff, 0xbd, 0x0b, 0x7c, 0xbc,
0x07, 0x9e, 0x9d, 0xe0, 0xf2, 0xc7, 0xc7, 0x22, 0xbf, 0xd8, 0x7e, 0xd4, 0x8a, 0x01, 0xd0, 0x62,
0x7f, 0x3c, 0x82, 0x73, 0x0f, 0x5e, 0x78, 0x14, 0x1e, 0xa4, 0x95, 0xc3, 0x60, 0xaf, 0x90, 0xf1,
0xa3, 0x2f, 0xbd, 0x22, 0xc0, 0x97, 0x42, 0x20, 0x5b, 0x97, 0x61, 0x78, 0xf5, 0xb5, 0x39, 0xc8,
0xba, 0x70, 0x45, 0x33, 0x18, 0xac, 0x53, 0xde, 0x75, 0xfa, 0x71, 0x1b, 0x8e, 0xc4, 0x2d, 0x38,
0x5a, 0x78, 0xb4, 0xd2, 0xde, 0x5b, 0x5d, 0xd6, 0xbf, 0x97, 0xfd, 0xa3, 0xd2, 0xfe, 0x46, 0x06,
0xfb, 0xe5, 0x0f, 0x8c, 0xdd, 0x89, 0x1c, 0xab, 0x5b, 0x5f, 0x9f, 0xd5, 0x88, 0x9b, 0x9c, 0x51,
0x0c, 0x86, 0x02, 0x62, 0x7c, 0x07, 0x0c, 0x99, 0x2b, 0x0e, 0x64, 0xfd, 0xe2, 0xd3, 0x5e, 0x68,
0x46, 0x7d, 0x91, 0x63, 0x66, 0x7f, 0xad, 0x80, 0xbf, 0x6c, 0x00, 0x7b, 0xa5, 0x73, 0xbf, 0x3e,
0xdf, 0x1b, 0x2f, 0x3c, 0x84, 0xc4, 0xd6, 0x21, 0xe8, 0x95, 0x35, 0xba, 0x71, 0x84, 0x72, 0xb5,
0xb6, 0xd8, 0xea, 0x27, 0x28, 0x79, 0x17, 0x7d, 0x9c, 0xcf, 0xa3, 0xb7, 0x6d, 0x44, 0x6e, 0x02,
0x0d, 0x1f, 0x6e, 0x08, 0xdf, 0x08, 0xe9, 0x75, 0xae, 0xbe, 0xe8, 0xde, 0x02, 0xd9, 0x41, 0xda,
0xca, 0x39, 0x0c, 0x5d, 0x05, 0x1a, 0x1f, 0xa1, 0x87, 0x85, 0x15, 0x90, 0x36, 0x37, 0x57, 0xf5,
0x80, 0xbe, 0x83, 0x0e, 0x94, 0x30, 0xf3, 0x4e, 0x23, 0xe3, 0xe4, 0x4c, 0x26, 0x7d, 0x69, 0xa4,
0x99, 0x76, 0x8d, 0x73, 0x06, 0xa6, 0x89, 0x31, 0x6c, 0x14, 0x50, 0xcd, 0xbf, 0x21, 0x38, 0x36,
0xce, 0xf0, 0xad, 0xaa, 0xe1, 0x61, 0x2e, 0x77, 0xf0, 0x2f, 0x8c, 0x72, 0x05, 0xae, 0x3e, 0x3f,
0xcc, 0x68, 0x0f, 0xfe, 0xc6, 0x28, 0x17, 0x81, 0x37, 0xeb, 0x69, 0x03, 0xb8, 0xf5, 0x17, 0x3f,
0x9e, 0x87, 0xfc, 0x9c, 0x2f, 0x57, 0xc7, 0x80, 0x99, 0xf5, 0xd5, 0xc1, 0x0a, 0xab, 0x05, 0x1a,
0x7a, 0x98, 0xc6, 0x68, 0x38, 0x49, 0xe0, 0x29, 0x8e, 0x42, 0xf5, 0x6e, 0x23, 0x25, 0x0d, 0x91,
0x9f, 0xc5, 0x66, 0x42, 0x58, 0x6e, 0xa7, 0x3b, 0xa9, 0x85, 0x8f, 0x9c, 0x75, 0x27, 0x2a, 0xe9,
0xa2, 0x8b, 0x23, 0x06, 0xd4, 0xbf, 0x2b, 0xd3, 0xd7, 0x01, 0xe3, 0xa4, 0xbc, 0x02, 0x32, 0xcc,
0x52, 0x8d, 0xc4, 0xe1, 0xd0, 0x57, 0x47, 0x45, 0x87, 0xbb, 0x34, 0xc8, 0x65, 0xdb, 0x95, 0x8f,
0x23, 0x8e, 0x5d, 0xf3, 0xbc, 0x02, 0x6d, 0xce, 0x64, 0x25, 0xac, 0x88, 0xa9, 0xb1, 0x84, 0xb1,
0xd0, 0x25, 0xc7, 0xab, 0x66, 0x62, 0x35, 0xc8, 0x25, 0xfb, 0x64, 0x09, 0x2c, 0x5e, 0x80, 0x17,
0x9f, 0x5b, 0xbc, 0x2e, 0x8d, 0xc4, 0x71, 0xcb, 0x62, 0xb4, 0x03, 0x86, 0x37, 0x43, 0xdb, 0x79,
0x5e, 0x8f, 0x9c, 0x14, 0xc3, 0xdd, 0x45, 0x30, 0xd9, 0xa9, 0xa6, 0x1a, 0x52, 0x3f, 0xfc, 0xd7,
0xf1, 0x59, 0xfe, 0xf1, 0x34, 0xff, 0xdc, 0x9a, 0x17, 0x9c, 0x48, 0xe3, 0x12, 0x23, 0x73, 0xe3,
0x46, 0xda, 0x0e, 0xec, 0x3d, 0x8c, 0x3f, 0xc2, 0x9b, 0xec, 0x48, 0x7d, 0xc1, 0x74, 0xd7, 0xdb,
0xb7, 0x4b, 0xfb, 0x98, 0xb8, 0x7d, 0xb9, 0xc2, 0x39, 0x4f, 0x5f, 0x2f, 0xcf, 0xf1, 0xf5, 0x58,
0x36, 0x75, 0x94, 0x7b, 0x79, 0x02, 0x22, 0x80, 0x8a, 0xb4, 0xa4, 0xbb, 0xd6, 0xa4, 0x8d, 0x98,
0x8d, 0x4b, 0x66, 0x28, 0xf9, 0xf9, 0xa1, 0x8f, 0x92, 0x5a, 0x38, 0xa8, 0xec, 0xf5, 0xa1, 0xef,
0x8b, 0x08, 0x82, 0x96, 0xb7, 0x7b, 0x45, 0x5f, 0xa3, 0xd3, 0xc0, 0xed, 0x2b, 0x20, 0xf0, 0x59,
0xbf, 0x26, 0x0a, 0x0e, 0xfc, 0x8d, 0xf0, 0xe5, 0xab, 0xa0, 0xcd, 0x45, 0x0e, 0x5c, 0x06, 0x41,
0xd0, 0xde, 0x16, 0x69, 0xaa, 0x28, 0xf2, 0x75, 0xd5, 0x9c, 0x91, 0x74, 0x3f, 0x5c, 0x2c, 0x5f,
0x8a, 0x95, 0xe3, 0xe7, 0xd0, 0x5b, 0x9d, 0x24, 0x69, 0xa2, 0x24, 0x63, 0x04, 0x74, 0x9f, 0x5e,
0xc3, 0xd6, 0xc7, 0xcd, 0x2d, 0xbe, 0xce, 0x3a, 0x07, 0x89, 0xf3, 0x05, 0x42, 0x08, 0xc5, 0x8e,
0xff, 0x6e, 0x87, 0xa5, 0x1d, 0x99, 0x60, 0x74, 0x1e, 0x83, 0x65, 0x27, 0xc4, 0xd9, 0x25, 0x63,
0x20, 0xcc, 0xeb, 0x21, 0x9c, 0x5d, 0x87, 0xbc, 0x40, 0x76, 0x18, 0x99, 0x07, 0xa1, 0x8c, 0x5e,
0xc4, 0x73, 0x14, 0xf6, 0x1b, 0xd5, 0xc3, 0xa0, 0xfb, 0x64, 0xd2, 0xa1, 0x16, 0x90, 0xe7, 0x1e,
0x87, 0x27, 0x3f, 0xf0, 0xdc, 0x8d, 0x78, 0x06, 0xea, 0x2e, 0xc6, 0x8e, 0xe3, 0x26, 0x53, 0xc7,
0x2b, 0x55, 0x8a, 0x9c, 0x5d, 0xf6, 0xdf, 0xcf, 0x83, 0xa7, 0x97, 0x59, 0x53, 0xe1, 0x66, 0x68,
0x8a, 0xe7, 0x5b, 0x86, 0x29, 0x89, 0xf4, 0x60, 0x08, 0x52, 0x37, 0x92, 0xe2, 0x48, 0xee, 0xd0,
0xa6, 0x26, 0x8f, 0x37, 0x6b, 0x82, 0xd1, 0xd0, 0x6c, 0x33, 0xdd, 0x62, 0xe5, 0x9d, 0xd1, 0x8d,
0x89, 0x6f, 0x09, 0x71, 0x70, 0xe7, 0xdb, 0x73, 0x8f, 0x18, 0x69, 0xe6, 0x1a, 0x31, 0xd2, 0x3d,
0xef, 0x5b, 0xc6, 0xd5, 0x02, 0x7d, 0x31, 0xc8, 0xc4, 0xdf, 0x41, 0xeb, 0x68, 0xb2, 0x4e, 0x47,
0xba, 0xfa, 0x02, 0x96, 0x35, 0x7b, 0x90, 0x78, 0xf9, 0x52, 0x42, 0xfc, 0xa7, 0x40, 0xf7, 0x73,
0xf9, 0x5f, 0x61, 0xb4, 0x9e, 0x04, 0x68, 0x0d, 0x77, 0xff, 0x85, 0xe8, 0x23, 0xcd, 0x88, 0x32,
0x05, 0x9f, 0x04, 0x3c, 0xa6, 0x80, 0xf6, 0x53, 0x30, 0x22, 0xf4, 0x92, 0xb9, 0x0c, 0xce, 0xb6,
0xb8, 0x13, 0x41, 0x04, 0xc1, 0x23, 0x1d, 0xb5, 0x58, 0x52, 0x8b, 0xbd, 0x78, 0x24, 0xc9, 0x01,
0x26, 0x60, 0xac, 0xf0, 0x17, 0x26, 0x27, 0xef, 0x2d, 0x48, 0xb3, 0x0f, 0x2b, 0x2d, 0x5d, 0xe2,
0xb7, 0x98, 0x42, 0x9f, 0x05, 0x46, 0x80, 0x9e, 0x2c, 0x03, 0x4d, 0xce, 0xd8, 0x58, 0x6b, 0xeb,
0x60, 0x05, 0x08, 0x9a, 0xc7, 0x80, 0xd1, 0xb7, 0xbc, 0xcc, 0x16, 0x98, 0xf1, 0x42, 0x04, 0x56,
0x11, 0x39, 0x06, 0x0c, 0xac, 0xa2, 0xf1, 0x58, 0xfa, 0xee, 0xa2, 0xa9, 0x5f, 0x6b, 0x56, 0x5a,
0x90, 0x18, 0xd1, 0x46, 0xe3, 0x8c, 0x31, 0x0b, 0xac, 0x63, 0x73, 0x33, 0x7a, 0x7b, 0x43, 0xa3,
0xe1, 0xb2, 0x55, 0xb0, 0x9a, 0xa3, 0x6d, 0x6e, 0x7e, 0x88, 0x6d, 0x1a, 0x0f, 0xf1, 0x2e, 0x90,
0xf8, 0x8a, 0x67, 0xcb, 0x4c, 0xc8, 0x7d, 0xc1, 0xee, 0x24, 0xc3, 0x4b, 0xb1, 0xb1, 0x4a, 0x82,
0x05, 0x8c, 0x34, 0x08, 0x56, 0x89, 0xc1, 0x13, 0x60, 0xe5, 0x51, 0x11, 0x08, 0x29, 0xb5, 0x1f,
0x3f, 0x4c, 0x60, 0x1d, 0x26, 0x70, 0x09, 0x33, 0x2d, 0x0b, 0x61, 0xe0, 0x3f, 0x85, 0x1f, 0xf8,
0x3f, 0xa8, 0x37, 0xf5, 0x2c, 0x6d, 0x3a, 0x66, 0x1a, 0xac, 0xb6, 0x58, 0xc0, 0x6e, 0x47, 0x1f,
0x49, 0x2f, 0xdd, 0x9d, 0x80, 0xa6, 0x22, 0x00, 0x0e, 0x11, 0xd8, 0x4e, 0xb4, 0x49, 0x80, 0xfb,
0x5e, 0xab, 0xd0, 0x64, 0x2d, 0xc7, 0x2a, 0xd2, 0xfb, 0x93, 0x62, 0x55, 0xdf, 0xab, 0x8d, 0xca,
0x58, 0x44, 0x30, 0x3a, 0x31, 0x45, 0xc5, 0x7b, 0xcd, 0xcc, 0x4a, 0xe5, 0x4c, 0x51, 0xdc, 0xb0,
0xd6, 0x34, 0xf7, 0x4e, 0xe6, 0xf5, 0x95, 0xb0, 0x74, 0x41, 0xcf, 0xff, 0xc1, 0x21, 0xde, 0x6d,
0xc0, 0x76, 0x73, 0xf0, 0x77, 0x32, 0xb8, 0x55, 0xc6, 0xbb, 0xcf, 0x13, 0xef, 0x1e, 0x5d, 0x5c,
0x5f, 0x9c, 0xc1, 0x20, 0xdd, 0xcc, 0x58, 0x7b, 0x01, 0xcd, 0x67, 0xee, 0x29, 0x26, 0x36, 0x4c,
0x11, 0x3b, 0x10, 0xe8, 0x9b, 0xc4, 0xc9, 0x64, 0x5f, 0x49, 0x35, 0xb2, 0x15, 0x7e, 0x3b, 0x4f,
0xcf, 0xcb, 0x63, 0xb3, 0xbc, 0xd0, 0x57, 0xb6, 0x73, 0xfe, 0xcf, 0xbc, 0x58, 0x02, 0x8c, 0x16,
0x0a, 0x8d, 0x46, 0x5f, 0xa1, 0x29, 0xa9, 0x46, 0x1e, 0x53, 0xc4, 0x4a, 0x28, 0x05, 0x00, 0x30,
0xce, 0xc4, 0x2e, 0x58, 0xf6, 0xa7, 0x87, 0xce, 0x7e, 0xe3, 0x57, 0xdf, 0xc1, 0xbb, 0xa5, 0xfa,
0xca, 0x1c, 0x2f, 0xea, 0x42, 0x5e, 0x56, 0x04, 0xbb, 0xf8, 0x17, 0x3f, 0x17, 0xaa, 0x59, 0x91,
0x91, 0x8b, 0x62, 0x74, 0xed, 0x88, 0xaf, 0x70, 0x71, 0xeb, 0x33, 0xdd, 0x9b, 0xc0, 0xfc, 0x88,
0xee, 0x1f, 0x5c, 0x14, 0x1d, 0x9c, 0x3a, 0x27, 0x91, 0x36, 0xb9, 0xaf, 0xf8, 0xde, 0x3b, 0x81,
0x84, 0xf6, 0x6b, 0x39, 0xfa, 0x0e, 0x57, 0x7b, 0x90, 0x60, 0xb7, 0x48, 0x73, 0xd4, 0x3b, 0x9f,
0x64, 0x2d, 0xc3, 0x3a, 0x5d, 0xf3, 0xda, 0x58, 0xda, 0x10, 0xb7, 0xfa, 0xb0, 0x70, 0xe4, 0x08,
0x69, 0x3c, 0x42, 0xcc, 0xdb, 0xb3, 0xfb, 0xe8, 0xd6, 0x21, 0x80, 0x35, 0xf1, 0x2e, 0x1d, 0xe2,
0x5d, 0x7b, 0x8a, 0x60, 0x92, 0x91, 0xbd, 0x36, 0x97, 0x85, 0x06, 0x8c, 0x70, 0x3b, 0x3b, 0xd8,
0x2d, 0xc2, 0x63, 0x41, 0xfe, 0x31, 0xf1, 0x50, 0xa3, 0x0d, 0x3c, 0x5b, 0x4f, 0x87, 0xc5, 0x0e,
0x7c, 0xf2, 0x73, 0xef, 0x1a, 0x1f, 0x7e, 0x36, 0x8f, 0x94, 0x9b, 0xcd, 0xf1, 0xe0, 0x4f, 0x64,
0x1c, 0xa2, 0xe0, 0x5d, 0xc7, 0xb2, 0xee, 0x6a, 0x49, 0xda, 0xd3, 0xf8, 0x69, 0xfd, 0x99, 0x55,
0x8b, 0x5c, 0xb2, 0x0c, 0x1d, 0xab, 0x85, 0x7b, 0x39, 0xc7, 0x68, 0xc6, 0x55, 0xe7, 0xaf, 0x23,
0x97, 0x57, 0x44, 0x11, 0x8a, 0x9b, 0xb0, 0x6b, 0x8e, 0x5f, 0x4f, 0x76, 0x2c, 0x14, 0x96, 0xa0,
0x8d, 0xc6, 0x62, 0x84, 0xff, 0x86, 0x0b, 0xf3, 0x2f, 0xf8, 0x2e, 0x4d, 0x40, 0x54, 0x04, 0x9f,
0xcb, 0x97, 0x22, 0x84, 0x91, 0x62, 0xa6, 0x2d, 0x80, 0xf1, 0xdd, 0x4c, 0x0f, 0x76, 0xe2, 0x37,
0x9f, 0x2c, 0x0d, 0x3e, 0x95, 0xc5, 0x9b, 0x6f, 0x05, 0x50, 0xe3, 0x6a, 0x6e, 0x32, 0x7a, 0x11,
0xd3, 0xd2, 0x21, 0xdf, 0x30, 0x93, 0xa1, 0x2f, 0x5c, 0xc0, 0xed, 0xbc, 0xc5, 0xdd, 0x02, 0xeb,
0x95, 0x8f, 0xc0, 0xc0, 0xc5, 0x23, 0x33, 0x5e, 0x94, 0x50, 0xdc, 0xc5, 0x11, 0x04, 0xec, 0xe3,
0x7a, 0x73, 0xf9, 0x95, 0xfb, 0xef, 0x8b, 0x40, 0x4d, 0x0d, 0x03, 0x4b, 0xbe, 0xfb, 0xf7, 0x75,
0xd5, 0xb5, 0xc5, 0xc9, 0x20, 0xf3, 0x87, 0x86, 0x97, 0x31, 0x39, 0x2b, 0xc3, 0x32, 0x69, 0xe4,
0x98, 0x17, 0x54, 0x71, 0x03, 0xbd, 0x05, 0xfd, 0xee, 0x16, 0x5f, 0xba, 0xdd, 0x92, 0xf0, 0x66,
0x96, 0xba, 0x13, 0xeb, 0xaf, 0x11, 0x84, 0x69, 0x78, 0x9d, 0x8e, 0x96, 0xe7, 0xb7, 0xb7, 0xb2,
0xd4, 0xc9, 0xc5, 0xf6, 0xf6, 0xe7, 0xd1, 0xdb, 0x57, 0x0c, 0x16, 0xef, 0xb4, 0xca, 0xc4, 0xaf,
0xfb, 0x91, 0x66, 0x18, 0x26, 0xaa, 0x99, 0x43, 0x27, 0x8a, 0x35, 0x5f, 0x3f, 0xa2, 0x87, 0x7d,
0xd3, 0xf4, 0x1d, 0x17, 0x49, 0xbc, 0x5c, 0x0f, 0x81, 0xe0, 0xec, 0xd0, 0xcb, 0x7b, 0x92, 0xa8,
0x55, 0xb1, 0x27, 0x76, 0xab, 0x12, 0x17, 0x8e, 0x03, 0x1f, 0xb2, 0x57, 0x63, 0x2c, 0xd4, 0xec,
0x25, 0xe3, 0x54, 0xd6, 0x87, 0x76, 0x72, 0xe5, 0x9e, 0xd6, 0x72, 0x4e, 0xd8, 0x5e, 0x64, 0xb9,
0x73, 0x16, 0xfc, 0xf9, 0x5f, 0xad, 0x15, 0x77, 0xdc, 0x2e, 0xde, 0x4f, 0xcc, 0x0b, 0xe7, 0x8d,
0x2c, 0x0d, 0x23, 0x18, 0x0b, 0x1a, 0xed, 0x4b, 0x43, 0x14, 0x26, 0x22, 0x73, 0xb0, 0x33, 0xff,
0x45, 0x87, 0x5e, 0x26, 0x8e, 0xee, 0xa6, 0x08, 0x3f, 0x1e, 0x1a, 0x6c, 0x8f, 0x22, 0x88, 0x8e,
0x48, 0xb3, 0xc3, 0x3a, 0xea, 0x0d, 0xbd, 0xa9, 0xc8, 0xd9, 0x89, 0x27, 0xfc, 0x10, 0x7f, 0xd6,
0x42, 0xc7, 0x41, 0xfa, 0x92, 0x73, 0x64, 0x9b, 0x34, 0x22, 0x8e, 0x9e, 0x5c, 0x8f, 0xc4, 0x1a,
0x91, 0xa5, 0xcd, 0x58, 0x16, 0xb9, 0xb1, 0x38, 0x8e, 0x09, 0x75, 0x01, 0x9d, 0xde, 0xfe, 0xb4,
0x38, 0xf7, 0x37, 0xaa, 0xc3, 0x6c, 0x59, 0x79, 0x49, 0x86, 0x42, 0x06, 0xbd, 0xa8, 0xb6, 0xc0,
0x4e, 0xa4, 0x67, 0x5d, 0x23, 0xa1, 0x6e, 0xfe, 0xdd, 0xd3, 0x41, 0x11, 0x98, 0x65, 0x0f, 0x53,
0x4e, 0x32, 0x78, 0x5f, 0x1c, 0xff, 0xfb, 0x77, 0xb8, 0xeb, 0x6e, 0xec, 0xb7, 0x89, 0x77, 0xf3,
0x01, 0x02, 0x7d, 0xf4, 0x00, 0x34, 0x54, 0xde, 0x1f, 0x02, 0x64, 0x7a, 0x21, 0x69, 0xa1, 0x57,
0x41, 0xf3, 0x3f, 0x18, 0xe6, 0x7f, 0x7a, 0x0e, 0xa3, 0x1b, 0xd3, 0x12, 0xfe, 0xab, 0xb5, 0x2a,
0x8e, 0xce, 0xf3, 0x1a, 0x7d, 0x49, 0xfa, 0xf3, 0x21, 0xf2, 0x61, 0x92, 0xa2, 0x66, 0x31, 0xb3,
0x39, 0x59, 0x01, 0x7f, 0xf4, 0x4b, 0x9d, 0x31, 0x1b, 0xee, 0xd6, 0x44, 0xf4, 0x2f, 0xfb, 0xa1,
0x2c, 0xca, 0xc4, 0xcb, 0x7d, 0x52, 0x49, 0xe7, 0x5f, 0x66, 0x66, 0xbc, 0x38, 0x7f, 0x99, 0xe3,
0xeb, 0xe2, 0x97, 0x06, 0xde, 0x24, 0xc5, 0xfa, 0xb8, 0x2d, 0x02, 0x33, 0xfc, 0x2e, 0xf2, 0x41,
0xc2, 0x77, 0x4c, 0xd8, 0xc6, 0x04, 0x63, 0x3b, 0x9d, 0xcd, 0x6d, 0x6e, 0x7e, 0x6a, 0x7c, 0x8d,
0x86, 0x87, 0x0e, 0x80, 0x03, 0x43, 0x5d, 0xbe, 0xed, 0x9b, 0xdb, 0xda, 0xd2, 0x38, 0x8f, 0x22,
0xb7, 0x1a, 0x0e, 0xf6, 0x2d, 0xbb, 0x65, 0xf8, 0x81, 0x4e, 0x12, 0x2e, 0xc2, 0x17, 0xc7, 0xeb,
0x02, 0x08, 0x94, 0x75, 0x30, 0xba, 0x78, 0x28, 0xf4, 0xb3, 0xc8, 0xc4, 0xbd, 0x35, 0x8f, 0xf4,
0x43, 0x8c, 0x82, 0x5d, 0x0d, 0x16, 0xd6, 0x34, 0x5c, 0xd3, 0xe2, 0x7c, 0x54, 0x1e, 0x12, 0x0c,
0x04, 0xae, 0x07, 0xf7, 0x34, 0xe3, 0xe9, 0xdb, 0x14, 0x17, 0xbd, 0xbb, 0xd9, 0x82, 0xb4, 0xad,
0x64, 0x4c, 0x6d, 0xf5, 0x5f, 0xd5, 0x1e, 0xd3, 0x5b, 0x77, 0xc4, 0x5a, 0x2c, 0x9b, 0xb5, 0x03,
0x38, 0xe3, 0x63, 0x70, 0xe9, 0x1d, 0xa6, 0x78, 0x59, 0x74, 0x98, 0x02, 0x82, 0xeb, 0x09, 0x89,
0x90, 0x4e, 0xa7, 0x5d, 0xa6, 0x2b, 0xf9, 0xce, 0x14, 0x5f, 0x41, 0x20, 0x75, 0xc6, 0x1b, 0xe8,
0xb5, 0x8e, 0xde, 0x31, 0x06, 0x28, 0xe7, 0x5f, 0x5f, 0xbe, 0xb9, 0xc9, 0x9e, 0x4c, 0x7e, 0x71,
0xd0, 0x3a, 0x81, 0x07, 0x7d, 0x12, 0x26, 0xef, 0xe5, 0xc0, 0x6a, 0xe4, 0x77, 0xe8, 0xa2, 0xfc,
0xfd, 0x3b, 0x2a, 0xe9, 0x88, 0x30, 0x83, 0xd4, 0xda, 0x0c, 0x15, 0xe0, 0x50, 0x6f, 0x20, 0x4d,
0xa0, 0xb5, 0xf8, 0xda, 0xca, 0xf2, 0x98, 0x37, 0x0f, 0x34, 0xb7, 0xa5, 0x61, 0xcc, 0xbd, 0x0b,
0xda, 0x3c, 0x95, 0x6c, 0xf9, 0x7a, 0x34, 0x50, 0xca, 0xa0, 0x04, 0x47, 0x2f, 0x72, 0xa3, 0x37,
0xba, 0xc1, 0x9c, 0x2f, 0x97, 0x1a, 0x98, 0x78, 0x9a, 0x14, 0x9d, 0xb3, 0x94, 0xa3, 0xad, 0x2f,
0x18, 0xbe, 0x9b, 0xed, 0xfd, 0x92, 0x14, 0x24, 0x08, 0x6f, 0x4e, 0xc0, 0xd5, 0xf7, 0x41, 0xb9,
0xa1, 0xf5, 0x51, 0xb1, 0xc5, 0x35, 0x6f, 0x7e, 0xb9, 0x8d, 0xef, 0x19, 0xef, 0x85, 0x16, 0x89,
0xef, 0x19, 0xdc, 0x5b, 0xc5, 0xef, 0xbe, 0x3b, 0xd0, 0xb7, 0x13, 0xff, 0x17, 0xbd, 0xc2, 0x8a,
0x1a, 0x03, 0xc5, 0x00, 0x00
};

File diff suppressed because it is too large Load Diff

View File

@ -210,7 +210,7 @@ void sendImprovInfoResponse() {
//Use serverDescription if it has been changed from the default "WLED", else mDNS name
bool useMdnsName = (strcmp(serverDescription, "WLED") == 0 && strlen(cmDNS) > 0);
char vString[20];
sprintf_P(vString, PSTR("0.14.0-b3/%i"), VERSION);
sprintf_P(vString, PSTR("0.14.0-b4/%i"), VERSION);
const char *str[4] = {"WLED", vString, bString, useMdnsName ? cmDNS : serverDescription};
sendImprovRPCResult(ImprovRPCType::Request_Info, 4, str);

View File

@ -29,8 +29,11 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
id = strip.getSegmentsNum()-1; // segments are added at the end of list
}
//DEBUG_PRINTLN("-- JSON deserialize segment.");
Segment& seg = strip.getSegment(id);
//DEBUG_PRINTF("-- Original segment: %p\n", &seg);
Segment prev = seg; //make a backup so we can tell if something changed
//DEBUG_PRINTF("-- Duplicate segment: %p\n", &prev);
uint16_t start = elem["start"] | seg.start;
if (stop < 0) {
@ -347,7 +350,9 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
JsonObject udpn = root["udpn"];
notifyDirect = udpn["send"] | notifyDirect;
syncGroups = udpn["sgrp"] | syncGroups;
receiveNotifications = udpn["recv"] | receiveNotifications;
receiveGroups = udpn["rgrp"] | receiveGroups;
if ((bool)udpn[F("nn")]) callMode = CALL_MODE_NO_NOTIFY; //send no notification just for this request
unsigned long timein = root["time"] | UINT32_MAX; //backup time source if NTP not synced
@ -564,6 +569,8 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
JsonObject udpn = root.createNestedObject("udpn");
udpn["send"] = notifyDirect;
udpn["recv"] = receiveNotifications;
udpn["sgrp"] = syncGroups;
udpn["rgrp"] = receiveGroups;
root[F("lor")] = realtimeOverride;
}
@ -735,6 +742,10 @@ void serializeInfo(JsonObject root)
#endif
root[F("uptime")] = millis()/1000 + rolloverMillis*4294967;
char time[32];
getTimeString(time);
root[F("time")] = time;
usermods.addToJsonInfo(root);
uint16_t os = 0;
@ -971,9 +982,10 @@ void serializeNodes(JsonObject root)
// deserializes mode data string into JsonArray
void serializeModeData(JsonArray fxdata)
{
char lineBuffer[128];
char lineBuffer[256];
for (size_t i = 0; i < strip.getModeCount(); i++) {
strncpy_P(lineBuffer, strip.getModeData(i), 127);
strncpy_P(lineBuffer, strip.getModeData(i), sizeof(lineBuffer)/sizeof(char)-1);
lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string
if (lineBuffer[0] != 0) {
char* dataPtr = strchr(lineBuffer,'@');
if (dataPtr) fxdata.add(dataPtr+1);
@ -984,10 +996,12 @@ void serializeModeData(JsonArray fxdata)
// deserializes mode names string into JsonArray
// also removes effect data extensions (@...) from deserialised names
void serializeModeNames(JsonArray arr) {
char lineBuffer[128];
void serializeModeNames(JsonArray arr)
{
char lineBuffer[256];
for (size_t i = 0; i < strip.getModeCount(); i++) {
strncpy_P(lineBuffer, strip.getModeData(i), 127);
strncpy_P(lineBuffer, strip.getModeData(i), sizeof(lineBuffer)/sizeof(char)-1);
lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string
if (lineBuffer[0] != 0) {
char* dataPtr = strchr(lineBuffer,'@');
if (dataPtr) *dataPtr = 0; // terminate mode data after name

View File

@ -37,12 +37,12 @@ void applyValuesToSelectedSegs()
if (effectSpeed != selsegPrev.speed) {seg.speed = effectSpeed; stateChanged = true;}
if (effectIntensity != selsegPrev.intensity) {seg.intensity = effectIntensity; stateChanged = true;}
if (effectPalette != selsegPrev.palette) {seg.setPalette(effectPalette); stateChanged = true;}
if (effectCurrent != selsegPrev.mode) {seg.setMode(effectCurrent); stateChanged = true;}
if (effectPalette != selsegPrev.palette) {seg.setPalette(effectPalette);}
if (effectCurrent != selsegPrev.mode) {seg.setMode(effectCurrent);}
uint32_t col0 = RGBW32( col[0], col[1], col[2], col[3]);
uint32_t col1 = RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]);
if (col0 != selsegPrev.colors[0]) {seg.setColor(0, col0); stateChanged = true;}
if (col1 != selsegPrev.colors[1]) {seg.setColor(1, col1); stateChanged = true;}
if (col0 != selsegPrev.colors[0]) {seg.setColor(0, col0);}
if (col1 != selsegPrev.colors[1]) {seg.setColor(1, col1);}
}
}
@ -161,6 +161,8 @@ void stateUpdated(byte callMode) {
void updateInterfaces(uint8_t callMode)
{
if (!interfaceUpdateCallMode || millis() - lastInterfaceUpdate < INTERFACE_UPDATE_COOLDOWN) return;
sendDataWs();
lastInterfaceUpdate = millis();
if (callMode == CALL_MODE_WS_SEND) return;
@ -179,7 +181,7 @@ void updateInterfaces(uint8_t callMode)
void handleTransitions()
{
//handle still pending interface update
if (interfaceUpdateCallMode && millis() - lastInterfaceUpdate > INTERFACE_UPDATE_COOLDOWN) updateInterfaces(interfaceUpdateCallMode);
updateInterfaces(interfaceUpdateCallMode);
#ifndef WLED_DISABLE_MQTT
if (doPublishMqtt) publishMqtt();
#endif

View File

@ -112,7 +112,7 @@ int16_t loadPlaylist(JsonObject playlistObj, byte presetId) {
if (playlistEndPreset == 255 && currentPreset > 0) playlistEndPreset = currentPreset;
if (playlistEndPreset > 250) playlistEndPreset = 0;
shuffle = shuffle || playlistObj["r"];
if (shuffle) playlistOptions += PL_OPTION_SHUFFLE;
if (shuffle) playlistOptions |= PL_OPTION_SHUFFLE;
currentPlaylist = presetId;
DEBUG_PRINTLN(F("Playlist loaded."));
@ -156,7 +156,7 @@ void serializePlaylist(JsonObject sObj) {
JsonArray ps = playlist.createNestedArray("ps");
JsonArray dur = playlist.createNestedArray("dur");
JsonArray transition = playlist.createNestedArray(F("transition"));
playlist[F("repeat")] = (playlistIndex < 0) ? playlistRepeat - 1 : playlistRepeat; // remove added repetition count (if not yet running)
playlist[F("repeat")] = (playlistIndex < 0 && playlistRepeat > 0) ? playlistRepeat - 1 : playlistRepeat; // remove added repetition count (if not yet running)
playlist["end"] = playlistEndPreset;
playlist["r"] = playlistOptions & PL_OPTION_SHUFFLE;
for (int i=0; i<playlistLen; i++) {

View File

@ -39,7 +39,7 @@ typedef struct message_structure {
} message_structure;
static int esp_now_state = ESP_NOW_STATE_UNINIT;
static uint32_t last_seq = -1;
static uint32_t last_seq = UINT32_MAX;
static int brightnessBeforeNightMode = NIGHT_MODE_DEACTIVATED;
static message_structure incoming;
@ -168,7 +168,7 @@ void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
void handleRemote() {
if (enable_espnow_remote) {
if (esp_now_state == ESP_NOW_STATE_UNINIT) {
if ((esp_now_state == ESP_NOW_STATE_UNINIT) && (interfacesInited || apActive)) { // ESPNOW requires Wifi to be initialized (either STA, or AP Mode)
DEBUG_PRINTLN(F("Initializing ESP_NOW listener"));
// Init ESP-NOW
if (esp_now_init() != 0) {
@ -197,4 +197,4 @@ void handleRemote() {
}
}
#endif
#endif

View File

@ -347,14 +347,14 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
#ifdef WLED_ENABLE_MQTT
mqttEnabled = request->hasArg(F("MQ"));
strlcpy(mqttServer, request->arg(F("MS")).c_str(), 33);
strlcpy(mqttServer, request->arg(F("MS")).c_str(), MQTT_MAX_SERVER_LEN+1);
t = request->arg(F("MQPORT")).toInt();
if (t > 0) mqttPort = t;
strlcpy(mqttUser, request->arg(F("MQUSER")).c_str(), 41);
if (!isAsterisksOnly(request->arg(F("MQPASS")).c_str(), 41)) strlcpy(mqttPass, request->arg(F("MQPASS")).c_str(), 65);
strlcpy(mqttClientID, request->arg(F("MQCID")).c_str(), 41);
strlcpy(mqttDeviceTopic, request->arg(F("MD")).c_str(), 33);
strlcpy(mqttGroupTopic, request->arg(F("MG")).c_str(), 33);
strlcpy(mqttDeviceTopic, request->arg(F("MD")).c_str(), MQTT_MAX_TOPIC_LEN+1);
strlcpy(mqttGroupTopic, request->arg(F("MG")).c_str(), MQTT_MAX_TOPIC_LEN+1);
buttonPublishMqtt = request->hasArg(F("BM"));
retainMqttMsg = request->hasArg(F("RT"));
#endif

View File

@ -177,6 +177,10 @@
#include "../usermods/boblight/boblight.h"
#endif
#ifdef USERMOD_INTERNAL_TEMPERATURE
#include "../usermods/Internal_Temperature_v2/usermod_internal_temperature.h"
#endif
#if defined(WLED_USE_SD_MMC) || defined(WLED_USE_SD_SPI)
// This include of SD.h and SD_MMC.h must happen here, else they won't be
// resolved correctly (when included in mod's header only)
@ -365,4 +369,8 @@ void registerUsermods()
#ifdef USERMOD_SHT
usermods.add(new ShtUsermod());
#endif
#ifdef USERMOD_INTERNAL_TEMPERATURE
usermods.add(new InternalTemperatureUsermod());
#endif
}

View File

@ -148,8 +148,14 @@ bool oappendi(int i)
bool oappend(const char* txt)
{
uint16_t len = strlen(txt);
if (olen + len >= SETTINGS_STACK_BUF_SIZE)
if (olen + len >= SETTINGS_STACK_BUF_SIZE) {
#ifdef WLED_DEBUG
DEBUG_PRINT(F("oappend() buffer overflow. Cannnot append "));
DEBUG_PRINT(len); DEBUG_PRINT(F(" bytes \t\""));
DEBUG_PRINT(txt); DEBUG_PRINTLN(F("\""));
#endif
return false; // buffer full
}
strcpy(obuf + olen, txt);
olen += len;
return true;
@ -233,7 +239,8 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe
if (mode < strip.getModeCount()) {
char lineBuffer[256];
//strcpy_P(lineBuffer, (const char*)pgm_read_dword(&(WS2812FX::_modeData[mode])));
strcpy_P(lineBuffer, strip.getModeData(mode));
strncpy_P(lineBuffer, strip.getModeData(mode), sizeof(lineBuffer)/sizeof(char)-1);
lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string
size_t len = strlen(lineBuffer);
size_t j = 0;
for (; j < maxLen && j < len; j++) {
@ -245,6 +252,12 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe
} else return 0;
}
if (src == JSON_palette_names && mode > GRADIENT_PALETTE_COUNT) {
snprintf_P(dest, maxLen, PSTR("~ Custom %d~"), 255-mode);
dest[maxLen-1] = '\0';
return strlen(dest);
}
uint8_t qComma = 0;
bool insideQuotes = false;
uint8_t printedChars = 0;
@ -355,9 +368,9 @@ uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxL
int16_t extractModeDefaults(uint8_t mode, const char *segVar)
{
if (mode < strip.getModeCount()) {
char lineBuffer[128] = "";
strncpy_P(lineBuffer, strip.getModeData(mode), 127);
lineBuffer[127] = '\0'; // terminate string
char lineBuffer[256];
strncpy_P(lineBuffer, strip.getModeData(mode), sizeof(lineBuffer)/sizeof(char)-1);
lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string
if (lineBuffer[0] != 0) {
char* startPtr = strrchr(lineBuffer, ';'); // last ";" in FX data
if (!startPtr) return -1;
@ -559,4 +572,4 @@ void enumerateLedmaps() {
}
}
}
}

View File

@ -195,12 +195,15 @@ void WLED::loop()
#if WLED_WATCHDOG_TIMEOUT > 0
// we finished our mainloop, reset the watchdog timer
if (!strip.isUpdating())
static unsigned long lastWDTFeed = 0;
if (!strip.isUpdating() || millis() - lastWDTFeed > (WLED_WATCHDOG_TIMEOUT*500)) {
#ifdef ARDUINO_ARCH_ESP32
esp_task_wdt_reset();
#else
ESP.wdtFeed();
#endif
lastWDTFeed = millis();
}
#endif
if (doReboot && (!doInitBusses || !doSerializeConfig)) // if busses have to be inited & saved, wait until next iteration

View File

@ -3,12 +3,12 @@
/*
Main sketch, global variable declarations
@title WLED project sketch
@version 0.14.0-b3
@version 0.14.0-b4
@author Christian Schwinne
*/
// version code in format yymmddb (b = daily build)
#define VERSION 2307180
#define VERSION 2309050
//uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG
@ -429,12 +429,18 @@ WLED_GLOBAL uint16_t pollReplyCount _INIT(0); // count numbe
// mqtt
WLED_GLOBAL unsigned long lastMqttReconnectAttempt _INIT(0); // used for other periodic tasks too
#ifndef WLED_DISABLE_MQTT
#ifndef MQTT_MAX_TOPIC_LEN
#define MQTT_MAX_TOPIC_LEN 32
#endif
#ifndef MQTT_MAX_SERVER_LEN
#define MQTT_MAX_SERVER_LEN 32
#endif
WLED_GLOBAL AsyncMqttClient *mqtt _INIT(NULL);
WLED_GLOBAL bool mqttEnabled _INIT(false);
WLED_GLOBAL char mqttStatusTopic[40] _INIT(""); // this must be global because of async handlers
WLED_GLOBAL char mqttDeviceTopic[33] _INIT(""); // main MQTT topic (individual per device, default is wled/mac)
WLED_GLOBAL char mqttGroupTopic[33] _INIT("wled/all"); // second MQTT topic (for example to group devices)
WLED_GLOBAL char mqttServer[33] _INIT(""); // both domains and IPs should work (no SSL)
WLED_GLOBAL char mqttDeviceTopic[MQTT_MAX_TOPIC_LEN+1] _INIT(""); // main MQTT topic (individual per device, default is wled/mac)
WLED_GLOBAL char mqttGroupTopic[MQTT_MAX_TOPIC_LEN+1] _INIT("wled/all"); // second MQTT topic (for example to group devices)
WLED_GLOBAL char mqttServer[MQTT_MAX_SERVER_LEN+1] _INIT(""); // both domains and IPs should work (no SSL)
WLED_GLOBAL char mqttUser[41] _INIT(""); // optional: username for MQTT auth
WLED_GLOBAL char mqttPass[65] _INIT(""); // optional: password for MQTT auth
WLED_GLOBAL char mqttClientID[41] _INIT(""); // override the client ID

View File

@ -478,6 +478,7 @@ void getSettingsJS(byte subPage, char* dest)
if (subPage == SUBPAGE_SYNC)
{
char nS[32];
sappend('v',SET_F("UP"),udpPort);
sappend('v',SET_F("U2"),udpPort2);
sappend('v',SET_F("GS"),syncGroups);
@ -534,6 +535,9 @@ void getSettingsJS(byte subPage, char* dest)
sappends('s',SET_F("MG"),mqttGroupTopic);
sappend('c',SET_F("BM"),buttonPublishMqtt);
sappend('c',SET_F("RT"),retainMqttMsg);
oappend(SET_F("d.Sf.MD.maxlength=")); oappend(itoa(MQTT_MAX_TOPIC_LEN,nS,10)); oappend(SET_F(";"));
oappend(SET_F("d.Sf.MG.maxlength=")); oappend(itoa(MQTT_MAX_TOPIC_LEN,nS,10)); oappend(SET_F(";"));
oappend(SET_F("d.Sf.MS.maxlength=")); oappend(itoa(MQTT_MAX_SERVER_LEN,nS,10)); oappend(SET_F(";"));
#else
oappend(SET_F("toggle('MQTT');")); // hide MQTT settings
#endif