diff --git a/LICENSE b/LICENSE index 5ad2e15..4e37bde 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2025 oomer +Copyright (c) 2025 Harvey Fong Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4eb85f1 --- /dev/null +++ b/Makefile @@ -0,0 +1,152 @@ +# Project configuration +EXECUTABLE_NAME = poomer-discord +PLATFORM = $(shell uname) +BUILD_TYPE ?= release# Default to release build if not specified + +# Version configuration (can be overridden) +DPP_VERSION ?= $(shell find ../DPP/build/library -name "libdpp.so.*.*.*" -type f | head -1 | sed 's/.*libdpp\.so\.//') +DPP_VERSION := $(or $(DPP_VERSION),10.1.4)# Fallback version if auto-detection fails + +# Common paths +DPP_PATH = ../DPP +OBJ_DIR = obj/$(PLATFORM)/$(BUILD_TYPE) +BIN_DIR = bin/$(PLATFORM)/$(BUILD_TYPE) +OUTPUT_FILE = $(BIN_DIR)/$(EXECUTABLE_NAME) + +# Platform-specific configuration +ifeq ($(PLATFORM), Darwin) + # macOS configuration + SDK_LIB_EXT = dylib + DPP_LIB_NAME = libdpp.$(SDK_LIB_EXT) + MACOS_SDK_PATH = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk + + # Compiler settings + CC = clang + CXX = clang++ + + # Architecture flags + ARCH_FLAGS = -arch arm64 -mmacosx-version-min=11.0 -isysroot $(MACOS_SDK_PATH) + + # Linking flags - Use multiple rpath entries to look in executable directory + LINKER_FLAGS = $(ARCH_FLAGS) -framework Cocoa -framework IOKit -fvisibility=hidden -O5 \ + -rpath @executable_path \ + -rpath . +else + # Linux configuration + SDK_LIB_EXT = so + DPP_LIB_NAME = libdpp.$(SDK_LIB_EXT) + + # Compiler settings + CC = gcc + CXX = g++ + + # Architecture flags + ARCH_FLAGS = -m64 -D_FILE_OFFSET_BITS=64 + + # Linking flags + LINKER_FLAGS = $(ARCH_FLAGS) -fvisibility=hidden -O3 -Wl,-rpath,'$$ORIGIN' -Wl,-rpath,'$$ORIGIN/lib' +endif + +# Common include and library paths +INCLUDE_PATHS = -I$(DPP_PATH)/include +DPP_BUILD_DIR = $(DPP_PATH)/build/library + +# Platform-specific versioned library filename +ifeq ($(PLATFORM), Darwin) + DPP_VERSIONED_FILE = libdpp.$(DPP_VERSION).$(SDK_LIB_EXT) +else + DPP_VERSIONED_FILE = libdpp.$(SDK_LIB_EXT).$(DPP_VERSION) +endif + +# Library flags +LIB_PATHS = -L$(DPP_BUILD_DIR) +LIBRARIES = -ldpp + +# Build type specific flags +ifeq ($(BUILD_TYPE), debug) + CPP_DEFINES = -D_DEBUG -DDL_USE_SHARED + COMMON_FLAGS = $(ARCH_FLAGS) -fvisibility=hidden -g -O0 $(INCLUDE_PATHS) +else + CPP_DEFINES = -DNDEBUG=1 -DDL_USE_SHARED + COMMON_FLAGS = $(ARCH_FLAGS) -fvisibility=hidden -O3 $(INCLUDE_PATHS) +endif + +# Language-specific flags +C_FLAGS = $(COMMON_FLAGS) -std=c17 +CXX_FLAGS = $(COMMON_FLAGS) -std=c++17 -Wno-deprecated-declarations + +# Objects +OBJECTS = $(EXECUTABLE_NAME).o +OBJECT_FILES = $(patsubst %,$(OBJ_DIR)/%,$(OBJECTS)) + +# Build rules +$(OBJ_DIR)/$(EXECUTABLE_NAME).o: $(EXECUTABLE_NAME).cpp + @mkdir -p $(@D) + $(CXX) -c -o $@ $< $(CXX_FLAGS) $(CPP_DEFINES) + +$(OUTPUT_FILE): $(OBJECT_FILES) + @mkdir -p $(@D) + $(CXX) -o $@ $(OBJECT_FILES) $(LINKER_FLAGS) $(LIB_PATHS) $(LIBRARIES) + @echo "Copying libraries to $(BIN_DIR)..." +ifeq ($(PLATFORM), Darwin) + @# macOS: Handle versioned libraries (same as Linux) + @if [ -f $(DPP_BUILD_DIR)/$(DPP_VERSIONED_FILE) ]; then \ + cp $(DPP_BUILD_DIR)/$(DPP_VERSIONED_FILE) $(BIN_DIR)/$(DPP_VERSIONED_FILE); \ + ln -sf $(DPP_VERSIONED_FILE) $(BIN_DIR)/$(DPP_LIB_NAME); \ + else \ + cp $(DPP_BUILD_DIR)/$(DPP_LIB_NAME) $(BIN_DIR)/$(DPP_LIB_NAME); \ + fi +else + @# Linux: Handle versioned libraries (follow standard convention) + @if [ -f $(DPP_BUILD_DIR)/$(DPP_VERSIONED_FILE) ]; then \ + cp $(DPP_BUILD_DIR)/$(DPP_VERSIONED_FILE) $(BIN_DIR)/$(DPP_VERSIONED_FILE); \ + ln -sf $(DPP_VERSIONED_FILE) $(BIN_DIR)/$(DPP_LIB_NAME); \ + else \ + cp $(DPP_BUILD_DIR)/$(DPP_LIB_NAME) $(BIN_DIR)/$(DPP_LIB_NAME); \ + fi +endif + @echo "Build complete: $(OUTPUT_FILE)" + +# Boilerplate for makefile +.PHONY: clean cleanall all info + +# Add default target +all: $(OUTPUT_FILE) + +info: + @echo "Build Configuration:" + @echo " Platform: $(PLATFORM)" + @echo " Build Type: $(BUILD_TYPE)" + @echo " DPP Version (detected): $(DPP_VERSION)" + @echo " DPP Versioned File: $(DPP_VERSIONED_FILE)" + @echo " SDK Library Extension: $(SDK_LIB_EXT)" + @echo " Output File: $(OUTPUT_FILE)" + +clean: + rm -f $(OBJ_DIR)/$(EXECUTABLE_NAME).o + rm -f $(OUTPUT_FILE) + rm -f $(BIN_DIR)/$(DPP_LIB_NAME) + rm -f $(BIN_DIR)/$(DPP_VERSIONED_FILE) + rm -f $(BIN_DIR)/*.$(SDK_LIB_EXT) + rmdir $(OBJ_DIR) 2>/dev/null || true + rmdir $(BIN_DIR) 2>/dev/null || true + +cleanall: + rm -f obj/*/release/*.o + rm -f obj/*/debug/*.o + rm -f bin/*/release/$(EXECUTABLE_NAME) + rm -f bin/*/debug/$(EXECUTABLE_NAME) + rm -f bin/*/release/$(DPP_LIB_NAME) + rm -f bin/*/debug/$(DPP_LIB_NAME) + rm -f bin/*/release/$(DPP_VERSIONED_FILE) + rm -f bin/*/debug/$(DPP_VERSIONED_FILE) + rm -f bin/*/release/*.$(SDK_LIB_EXT) + rm -f bin/*/debug/*.$(SDK_LIB_EXT) + rmdir obj/*/release 2>/dev/null || true + rmdir obj/*/debug 2>/dev/null || true + rmdir bin/*/release 2>/dev/null || true + rmdir bin/*/debug 2>/dev/null || true + rmdir obj/* 2>/dev/null || true + rmdir bin/* 2>/dev/null || true + rmdir obj 2>/dev/null || true + rmdir bin 2>/dev/null || true \ No newline at end of file diff --git a/README.md b/README.md index 952b359..c5f6e92 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,67 @@ # poomer-discord +### Overview + +Prototype C++ Discord bot, that supports 2 features +- listens for /oomer command and replies, then 5 seconds later it sends a jpeg +- When one or more jpegs are dropped into a channel, it will read the jpeg, flip it vertically and send it back to the channel +- In both cases it will use @{user} in the reponse to notify the submitter + +---- +### Technical details +- Uses DPP c++ lib for Discord comms + - depends on openssl and zlib +- non blocking multi-threaded, so it can listen for other users sending /oomer command while spawning one or more threads to deal with jpeg replies +- jpeg image is stored as byte data in embedded_image.h to avoid an on disk jpeg. + + +## Build + +### Ubuntu 24.04 LTS + +``` +sudo apt update +sudo apt upgrade -y +sudo apt install build-essential curl git cmake zlib1g-dev libssl-dev -y +mkdir workdir +cd workdir +git clone https://github.com/brainboxdotcc/DPP.git +cd DPP +cmake -B ./build +cmake --build ./build -j8 +cd .. +git clone https://git.indoodle.com/oomer/poomer-discord.git +cd poomer-discord +curl -LO https://raw.githubusercontent.com/nothings/stb/master/stb_image.h +curl -LO https://raw.githubusercontent.com/nothings/stb/master/stb_image_write.h +make all -j4 +``` + + +### MacOS + +``` +mkdir workdir +cd workdir +mkdir homebrew +curl -L https://github.com/Homebrew/brew/tarball/master | tar xz --strip-components 1 -C homebrew +eval "$(homebrew/bin/brew shellenv)" +brew update --force --quiet +brew install openssl +git clone https://github.com/brainboxdotcc/DPP.git +cd DPP +/Applications/CMake.app/Contents/bin/cmake -B ./build \ + -DOPENSSL_ROOT_DIR=../homebrew/opt/openssl@3 \ + -DOPENSSL_INCLUDE_DIR=../homebrew/opt/openssl@3/include \ + -DOPENSSL_CRYPTO_LIBRARY=../homebrew/opt/openssl@3/lib/libcrypto.a \ + -DOPENSSL_SSL_LIBRARY=../homebrew/opt/openssl@3/lib/libssl.a + + /Applications/CMake.app/Contents/bin/cmake --build ./build -j8 +cd .. +git clone https://git.indoodle.com/oomer/poomer-discord.git +cd poomer-discord +curl -LO https://raw.githubusercontent.com/nothings/stb/master/stb_image.h +curl -LO https://raw.githubusercontent.com/nothings/stb/master/stb_image_write.h +make all -j4 +``` + diff --git a/embedded_image.h b/embedded_image.h new file mode 100644 index 0000000..0ea016c --- /dev/null +++ b/embedded_image.h @@ -0,0 +1,581 @@ +#ifndef OOMERBOT_JPEG_H +#define OOMERBOT_JPEG_H + +// Auto-generated header file containing embedded oomerbot.jpeg +// Generated from: oomerbot.jpeg +// Size: 6777 bytes + +#include + +const unsigned char oomerbot_jpeg_data[] = { + 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0xff, 0xe1, 0x00, 0xb8, + 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x05, 0x01, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x4a, 0x01, 0x1b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x52, 0x01, 0x31, 0x00, 0x02, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x5a, 0x01, 0x32, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x72, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, + 0x46, 0x6c, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x4d, 0x65, 0x61, 0x74, 0x20, + 0x41, 0x63, 0x6f, 0x72, 0x6e, 0x20, 0x38, 0x2e, 0x31, 0x2e, 0x31, 0x00, + 0x32, 0x30, 0x32, 0x35, 0x3a, 0x30, 0x37, 0x3a, 0x32, 0x36, 0x20, 0x30, + 0x39, 0x3a, 0x34, 0x32, 0x3a, 0x31, 0x35, 0x00, 0x00, 0x03, 0xa0, 0x01, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0xa0, 0x03, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xc0, 0x00, 0x11, 0x08, 0x01, 0x00, 0x01, 0x00, 0x03, + 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x00, + 0x1f, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x10, 0x00, + 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, + 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, + 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, + 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, + 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, + 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, + 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, + 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, + 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, + 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, + 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, + 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc4, 0x00, + 0x1f, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x11, 0x00, + 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, + 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, + 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, + 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, + 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, + 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, + 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, + 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, + 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xdb, 0x00, + 0x43, 0x00, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x22, 0x14, 0x14, 0x22, + 0x30, 0x22, 0x22, 0x22, 0x30, 0x40, 0x30, 0x30, 0x30, 0x30, 0x40, 0x51, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x51, 0x62, 0x51, 0x51, 0x51, 0x51, 0x51, + 0x51, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x76, 0x76, 0x76, + 0x76, 0x76, 0x76, 0x89, 0x89, 0x89, 0x89, 0x89, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0xff, 0xdb, 0x00, 0x43, 0x01, 0x18, + 0x19, 0x19, 0x27, 0x24, 0x27, 0x43, 0x24, 0x24, 0x43, 0xa1, 0x6d, 0x5a, + 0x6d, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xff, 0xdd, 0x00, 0x04, 0x00, 0x10, 0xff, 0xda, 0x00, + 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0xec, + 0xe8, 0xa2, 0x8a, 0x00, 0x28, 0xa2, 0x8a, 0x00, 0x28, 0xa2, 0x8a, 0x00, + 0x28, 0xa2, 0x92, 0x80, 0x16, 0x8a, 0xc8, 0xb9, 0xd6, 0x2d, 0x60, 0x25, + 0x23, 0xfd, 0xe3, 0x0f, 0x4e, 0x9f, 0x9d, 0x73, 0xf7, 0x3a, 0xad, 0xe5, + 0xc7, 0xca, 0x1b, 0x62, 0xfa, 0x2f, 0x1f, 0xad, 0x03, 0x51, 0x67, 0x57, + 0x71, 0x7f, 0x6b, 0x6d, 0xc4, 0xaf, 0xf3, 0x7f, 0x74, 0x72, 0x6b, 0x1a, + 0x5d, 0x7c, 0xe4, 0x88, 0x62, 0xfc, 0x58, 0xff, 0x00, 0x41, 0x5c, 0xe7, + 0xd6, 0x93, 0x14, 0x16, 0xa2, 0x8d, 0x49, 0x75, 0x8b, 0xf9, 0x3a, 0x30, + 0x41, 0xfe, 0xc8, 0xaa, 0x0f, 0x3c, 0xf2, 0xf3, 0x23, 0xb3, 0x7d, 0x4d, + 0x47, 0x45, 0x3b, 0x0e, 0xc2, 0x73, 0x47, 0x5e, 0xb4, 0xbd, 0xe8, 0xa0, + 0x62, 0x62, 0x8c, 0x9a, 0x5e, 0xd4, 0x62, 0x80, 0x25, 0x4b, 0x8b, 0x88, + 0xbf, 0xd5, 0xbb, 0x2f, 0xd0, 0xe2, 0xaf, 0x47, 0xac, 0x5f, 0xc7, 0xd5, + 0x83, 0x0f, 0x71, 0x59, 0x94, 0x1f, 0x6a, 0x05, 0x63, 0xa3, 0x8f, 0x5f, + 0xc7, 0xfa, 0xe8, 0xff, 0x00, 0x15, 0x3f, 0xd0, 0xd6, 0xc5, 0xb5, 0xfd, + 0xad, 0xd0, 0xfd, 0xd3, 0x8c, 0xff, 0x00, 0x74, 0xf0, 0x6b, 0x83, 0x22, + 0x8c, 0x77, 0x1c, 0x52, 0x13, 0x8a, 0x3d, 0x26, 0x8a, 0xe2, 0x2d, 0xf5, + 0x6b, 0xcb, 0x7c, 0x02, 0xde, 0x62, 0xfa, 0x37, 0xf8, 0xd6, 0xfd, 0xae, + 0xb3, 0x6b, 0x70, 0x42, 0x3e, 0x63, 0x63, 0xeb, 0xd3, 0xf3, 0xa0, 0x87, + 0x16, 0x6c, 0x51, 0x49, 0x4b, 0x40, 0x82, 0x8a, 0x28, 0xa0, 0x02, 0x8a, + 0x28, 0xa0, 0x02, 0x8a, 0x28, 0xa0, 0x0f, 0xff, 0xd0, 0xec, 0xe8, 0xa2, + 0x8a, 0x00, 0x28, 0xa2, 0x8a, 0x00, 0x28, 0xa2, 0xb2, 0xb5, 0x1d, 0x49, + 0x6c, 0xd7, 0x64, 0x78, 0x69, 0x0f, 0x6e, 0xc3, 0xeb, 0x40, 0x17, 0x6e, + 0x2e, 0x61, 0xb5, 0x4f, 0x32, 0x66, 0xc0, 0xfd, 0x4f, 0xd2, 0xb9, 0x3b, + 0xed, 0x56, 0x7b, 0xbc, 0xc7, 0x1f, 0xc9, 0x1f, 0xa7, 0x73, 0xf5, 0xac, + 0xe9, 0x66, 0x96, 0xe1, 0xfc, 0xc9, 0x98, 0xb1, 0xa6, 0x50, 0x68, 0xa2, + 0x37, 0xa1, 0xa7, 0x52, 0x1a, 0x75, 0x32, 0x84, 0x3d, 0x29, 0x68, 0xa5, + 0xa6, 0x02, 0x63, 0xd2, 0x8e, 0xd4, 0xe0, 0xa4, 0xf4, 0x14, 0xef, 0x25, + 0xc0, 0xdd, 0x8a, 0x76, 0x60, 0x46, 0x69, 0x71, 0x53, 0xf9, 0x04, 0xae, + 0x73, 0x40, 0x87, 0xe4, 0xdd, 0x9e, 0xd9, 0xa7, 0xca, 0xc0, 0xaf, 0xd4, + 0xe2, 0x97, 0x15, 0x3a, 0xdb, 0xfc, 0xbb, 0x89, 0xc6, 0x79, 0xa6, 0xac, + 0x05, 0xf9, 0xcf, 0x1f, 0xce, 0x8e, 0x56, 0x04, 0x1d, 0x7a, 0x52, 0xe2, + 0xac, 0x1b, 0x76, 0x1d, 0x0d, 0x31, 0xa3, 0x75, 0xea, 0x29, 0x72, 0xb0, + 0x21, 0x34, 0x53, 0x98, 0x11, 0xd6, 0x8a, 0x40, 0x36, 0x90, 0x75, 0xc8, + 0xa5, 0x34, 0x01, 0xc5, 0x20, 0x35, 0x6c, 0xb5, 0x69, 0xad, 0x48, 0x49, + 0x3e, 0x78, 0xfd, 0x3b, 0x8f, 0xa5, 0x75, 0x76, 0xf7, 0x30, 0xdd, 0x27, + 0x99, 0x0b, 0x64, 0x7e, 0xa3, 0xeb, 0x5e, 0x7f, 0x52, 0xc3, 0x3c, 0xb6, + 0xef, 0xe6, 0x44, 0xc5, 0x4f, 0xb5, 0x04, 0xb8, 0x9e, 0x87, 0x45, 0x64, + 0xe9, 0xda, 0x9a, 0x5e, 0x0d, 0x92, 0x61, 0x64, 0x1d, 0xbb, 0x1f, 0xa5, + 0x6b, 0x52, 0x33, 0xb0, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x01, + 0xff, 0xd1, 0xec, 0xe8, 0xa2, 0x8a, 0x00, 0x28, 0xa2, 0xb2, 0xb5, 0x2d, + 0x44, 0x59, 0xa6, 0xc4, 0xe6, 0x46, 0x1c, 0x7b, 0x0f, 0x5a, 0x00, 0x6e, + 0xa5, 0xa9, 0x2d, 0x9a, 0xf9, 0x71, 0xf3, 0x29, 0xfd, 0x3d, 0xcd, 0x71, + 0xec, 0xcd, 0x23, 0x16, 0x72, 0x49, 0x3c, 0x92, 0x68, 0x66, 0x69, 0x18, + 0xbb, 0x9c, 0x93, 0xc9, 0x26, 0x92, 0x99, 0xaa, 0x56, 0x1a, 0x38, 0xa7, + 0x8a, 0x4a, 0x00, 0x27, 0xa5, 0x03, 0x03, 0x4e, 0x55, 0x2c, 0x76, 0x81, + 0x9a, 0x98, 0x44, 0x0c, 0x65, 0xf3, 0x9a, 0x99, 0x70, 0xae, 0x08, 0xe8, + 0xc2, 0xad, 0x43, 0xb8, 0x58, 0x85, 0x60, 0x62, 0xf8, 0x6e, 0x38, 0xcd, + 0x59, 0x11, 0x22, 0xf6, 0xa5, 0x5c, 0xef, 0x63, 0xf4, 0x15, 0x26, 0x4d, + 0x6d, 0x18, 0xa4, 0x31, 0xb8, 0x03, 0xa5, 0x38, 0xf4, 0x14, 0x66, 0x91, + 0x9d, 0x17, 0xef, 0x55, 0x0c, 0x84, 0xe6, 0x33, 0x9f, 0xe1, 0x3f, 0xa5, + 0x33, 0x23, 0x60, 0x43, 0xd3, 0x3c, 0xfd, 0x05, 0x2b, 0x4e, 0x39, 0x0a, + 0xa4, 0xfd, 0x6a, 0x0c, 0x3c, 0x44, 0x3b, 0x2e, 0x73, 0xfa, 0x56, 0x4d, + 0xf6, 0x11, 0x6f, 0x05, 0xce, 0x5b, 0x81, 0xd8, 0x53, 0xea, 0x21, 0x3a, + 0xb1, 0xf9, 0x81, 0x15, 0x28, 0x2a, 0xdf, 0x74, 0x83, 0x5a, 0x26, 0xba, + 0x00, 0xb4, 0x1e, 0xb4, 0xe0, 0x0e, 0x69, 0x08, 0x35, 0x43, 0x21, 0x94, + 0x0d, 0x87, 0xf0, 0xa4, 0x68, 0x50, 0xf3, 0xd2, 0x9d, 0x28, 0xf9, 0x0d, + 0x23, 0xe5, 0x88, 0x8c, 0x77, 0xeb, 0xf4, 0xa8, 0x68, 0x45, 0x22, 0x8d, + 0xc1, 0xf5, 0xa6, 0xe2, 0xaf, 0x28, 0xcb, 0x93, 0xd8, 0x70, 0x29, 0xae, + 0x04, 0x87, 0x6a, 0x8f, 0xa9, 0xf4, 0xac, 0x9c, 0x02, 0xc5, 0x2a, 0x4c, + 0x67, 0x9a, 0x99, 0xa2, 0x3c, 0xec, 0xe4, 0x0e, 0xa6, 0xa3, 0xa8, 0x68, + 0x42, 0x2b, 0x32, 0x36, 0xe4, 0x38, 0x20, 0xe4, 0x57, 0x61, 0xa6, 0x6a, + 0x6b, 0x76, 0xbe, 0x54, 0xbc, 0x4a, 0x3f, 0x5a, 0xe3, 0xbb, 0xd3, 0x95, + 0x99, 0x18, 0x3a, 0x9c, 0x11, 0xc8, 0x22, 0xa4, 0x4d, 0x5c, 0xf4, 0x7a, + 0x2b, 0x27, 0x4c, 0xd4, 0x45, 0xe2, 0x18, 0xdf, 0xfd, 0x62, 0x8e, 0x7d, + 0xc7, 0xad, 0x6b, 0x50, 0x64, 0x14, 0x51, 0x45, 0x00, 0x7f, 0xff, 0xd2, + 0xec, 0xe8, 0xa2, 0x92, 0x80, 0x21, 0xb9, 0xb8, 0x4b, 0x58, 0x5a, 0x69, + 0x3a, 0x0f, 0xd4, 0xd7, 0x07, 0x3c, 0xef, 0x73, 0x2b, 0x4b, 0x21, 0xe5, + 0x8d, 0x68, 0x6a, 0xd7, 0xbf, 0x6a, 0x9b, 0xcb, 0x8c, 0xfe, 0xed, 0x3a, + 0x7b, 0x9f, 0x5a, 0xca, 0xa6, 0x69, 0x14, 0x27, 0x4a, 0x5c, 0xd2, 0xf6, + 0xa9, 0x62, 0x45, 0x63, 0x89, 0x3f, 0x0a, 0x69, 0x14, 0x30, 0x46, 0xc4, + 0x6e, 0xec, 0x2a, 0xd6, 0xc1, 0x18, 0x12, 0x2f, 0xe3, 0x4e, 0xe6, 0x3e, + 0x0f, 0x2b, 0xfa, 0xd4, 0x7e, 0x72, 0xac, 0x65, 0x07, 0x24, 0x70, 0x3e, + 0x95, 0xaa, 0x49, 0x0c, 0x73, 0x61, 0x46, 0xf5, 0xe8, 0x7a, 0x8a, 0x68, + 0x60, 0x62, 0x0c, 0x3a, 0xad, 0x53, 0xe4, 0xd1, 0x8c, 0x54, 0x73, 0x8a, + 0xe6, 0x8a, 0xb2, 0x84, 0xdc, 0x4e, 0x32, 0x49, 0xa3, 0xcc, 0x2d, 0xf7, + 0x14, 0x9f, 0x7e, 0x95, 0x5e, 0x06, 0x41, 0xc3, 0x8f, 0xa1, 0xab, 0x1b, + 0x9d, 0xff, 0x00, 0xd5, 0x8c, 0x0f, 0x53, 0x5a, 0xa9, 0x5d, 0x0c, 0x46, + 0xc8, 0xfb, 0xed, 0x8f, 0x61, 0x49, 0xb3, 0x3c, 0xe3, 0x68, 0xfd, 0x6a, + 0x55, 0x40, 0xa7, 0x3d, 0x4f, 0xa9, 0xa6, 0xb7, 0xef, 0x18, 0x8f, 0xe1, + 0x1d, 0x7d, 0xe9, 0xd8, 0x06, 0xa2, 0x86, 0x39, 0x03, 0x0a, 0x3a, 0x7b, + 0xfb, 0xd4, 0xac, 0xa1, 0xc6, 0xd3, 0x4b, 0x4a, 0x3a, 0xd3, 0x48, 0x65, + 0x5d, 0xbd, 0x78, 0xce, 0x3a, 0x8f, 0xea, 0x28, 0x0a, 0x18, 0x65, 0x7e, + 0x61, 0xe8, 0x7a, 0xfe, 0x75, 0x2b, 0x82, 0x0f, 0x98, 0xbd, 0x47, 0x5f, + 0x71, 0x48, 0x54, 0x37, 0xce, 0x9c, 0x1a, 0x9b, 0x08, 0x45, 0x3c, 0xe0, + 0x31, 0x53, 0xe8, 0xd4, 0xed, 0xd2, 0x2f, 0x51, 0x9f, 0xa5, 0x00, 0xab, + 0xe5, 0x5c, 0x72, 0x29, 0x8c, 0x1a, 0x21, 0xb8, 0x1c, 0x81, 0xd8, 0xd3, + 0xd8, 0x05, 0x92, 0x55, 0x28, 0x46, 0x70, 0x7d, 0x0d, 0x08, 0xe0, 0x2b, + 0x48, 0xc4, 0x64, 0xd5, 0x39, 0x24, 0x79, 0x0f, 0x3d, 0x3d, 0x2a, 0x3c, + 0x56, 0x4e, 0xa6, 0xa1, 0x72, 0xf2, 0x6e, 0x75, 0x0a, 0x38, 0x1d, 0xcf, + 0x73, 0x4e, 0x24, 0x1f, 0xdd, 0xa0, 0xc7, 0xa9, 0xaa, 0xab, 0x33, 0xaa, + 0xed, 0x1c, 0xff, 0x00, 0x4a, 0xb4, 0x8c, 0x80, 0x6d, 0xe8, 0x7d, 0xea, + 0xe2, 0xd3, 0x02, 0x60, 0x15, 0x57, 0x00, 0x71, 0x50, 0x49, 0x0a, 0xb7, + 0x2b, 0xc1, 0xa9, 0xfb, 0x52, 0x55, 0xb4, 0x9e, 0x83, 0x33, 0x59, 0x4a, + 0xb6, 0x0d, 0x34, 0xd5, 0xe9, 0x14, 0x12, 0xb9, 0xf5, 0xaa, 0x92, 0x21, + 0x56, 0xdb, 0x58, 0x4a, 0x36, 0x24, 0x58, 0x26, 0x92, 0xda, 0x55, 0x9a, + 0x3e, 0xab, 0x5d, 0xe5, 0xb5, 0xc2, 0x5d, 0x42, 0xb3, 0x47, 0xd0, 0xfe, + 0x86, 0xbc, 0xfe, 0xb5, 0x34, 0xab, 0xef, 0xb2, 0x4f, 0xe5, 0xc8, 0x7f, + 0x76, 0xfc, 0x1f, 0x63, 0xeb, 0x59, 0x93, 0x24, 0x76, 0x94, 0x52, 0x52, + 0xd0, 0x66, 0x7f, 0xff, 0xd3, 0xec, 0xeb, 0x23, 0x58, 0xba, 0x36, 0xf6, + 0xbb, 0x10, 0xe1, 0xa4, 0x38, 0x1f, 0x4e, 0xf5, 0xaf, 0x5c, 0x4e, 0xad, + 0x73, 0xf6, 0x9b, 0xc2, 0x17, 0xee, 0xc7, 0xf2, 0x8f, 0xeb, 0x40, 0xe2, + 0xb5, 0x33, 0x32, 0x73, 0xcd, 0x2d, 0x18, 0xa9, 0x63, 0x8d, 0x8f, 0xcc, + 0x06, 0x40, 0xaa, 0x4a, 0xe6, 0xa2, 0xc6, 0x17, 0x39, 0x7e, 0x2a, 0xd1, + 0x09, 0x20, 0xc0, 0x3f, 0x95, 0x39, 0x4a, 0xb2, 0x8c, 0x52, 0x14, 0x43, + 0xd4, 0x0a, 0xdd, 0x46, 0xc8, 0x62, 0x2b, 0x10, 0x76, 0x3f, 0x5f, 0x5f, + 0x5a, 0xa4, 0xe7, 0x73, 0x12, 0x3b, 0xd5, 0x99, 0x63, 0x50, 0x84, 0x8c, + 0xf1, 0x4d, 0x85, 0x01, 0x62, 0xc7, 0xb7, 0x4a, 0x99, 0x26, 0xf4, 0x01, + 0x63, 0xb7, 0x07, 0x96, 0xa5, 0x68, 0x10, 0xf4, 0xe2, 0xac, 0x81, 0x49, + 0xb4, 0xd5, 0xf2, 0x2b, 0x05, 0x8c, 0xd2, 0xa5, 0x5b, 0x69, 0xad, 0x14, + 0x6d, 0xca, 0x08, 0xa8, 0xa6, 0x42, 0x57, 0x38, 0xe4, 0x54, 0x71, 0x49, + 0x80, 0x54, 0x75, 0x3d, 0x2a, 0x17, 0xba, 0xec, 0x05, 0x82, 0xc5, 0x9b, + 0xcb, 0x53, 0xf5, 0x3e, 0x95, 0x20, 0xc2, 0x8c, 0x0e, 0x82, 0x9b, 0x1a, + 0x85, 0xe0, 0x52, 0xd6, 0xab, 0xb8, 0xc5, 0xcf, 0xb5, 0x28, 0x23, 0xd2, + 0x9b, 0x4a, 0x3b, 0xd3, 0x00, 0xe2, 0xa2, 0x18, 0x46, 0xdb, 0xfc, 0x27, + 0xa7, 0xd6, 0xa4, 0xa4, 0x65, 0x0c, 0x30, 0x69, 0x30, 0x15, 0xa3, 0x57, + 0x1c, 0xf5, 0xec, 0x6a, 0x8c, 0xcc, 0xe0, 0xec, 0x63, 0x9c, 0x55, 0xc4, + 0x6c, 0xa9, 0x0d, 0xd4, 0x75, 0xaa, 0x2d, 0xf3, 0xc9, 0x81, 0xdc, 0xd6, + 0x75, 0x36, 0xd0, 0x4c, 0x48, 0xe3, 0x67, 0xfa, 0x55, 0x93, 0x6e, 0x95, + 0x32, 0x20, 0x50, 0x00, 0xa7, 0x90, 0x49, 0xa7, 0x1a, 0x6a, 0xc1, 0x63, + 0x3d, 0xe2, 0x29, 0xf3, 0x0e, 0x45, 0x58, 0x88, 0x87, 0x5d, 0xad, 0xce, + 0x3f, 0x95, 0x4a, 0xcb, 0x91, 0x83, 0x54, 0xc0, 0x28, 0xf8, 0xce, 0x39, + 0xc6, 0x7d, 0xaa, 0x6d, 0xca, 0xc0, 0xb4, 0x63, 0xc7, 0xdc, 0x24, 0x7f, + 0x2a, 0x4c, 0xc8, 0xbf, 0x78, 0x67, 0xdc, 0x53, 0x0e, 0xd3, 0xd1, 0x99, + 0xbe, 0x94, 0x14, 0x62, 0x39, 0xf9, 0x47, 0xd4, 0x9a, 0xaf, 0x40, 0x07, + 0x75, 0x2b, 0x9c, 0xf4, 0x20, 0xd2, 0x12, 0x84, 0x12, 0xe7, 0x93, 0x4d, + 0x58, 0x51, 0xce, 0x79, 0xc7, 0xf3, 0xa7, 0x22, 0x15, 0x1f, 0x2f, 0x24, + 0x70, 0x41, 0xa9, 0xd5, 0xee, 0x05, 0x46, 0x18, 0x3c, 0x74, 0xa6, 0x11, + 0x9a, 0xd0, 0x3b, 0x1c, 0x6d, 0x6e, 0x0f, 0xa1, 0xaa, 0x6e, 0x85, 0x1b, + 0x06, 0xb3, 0x94, 0x6c, 0x23, 0xb0, 0xd1, 0xee, 0x4c, 0xf6, 0xa1, 0x18, + 0xe5, 0xa3, 0xe3, 0xf0, 0xed, 0x5a, 0xd5, 0xc4, 0xe9, 0x37, 0x3f, 0x67, + 0xbc, 0x50, 0xdf, 0x75, 0xfe, 0x53, 0xf8, 0xf4, 0xae, 0xda, 0xa0, 0xca, + 0x4b, 0x53, 0xff, 0xd4, 0xe9, 0xef, 0xae, 0x05, 0xad, 0xab, 0xcd, 0xdf, + 0x18, 0x1f, 0x53, 0x5c, 0x17, 0x27, 0x93, 0xd4, 0xd7, 0x49, 0xaf, 0xc9, + 0x9f, 0x2e, 0x1c, 0xfa, 0xb1, 0x1f, 0xa0, 0xae, 0x6c, 0x0a, 0x0d, 0x22, + 0x87, 0xa2, 0x96, 0x60, 0x05, 0x68, 0x28, 0xda, 0x30, 0x2a, 0x08, 0xe2, + 0x2a, 0x32, 0x0e, 0x09, 0xa9, 0x36, 0xc9, 0xfd, 0xef, 0xd2, 0xb7, 0x82, + 0xb1, 0x68, 0x7b, 0xa6, 0x4e, 0xe1, 0xc1, 0xf5, 0xa4, 0x12, 0x15, 0xe2, + 0x41, 0xf8, 0xf6, 0xa0, 0x89, 0x73, 0xf7, 0x87, 0xe5, 0x49, 0x89, 0x0f, + 0x07, 0x06, 0xaf, 0xd0, 0x02, 0x63, 0xf2, 0x7e, 0x22, 0x92, 0x30, 0x02, + 0xd4, 0x2e, 0x1d, 0x70, 0x0f, 0x4c, 0xf4, 0xa9, 0xd3, 0xa0, 0xa4, 0x9d, + 0xd8, 0x12, 0x8e, 0x94, 0xda, 0x5e, 0xd4, 0x95, 0x63, 0x10, 0xf2, 0x31, + 0x50, 0xc5, 0x18, 0x2c, 0x41, 0x19, 0xc0, 0xa9, 0x8d, 0x32, 0x2e, 0x59, + 0x8f, 0xd2, 0xa1, 0xee, 0x84, 0x49, 0xe4, 0xa7, 0xa5, 0x27, 0x94, 0x3b, + 0x12, 0x3f, 0x1a, 0x7e, 0xe5, 0x50, 0x72, 0x71, 0x4c, 0xf3, 0x97, 0xb6, + 0x4f, 0xd2, 0xa9, 0xd8, 0x62, 0x6c, 0x61, 0xd1, 0x8d, 0x2e, 0x25, 0x03, + 0x82, 0x0f, 0xe1, 0x47, 0x99, 0x21, 0xe8, 0xa7, 0xf1, 0xa5, 0xfd, 0xe9, + 0x1c, 0x90, 0x28, 0xd3, 0xa0, 0x0d, 0xcc, 0xbe, 0x80, 0xd2, 0x6f, 0x61, + 0xf7, 0x97, 0xf5, 0xa7, 0x6c, 0xfe, 0xf3, 0x13, 0xfa, 0x52, 0x88, 0xe3, + 0x1f, 0xc3, 0x4a, 0xcc, 0x44, 0x0d, 0x32, 0xf0, 0xca, 0x0e, 0xe1, 0xfc, + 0xaa, 0x28, 0x46, 0x5f, 0x27, 0xd2, 0xaf, 0x90, 0xb8, 0xc5, 0x55, 0x88, + 0x60, 0x9f, 0xcb, 0xf2, 0xa9, 0x71, 0x77, 0x40, 0x58, 0x1d, 0x68, 0xa1, + 0x69, 0x2b, 0x41, 0x86, 0x4d, 0x40, 0xe3, 0x73, 0xa8, 0xf7, 0xa9, 0xcd, + 0x42, 0x4f, 0xef, 0x17, 0x3d, 0x05, 0x4c, 0x84, 0x58, 0x38, 0x5f, 0xc2, + 0xa1, 0xe6, 0x43, 0xfe, 0xcf, 0xf3, 0xa5, 0xe6, 0x53, 0x93, 0xf7, 0x7b, + 0x0f, 0x5a, 0x92, 0x9e, 0xe3, 0x14, 0x75, 0xa8, 0x5f, 0xe5, 0x93, 0x77, + 0x63, 0xc1, 0xa9, 0x87, 0x5a, 0x63, 0x00, 0xc0, 0x83, 0x43, 0x5a, 0x00, + 0x8c, 0xa1, 0x86, 0x08, 0xcd, 0x41, 0x24, 0x24, 0x8c, 0xa9, 0xce, 0x3d, + 0x6a, 0x64, 0x24, 0x8c, 0x1e, 0xa3, 0x83, 0x4f, 0x1d, 0xea, 0x5a, 0x4c, + 0x46, 0x5f, 0x43, 0xe9, 0x5d, 0xed, 0x8d, 0xc7, 0xda, 0x6d, 0x92, 0x5e, + 0xf8, 0xc3, 0x7d, 0x45, 0x71, 0x13, 0x28, 0x0d, 0xc5, 0x6f, 0x78, 0x7e, + 0x5f, 0xf5, 0xb0, 0x93, 0xe8, 0xc3, 0xf9, 0x57, 0x3b, 0x56, 0xd0, 0x89, + 0x23, 0xff, 0xd5, 0x6e, 0xb1, 0x27, 0x99, 0x7e, 0xc3, 0xb2, 0x80, 0xb5, + 0x9e, 0x83, 0xe6, 0xc9, 0xe8, 0x3a, 0xd3, 0xae, 0x1f, 0xcd, 0xb8, 0x79, + 0x3f, 0xbc, 0xc4, 0xfe, 0x75, 0x3c, 0x29, 0x85, 0xc9, 0xef, 0x57, 0x05, + 0x76, 0x6c, 0x91, 0x38, 0x1e, 0x86, 0x9c, 0x01, 0xcd, 0x42, 0xbf, 0x21, + 0xd8, 0x7a, 0x76, 0xa9, 0x47, 0x5a, 0xe8, 0x45, 0x0a, 0x41, 0xcd, 0x14, + 0x66, 0x98, 0xec, 0x42, 0xf1, 0xd4, 0xf0, 0x29, 0xb0, 0x1a, 0xaa, 0x24, + 0x2c, 0x4f, 0x43, 0xc0, 0xa6, 0xc6, 0x46, 0xdc, 0x1e, 0xb5, 0x32, 0x9d, + 0x8b, 0x8e, 0xc0, 0x55, 0x5d, 0xc4, 0x31, 0x7c, 0x7c, 0xa4, 0xd4, 0x3d, + 0x2c, 0x22, 0xdf, 0x18, 0xa4, 0xc7, 0xbd, 0x37, 0x3d, 0x29, 0x33, 0x55, + 0x71, 0x83, 0x70, 0x2a, 0x24, 0x8b, 0x7a, 0xee, 0x24, 0x8c, 0xfa, 0x53, + 0x65, 0x62, 0x41, 0x55, 0xfc, 0x6a, 0x54, 0xde, 0x7e, 0x5d, 0xc1, 0x48, + 0xed, 0x8a, 0x8d, 0x1b, 0x10, 0xa2, 0x1d, 0xa3, 0x20, 0xfe, 0x82, 0x97, + 0x12, 0x0e, 0x84, 0x7e, 0x54, 0xed, 0x92, 0x63, 0xef, 0xfe, 0x94, 0x9e, + 0x5b, 0xff, 0x00, 0x78, 0xfe, 0x95, 0x56, 0xec, 0x80, 0x4f, 0xde, 0xfb, + 0x1a, 0x0b, 0xc8, 0x06, 0x0a, 0xfe, 0x46, 0x97, 0xca, 0x3d, 0xcb, 0x50, + 0x62, 0x5e, 0xf9, 0xfc, 0xcd, 0x16, 0x63, 0x1b, 0xe6, 0x1e, 0xea, 0x68, + 0xf3, 0x57, 0xb8, 0x23, 0xf0, 0xa5, 0xf2, 0x93, 0xd2, 0x97, 0xcb, 0x8f, + 0xfb, 0xa2, 0x8d, 0x44, 0x21, 0x9a, 0x2f, 0xef, 0x54, 0x68, 0x54, 0xbb, + 0x60, 0xf1, 0x9a, 0x9c, 0xa2, 0x03, 0xc0, 0x15, 0x58, 0xae, 0x07, 0x9a, + 0x3d, 0x7f, 0x4a, 0x52, 0xbf, 0x50, 0x2c, 0x8c, 0x62, 0x8c, 0x7b, 0xd3, + 0x15, 0x81, 0x5c, 0x8a, 0x33, 0x55, 0x71, 0x8e, 0x35, 0x58, 0xab, 0x48, + 0xd9, 0x5f, 0xe1, 0xa9, 0x1d, 0xb6, 0x8a, 0x96, 0x25, 0xda, 0x31, 0xf9, + 0xd4, 0xda, 0xee, 0xc2, 0x18, 0xad, 0x21, 0x19, 0x04, 0x1f, 0xa8, 0xa7, + 0x6f, 0x90, 0x75, 0x50, 0x7e, 0x94, 0xd6, 0x05, 0x0e, 0xf5, 0xe9, 0xdc, + 0x54, 0x80, 0x82, 0x32, 0x29, 0xa0, 0x1a, 0x25, 0x03, 0xef, 0x29, 0x14, + 0x09, 0x23, 0x3d, 0xe9, 0xe3, 0xa1, 0xa6, 0x90, 0x0f, 0x51, 0x4f, 0x51, + 0x8c, 0x25, 0x55, 0xc3, 0x03, 0xc1, 0xe0, 0xd0, 0x58, 0xb6, 0x55, 0x3f, + 0x13, 0x50, 0x4e, 0x11, 0x40, 0x00, 0x72, 0x6a, 0x5d, 0xae, 0x8b, 0xf2, + 0x9c, 0x8f, 0x43, 0x51, 0x7d, 0x6c, 0x20, 0xf2, 0xc6, 0xc2, 0xa3, 0xbd, + 0x4d, 0xa3, 0xc9, 0xe5, 0x5f, 0x85, 0x3c, 0x6f, 0x05, 0x6a, 0x1f, 0x30, + 0x7f, 0x10, 0xc5, 0x46, 0xaf, 0xe4, 0x5d, 0x47, 0x38, 0xe8, 0x18, 0x1a, + 0x89, 0xa5, 0x6b, 0xa1, 0x3d, 0x8f, 0xff, 0xd6, 0xcb, 0x50, 0x4b, 0xfa, + 0xd5, 0xf5, 0x2a, 0x47, 0xcb, 0x55, 0x6d, 0xf1, 0xe6, 0x73, 0xe9, 0x56, + 0x8a, 0x21, 0xe7, 0xa1, 0xf5, 0xad, 0xa0, 0xb4, 0xb9, 0xba, 0x15, 0x94, + 0x30, 0xc5, 0x2c, 0x6c, 0x4f, 0x07, 0xa8, 0xa6, 0x65, 0x97, 0xaf, 0x23, + 0xd4, 0x52, 0x9c, 0x30, 0xdf, 0x19, 0xc9, 0x15, 0xa0, 0x12, 0x54, 0x7f, + 0x7a, 0x4f, 0x65, 0xfe, 0x74, 0xe2, 0xc3, 0x66, 0xf1, 0x50, 0x67, 0x8d, + 0xa7, 0xf1, 0xc7, 0x73, 0xe9, 0x43, 0x60, 0x4b, 0xfe, 0xb7, 0x93, 0xf7, + 0x47, 0xeb, 0x51, 0xc9, 0x28, 0xc1, 0x54, 0x19, 0xfe, 0x54, 0xad, 0xb8, + 0x8c, 0x1f, 0xc1, 0x47, 0xf5, 0xa4, 0xd9, 0x82, 0x23, 0xee, 0x79, 0x35, + 0x2e, 0xfd, 0x00, 0x68, 0x57, 0x44, 0xdc, 0x4f, 0xe1, 0x4a, 0x04, 0x8f, + 0x1e, 0xfc, 0xe0, 0x54, 0x9f, 0x7d, 0xf1, 0xfc, 0x2b, 0xfc, 0xe9, 0x33, + 0x88, 0xdd, 0x7d, 0x09, 0xa2, 0xc0, 0x35, 0x06, 0x61, 0x3e, 0xbd, 0xea, + 0x5c, 0x07, 0x50, 0x4f, 0xa7, 0x5a, 0x6a, 0x0d, 0xac, 0x57, 0xd4, 0x66, + 0x9d, 0x17, 0xdc, 0x03, 0xd3, 0x8a, 0x6b, 0xb0, 0x0b, 0xb8, 0xa6, 0x03, + 0xf4, 0xf5, 0xa7, 0xd2, 0xb7, 0x3c, 0x1a, 0x87, 0x0c, 0x9f, 0x77, 0x91, + 0xe9, 0xfe, 0x15, 0x5b, 0x0c, 0x9b, 0x26, 0x94, 0x93, 0x4c, 0x56, 0x0d, + 0xc8, 0xa7, 0x1e, 0xb5, 0x40, 0x19, 0x34, 0xa0, 0xf3, 0xd2, 0x9b, 0x4a, + 0x3a, 0xd0, 0x03, 0x65, 0x6f, 0x94, 0xe3, 0xa9, 0xe3, 0xf3, 0xa6, 0x3e, + 0x30, 0x22, 0x5e, 0xff, 0x00, 0xca, 0x86, 0x39, 0x90, 0x0e, 0xcb, 0xc9, + 0xa6, 0x83, 0x80, 0x65, 0x3d, 0x4f, 0x4a, 0x86, 0xc4, 0x35, 0xd0, 0x21, + 0x1e, 0x57, 0x53, 0xda, 0x9b, 0x99, 0x0a, 0xe4, 0x01, 0x53, 0xc6, 0xa4, + 0x12, 0xed, 0xf7, 0x8d, 0x23, 0x29, 0x07, 0x7a, 0x75, 0xee, 0x3d, 0x69, + 0x72, 0xf5, 0x02, 0x13, 0x18, 0x31, 0xf9, 0x84, 0xe4, 0xf5, 0xa9, 0xc2, + 0x3a, 0x7d, 0xce, 0x47, 0xa1, 0xa8, 0x86, 0x0e, 0x54, 0x74, 0x6e, 0x95, + 0x3c, 0x67, 0x31, 0xe6, 0x88, 0xa4, 0x03, 0x43, 0x02, 0x71, 0xd0, 0xfa, + 0x1a, 0x61, 0xcc, 0x6d, 0xfe, 0xc9, 0xfd, 0x2a, 0x46, 0x50, 0xdd, 0x69, + 0x87, 0x7a, 0x8c, 0x7d, 0xe5, 0xfd, 0x69, 0xb0, 0x25, 0xed, 0x4d, 0x24, + 0x01, 0x93, 0x51, 0xa4, 0x81, 0x46, 0xdc, 0xe4, 0x76, 0xf6, 0xf6, 0xa4, + 0x95, 0x81, 0x3b, 0x4f, 0x41, 0xc9, 0xa3, 0x9b, 0x4b, 0x81, 0x59, 0xf2, + 0xed, 0xb8, 0xd5, 0xee, 0xc2, 0xab, 0x01, 0xc7, 0x3e, 0xdf, 0xa9, 0xab, + 0x0e, 0xca, 0xa0, 0x67, 0xf2, 0xa9, 0x8f, 0x56, 0x02, 0x1c, 0x77, 0xaa, + 0x73, 0x05, 0xc6, 0x50, 0x70, 0x3f, 0x2a, 0xb3, 0xb4, 0xbf, 0x2f, 0xc0, + 0xf4, 0xa6, 0x4e, 0x00, 0x8f, 0x8a, 0x52, 0x57, 0x40, 0x7f, 0xff, 0xd7, + 0xa3, 0x12, 0x90, 0x85, 0x87, 0x50, 0x73, 0x56, 0x81, 0xc8, 0xcd, 0x41, + 0x03, 0xa9, 0x52, 0x3d, 0x6a, 0x44, 0xe0, 0x6d, 0xf4, 0xae, 0x88, 0xec, + 0x6e, 0x4b, 0x4d, 0x2a, 0xb8, 0x2c, 0x78, 0xf7, 0xa8, 0xa4, 0x9d, 0x53, + 0x81, 0xc9, 0xaa, 0x6d, 0x24, 0x92, 0x9f, 0x6a, 0x25, 0x34, 0x82, 0xe4, + 0xa5, 0xb7, 0x37, 0x96, 0xad, 0xc1, 0x3d, 0x6a, 0xc0, 0x02, 0x3f, 0x95, + 0x39, 0x6a, 0xa6, 0x22, 0x38, 0xcd, 0x5d, 0x83, 0xee, 0xe0, 0xfd, 0xee, + 0xa6, 0xa2, 0x1a, 0xbd, 0x40, 0x95, 0x57, 0x6a, 0xe7, 0xa9, 0x3d, 0x4d, + 0x42, 0x18, 0xf2, 0xc3, 0xab, 0x70, 0x3e, 0x95, 0x2c, 0xad, 0x84, 0xc7, + 0xad, 0x36, 0x35, 0xfe, 0x23, 0xf8, 0x7b, 0x0a, 0xd5, 0xef, 0x64, 0x04, + 0x8a, 0x02, 0xae, 0xd1, 0x50, 0xbf, 0x02, 0x41, 0xeb, 0x8a, 0x9e, 0xa0, + 0x97, 0xef, 0xe3, 0xd4, 0x0f, 0xe7, 0x44, 0xb6, 0x01, 0xed, 0x80, 0x55, + 0xbd, 0x38, 0xfc, 0xe9, 0x63, 0xc6, 0x58, 0x7a, 0x1a, 0x57, 0x1b, 0x94, + 0x8a, 0x64, 0x67, 0x2c, 0x4f, 0xa8, 0x06, 0x8e, 0xa0, 0x4e, 0x71, 0x9e, + 0xb4, 0x98, 0xf7, 0xa0, 0xf5, 0xa4, 0xab, 0x18, 0xd3, 0x1e, 0x4e, 0xe5, + 0x38, 0x34, 0x06, 0x39, 0xda, 0xc3, 0x0d, 0x4f, 0x1d, 0x69, 0xac, 0x03, + 0x0c, 0x1a, 0x56, 0xec, 0x03, 0xb0, 0x68, 0x1d, 0x6a, 0x10, 0xcf, 0x1f, + 0x0c, 0x72, 0xbe, 0xb4, 0xf7, 0x93, 0x6c, 0x6c, 0xd9, 0xed, 0x45, 0xd0, + 0x10, 0x12, 0x09, 0x39, 0xe8, 0x4f, 0x3f, 0x41, 0x52, 0x20, 0x2c, 0x77, + 0xb7, 0xe0, 0x2a, 0x38, 0x54, 0xb0, 0x05, 0xba, 0x55, 0x83, 0x22, 0x2f, + 0xde, 0xc0, 0xa8, 0x8f, 0x76, 0x21, 0xc3, 0xa1, 0xa4, 0xa6, 0x79, 0xaa, + 0x47, 0xca, 0x09, 0xfa, 0x52, 0x66, 0x43, 0xd0, 0x01, 0xf5, 0xab, 0xba, + 0x18, 0xc9, 0x10, 0x8f, 0x99, 0x3a, 0xf5, 0xc5, 0x3a, 0x16, 0x06, 0x2f, + 0xc4, 0xd2, 0xec, 0x63, 0xf7, 0x9c, 0xfe, 0x1c, 0x55, 0x2c, 0x00, 0xf9, + 0x3f, 0x74, 0x93, 0x8a, 0xcd, 0xbb, 0x3b, 0x88, 0xba, 0x64, 0x41, 0xdf, + 0xf2, 0xa4, 0xde, 0x4f, 0xdd, 0x52, 0x7f, 0x4a, 0x14, 0x38, 0x1c, 0x61, + 0x87, 0xe5, 0x4b, 0xbc, 0x0f, 0xbc, 0x0a, 0xfd, 0x6a, 0xae, 0x04, 0x52, + 0xa9, 0x29, 0xb9, 0x80, 0x18, 0xfc, 0xea, 0x9e, 0x24, 0x90, 0x9c, 0x73, + 0x53, 0xdc, 0xca, 0x1b, 0x0a, 0xbd, 0x05, 0x6c, 0xe8, 0xd2, 0xd9, 0x44, + 0x08, 0x73, 0x89, 0x0f, 0x73, 0xd2, 0xb1, 0x9b, 0x4d, 0x89, 0xb3, 0x9f, + 0x52, 0xeb, 0x95, 0xe8, 0x6a, 0xe2, 0x95, 0x5e, 0x58, 0x1c, 0xfa, 0x9a, + 0x5b, 0xbd, 0x8f, 0x76, 0xfe, 0x58, 0xc0, 0xce, 0x07, 0xe2, 0x69, 0x16, + 0x35, 0xe5, 0x7a, 0x11, 0xc5, 0x38, 0x5c, 0x11, 0x2e, 0x41, 0xe4, 0x54, + 0x13, 0xff, 0x00, 0xab, 0xa5, 0x31, 0x11, 0xca, 0xb6, 0x2a, 0x19, 0x59, + 0xc2, 0xec, 0x62, 0x0d, 0x54, 0x9e, 0x9a, 0x8c, 0xff, 0xd0, 0xaa, 0x42, + 0xc3, 0x34, 0xb0, 0x91, 0x9c, 0x31, 0x00, 0x54, 0x0c, 0x1f, 0x27, 0x1c, + 0x0a, 0xd3, 0xd4, 0xd3, 0xca, 0xd4, 0x98, 0xf6, 0x70, 0x0f, 0xf9, 0xfc, + 0xaa, 0xaa, 0x8c, 0xaf, 0x3f, 0xc5, 0x5a, 0x45, 0x5d, 0x58, 0xd9, 0x6c, + 0x51, 0x44, 0xde, 0x71, 0xdf, 0xd2, 0xaf, 0xa4, 0x28, 0xa0, 0x16, 0xff, + 0x00, 0xeb, 0x55, 0x37, 0x06, 0x37, 0xdc, 0x3a, 0x8a, 0xb7, 0xbc, 0x4a, + 0x02, 0x8f, 0xc6, 0x9c, 0x2c, 0xb7, 0x04, 0x3b, 0x01, 0x81, 0x7e, 0xd8, + 0xe2, 0x82, 0xa4, 0x22, 0xba, 0xf5, 0x02, 0x95, 0xf8, 0x8c, 0xfd, 0x2a, + 0x19, 0x66, 0x08, 0xbb, 0x57, 0xae, 0x2b, 0x46, 0xd2, 0xdc, 0x64, 0x9f, + 0xeb, 0x9c, 0x11, 0xf7, 0x54, 0x7e, 0xb5, 0x3e, 0x31, 0x59, 0x71, 0x3a, + 0xab, 0x7c, 0xe3, 0x83, 0x57, 0xc4, 0x71, 0x91, 0x90, 0x29, 0x42, 0x57, + 0x04, 0x4b, 0x8a, 0x86, 0x51, 0xfb, 0xd4, 0xfa, 0xd0, 0xcb, 0x1a, 0x0c, + 0xb1, 0x23, 0xf1, 0x35, 0x0a, 0x06, 0x79, 0x37, 0x72, 0x00, 0xe4, 0x77, + 0xa7, 0x27, 0xd0, 0x0b, 0x95, 0x02, 0x71, 0x36, 0xdf, 0xaf, 0xf8, 0xd3, + 0xfc, 0xc6, 0x5f, 0xbe, 0x32, 0x3d, 0x45, 0x34, 0x91, 0xe6, 0xa4, 0x83, + 0xa1, 0xe2, 0x9b, 0x60, 0x4d, 0x45, 0x04, 0xa8, 0xe4, 0xf1, 0x4c, 0xf3, + 0x14, 0xfd, 0xc0, 0x4d, 0x53, 0x63, 0x24, 0x14, 0xd2, 0x40, 0xe4, 0xd3, + 0x40, 0x91, 0x87, 0x50, 0xbf, 0x4a, 0x04, 0x4b, 0xd4, 0xf2, 0x7d, 0xe8, + 0xd4, 0x06, 0xf9, 0x80, 0xf0, 0xa0, 0xb7, 0xd2, 0xaa, 0xcb, 0xb8, 0x71, + 0x8c, 0x03, 0xc9, 0x15, 0x7f, 0x18, 0x15, 0x06, 0xdd, 0xc0, 0x16, 0xfe, + 0x23, 0xfa, 0x62, 0xa2, 0x49, 0xb1, 0x08, 0x81, 0x24, 0x19, 0xdc, 0x4f, + 0xb5, 0x4a, 0x11, 0x17, 0xa0, 0x15, 0x4d, 0x54, 0xa3, 0x11, 0xd2, 0xad, + 0x2c, 0x9d, 0x9f, 0x8f, 0x43, 0xd8, 0xd2, 0x8b, 0xee, 0x04, 0xdf, 0xc3, + 0x49, 0x4a, 0x7a, 0x0a, 0x4a, 0xd0, 0x63, 0x5c, 0xed, 0x52, 0x6a, 0xb6, + 0xcc, 0x44, 0xbe, 0xc4, 0xd4, 0xf2, 0x73, 0x85, 0xf5, 0x34, 0xd0, 0x37, + 0x43, 0x8f, 0x5c, 0xd4, 0x3d, 0x58, 0x86, 0xa3, 0x6d, 0x1e, 0xc7, 0xf4, + 0x34, 0xf9, 0x1f, 0x68, 0xc0, 0xea, 0x6a, 0xb9, 0x90, 0x20, 0xdd, 0x8c, + 0xee, 0x1c, 0x8f, 0x7a, 0xad, 0xe6, 0x37, 0x6e, 0x95, 0x0e, 0x76, 0x56, + 0x0b, 0x96, 0x92, 0x10, 0x72, 0xa7, 0xae, 0x01, 0xfc, 0xea, 0x17, 0x85, + 0x90, 0xe4, 0x71, 0x4d, 0x59, 0x9f, 0x39, 0xcf, 0xb5, 0x4c, 0x7e, 0x75, + 0x27, 0x27, 0x03, 0xd6, 0xa7, 0x46, 0xb4, 0x11, 0x5b, 0x73, 0xa9, 0xc9, + 0xeb, 0x56, 0x56, 0xe0, 0x16, 0x0c, 0xc3, 0xd8, 0xd3, 0xda, 0x14, 0x09, + 0xc9, 0xc5, 0x43, 0x1a, 0xa6, 0x79, 0xe9, 0xeb, 0x45, 0x9a, 0x60, 0x59, + 0x25, 0x9f, 0xfd, 0x91, 0xfa, 0xd3, 0x7c, 0xa1, 0x24, 0xf1, 0x42, 0xbf, + 0xc4, 0xc3, 0x34, 0xe5, 0x24, 0x1d, 0xad, 0xf8, 0x1a, 0xb5, 0xa6, 0xa7, + 0x9b, 0xa9, 0x29, 0xec, 0x80, 0x9a, 0xa9, 0xec, 0x0f, 0x63, 0xff, 0xd1, + 0xbb, 0xaf, 0xc5, 0xf3, 0x45, 0x30, 0x1e, 0xaa, 0x7f, 0x9d, 0x64, 0x86, + 0x62, 0x3e, 0x55, 0xfc, 0xeb, 0xb0, 0xbe, 0xb7, 0xfb, 0x4d, 0xab, 0xc6, + 0x3e, 0xf7, 0x55, 0xfa, 0x8a, 0xe3, 0xe1, 0x7c, 0xae, 0x0f, 0x51, 0xc5, + 0x69, 0x4f, 0xb1, 0xa4, 0x19, 0x1c, 0x81, 0xc9, 0x19, 0xc7, 0xe1, 0x4e, + 0x58, 0xe3, 0xdd, 0x81, 0xd0, 0x8c, 0xd4, 0xa0, 0xe6, 0x42, 0x7d, 0x06, + 0x2a, 0x19, 0x49, 0x57, 0x26, 0x3f, 0x4e, 0x7d, 0xaa, 0xda, 0xb6, 0xa5, + 0x8c, 0x99, 0xc2, 0x7c, 0xaa, 0x4f, 0xbd, 0x57, 0x03, 0x27, 0x2d, 0xf8, + 0x50, 0x07, 0xcc, 0x4b, 0xd5, 0x87, 0x58, 0xf6, 0x70, 0x4e, 0x7d, 0xeb, + 0x2d, 0xf5, 0x10, 0x8d, 0x1e, 0x7a, 0x1c, 0x8c, 0x66, 0x88, 0xe5, 0x78, + 0xc6, 0x0f, 0x22, 0x92, 0x20, 0xe3, 0x90, 0x78, 0xe9, 0x9a, 0x98, 0x46, + 0xaa, 0x1f, 0x3c, 0xed, 0xaa, 0x4b, 0xaa, 0x01, 0xc8, 0xbe, 0x6b, 0x64, + 0x9c, 0x8e, 0xe7, 0xfa, 0x54, 0x91, 0xf2, 0x59, 0xbd, 0xf1, 0xf9, 0x55, + 0x18, 0xe4, 0x74, 0x62, 0x16, 0xac, 0xa2, 0x9e, 0x12, 0x42, 0x47, 0xb7, + 0xad, 0x5c, 0x65, 0x71, 0x93, 0x19, 0x14, 0x1c, 0x0e, 0x4f, 0xa0, 0xaa, + 0x92, 0x6e, 0x2e, 0x36, 0x8c, 0x1c, 0xf6, 0xab, 0x27, 0xfe, 0x79, 0xc7, + 0xc7, 0xaf, 0xb5, 0x31, 0x40, 0xf3, 0x46, 0x3a, 0x0c, 0xd3, 0x96, 0xa0, + 0x11, 0x18, 0xdf, 0xaf, 0xde, 0x1d, 0x73, 0x56, 0x2a, 0xac, 0xb1, 0x95, + 0x7d, 0xeb, 0xc6, 0x6a, 0x64, 0x90, 0x37, 0x07, 0xad, 0x38, 0xbb, 0x68, + 0xc0, 0x9b, 0xb5, 0x25, 0x2f, 0x6a, 0x4a, 0xb1, 0x91, 0x48, 0x73, 0x84, + 0x1f, 0xc5, 0xfc, 0xa9, 0xcf, 0xc1, 0x4f, 0xaf, 0xf4, 0xa6, 0xa7, 0xcc, + 0x4b, 0xfe, 0x02, 0x9d, 0x27, 0x54, 0xfa, 0xd4, 0xf9, 0x88, 0x63, 0xaf, + 0xcd, 0xf5, 0xfe, 0x62, 0x9a, 0x0e, 0x07, 0x23, 0xe5, 0x3d, 0x47, 0xa1, + 0xa9, 0x5c, 0x6e, 0x5c, 0x0e, 0xbd, 0xa9, 0x99, 0xe9, 0x27, 0x63, 0xd6, + 0x93, 0x5a, 0x80, 0xe2, 0xcd, 0x1f, 0x5f, 0x99, 0x7d, 0x7b, 0x8a, 0x90, + 0x10, 0x46, 0x45, 0x47, 0xfe, 0xac, 0xe0, 0xfd, 0xd3, 0xd3, 0xda, 0x9a, + 0xc3, 0x67, 0xcc, 0x87, 0xea, 0x3b, 0x55, 0x5e, 0xc0, 0x2b, 0x10, 0x64, + 0xff, 0x00, 0x74, 0x66, 0x91, 0x19, 0x56, 0x35, 0x1d, 0x49, 0x1d, 0x2a, + 0xb3, 0x4e, 0x06, 0xe2, 0x07, 0x5a, 0x58, 0xd5, 0x8a, 0xee, 0xeb, 0xf8, + 0xd6, 0x7c, 0xda, 0xe8, 0x04, 0x2c, 0x32, 0xfb, 0x4f, 0x73, 0x5d, 0x7d, + 0xa6, 0x99, 0x6b, 0xf6, 0x75, 0x2e, 0x37, 0x16, 0x19, 0xcf, 0x4e, 0xb5, + 0xc9, 0xed, 0x05, 0xb8, 0xe3, 0x06, 0xba, 0x6d, 0x2f, 0x52, 0x8d, 0xd0, + 0x41, 0x29, 0x0a, 0x57, 0x80, 0x4f, 0xf2, 0xac, 0x88, 0x95, 0xfa, 0x15, + 0x35, 0x3d, 0x28, 0x46, 0xbe, 0x74, 0x1f, 0x74, 0x75, 0x1e, 0x95, 0x85, + 0x1e, 0x49, 0xc1, 0x38, 0xc1, 0xe6, 0xbb, 0x6b, 0xeb, 0x88, 0x16, 0xd5, + 0xf7, 0x30, 0x3b, 0x81, 0x03, 0x1c, 0xf3, 0x5c, 0x59, 0x09, 0xf7, 0xbb, + 0x13, 0x42, 0xee, 0x11, 0x64, 0xe1, 0x55, 0x8e, 0x4f, 0x4f, 0x53, 0xd4, + 0xd3, 0xcb, 0x46, 0x06, 0x32, 0x31, 0x4c, 0x09, 0x9f, 0xba, 0x80, 0x7d, + 0x69, 0x76, 0x1f, 0xe2, 0x3f, 0x97, 0x15, 0xaa, 0x2c, 0x89, 0xca, 0x01, + 0xf2, 0xb7, 0xe1, 0x5b, 0x9a, 0x04, 0x64, 0xb4, 0xb7, 0x07, 0xbe, 0x14, + 0x7f, 0x33, 0x58, 0xb2, 0xed, 0x54, 0xda, 0xa3, 0x93, 0xc5, 0x76, 0x56, + 0x16, 0xff, 0x00, 0x65, 0xb5, 0x48, 0x8f, 0x5c, 0x64, 0xfd, 0x4d, 0x67, + 0x3d, 0xc8, 0x91, 0xff, 0xd2, 0xec, 0xeb, 0x8d, 0xd4, 0x20, 0xfb, 0x2d, + 0xf3, 0x11, 0xf7, 0x64, 0xe4, 0x7e, 0x3f, 0xfd, 0x7a, 0xec, 0xab, 0x23, + 0x58, 0xb6, 0x33, 0xdb, 0x6f, 0x41, 0x96, 0x8f, 0x9f, 0xc3, 0xbd, 0x34, + 0xec, 0xee, 0x54, 0x5d, 0x99, 0xcc, 0xab, 0x1c, 0x61, 0x7a, 0xb1, 0xa9, + 0x55, 0x42, 0x8c, 0x55, 0x78, 0x4b, 0x11, 0xf2, 0x8c, 0xfd, 0x4d, 0x4b, + 0xfb, 0xd3, 0xe9, 0x5b, 0xc5, 0xe9, 0x73, 0x52, 0x19, 0xa1, 0xee, 0x3a, + 0x7f, 0x2a, 0x64, 0x72, 0xbc, 0x60, 0xa9, 0x19, 0xf4, 0xab, 0x24, 0x3e, + 0x32, 0xcd, 0xfa, 0x53, 0x23, 0x42, 0xa3, 0x71, 0x1b, 0x87, 0xa7, 0xa5, + 0x4b, 0x8e, 0xb7, 0x40, 0x28, 0x0a, 0x07, 0xc8, 0x72, 0x3b, 0x8a, 0x89, + 0x9f, 0x82, 0xbf, 0x41, 0xf9, 0x54, 0xca, 0x10, 0x9c, 0x1e, 0x41, 0xe8, + 0x7f, 0xa5, 0x43, 0x34, 0x6a, 0xad, 0x85, 0xef, 0x43, 0xbd, 0x80, 0x6c, + 0x08, 0x4b, 0x02, 0x7b, 0xf3, 0x56, 0x27, 0x95, 0x55, 0x76, 0xf5, 0x35, + 0x0f, 0x9a, 0x23, 0xce, 0x3a, 0xe3, 0x02, 0xa2, 0x44, 0x69, 0x1b, 0x27, + 0x9a, 0x9b, 0xd9, 0x59, 0x08, 0x92, 0x29, 0xdd, 0x01, 0xe3, 0x39, 0xa9, + 0xa3, 0x75, 0x56, 0xc3, 0x71, 0x81, 0x8e, 0x69, 0x7c, 0xb0, 0x8a, 0x07, + 0x72, 0x45, 0x54, 0x90, 0xef, 0x93, 0xeb, 0x4e, 0xee, 0x20, 0x69, 0x36, + 0x1d, 0x71, 0x50, 0x10, 0x31, 0x92, 0x3f, 0xde, 0xc7, 0x63, 0xea, 0x2a, + 0x98, 0xde, 0xbc, 0xa9, 0xfe, 0x95, 0x2a, 0x49, 0x26, 0xec, 0xb1, 0x1d, + 0x3b, 0xd3, 0xe7, 0xbe, 0xe3, 0xb9, 0x75, 0x5b, 0x18, 0x57, 0xfc, 0x0f, + 0xad, 0x12, 0x1c, 0x0c, 0x0e, 0xa7, 0x8a, 0xa8, 0xd2, 0x32, 0x0d, 0xae, + 0xbc, 0x1e, 0x45, 0x2a, 0x4a, 0xe7, 0xe6, 0x23, 0x76, 0x2a, 0xb9, 0xfa, + 0x05, 0xcb, 0xa1, 0x40, 0x18, 0x07, 0xa5, 0x32, 0x51, 0xca, 0x7d, 0x69, + 0x8b, 0x23, 0x37, 0x21, 0x73, 0xf8, 0xd3, 0x26, 0x90, 0x8c, 0x12, 0xb8, + 0xc1, 0xaa, 0x72, 0x56, 0x02, 0xce, 0x0d, 0x45, 0xc2, 0x12, 0xad, 0xd0, + 0xf3, 0xfe, 0x35, 0x54, 0xcf, 0x23, 0x0c, 0x82, 0x14, 0x53, 0x17, 0x76, + 0xf0, 0x58, 0xe7, 0x3d, 0xcd, 0x43, 0x9a, 0xe8, 0x17, 0x26, 0x69, 0xc2, + 0x82, 0x83, 0xe6, 0x03, 0xbf, 0xb5, 0x32, 0x22, 0xce, 0x72, 0x46, 0xec, + 0x76, 0xa9, 0xde, 0x20, 0x63, 0xc2, 0xf3, 0x8e, 0x7e, 0xb5, 0x4d, 0x1c, + 0xc2, 0xf9, 0x1d, 0xaa, 0x64, 0xda, 0x6a, 0xe0, 0x4b, 0x30, 0x56, 0xe8, + 0x30, 0x7d, 0x2a, 0x38, 0x9d, 0x97, 0x2a, 0x3a, 0x9e, 0x28, 0x67, 0x92, + 0x53, 0x93, 0xd2, 0x9a, 0xe0, 0xa9, 0x04, 0xfe, 0x35, 0x2d, 0xeb, 0x74, + 0x22, 0xcb, 0xab, 0x85, 0x04, 0x00, 0x31, 0x50, 0x15, 0x2a, 0xc4, 0xa9, + 0xc1, 0xef, 0x56, 0x44, 0x81, 0xa2, 0x04, 0xf5, 0x04, 0x66, 0x84, 0x05, + 0xf8, 0xec, 0x7a, 0x9f, 0xe9, 0x56, 0xd2, 0x7b, 0x0c, 0xad, 0xf3, 0x91, + 0x86, 0x6e, 0x3d, 0xaa, 0x55, 0x52, 0x09, 0x05, 0x73, 0x81, 0xd2, 0xa5, + 0x64, 0x0a, 0x70, 0x3e, 0xeb, 0x7e, 0x86, 0x91, 0x4f, 0xcc, 0x09, 0xfa, + 0x1a, 0x4a, 0x36, 0x7a, 0x80, 0x8a, 0xfb, 0x7e, 0xef, 0x23, 0xd3, 0xb8, + 0xa9, 0x41, 0x04, 0x64, 0x53, 0x99, 0x03, 0x0e, 0x45, 0x56, 0x75, 0x68, + 0xfe, 0x65, 0x3d, 0x7b, 0x1a, 0xbd, 0x50, 0x17, 0xb4, 0xf8, 0x3e, 0xd5, + 0x7e, 0x33, 0xf7, 0x63, 0xf9, 0x8f, 0xe1, 0x5d, 0x95, 0x64, 0x68, 0xd6, + 0xa6, 0xde, 0xd7, 0x7b, 0x8c, 0x3c, 0x87, 0x27, 0xe9, 0xda, 0xb5, 0xeb, + 0x06, 0xef, 0xa9, 0x94, 0x9d, 0xd9, 0xff, 0xd3, 0xec, 0xe9, 0x29, 0x68, + 0xa0, 0x0e, 0x3f, 0x51, 0xb5, 0x36, 0x37, 0x1e, 0x6c, 0x63, 0xf7, 0x52, + 0x7e, 0x87, 0xd2, 0xa0, 0x0d, 0x9e, 0x6b, 0xb0, 0xb9, 0xb7, 0x8e, 0xe6, + 0x16, 0x86, 0x4e, 0x87, 0xf4, 0xae, 0x25, 0x95, 0xed, 0x64, 0x6b, 0x79, + 0x87, 0x2b, 0xd3, 0xde, 0xb4, 0x84, 0xad, 0xa1, 0xa4, 0x58, 0xf6, 0x3b, + 0x98, 0x2f, 0x61, 0xc9, 0xa9, 0x86, 0x30, 0x6a, 0xb2, 0xb0, 0x51, 0xc7, + 0xcc, 0x4f, 0x5c, 0x53, 0xf1, 0x23, 0x0e, 0x4e, 0xd1, 0xed, 0x5a, 0xa6, + 0x58, 0x92, 0x2a, 0x63, 0x39, 0xda, 0x6a, 0xb1, 0xcc, 0x8d, 0x96, 0xab, + 0x24, 0x2c, 0x6a, 0x58, 0x72, 0x69, 0xac, 0xbf, 0x28, 0x88, 0x75, 0xea, + 0x6a, 0x64, 0xae, 0x04, 0x29, 0x13, 0x48, 0xd9, 0x3f, 0xfe, 0xaa, 0xbc, + 0xb1, 0x84, 0x18, 0x15, 0x0a, 0x2b, 0x2a, 0x29, 0x53, 0x9c, 0xf6, 0x34, + 0xf1, 0x20, 0xce, 0x1b, 0x83, 0xef, 0x4e, 0x29, 0x20, 0x1b, 0x39, 0xda, + 0x05, 0x50, 0x19, 0xc9, 0x20, 0x13, 0xda, 0xac, 0xdc, 0x9e, 0x47, 0xb5, + 0x45, 0x19, 0x40, 0x06, 0xee, 0x95, 0x9c, 0xf5, 0x90, 0x85, 0x13, 0x73, + 0x9d, 0xbc, 0xf7, 0xa9, 0x50, 0x00, 0x7c, 0xcf, 0xcc, 0x7a, 0x52, 0x34, + 0x67, 0x1b, 0xc7, 0x03, 0xd3, 0xad, 0x48, 0xfb, 0xb1, 0x96, 0xc6, 0x7d, + 0x47, 0x14, 0xd2, 0x7d, 0x46, 0x45, 0x77, 0xf7, 0xbf, 0x01, 0x52, 0x5b, + 0x0f, 0x94, 0x9f, 0xa5, 0x42, 0x09, 0x66, 0x01, 0x87, 0xff, 0x00, 0xaa, + 0xac, 0xc4, 0x55, 0x55, 0x89, 0xe3, 0x9a, 0x71, 0xd6, 0x57, 0x01, 0xec, + 0x88, 0x79, 0x3c, 0x7b, 0xd5, 0x39, 0x0b, 0x39, 0xc6, 0x72, 0x07, 0x7f, + 0xad, 0x5b, 0xe1, 0xfe, 0x67, 0xe1, 0x47, 0x6f, 0x5a, 0x87, 0x05, 0xd4, + 0xe3, 0xbf, 0x27, 0xfc, 0x2a, 0xa5, 0xa8, 0x15, 0x55, 0xf6, 0x11, 0x91, + 0xd3, 0x8e, 0x6a, 0x51, 0x32, 0xe4, 0x0c, 0x70, 0x0d, 0x4a, 0xd1, 0xa0, + 0x40, 0x4b, 0x75, 0x1d, 0x0f, 0x35, 0x51, 0x80, 0x03, 0x70, 0x18, 0xc5, + 0x64, 0xef, 0x11, 0x17, 0x32, 0x07, 0x31, 0xe4, 0x7e, 0x1c, 0x55, 0x57, + 0xe4, 0x6e, 0xc6, 0x2a, 0xc2, 0x09, 0x19, 0x46, 0x46, 0x54, 0x7a, 0x53, + 0xa4, 0x2a, 0x57, 0x04, 0x10, 0x47, 0x4c, 0x8a, 0xa6, 0xae, 0x86, 0x47, + 0x03, 0x12, 0x49, 0x5c, 0x67, 0xde, 0x96, 0x6c, 0x1f, 0xbc, 0x30, 0x7a, + 0x54, 0x28, 0xac, 0xbf, 0x3a, 0x76, 0xab, 0x2a, 0x04, 0x83, 0xe7, 0xe4, + 0xfa, 0x51, 0x1d, 0x55, 0x84, 0x56, 0x08, 0xd8, 0x1e, 0x95, 0x79, 0x19, + 0x36, 0xed, 0x1c, 0x7b, 0x1a, 0x8c, 0x0e, 0x0c, 0x4d, 0xf8, 0x53, 0xc6, + 0x24, 0x8c, 0x6e, 0x1c, 0xd5, 0x45, 0x5b, 0x61, 0x8e, 0x60, 0x18, 0x60, + 0xd5, 0x56, 0xca, 0x9c, 0x1e, 0xb5, 0x36, 0xd7, 0x5f, 0xba, 0x73, 0xec, + 0x6a, 0x39, 0x08, 0x61, 0xcf, 0x06, 0x89, 0x01, 0x39, 0x3f, 0x28, 0x35, + 0x63, 0x4d, 0xb5, 0x37, 0xb7, 0x1e, 0x64, 0x83, 0xf7, 0x49, 0xfa, 0x9f, + 0x4a, 0xa3, 0x12, 0xc9, 0x72, 0x52, 0xde, 0x31, 0xf3, 0x1e, 0x2b, 0xb6, + 0xb6, 0xb7, 0x4b, 0x58, 0x56, 0x18, 0xfa, 0x0f, 0xd4, 0xfa, 0xd4, 0x4e, + 0x57, 0xd0, 0x89, 0x32, 0x7a, 0x5a, 0x28, 0xac, 0xcc, 0xcf, 0xff, 0xd4, + 0xec, 0xe8, 0xa2, 0x8a, 0x00, 0x2b, 0x2b, 0x52, 0xd3, 0x96, 0xf1, 0x37, + 0xa7, 0x12, 0x28, 0xe3, 0xdf, 0xda, 0xb5, 0x68, 0xa0, 0x0e, 0x0d, 0x49, + 0x46, 0x31, 0x48, 0x36, 0xb2, 0xf0, 0x45, 0x4b, 0xda, 0xba, 0x1d, 0x4b, + 0x4d, 0x5b, 0xb5, 0xf3, 0x23, 0xf9, 0x65, 0x5e, 0x87, 0xd7, 0xd8, 0xd7, + 0x34, 0x1d, 0x94, 0x98, 0xe5, 0x18, 0x65, 0xeb, 0x9a, 0xda, 0x13, 0xe8, + 0xcd, 0xa3, 0x2b, 0x88, 0xe4, 0x6e, 0x1e, 0xdc, 0xff, 0x00, 0x85, 0x39, + 0x14, 0x80, 0x4b, 0x75, 0x3d, 0x6a, 0x38, 0xfe, 0x72, 0x5c, 0x8e, 0x3b, + 0x55, 0x8e, 0x2a, 0x96, 0xba, 0x8c, 0x6c, 0x67, 0xf7, 0x6b, 0x4e, 0x20, + 0x11, 0x83, 0x4c, 0x8f, 0x1b, 0x71, 0xe8, 0x4d, 0x12, 0x1e, 0x88, 0x0f, + 0x26, 0xab, 0xa0, 0x10, 0x79, 0x61, 0xdf, 0x8e, 0x9d, 0xa9, 0xaf, 0x6e, + 0xc0, 0xff, 0x00, 0x85, 0x5a, 0x23, 0x0e, 0x98, 0xfa, 0x54, 0x84, 0x1c, + 0xd4, 0xf2, 0x26, 0x16, 0x32, 0xf6, 0x32, 0x9c, 0x13, 0x8a, 0xb6, 0x9b, + 0x50, 0xe7, 0x86, 0x1e, 0xbd, 0xe9, 0xd2, 0x29, 0x73, 0xb4, 0x76, 0x19, + 0xa7, 0xaa, 0x23, 0x80, 0x48, 0x1c, 0xd2, 0x8c, 0x6c, 0xf4, 0x02, 0x09, + 0x08, 0x69, 0x72, 0x39, 0x15, 0x0f, 0x9a, 0x55, 0xba, 0x64, 0x66, 0xac, + 0x08, 0x93, 0x7b, 0x0c, 0x7a, 0x54, 0x9e, 0x52, 0x7b, 0xfe, 0x74, 0xb9, + 0x5b, 0xd4, 0x0a, 0xc6, 0x76, 0x94, 0x6c, 0x03, 0x19, 0xa7, 0xe4, 0x85, + 0xda, 0x4e, 0xdf, 0x6c, 0x50, 0xe8, 0x01, 0xf9, 0x73, 0xf2, 0xf2, 0x6a, + 0x47, 0x8d, 0x02, 0x13, 0x8e, 0xd4, 0x59, 0xf5, 0x02, 0x89, 0x0c, 0x6a, + 0x58, 0xe1, 0x66, 0xe7, 0xaf, 0xf2, 0xa9, 0xe3, 0x50, 0xbf, 0x29, 0x1d, + 0x47, 0x14, 0xf8, 0xfe, 0x5c, 0xa1, 0xed, 0xd2, 0x94, 0x61, 0xdc, 0x56, + 0x22, 0x8b, 0x3f, 0x74, 0xb1, 0x1f, 0x4a, 0x9f, 0x62, 0xf5, 0x3c, 0x9f, + 0x7a, 0x62, 0xa8, 0x60, 0xca, 0x7b, 0x1a, 0x72, 0xb1, 0xce, 0xc6, 0xeb, + 0xfc, 0xea, 0xd7, 0x98, 0xc4, 0x1c, 0x6e, 0x8f, 0xd7, 0x91, 0x48, 0x06, + 0xf4, 0x56, 0x1c, 0x10, 0x3a, 0xd3, 0x9f, 0x8d, 0xaf, 0xf8, 0x1a, 0x6c, + 0x7c, 0x02, 0x3d, 0x0d, 0x1d, 0x6c, 0x02, 0x16, 0xde, 0x31, 0x8c, 0x38, + 0xa5, 0x52, 0x37, 0x67, 0xfb, 0xdf, 0xce, 0x9c, 0xca, 0x1a, 0xa0, 0x72, + 0xc8, 0x41, 0x34, 0x3d, 0x35, 0x02, 0xcf, 0x15, 0x13, 0x92, 0xcc, 0x22, + 0x8c, 0x6e, 0x66, 0xe3, 0x14, 0x85, 0x99, 0x88, 0x48, 0x86, 0xe6, 0x6e, + 0x98, 0xae, 0x9f, 0x4d, 0xd3, 0x56, 0xd1, 0x7c, 0xd9, 0x7e, 0x69, 0x5b, + 0xa9, 0xf4, 0xf6, 0x15, 0x33, 0x9f, 0x44, 0x29, 0x4a, 0xc2, 0xe9, 0x9a, + 0x70, 0xb3, 0x4d, 0xef, 0xcc, 0x8c, 0x39, 0xf6, 0xf6, 0xad, 0x5a, 0x28, + 0xac, 0x4c, 0x42, 0x8a, 0x28, 0xa0, 0x0f, 0xff, 0xd5, 0xec, 0xe8, 0xa2, + 0x8a, 0x00, 0x28, 0xa2, 0x8a, 0x00, 0x2b, 0x2b, 0x51, 0xd3, 0x52, 0xf1, + 0x77, 0xa6, 0x16, 0x41, 0xdf, 0xd7, 0xeb, 0x5a, 0xb4, 0x50, 0x07, 0x08, + 0xeb, 0x2d, 0xb3, 0xf9, 0x57, 0x0a, 0x54, 0x8a, 0x78, 0x39, 0xae, 0xc6, + 0xe2, 0xda, 0x1b, 0xa4, 0xf2, 0xe6, 0x5c, 0x8f, 0xd4, 0x57, 0x2d, 0x75, + 0xa6, 0x5c, 0x59, 0x13, 0x24, 0x5f, 0xbc, 0x8f, 0xaf, 0xb8, 0xfa, 0xd6, + 0x91, 0x9f, 0x73, 0x45, 0x22, 0xaa, 0xf0, 0x5b, 0xd8, 0xd2, 0x20, 0xcf, + 0xce, 0x7b, 0xf4, 0xfa, 0x54, 0x41, 0xf7, 0xb1, 0x5e, 0x99, 0x39, 0x35, + 0x62, 0xb4, 0x5a, 0x96, 0x35, 0xfb, 0x37, 0xa1, 0x15, 0x26, 0x7b, 0xd3, + 0x18, 0x65, 0x48, 0xf6, 0xa6, 0x31, 0xca, 0x00, 0x3b, 0xd3, 0xbd, 0x80, + 0x74, 0x64, 0x90, 0x5b, 0xd4, 0xd3, 0xa2, 0x24, 0x6e, 0x5f, 0x4a, 0x51, + 0x80, 0x30, 0x29, 0x84, 0xed, 0x70, 0x7d, 0x78, 0xa3, 0x60, 0x1c, 0xa4, + 0xf9, 0x8d, 0xf8, 0x53, 0x99, 0xc2, 0x82, 0x4f, 0x6a, 0x8d, 0x4f, 0xef, + 0x1b, 0xf0, 0xa4, 0x3f, 0x3b, 0x6d, 0xec, 0x39, 0x34, 0x5f, 0x40, 0x1c, + 0x07, 0xee, 0x9b, 0x3d, 0x4f, 0x34, 0x8c, 0x41, 0x87, 0x3e, 0xa2, 0xa4, + 0xed, 0x55, 0xff, 0x00, 0xe5, 0x9e, 0xdf, 0x7c, 0x7e, 0xb4, 0x30, 0x26, + 0x65, 0x0c, 0xbc, 0x1e, 0x47, 0x4a, 0x66, 0x47, 0xcb, 0x27, 0xe0, 0x6a, + 0x4a, 0x8e, 0x45, 0x03, 0x9e, 0xc7, 0xaf, 0xf8, 0xd0, 0xfb, 0x80, 0xe5, + 0x18, 0x91, 0x87, 0xd0, 0xd2, 0xb2, 0x6e, 0x15, 0x12, 0x9c, 0xb0, 0x27, + 0xae, 0x31, 0xf9, 0x54, 0xb4, 0x20, 0x18, 0x72, 0xc3, 0xcb, 0x6e, 0xb4, + 0xd8, 0xc9, 0x24, 0xe6, 0xa4, 0x71, 0x9f, 0xa8, 0xaa, 0xde, 0x67, 0x97, + 0x21, 0x2d, 0xdc, 0x54, 0xb7, 0x67, 0xa8, 0x16, 0x89, 0xc5, 0x46, 0x16, + 0x5b, 0xb7, 0xf2, 0x6d, 0xd7, 0x71, 0xf5, 0xec, 0x2a, 0xe5, 0xa6, 0x9b, + 0x71, 0x7b, 0x89, 0x26, 0xfd, 0xdc, 0x5f, 0xa9, 0xfa, 0x57, 0x53, 0x05, + 0xbc, 0x36, 0xc9, 0xe5, 0xc2, 0xbb, 0x47, 0xea, 0x6a, 0x25, 0x3b, 0xe8, + 0x88, 0x72, 0x28, 0xe9, 0xda, 0x62, 0x59, 0x8d, 0xee, 0x77, 0x48, 0x7b, + 0xfa, 0x7d, 0x2b, 0x56, 0x8a, 0x2b, 0x33, 0x30, 0xa2, 0x8a, 0x28, 0x00, + 0xa2, 0x8a, 0x28, 0x03, 0xff, 0xd6, 0xec, 0xe8, 0xa2, 0x8a, 0x00, 0x28, + 0xa2, 0x8a, 0x00, 0x28, 0xa2, 0x8a, 0x00, 0x29, 0x29, 0x68, 0xa0, 0x0c, + 0x8b, 0xad, 0x1e, 0xda, 0xe0, 0x97, 0x4f, 0xdd, 0xb9, 0xee, 0x3a, 0x7e, + 0x55, 0x87, 0x3e, 0x9d, 0x7d, 0x6b, 0xce, 0x3c, 0xc5, 0xf5, 0x5e, 0x6b, + 0xb3, 0xa2, 0x9a, 0x76, 0xd8, 0xa5, 0x26, 0x8e, 0x09, 0x66, 0x43, 0xc3, + 0x7c, 0xa7, 0xde, 0x92, 0x3f, 0x98, 0x81, 0xfd, 0xd1, 0x5d, 0x95, 0xc5, + 0x85, 0xad, 0xd7, 0xfa, 0xd4, 0x19, 0xf5, 0x1c, 0x1a, 0xc7, 0x7d, 0x05, + 0x97, 0x26, 0x09, 0x7f, 0x06, 0x1f, 0xd4, 0x55, 0xa9, 0xf7, 0x29, 0x4c, + 0xcc, 0xc5, 0x35, 0xc1, 0x28, 0x71, 0xd7, 0xad, 0x59, 0x7d, 0x33, 0x52, + 0x8b, 0xa2, 0x87, 0x1e, 0xc7, 0xff, 0x00, 0xd5, 0x55, 0x18, 0xcf, 0x17, + 0x12, 0xc6, 0xcb, 0xf5, 0x18, 0xab, 0xe7, 0x4c, 0xab, 0xa1, 0xa0, 0xe0, + 0xb3, 0x7b, 0x0a, 0x7a, 0x0c, 0x0c, 0x9e, 0xa7, 0x93, 0x55, 0x55, 0xc0, + 0x38, 0x3d, 0x2a, 0x7f, 0xb4, 0x2f, 0xbd, 0x25, 0x25, 0xd4, 0x64, 0xdd, + 0xaa, 0x03, 0xc3, 0x6d, 0xf5, 0x20, 0xd2, 0xfd, 0xa1, 0x2a, 0x36, 0x95, + 0x4b, 0x86, 0x03, 0xa5, 0x39, 0x49, 0x01, 0x66, 0x86, 0xe4, 0x62, 0x98, + 0x9e, 0x7c, 0xbc, 0x43, 0x13, 0x37, 0xd0, 0x55, 0xb4, 0xd3, 0x75, 0x29, + 0x7a, 0xa8, 0x41, 0xee, 0x7f, 0xfd, 0x74, 0x39, 0xa1, 0x5d, 0x19, 0xc3, + 0x29, 0x20, 0x07, 0xa5, 0x3d, 0xa5, 0x51, 0xc0, 0xe4, 0xfb, 0x56, 0xd4, + 0x7a, 0x0b, 0x12, 0x0c, 0xf2, 0xfe, 0x00, 0x7f, 0x53, 0x5b, 0x16, 0xf6, + 0x36, 0xb6, 0xbf, 0xea, 0x90, 0x67, 0xd4, 0xf2, 0x6b, 0x3e, 0x7b, 0x6c, + 0x4f, 0x31, 0xcd, 0x43, 0xa7, 0x5f, 0x5d, 0x7c, 0xc4, 0x79, 0x6b, 0xea, + 0x78, 0xff, 0x00, 0xeb, 0xd6, 0xdd, 0xb6, 0x91, 0x6b, 0x6e, 0x43, 0xb7, + 0xef, 0x18, 0x77, 0x3d, 0x3f, 0x2a, 0xd6, 0xa2, 0xa5, 0xb6, 0xf7, 0x25, + 0xc9, 0xb1, 0x29, 0x68, 0xa2, 0x91, 0x21, 0x45, 0x14, 0x50, 0x01, 0x45, + 0x14, 0x50, 0x01, 0x45, 0x14, 0x50, 0x07, 0xff, 0xd9 + +}; + +const size_t oomerbot_jpeg_size = 6777; + +#endif // OOMERBOT_JPEG_H diff --git a/poomer-discord.cpp b/poomer-discord.cpp new file mode 100644 index 0000000..2e2e89e --- /dev/null +++ b/poomer-discord.cpp @@ -0,0 +1,428 @@ +// Discord bot library - provides all Discord API functionality +#include "../DPP/include/dpp/dpp.h" + +// Standard input/output - for console logging (std::cout, std::cerr) +#include + +// String handling - for std::string class and std::to_string() function +#include + +// Terminal I/O control - for hiding password input in getHiddenInput() +#include + +// Unix standard definitions - provides STDIN_FILENO constant +#include + +// Time functions - for std::time() to generate timestamps +#include + +// Dynamic arrays - for std::vector to hold image byte data +#include + +// Threading support - for std::thread to send images asynchronously +#include + +// Time utilities - for std::chrono::seconds() delays +#include + +// Algorithm functions - for std::transform (string case conversion) +#include + +// STB Image library - single header for loading images (JPEG, PNG, etc.) +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +// STB Image Write library - single header for saving images +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "stb_image_write.h" + +// Embedded image data - contains the JPEG as a byte array (our generated header) +#include "embedded_image.h" + +/** + * Function to securely input text without displaying it on screen (like password input) + * This is used for Discord bot tokens to keep them private in the terminal + * + * @param prompt - The text to display before asking for input + * @return std::string - The hidden text that was typed + */ +std::string getHiddenInput(const std::string& prompt) { + // Display the prompt to the user + std::cout << prompt; + std::cout.flush(); // Force immediate output (don't wait for newline) + + // Save current terminal settings so we can restore them later + termios oldt; + tcgetattr(STDIN_FILENO, &oldt); // Get current terminal attributes + + // Create new terminal settings that disable echo (hide typed characters) + termios newt = oldt; // Copy current settings + newt.c_lflag &= ~ECHO; // Remove the ECHO flag (disables character display) + tcsetattr(STDIN_FILENO, TCSANOW, &newt); // Apply new settings immediately + + // Read the hidden input + std::string input; + std::getline(std::cin, input); // Read entire line (including spaces) + + // Restore original terminal settings (re-enable echo) + tcsetattr(STDIN_FILENO, TCSANOW, &oldt); + std::cout << std::endl; // Add newline since user's Enter wasn't displayed + + return input; +} + +/** + * Function to vertically flip JPEG image data using STB libraries (upside-down) + * + * @param image_data - The JPEG data as bytes + * @return std::vector - The vertically flipped JPEG image data + */ +std::vector flipImageVertically(const std::vector& image_data) { + std::cout << "šŸ“ø Flipping image (" << image_data.size() << " bytes)..." << std::endl; + + // Step 1: Decode JPEG to raw pixel data + int width, height, channels; + unsigned char* decoded_data = stbi_load_from_memory( + image_data.data(), // Input JPEG data + image_data.size(), // Size of input data + &width, // Output: image width + &height, // Output: image height + &channels, // Output: number of color channels + 0 // Desired channels (0 = keep original) + ); + + if (!decoded_data) { + std::cout << "āŒ Failed to decode JPEG image: " << stbi_failure_reason() << std::endl; + return image_data; // Return original if decode fails + } + + std::cout << "āœ… Decoded image: " << width << "x" << height << " pixels, " << channels << " channels" << std::endl; + + // Step 2: Create buffer for flipped image data + int bytes_per_pixel = channels; + int row_size = width * bytes_per_pixel; + std::vector flipped_pixels(width * height * bytes_per_pixel); + + // Step 3: Flip vertically (reverse row order to make image upside-down) + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + // Calculate source and destination pixel positions + int src_index = (y * width + x) * bytes_per_pixel; // Original position + int dst_index = ((height - 1 - y) * width + x) * bytes_per_pixel; // Flipped position (vertically) + + // Copy all color channels for this pixel + for (int c = 0; c < bytes_per_pixel; c++) { + flipped_pixels[dst_index + c] = decoded_data[src_index + c]; + } + } + } + + // Step 4: Encode flipped pixels back to JPEG + std::vector output_jpeg; + + // Helper function to capture JPEG data written by stb_image_write + // This is a "callback" - STB calls this function whenever it has encoded data to write + auto write_func = [](void* context, void* data, int size) { + // Cast void* back to our vector (context is the &output_jpeg from below) + std::vector* output = static_cast*>(context); + uint8_t* bytes = static_cast(data); + // Append the new JPEG bytes to our output vector + output->insert(output->end(), bytes, bytes + size); + }; + + // Write flipped pixels to JPEG format (quality: 90/100) + int success = stbi_write_jpg_to_func( + write_func, // Callback function to capture data + &output_jpeg, // Context passed to callback + width, // Image width + height, // Image height + channels, // Number of channels + flipped_pixels.data(), // Pixel data + 90 // JPEG quality (0-100) + ); + + // Clean up decoded image memory + stbi_image_free(decoded_data); + + if (!success) { + std::cout << "āŒ Failed to encode flipped image to JPEG" << std::endl; + return image_data; // Return original if encode fails + } + return output_jpeg; +} + +/** + * MAIN FUNCTION - Entry point of the Discord bot program + * Sets up the bot, registers commands, and starts the event loop + */ +int main() { + // Step 1: Get Discord bot token securely from user + std::cout << "=== Discord Bot Startup ===" << std::endl; + std::string BOT_TOKEN = getHiddenInput("Enter Discord Bot Token: "); + + // Validate that a token was provided + if (BOT_TOKEN.empty()) { + std::cerr << "Error: Bot token cannot be empty!" << std::endl; + return 1; // Exit with error code + } + + // Step 2: Create Discord bot instance + // dpp::cluster is the main bot class that handles all Discord connections + // i_default_intents gives basic permissions, i_message_content allows reading message text + dpp::cluster bot(BOT_TOKEN, dpp::i_default_intents | dpp::i_message_content); + + // Step 3: Enable logging to see what the bot is doing + bot.on_log(dpp::utility::cout_logger()); + + // Step 4: Set up event handler for file uploads + // Lambda function that gets called whenever a message with attachments is posted + bot.on_message_create([&bot](const dpp::message_create_t& event) { + // Ignore messages sent by bots (including our own) to prevent loops + if (event.msg.author.is_bot()) { + return; // Early exit - don't process bot messages + } + + // Check if this message has any file attachments + if (!event.msg.attachments.empty()) { + // Debug output to console (only you see this, not Discord users) + std::cout << "\n=== FILE UPLOAD DETECTED ===" << std::endl; + std::cout << "User: " << event.msg.author.username << "#" << event.msg.author.discriminator << std::endl; + std::cout << "Channel ID: " << event.msg.channel_id << std::endl; + std::cout << "Message ID: " << event.msg.id << std::endl; + std::cout << "Attachments: " << event.msg.attachments.size() << std::endl; + + // Flag to track if we found any JPEG files + bool found_jpeg = false; + std::vector jpeg_attachments; // Store JPEG attachments for processing + + // Loop through each attached file + for (const auto& attachment : event.msg.attachments) { + // Print file details to console + std::cout << " - File: " << attachment.filename << std::endl; + std::cout << " Size: " << attachment.size << " bytes" << std::endl; + std::cout << " URL: " << attachment.url << std::endl; + + // Check if filename ends with ".jpg" or ".jpeg" (case-insensitive) + std::string filename_lower = attachment.filename; + // Convert entire filename to lowercase for easier comparison + std::transform(filename_lower.begin(), filename_lower.end(), filename_lower.begin(), ::tolower); + + // Check if file ends with .jpg (4 chars) or .jpeg (5 chars) + // substr(length - N) gets the last N characters of the string + if ((filename_lower.length() >= 4 && filename_lower.substr(filename_lower.length() - 4) == ".jpg") || + (filename_lower.length() >= 5 && filename_lower.substr(filename_lower.length() - 5) == ".jpeg")) { + std::cout << " āœ… JPEG FILE DETECTED!" << std::endl; + found_jpeg = true; + jpeg_attachments.push_back(attachment); // Store for processing + } + } + + // If we found JPEG files, process them + if (found_jpeg) { + std::cout << "\nšŸŽÆ ACTION: Processing JPEG file upload" << std::endl; + + // Add emoji reaction to the message (like clicking a reaction in Discord) + bot.message_add_reaction(event.msg.id, event.msg.channel_id, "šŸ”„"); + + // Send a text response in the same channel + bot.message_create(dpp::message(event.msg.channel_id, "šŸ“ø JPEG detected! Flipping image upside-down...")); + + // Process each JPEG in a separate thread (after 5 second delay) + // We use threads so the bot doesn't freeze while processing images + for (const auto& jpeg_attachment : jpeg_attachments) { + std::thread([&bot, // Capture bot by reference (shared) + channel_id = event.msg.channel_id, // Copy these values into the thread + user_id = event.msg.author.id, // so they don't get lost when event ends + attachment_url = jpeg_attachment.url, + original_filename = jpeg_attachment.filename + ]() { + // Wait 5 seconds before processing + std::this_thread::sleep_for(std::chrono::seconds(5)); + + std::cout << "\n--- PROCESSING JPEG IMAGE ---" << std::endl; + std::cout << "Downloading: " << original_filename << std::endl; + std::cout << "From URL: " << attachment_url << std::endl; + + // Download the image using DPP's HTTP client + std::cout << "🌐 Starting image download..." << std::endl; + bot.request(attachment_url, dpp::m_get, [&bot, channel_id, user_id, original_filename](const dpp::http_request_completion_t& response) { + if (response.status == 200) { + std::cout << "āœ… Downloaded image (" << response.body.size() << " bytes)" << std::endl; + + // Convert response body to vector for processing + std::vector original_data(response.body.begin(), response.body.end()); + + // Flip the image vertically (upside-down) + std::vector flipped_data = flipImageVertically(original_data); + + // Create new filename for flipped image + std::string flipped_filename = "upside_down_" + original_filename; + + // Create message with flipped image + std::string message_text = "šŸ”„ Here's your upside-down image! <@" + std::to_string(user_id) + ">"; + dpp::message msg(channel_id, message_text); + msg.add_file(flipped_filename, std::string(flipped_data.begin(), flipped_data.end())); + + // Send the flipped image + bot.message_create(msg, [original_filename](const dpp::confirmation_callback_t& callback) { + if (callback.is_error()) { + std::cout << "āŒ Failed to send flipped image: " << callback.get_error().message << std::endl; + } else { + std::cout << "āœ… Successfully sent flipped " << original_filename << "!" << std::endl; + } + }); + + } else { + std::cout << "āŒ Failed to download image. Status: " << response.status << std::endl; + bot.message_create(dpp::message(channel_id, "āŒ Failed to download image for processing.")); + } + }); + + }).detach(); // detach() = thread runs independently, we don't wait for it to finish + } + } + + std::cout << "============================" << std::endl; + } + }); + + // Step 5: Set up slash command handler (commands that start with /) + // [&bot] captures the bot variable by reference so we can use it inside the lambda + bot.on_slashcommand([&bot](const dpp::slashcommand_t& event) { + // Generate unique ID for this command execution (for debugging) + static int command_counter = 0; // static = keeps value between function calls (like a global variable) + int command_id = ++command_counter; // Pre-increment: add 1 then use value (so first command = 1) + + // Print detailed debug information about the command + std::cout << "\n=== COMMAND RECEIVED #" << command_id << " ===" << std::endl; + std::cout << "Command: " << event.command.get_command_name() << std::endl; + std::cout << "User: " << event.command.get_issuing_user().username << "#" << event.command.get_issuing_user().discriminator << std::endl; + std::cout << "User ID: " << event.command.get_issuing_user().id << std::endl; + std::cout << "Guild ID: " << event.command.guild_id << std::endl; + std::cout << "Channel ID: " << event.command.channel_id << std::endl; + std::cout << "Timestamp: " << std::time(nullptr) << std::endl; // Unix timestamp + std::cout << "Interaction ID: " << event.command.id << std::endl; + + // Check if this is the command we care about + if (event.command.get_command_name() == "oomer") { + std::cout << "Status: EXPECTED COMMAND (Execution #" << command_id << ")" << std::endl; + std::cout << "========================" << std::endl; + + // CRITICAL: Must acknowledge Discord interaction within 3 seconds + // or Discord shows "The application did not respond" error + std::cout << "Sending interaction acknowledgment for command #" << command_id << "..." << std::endl; + event.reply("šŸš€ Render started... (Command #" + std::to_string(command_id) + ")"); + + // Generate unique thread ID for debugging async operations + static int thread_counter = 0; // Separate counter for threads + int current_thread_id = ++thread_counter; + + // Start background thread to send image after delay + // This prevents blocking the main Discord event loop + std::thread([&bot, // Capture bot by reference + channel_id = event.command.channel_id, // Copy channel ID + user_id = event.command.get_issuing_user().id, // Copy user ID + thread_id = current_thread_id, // Copy thread ID + cmd_id = command_id // Copy command ID + ]() { + // Wait 5 seconds before sending image (simulates processing time) + std::this_thread::sleep_for(std::chrono::seconds(5)); + + std::cout << "\n--- SENDING EMBEDDED IMAGE ---" << std::endl; + std::cout << "Using embedded JPEG data (" << oomerbot_jpeg_size << " bytes)..." << std::endl; + + // Create vector from embedded image data (no file I/O needed!) + // This copies data from the compiled-in byte array to a vector + std::vector image_data(oomerbot_jpeg_data, oomerbot_jpeg_data + oomerbot_jpeg_size); + + // Sanity check - make sure we have image data + if (!image_data.empty()) { + // Create message text with user mention (@username becomes clickable) + std::string message_text = "šŸŽØ Render complete! <@" + std::to_string(user_id) + ">"; + + // Create Discord message object + dpp::message msg(channel_id, message_text); + + // Attach the image file to the message + // First parameter: filename that appears in Discord + // Second parameter: file data as string + msg.add_file("oomerbot.jpeg", std::string(image_data.begin(), image_data.end())); + + // Send the message with image attachment + // Lambda function handles the response (success or error) + bot.message_create(msg, [thread_id, cmd_id](const dpp::confirmation_callback_t& callback) { + if (callback.is_error()) { + // Something went wrong - print error details + std::cout << "āŒ Thread #" << thread_id << " (command #" << cmd_id << ") failed to send image: " << callback.get_error().message << std::endl; + std::cout << "Error code: " << callback.get_error().code << std::endl; + } else { + // Success! + std::cout << "āœ… Thread #" << thread_id << " (command #" << cmd_id << ") successfully sent embedded oomerbot.jpeg!" << std::endl; + } + }); + } else { + // This should never happen since image is embedded at compile time + std::cout << "āŒ Embedded image data is empty" << std::endl; + } + }).detach(); // detach() = thread runs independently, don't wait for it + + } else { + // Handle unexpected commands (probably old registrations) + std::cout << "Status: UNEXPECTED COMMAND (probably old registration) - Command #" << command_id << std::endl; + std::cout << "========================" << std::endl; + event.reply("āš ļø This command is no longer supported. Please use `/oomer` instead."); + } + }); + + // Step 6: Set up bot ready event (called when bot successfully connects to Discord) + bot.on_ready([&bot](const dpp::ready_t& event) { + // run_once ensures this only happens on first connection, not reconnections + if (dpp::run_once()) { + std::cout << "Bot is ready! Starting command registration..." << std::endl; + std::cout << "Bot user: " << bot.me.username << " (ID: " << bot.me.id << ")" << std::endl; + + // Clean up any old slash commands before registering new ones + std::cout << "Clearing old global commands..." << std::endl; + bot.global_commands_get([&bot](const dpp::confirmation_callback_t& callback) { + if (!callback.is_error()) { + // Get list of existing commands + auto commands = std::get(callback.value); + std::cout << "Found " << commands.size() << " existing commands" << std::endl; + + // Delete each old command + for (auto& command : commands) { + std::cout << "Deleting old command: " << command.second.name << std::endl; + bot.global_command_delete(command.first, [](const dpp::confirmation_callback_t& del_callback) { + if (del_callback.is_error()) { + std::cout << "āŒ Failed to delete command: " << del_callback.get_error().message << std::endl; + } else { + std::cout << "āœ… Command deleted successfully" << std::endl; + } + }); + } + } else { + std::cout << "āŒ Failed to get existing commands: " << callback.get_error().message << std::endl; + } + + // Register our new slash command + std::cout << "Registering oomer command..." << std::endl; + // Parameters: command_name, description, bot_id + bot.global_command_create(dpp::slashcommand("oomer", "render oj", bot.me.id), [](const dpp::confirmation_callback_t& reg_callback) { + if (reg_callback.is_error()) { + std::cout << "āŒ Failed to register command: " << reg_callback.get_error().message << std::endl; + } else { + std::cout << "āœ… Command registered successfully!" << std::endl; + } + }); + }); + } + }); + + // Step 7: Start the bot and run forever + // st_wait means the program will not exit, it will keep running until interrupted + std::cout << "Starting bot event loop..." << std::endl; + bot.start(dpp::st_wait); + + // This line should never be reached unless the bot shuts down + return 0; +} \ No newline at end of file