diff --git a/Makefile b/Makefile index 895730e6..564a965f 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,8 @@ BUILD_TYPE = Debug INSTALL_PREFIX = fpsdk BUILD_TESTING = VERBOSE = 0 +USE_PROJ = +USE_FFMPEG = # User vars -include config.mk @@ -26,13 +28,13 @@ else ifeq ($(ROS_VERSION),2) endif # A unique ID for this exact config we're using -configuid=$(shell echo "$(BUILD_TYPE) $(INSTALL_PREFIX) $(BUILD_TESTING) $(FPSDK_VERSION_STRING) $(CC) $(CXX) $$(uname -a)" | md5sum | cut -d " " -f1) +configuid=$(shell echo "$(BUILD_TYPE) $(INSTALL_PREFIX) $(BUILD_TESTING) $(USE_PROJ) $(USE_FFMPEG) $(FPSDK_VERSION_STRING) $(CC) $(CXX) $$(uname -a)" | md5sum | cut -d " " -f1) .PHONY: help help: @echo "Usage:" @echo - @echo " make [INSTALL_PREFIX=...] [BUILD_TYPE=Debug|Release] [BUILD_TESTING=|ON|OFF] [FPSDK_VERSION_STRING=x.x.x-gggggggg] [VERBOSE=1]" + @echo " make [INSTALL_PREFIX=...] [BUILD_TYPE=Debug|Release] [BUILD_TESTING=|ON|OFF] [USE_PROJ=|ON|OFF] [USE_FFMPEG=|ON|OFF] [FPSDK_VERSION_STRING=x.x.x-gggggggg] [VERBOSE=1]" @echo @echo "Where possible s are:" @echo @@ -57,6 +59,7 @@ help: @echo "- All s but 'clean' and 'pre-commit' require that the same command-line variables are passed" @echo "- Command-line variables can be stored into a config.mk file, which is automatically loaded" @echo "- 'make ci' runs the CI (more or less) like on Github. INSTALL_PREFIX and BUILD_TYPE have no effect here." + @echo "- For more control, such as disabling features, use cmake instead of this Makefile" @echo ######################################################################################################################## @@ -125,7 +128,13 @@ ifneq ($(ROS_PACKAGE_PATH),) CMAKE_ARGS += -DROS_PACKAGE_PATH=$(ROS_PACKAGE_PATH) endif ifneq ($(BUILD_TESTING),) - CMAKE_ARGS += -DBUILD_TESTING=$(BUILD_TESTING) + CMAKE_ARGS += -DFPSDK_BUILD_TESTING=$(BUILD_TESTING) +endif +ifneq ($(USE_PROJ),) + CMAKE_ARGS += -DFPSDK_USE_PROJ=$(USE_PROJ) +endif +ifneq ($(USE_FFMPEG),) + CMAKE_ARGS += -DFPSDK_USE_FFMPEG=$(USE_FFMPEG) endif ifneq ($(FPSDK_VERSION_STRING),) CMAKE_ARGS += -DVERSION_STRING=$(FPSDK_VERSION_STRING) @@ -166,26 +175,10 @@ distclean: $(BUILD_DIR): $(V)$(MKDIR) -p $@ -# Detect changed build config -ifneq ($(MAKECMDGOALS),) -ifneq ($(MAKECMDGOALS),help) -ifneq ($(MAKECMDGOALS),pre-commit) -ifneq ($(MAKECMDGOALS),ci) -ifneq ($(MAKECMDGOALS),distclean) -ifneq ($(MAKECMDGOALS),doc) -builddiruid=$(shell $(CAT) $(BUILD_DIR)/.make-uid 2>/dev/null || echo "none") -ifneq ($(builddiruid),$(configuid)) - dummy=$(shell $(RM) -f $(BUILD_DIR)/.make-uid) -endif -endif -endif -endif -endif -endif -endif - -$(BUILD_DIR)/.make-uid: | $(BUILD_DIR) - $(V)$(ECHO) $(configuid) > $@ +$(BUILD_DIR)/.make-configuid-$(configuid): | $(BUILD_DIR) + @echo "Build config changed" + $(V)$(RM) -vf $(BUILD_DIR)/.make-configuid-* + $(V)$(TOUCH) $@ # ---------------------------------------------------------------------------------------------------------------------- @@ -194,7 +187,7 @@ cmake: $(BUILD_DIR)/.make-cmake deps_cmake := Makefile $(wildcard config.mk) $(wildcard $(sort $(wildcard CMakeLists.txt */CMakeLists.txt */cmake/*))) -$(BUILD_DIR)/.make-cmake: $(deps_cmake) $(BUILD_DIR)/.make-uid +$(BUILD_DIR)/.make-cmake: $(deps_cmake) $(BUILD_DIR)/.make-configuid-$(configuid) @echo "$(HLW)***** Configure ($(BUILD_TYPE)) *****$(HLO)" $(V)$(CMAKE) -B $(BUILD_DIR) $(CMAKE_ARGS) $(V)$(TOUCH) $@ @@ -210,7 +203,7 @@ deps_build = $(sort $(wildcard fpsdk.sh fpsdk_doc/* \ .PHONY: build build: $(BUILD_DIR)/.make-build -$(BUILD_DIR)/.make-build: $(deps_build) $(BUILD_DIR)/.make-cmake $(BUILD_DIR)/.make-uid +$(BUILD_DIR)/.make-build: $(deps_build) $(BUILD_DIR)/.make-cmake @echo "$(HLW)***** Build ($(BUILD_TYPE)) *****$(HLO)" $(V)$(NICE_BUILD) $(CMAKE) --build $(BUILD_DIR) $(CMAKE_ARGS_BUILD) -- -k $(V)$(TOUCH) $@ diff --git a/docker/Dockerfile.humble-base b/docker/Dockerfile.humble-base index 1ac2dfd9..6c03373f 100644 --- a/docker/Dockerfile.humble-base +++ b/docker/Dockerfile.humble-base @@ -41,6 +41,10 @@ RUN /tmp/install_proj.sh && rm -f /tmp/install_proj.sh COPY docker/scripts/install_doxygen.sh /tmp RUN /tmp/install_doxygen.sh && rm -f /tmp/install_doxygen.sh +# Download, build and install FFmpeg libraries (LGPL) +COPY docker/scripts/install_ffmpeg.sh /tmp +RUN /tmp/install_ffmpeg.sh && rm -f /tmp/install_ffmpeg.sh + # Update ld cache for the installed libraries RUN /usr/sbin/ldconfig diff --git a/docker/Dockerfile.jazzy-base b/docker/Dockerfile.jazzy-base index 53772342..bb7d6b2a 100644 --- a/docker/Dockerfile.jazzy-base +++ b/docker/Dockerfile.jazzy-base @@ -33,6 +33,10 @@ RUN /tmp/install_clangformat.sh && rm -f /tmp/install_clangformat.sh COPY docker/scripts/install_doxygen.sh /tmp RUN /tmp/install_doxygen.sh && rm -f /tmp/install_doxygen.sh +# Download, build and install FFmpeg libraries (LGPL) +COPY docker/scripts/install_ffmpeg.sh /tmp +RUN /tmp/install_ffmpeg.sh && rm -f /tmp/install_ffmpeg.sh + # Update ld cache for the installed libraries RUN /usr/sbin/ldconfig diff --git a/docker/Dockerfile.noetic-base b/docker/Dockerfile.noetic-base index 62428349..ab4decbb 100644 --- a/docker/Dockerfile.noetic-base +++ b/docker/Dockerfile.noetic-base @@ -42,6 +42,14 @@ RUN /tmp/install_proj.sh && rm -f /tmp/install_proj.sh COPY docker/scripts/install_doxygen.sh /tmp RUN /tmp/install_doxygen.sh && rm -f /tmp/install_doxygen.sh +# Download, build and install NASM (for FFmpeg) +COPY docker/scripts/install_nasm.sh /tmp +RUN /tmp/install_nasm.sh && rm -f /tmp/install_nasm.sh + +# Download, build and install FFmpeg libraries (LGPL) +COPY docker/scripts/install_ffmpeg.sh /tmp +RUN /tmp/install_ffmpeg.sh && rm -f /tmp/install_ffmpeg.sh + # Download, build and install pre-commit COPY docker/scripts/install_precommit.sh /tmp RUN /tmp/install_precommit.sh && rm -f /tmp/install_precommit.sh diff --git a/docker/Dockerfile.trixie-base b/docker/Dockerfile.trixie-base index c1750e12..94d75180 100644 --- a/docker/Dockerfile.trixie-base +++ b/docker/Dockerfile.trixie-base @@ -26,6 +26,10 @@ RUN /tmp/install_apt_base.sh && rm -f /tmp/install_apt_base.sh COPY docker/scripts/install_doxygen.sh /tmp RUN /tmp/install_doxygen.sh && rm -f /tmp/install_doxygen.sh +# Download, build and install FFmpeg libraries (LGPL) +COPY docker/scripts/install_ffmpeg.sh /tmp +RUN /tmp/install_ffmpeg.sh && rm -f /tmp/install_ffmpeg.sh + # Update ld cache for the installed libraries RUN /usr/sbin/ldconfig diff --git a/docker/ci.sh b/docker/ci.sh index 505b7d6d..99f716e3 100755 --- a/docker/ci.sh +++ b/docker/ci.sh @@ -233,7 +233,7 @@ function build_toplevel_release_noros_mindeps cmake -B build/${buildname} -S . \ -DCMAKE_INSTALL_PREFIX=install/${buildname} \ -DCMAKE_BUILD_TYPE=Release \ - -DFPSDK_USE_PROJ=OFF || return 1 + -DFPSDK_USE_PROJ=OFF -DFPSDK_USE_FFMPEG=OFF || return 1 cmake --build build/${buildname} ${CMAKE_BUILD_ARGS} || return 1 cmake --install build/${buildname} || return 1 @@ -253,7 +253,7 @@ function build_projs_release_noros_mindeps cmake -B build/${buildname}/fpsdk_common -S fpsdk_common \ -DCMAKE_INSTALL_PREFIX=install/${buildname} \ -DCMAKE_BUILD_TYPE=Release \ - -DFPSDK_USE_PROJ=OFF || return 1 + -DFPSDK_USE_PROJ=OFF -DFPSDK_USE_FFMPEG=OFF || return 1 cmake --build build/${buildname}/fpsdk_common ${CMAKE_BUILD_ARGS} || return 1 cmake --install build/${buildname}/fpsdk_common || return 1 diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 29a13e17..3886bdf9 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -56,6 +56,7 @@ services: # Apparently required... (so that the container doesn't kill itself?) command: tail -f + # ----- ROS1 ------------------------------------------------------------------------------------------------------- build-noetic-base: @@ -100,6 +101,7 @@ services: hostname: fpsdk-noetic-dev-${USER} command: tail -f + # ----- ROS2 ------------------------------------------------------------------------------------------------------- build-humble-base: diff --git a/docker/scripts/install_apt_base.sh b/docker/scripts/install_apt_base.sh index 295275fc..d05dadc9 100755 --- a/docker/scripts/install_apt_base.sh +++ b/docker/scripts/install_apt_base.sh @@ -45,7 +45,9 @@ packages=$(awk -v filt=${FPSDK_IMAGE%-*} '$1 ~ filt { print $2 }' < #include #include #include @@ -19,25 +20,37 @@ /* EXTERNAL */ #include +#if defined(FPSDK_USE_ROS1) +# include +#elif defined(FPSDK_USE_ROS2) +# include +# include +#endif /* Fixposition SDK */ #include +#include #include #include +#include +#include +#include #include #include #include +#include #include +#include +#include +#include +#include +#include +#include +#include #include -#if defined(FPSDK_USE_ROS1) -# include -#elif defined(FPSDK_USE_ROS2) -# include -#endif /* PACKAGE */ #include "fpltool_extract.hpp" -#include "fpltool_utils.hpp" namespace fpsdk { namespace apps { @@ -45,11 +58,18 @@ namespace fpltool { /* ****************************************************************************************************************** */ using namespace fpsdk::common::app; +using namespace fpsdk::common::cam; using namespace fpsdk::common::fpl; +using namespace fpsdk::common::parser; +using namespace fpsdk::common::parser::fpa; +using namespace fpsdk::common::parser::fpb; +using namespace fpsdk::common::parser::nmea; using namespace fpsdk::common::path; using namespace fpsdk::common::ros1; using namespace fpsdk::common::string; +using namespace fpsdk::common::time; using namespace fpsdk::common::types; +using namespace fpsdk::common::video; #ifdef FPSDK_USE_ROS1 using namespace fpsdk::ros1::bagwriter; #endif @@ -59,47 +79,48 @@ using namespace fpsdk::ros2::bagwriter; // --------------------------------------------------------------------------------------------------------------------- -bool DoExtract(const FplToolOptions& opts) +FplToolExtract::FplToolExtract(FplToolOptions& opts) /* clang-format off */ : + opts_ { opts } // clang-format on { - if (opts.inputs_.size() != 1) { +} + +bool FplToolExtract::Run() +{ + // Check options, determine output file names + if (opts_.inputs_.size() != 1) { WARNING("Need exactly one input file"); return false; } - const std::string input_fpl = opts.inputs_[0]; - const std::string output_prefix = opts.GetOutputPrefix(input_fpl); + const std::string input_fpl = opts_.inputs_[0]; + output_prefix_ = opts_.GetOutputPrefix(input_fpl); + jsonl_name_ = (opts_.compress_ > 0 ? "all.jsonl.gz" : "all.jsonl"); + raw_ext_ = (opts_.compress_ > 0 ? ".raw.gz" : ".raw"); // Open input log - FplFileReader reader; - if (!reader.Open(input_fpl)) { + FplFileReader fpl_reader; + if (!fpl_reader.Open(input_fpl)) { return false; } - // Output files - OutputFileHelper output(output_prefix, opts); - const char* JSONL_NAME = (opts.compress_ > 0 ? "all.jsonl.gz" : "all.jsonl"); - const char* RAW_EXT = (opts.compress_ > 0 ? ".raw.gz" : ".raw"); - // Check which output formats we want - bool doJsonl = opts.formats_.empty(); - bool doRaw = opts.formats_.empty(); - bool doFile = opts.formats_.empty(); + do_jsonl_ = opts_.formats_.empty(); + do_raw_ = opts_.formats_.empty(); + do_file_ = opts_.formats_.empty(); #if defined(FPSDK_USE_ROS1) || defined(FPSDK_USE_ROS2) - bool doRos = opts.formats_.empty(); + do_ros_ = opts_.formats_.empty(); #else - bool doRos = false; + do_ros_ = false; #endif - - for (auto& fmt : opts.formats_) { - if (fmt == opts.FORMAT_JSONL) { - doJsonl = true; - } else if (fmt == opts.FORMAT_RAW) { - doRaw = true; - } else if (fmt == opts.FORMAT_FILE) { - doFile = true; - } else if (fmt == opts.FORMAT_ROS) { + do_cam_ = opts_.formats_.empty(); + for (auto& fmt : opts_.formats_) { // clang-format off + if (fmt == opts_.FORMAT_JSONL) { do_jsonl_ = true; } + else if (fmt == opts_.FORMAT_RAW) { do_raw_ = true; } + else if (fmt == opts_.FORMAT_FILE) { do_file_ = true; } + else if (fmt == opts_.FORMAT_CAM) { do_cam_ = true; } + else if (fmt == opts_.FORMAT_ROS) { // clang-format on #if defined(FPSDK_USE_ROS1) || defined(FPSDK_USE_ROS2) - doRos = true; + do_ros_ = true; #else WARNING("Cannot extract to ROS bag. This fpltool is not built with ROS support."); return false; @@ -109,223 +130,594 @@ bool DoExtract(const FplToolOptions& opts) return false; } } - - if (!doJsonl && !doRaw && !doFile && !doRos) { + if (!do_jsonl_ && !do_raw_ && !do_file_ && !do_ros_ && !do_cam_) { WARNING("No output formats selected"); return false; } - NOTICE("Extracting from %s to %s_...", input_fpl.c_str(), output_prefix.c_str()); + NOTICE("Extracting from %s to %s_...", input_fpl.c_str(), output_prefix_.c_str()); + TicToc tt; #if defined(FPSDK_USE_ROS1) || defined(FPSDK_USE_ROS2) - BagWriter bag; # ifdef FPSDK_USE_ROS1 - const auto output_bag = output_prefix + ".bag"; // File + const auto output_bag = output_prefix_ + ".bag"; // File # else - const auto output_bag = output_prefix + "_bag"; // Directory! (even for single-file .mcap) + const auto output_bag = output_prefix_ + "_bag"; // Directory! (even for single-file .mcap) # endif if (PathExists(output_bag)) { - if (!opts.overwrite_) { + if (!opts_.overwrite_) { WARNING("Output bag %s already exists", output_bag.c_str()); return false; } else { RemoveAll(output_bag); } } - if (doRos && !bag.Open(output_bag, opts.compress_)) { + if (do_ros_ && !bag_.Open(output_bag, opts_.compress_)) { return false; } NOTICE("Extracting to %s", output_bag.c_str()); #endif - // Helper for converting ROS1 messages to JSON - RosMsgHelper rosmsg_helper; - // Helper for processing STREAMMSGs - ParserMsgHelper parsermsg_helper; - // Handle SIGINT (C-c) to abort nicely SigIntHelper sig_int; // Process log - FplMessage log_msg; double progress = 0.0; double rate = 0.0; bool ok = true; - bool have_meta = false; - std::set files_dumped; + FplMessage fpl_msg; + bool do_extract = (opts_.skip_ == 0); uint32_t time_into_log = 0; std::size_t errors = 0; - while (!sig_int.ShouldAbort() && reader.Next(log_msg) && ok) { + while (!sig_int.ShouldAbort() && fpl_reader.Next(fpl_msg) && ok) { // Report progress - if (opts.progress_ > 0) { - if (reader.GetProgress(progress, rate)) { + if (opts_.progress_ > 0) { + if (fpl_reader.GetProgress(progress, rate)) { INFO("Extracting... %.1f%% (%.0f MiB/s)\r", progress, rate); } } - // Check if we want to skip this message - const bool skip = ((opts.skip_ > 0) && (time_into_log < opts.skip_)); - - // Maybe we can abort early - if ((opts.duration_ > 0) && (time_into_log > (opts.skip_ + opts.duration_))) { + do_extract = (time_into_log >= opts_.skip_); + if ((opts_.duration_ > 0) && (time_into_log > (opts_.skip_ + opts_.duration_))) { DEBUG("abort early"); break; } // Process message - const auto log_type = log_msg.PayloadType(); - switch (log_type) { - case FplType::LOGSTATUS: { - const LogStatus logstatus(log_msg); - if (logstatus.valid_) { - time_into_log = logstatus.log_duration_; - if (!skip && doJsonl && !output.WriteJson(JSONL_NAME, logstatus)) { - ok = false; - } - } else { - WARNING("Invalid LOGSTATUS"); - errors++; - } - break; - } + const auto log_type = fpl_msg.PayloadType(); + ProcRes res = ProcRes::OK; + switch (log_type) { // clang-format off + case FplType::LOGSTATUS: res = ProcessLogStatus(fpl_msg, do_extract, time_into_log); break; + case FplType::LOGMETA: res = ProcessLogMeta(fpl_msg, do_extract); break; + case FplType::STREAMMSG: res = ProcessStreamMsg(fpl_msg, do_extract); break; + case FplType::ROSMSGDEF: res = ProcessRosMsgDef(fpl_msg, do_extract); break; + case FplType::ROSMSGBIN: res = ProcessRosMsgBin(fpl_msg, do_extract); break; + case FplType::FILEDUMP: res = ProcessFileDump(fpl_msg, do_extract); break; + case FplType::CAMDATA: res = ProcessCamData(fpl_msg, do_extract); break; + case FplType::BLOB: + case FplType::INT_D: + case FplType::INT_F: + case FplType::INT_X: + case FplType::UNSPECIFIED: break; + } // clang-format on - case FplType::LOGMETA: { - const LogMeta logmeta(log_msg); - if (logmeta.valid_) { - if ((!have_meta || !skip) && doJsonl && !output.WriteJson(JSONL_NAME, logmeta)) { - ok = false; - } - have_meta = true; - } else { - WARNING("Invalid LOGMETA"); - errors++; + switch (res) { + case ProcRes::OK: + break; + case ProcRes::ERROR: + if (errors >= 100) { + WARNING("Too many errors, giving up"); + ok = false; } break; - } + case ProcRes::FATAL: + WARNING("Giving up"); + ok = false; + break; + } + } + + // We were interrupted + if (sig_int.ShouldAbort()) { + ok = false; + } - case FplType::STREAMMSG: - if (!skip) { - const StreamMsg streammsg(log_msg); - if (streammsg.valid_) { - if (doJsonl || doRos) { - parsermsg_helper.UpdateParserMsg(streammsg); - } - if (doRaw && !output.WriteData(streammsg.stream_name_ + RAW_EXT, streammsg.msg_data_)) { - ok = false; - } - if (doJsonl && - !output.WriteStreamMsg(JSONL_NAME, streammsg, parsermsg_helper.GetParserMsg(true))) { - ok = false; - } + // Close output files + CloseAll(ok); #if defined(FPSDK_USE_ROS1) || defined(FPSDK_USE_ROS2) - if (doRos) { - bag.WriteMessage(parsermsg_helper.GetRosMsg(), "/" + streammsg.stream_name_ + "/raw", - parsermsg_helper.GetRosStamp()); - } + if (do_ros_) { + bag_.Close(); + if (ok) { + INFO("Wrote bag %s (%s)", output_bag.c_str(), OutputSizeStr(output_bag).c_str()); + } else { + WARNING("Incomplete bag %s (%s)", output_bag.c_str(), OutputSizeStr(output_bag).c_str()); + } + } #endif - } else { - WARNING("Invalid STREAMMSG"); - errors++; - } - } - break; - case FplType::ROSMSGDEF: { - RosMsgDef rosmsgdef(log_msg); - if (rosmsgdef.valid_) { - rosmsgdef.topic_name_ = FixTopicName(rosmsgdef.topic_name_); + const auto dur_wall = tt.Toc().GetSec(); + const double dur_log = (double)time_into_log - (double)opts_.skip_; + if ((dur_log > 0.0) && (dur_wall > 0.0)) { + NOTICE("Processed %.0fs of data in %.0fs (%.1fx)", // clang-format off + dur_log, dur_wall, dur_log / dur_wall); // clang-format on + } + + return ok; +} + +// --------------------------------------------------------------------------------------------------------------------- + +FplToolExtract::ProcRes FplToolExtract::ProcessLogStatus( + const common::fpl::FplMessage& fpl_msg, const bool do_extract, uint32_t& time_into_log) +{ + const LogStatus logstatus(fpl_msg); + if (!logstatus.valid_) { + WARNING("Invalid LOGSTATUS"); + return ProcRes::ERROR; + } + TRACE("LOGSTATUS %s", logstatus.info_.c_str()); + + time_into_log = logstatus.log_duration_; + + if (do_extract && do_jsonl_ && !WriteJson(jsonl_name_, logstatus)) { + return ProcRes::FATAL; + } + + return ProcRes::OK; +} + +// --------------------------------------------------------------------------------------------------------------------- + +FplToolExtract::ProcRes FplToolExtract::ProcessLogMeta(const common::fpl::FplMessage& fpl_msg, const bool do_extract) +{ + const LogMeta logmeta(fpl_msg); + if (!logmeta.valid_) { + WARNING("Invalid LOGMETA"); + return ProcRes::ERROR; + } + TRACE("LOGMETA %s", logmeta.info_.c_str()); + + ProcRes res = ProcRes::OK; + + // Always save first LOGMETA, even with skip + if ((!have_logmeta_ || do_extract) && do_jsonl_ && !WriteJson(jsonl_name_, logmeta)) { + res = ProcRes::FATAL; + } + have_logmeta_ = true; + + return res; +} + +// --------------------------------------------------------------------------------------------------------------------- + +FplToolExtract::ProcRes FplToolExtract::ProcessRosMsgDef(const FplMessage& fpl_msg, const bool do_extract) +{ + UNUSED(do_extract); + + RosMsgDef rosmsgdef(fpl_msg); + if (!rosmsgdef.valid_) { + WARNING("Invalid ROSMSGDEF"); + return ProcRes::ERROR; + } + TRACE("ROSMSGDEF %s", rosmsgdef.info_.c_str()); + + rosmsgdef.topic_name_ = FixTopicName(rosmsgdef.topic_name_); + + if (do_jsonl_ || do_ros_) { + if ((rosmsgdefs_.find(rosmsgdef.topic_name_) == rosmsgdefs_.end())) { + rosmsgdefs_.emplace(rosmsgdef.topic_name_, rosmsgdef); + } + } - if (doJsonl || doRos) { - rosmsg_helper.AddDef(rosmsgdef); - } #if defined(FPSDK_USE_ROS1) || defined(FPSDK_USE_ROS2) - if (doRos) { - bag.AddMsgDef(rosmsgdef); - } + if (do_ros_) { + bag_.AddMsgDef(rosmsgdef); + } #endif - } else { - WARNING("Invalid ROSMSGDEF"); - errors++; - } - break; + + return ProcRes::OK; +} + +// --------------------------------------------------------------------------------------------------------------------- + +FplToolExtract::ProcRes FplToolExtract::ProcessRosMsgBin(const FplMessage& fpl_msg, const bool do_extract) +{ + if (!do_extract) { + return ProcRes::OK; + } + + RosMsgBin rosmsgbin(fpl_msg); + if (!rosmsgbin.valid_) { + WARNING("Invalid ROSMSGBIN"); + return ProcRes::ERROR; + } + TRACE("ROSMSGBIN %s", rosmsgbin.info_.c_str()); + + rosmsgbin.topic_name_ = FixTopicName(rosmsgbin.topic_name_); + + if (do_jsonl_) { + const auto& entry = rosmsgdefs_.find(rosmsgbin.topic_name_); + if (entry == rosmsgdefs_.end()) { + WARNING("Missing ROSMSGDEF for ROSMSGBIN %s", rosmsgbin.info_.c_str()); + return ProcRes::ERROR; + } + + const auto& rosmsgdef = entry->second; + TRACE("ROSMSGBIN %s using ROSMSGDEF %s", rosmsgbin.info_.c_str(), rosmsgdef.info_.c_str()); + + // Try the implemented conversions + nlohmann::json jdata; + bool jok = false; + try { + if (RosMsgToJson(rosmsgdef, rosmsgbin, jdata) || + RosMsgToJson(rosmsgdef, rosmsgbin, jdata) || + RosMsgToJson(rosmsgdef, rosmsgbin, jdata) || + RosMsgToJson(rosmsgdef, rosmsgbin, jdata) || + RosMsgToJson(rosmsgdef, rosmsgbin, jdata)) { + jdata["_type"] = FplTypeStr(FplType::ROSMSGBIN); + jdata["_msg"] = rosmsgdef.msg_name_; + jdata["_topic"] = rosmsgbin.topic_name_; + jdata["_stamp"] = rosmsgbin.rec_time_; + jok = true; + } else { + throw std::runtime_error("conversion not implemented"); } + } catch (std::exception& ex) { + WARNING("ROSMSGBIN %s ROSMSGDEF %s ToJson fail: %s", rosmsgbin.info_.c_str(), rosmsgdef.info_.c_str(), + ex.what()); + } - case FplType::ROSMSGBIN: - if (!skip) { - RosMsgBin rosmsgbin(log_msg); - if (rosmsgbin.valid_) { - rosmsgbin.topic_name_ = FixTopicName(rosmsgbin.topic_name_); + if (jok && !WriteJson(jsonl_name_, jdata)) { + return ProcRes::FATAL; + } + } - nlohmann::json jdata; - if (doJsonl && rosmsg_helper.ToJson(rosmsgbin, jdata) && !output.WriteJson(JSONL_NAME, jdata)) { - ok = false; - } #if defined(FPSDK_USE_ROS1) || defined(FPSDK_USE_ROS2) - if (doRos && !bag.WriteMessage(rosmsgbin)) { - ok = false; - } + if (do_ros_ && !bag_.WriteMessage(rosmsgbin)) { + return ProcRes::FATAL; + } #endif - } else { - WARNING("Invalid ROSMSGBIN"); - errors++; - } - } - break; - case FplType::FILEDUMP: { - FileDump filedump(log_msg); - if (filedump.valid_) { - const bool dumped_before = files_dumped.count(filedump.filename_) == 0; - if ((!dumped_before || !skip) && doFile && - !output.WriteData( - FileDumpOutName(filedump) + (opts.compress_ > 0 ? ".gz" : ""), filedump.data_)) { - ok = false; - } - if ((!dumped_before || !skip) && doJsonl && !output.WriteJson(JSONL_NAME, filedump)) { - ok = false; - } - if (!dumped_before) { - files_dumped.emplace(filedump.filename_); - } - } else { - WARNING("Invalid FILEDUMP"); - errors++; - } + return ProcRes::OK; +} + +// --------------------------------------------------------------------------------------------------------------------- + +FplToolExtract::ProcRes FplToolExtract::ProcessStreamMsg(const FplMessage& fpl_msg, const bool do_extract) +{ + if (!do_extract) { + return ProcRes::OK; + } + + const StreamMsg streammsg(fpl_msg); + if (!streammsg.valid_) { + WARNING("Invalid STREAMMSG"); + return ProcRes::ERROR; + } + TRACE("STREAMMSG %s", streammsg.info_.c_str()); + + if (do_raw_ && !WriteData(streammsg.stream_name_ + raw_ext_, streammsg.msg_data_)) { + return ProcRes::FATAL; + } + + if (do_jsonl_ || do_ros_) { + parser_.Reset(); + ParserMsg msg; + if (!parser_.Add(streammsg.msg_data_) || !parser_.Process(msg) || + (msg.data_.size() != streammsg.msg_data_.size())) { + msg.proto_ = Protocol::OTHER; + msg.name_ = ProtocolStr(Protocol::OTHER); + msg.data_ = streammsg.msg_data_; + } + msg.info_.clear(); + auto seq = stream_seq_.find(streammsg.stream_name_); + if (seq == stream_seq_.end()) { + seq = stream_seq_.emplace(streammsg.stream_name_, 1).first; + } + msg.seq_ = seq->second++; + + if (do_jsonl_ && !WriteStreamMsg(jsonl_name_, streammsg, msg)) { + return ProcRes::FATAL; + } + +#if defined(FPSDK_USE_ROS1) || defined(FPSDK_USE_ROS2) + if (do_ros_) { +# if defined(FPSDK_USE_ROS1) + std_msgs::ByteMultiArray rosmsg; + ros::Time stamp; +# else + std_msgs::msg::ByteMultiArray rosmsg; + rclcpp::Time stamp; +# endif + rosmsg.layout.dim.resize(1); + rosmsg.layout.dim[0].label = msg.name_; + rosmsg.layout.dim[0].size = msg.data_.size(); + rosmsg.layout.dim[0].stride = msg.data_.size(); + rosmsg.data = { msg.data_.data(), msg.data_.data() + msg.data_.size() }; +# if defined(FPSDK_USE_ROS1) + stamp = { streammsg.rec_time_.sec_, streammsg.rec_time_.nsec_ }; +# else + stamp = { (int)streammsg.rec_time_.sec_, streammsg.rec_time_.nsec_, RCL_ROS_TIME }; +# endif + if (!bag_.WriteMessage(rosmsg, "/" + streammsg.stream_name_ + "/raw", stamp)) { + return ProcRes::FATAL; } + } +#endif + } - case FplType::BLOB: - case FplType::INT_D: - case FplType::INT_F: - case FplType::INT_X: - case FplType::UNSPECIFIED: + return ProcRes::OK; +} + +// --------------------------------------------------------------------------------------------------------------------- + +FplToolExtract::ProcRes FplToolExtract::ProcessFileDump(const FplMessage& fpl_msg, const bool do_extract) +{ + FileDump filedump(fpl_msg); + if (!filedump.valid_) { + WARNING("Invalid FILEDUMP"); + return ProcRes::ERROR; + } + TRACE("FILEDUMP %s", filedump.info_.c_str()); + + const bool dumped_before = files_dumped_.count(filedump.filename_) == 0; + + if ((!dumped_before || do_extract) && do_file_ && + !WriteData(FileDumpOutName(filedump) + (opts_.compress_ > 0 ? ".gz" : ""), filedump.data_)) { + return ProcRes::FATAL; + } + if ((!dumped_before || do_extract) && do_jsonl_ && !WriteJson(jsonl_name_, filedump)) { + return ProcRes::FATAL; + } + + if (!dumped_before) { + files_dumped_.emplace(filedump.filename_); + } + + return ProcRes::OK; +} + +// --------------------------------------------------------------------------------------------------------------------- + +FplToolExtract::ProcRes FplToolExtract::ProcessCamData(const FplMessage& fpl_msg, const bool do_extract) +{ + if (!do_extract) { + return ProcRes::OK; + } + + CamData camdata(fpl_msg); + if (!camdata.valid_) { + WARNING("Invalid CAMDATA"); + return ProcRes::ERROR; + } + TRACE("CAMDATA %s", camdata.info_.c_str()); + + const std::string name = Sprintf( + "%s-%s-%s", CamIdToStr(camdata.cam_id_), CamDataTypeToStr(camdata.type_), CamDataFmtToStr(camdata.fmt_)); + + if (do_raw_ && do_cam_ && !WriteData(name + raw_ext_, camdata.data_)) { + return ProcRes::FATAL; + } + + if (do_jsonl_ && do_cam_ && !WriteJson(jsonl_name_, camdata)) { + return ProcRes::FATAL; + } + + ProcRes res = ProcRes::OK; + +#if FPSDK_USE_FFMPEG && (defined(FPSDK_USE_ROS1) || defined(FPSDK_USE_ROS2)) + VideoCodec codec = VideoCodec::UNSPECIFIED; + switch (camdata.fmt_) { // clang-format off + case CamDataFmt::H264_NAL: codec = VideoCodec::H264; break; + case CamDataFmt::H265_NAL: codec = VideoCodec::H265; break; + case CamDataFmt::UNSPECIFIED: + case CamDataFmt::MJPEG: + case CamDataFmt::JPEG: + case CamDataFmt::Y8: + case CamDataFmt::NV12: + case CamDataFmt::RGB24: break; + } // clang-format on + while (do_cam_ && do_ros_ && (codec != VideoCodec::UNSPECIFIED)) { + auto entry = decoder_states_.find(name); + if (entry == decoder_states_.end()) { + // We can only start decoding from the first I-frame onwards + if (camdata.frm_ != CamDataFrm::I_FRAME) { break; + } + AsyncDecState state; + state.dec_ = CreateVideoFrameDecoder({ name, codec, opts_.pixelfmt_, opts_.scale_ }); + entry = decoder_states_.emplace(name, std::move(state)).first; + } + + if (!entry->second.dec_) { + WARNING("No decoder for %s", name.c_str()); + return ProcRes::FATAL; } - if (errors >= 100) { - WARNING("Too many errors!"); - ok = false; + auto& dec = entry->second.dec_; + auto& fut = entry->second.fut_; + + // Wait for decoding results from previous frame + if (fut.valid()) { + res = ProcessAsyncDecData(fut.get()); } + + // Decode this frame + fut = std::async(std::launch::async, + [&dec, /*copy!*/ camdata]() -> AsyncDecData { return { camdata, dec->DecodeFrame(camdata.data_) }; }); + + break; } +#endif - // We were interrupted - if (sig_int.ShouldAbort()) { - ok = false; + return res; +} + +// --------------------------------------------------------------------------------------------------------------------- + +#if FPSDK_USE_FFMPEG && (defined(FPSDK_USE_ROS1) || defined(FPSDK_USE_ROS2)) +FplToolExtract::ProcRes FplToolExtract::ProcessAsyncDecData(const FplToolExtract::AsyncDecData& decdata) +{ + auto& camdata = decdata.camdata_; + if (!decdata.img_) { + WARNING("No image from CAMDATA %s", camdata.info_.c_str()); + return ProcRes::ERROR; } + auto& img = *decdata.img_; + TRACE("CAMDATA decoded %s -> %dx%d %s", camdata.info_.c_str(), img.width_, img.height_, PixelFmtToStr(img.fmt_)); - // Close output files - output.CloseAll(ok); -#if defined(FPSDK_USE_ROS1) || defined(FPSDK_USE_ROS2) - if (doRos) { - bag.Close(); +# if defined(FPSDK_USE_ROS1) + sensor_msgs::Image rosmsg; + rosmsg.header.stamp.fromNSec(camdata.meta_.ts_); + rosmsg.header.seq = camdata.meta_.seq_; +# else + sensor_msgs::msg::Image rosmsg; + rosmsg.header.stamp = rclcpp::Time(static_cast(camdata.meta_.ts_), RCL_ROS_TIME); +# endif + rosmsg.header.frame_id = CamIdToStr(camdata.cam_id_); + rosmsg.width = img.width_; + rosmsg.height = img.height_; + + switch (img.fmt_) { // clang-format off + case PixelFmt::Y8: rosmsg.encoding = "mono8"; rosmsg.step = img.width_; break; + case PixelFmt::RGB24: rosmsg.encoding = "rgb8"; rosmsg.step = img.width_ * 3; break; + case PixelFmt::GBRP: rosmsg.encoding = "8UC3"; rosmsg.step = img.width_; break; + case PixelFmt::UNSPECIFIED: break; + } // clang-format on + rosmsg.data = { img.data_.data(), img.data_.data() + img.data_.size() }; + // Abuse pixel (0, 0) to store the exposure duration in [0.1ms] + if (!rosmsg.data.empty()) { + const uint32_t dt01ms = camdata.meta_.dt_ / 100000; // [ns] -> [0.1ms] + rosmsg.data[0] = std::clamp(dt01ms, 0, 255); + } + +# if defined(FPSDK_USE_ROS1) + ros::Time stamp; + stamp.fromNSec(camdata.meta_.rec_time_); +# else + rclcpp::Time stamp(static_cast(camdata.meta_.rec_time_), RCL_ROS_TIME); +# endif + if (!bag_.WriteMessage(rosmsg, Sprintf("/%s/image", CamIdToStr(camdata.cam_id_)), stamp)) { + return ProcRes::FATAL; + } + + return ProcRes::OK; +} +#endif + +// --------------------------------------------------------------------------------------------------------------------- + +bool FplToolExtract::WriteData(const std::string& name, const std::vector& data) +{ + auto f = GetOutputFile(name); + return f ? f->Write(data) : false; +} + +// --------------------------------------------------------------------------------------------------------------------- + +bool FplToolExtract::WriteJson(const std::string& name, const nlohmann::json& json) +{ + auto f = GetOutputFile(name); + if (!f) { + return false; + } + return f->Write(json.dump()) && f->Write("\n"); +} + +// --------------------------------------------------------------------------------------------------------------------- + +bool FplToolExtract::WriteStreamMsg(const std::string& name, const StreamMsg& streammsg, const ParserMsg& parsermsg) +{ + parsermsg.MakeInfo(); + nlohmann::json jdata = streammsg; // magic to_json() (_type, _stamp, _stream, _data/_data_b64) + jdata.update(parsermsg); // magic to_json() (_proto, _name, _seq, _info, _data/_data_b64) + + // We can decode some messages of some protocols + if (parsermsg.proto_ == Protocol::FP_A) { + jdata.update(FpaDecodeMessage(parsermsg.data_)); // magic to_json() + } else if (parsermsg.proto_ == Protocol::NMEA) { + jdata.update(NmeaDecodeMessage(parsermsg.data_)); // magic to_json() + } else if (parsermsg.proto_ == Protocol::FP_B) { + jdata.update(to_json_FP_B(parsermsg)); // magic to_json() + } + return WriteJson(name, jdata); +} + +// --------------------------------------------------------------------------------------------------------------------- + +void FplToolExtract::CloseAll(const bool ok) +{ + for (auto& file : files_) { + const std::string path = file.second->Path(); + file.second->Close(); + const auto size_str = OutputSizeStr(path); if (ok) { - INFO("Wrote bag %s (%s)", output_bag.c_str(), OutputSizeStr(output_bag).c_str()); + INFO("Wrote file %s (%s)", path.c_str(), size_str.c_str()); } else { - WARNING("Incomplete bag %s (%s)", output_bag.c_str(), OutputSizeStr(output_bag).c_str()); + WARNING("Incomplete file %s (%s)", path.c_str(), size_str.c_str()); } } -#endif + files_.clear(); +} - return ok; +// --------------------------------------------------------------------------------------------------------------------- + +OutputFile* FplToolExtract::GetOutputFile(const std::string& name) +{ + auto file = files_.find(name); + if (file == files_.end()) { + const std::string path = output_prefix_ + "_" + name; + file = files_.emplace(name, std::make_unique()).first; + if (!opts_.overwrite_ && PathExists(path)) { + WARNING("Output file %s already exists", path.c_str()); + return nullptr; + } + NOTICE("Extracting to %s", path.c_str()); + if (!file->second->Open(path)) { + return nullptr; + } + } + return file->second.get(); +} + +// --------------------------------------------------------------------------------------------------------------------- + +const std::string& FplToolExtract::FixTopicName(const std::string& in_topic) const +{ + if (in_topic == "/fusion_optim/imu_biases") { + static std::string str = "/imu/biases"; + return str; + } else { + return in_topic; + } +} + +// --------------------------------------------------------------------------------------------------------------------- + +std::string FplToolExtract::FileDumpOutName(const FileDump& filedump) const +{ + const auto parts = StrSplit(filedump.filename_, ".", 2); + std::string outname = parts[0]; + const auto m = filedump.mtime_.GetUtcTime(0); + outname += Sprintf("_%04d%02d%02d-%02d%02d%02.0f", m.year_, m.month_, m.day_, m.hour_, m.min_, m.sec_); + if (parts.size() > 1) { + outname += "." + parts[1]; + } + return outname; +} + +// --------------------------------------------------------------------------------------------------------------------- + +std::string FplToolExtract::OutputSizeStr(const std::string& path) const +{ + const double size = (double)(PathIsDirectory(path) ? DirSize(path) : FileSize(path)); + if (size < 1024.0) { + return Sprintf("%.0f B", size); + } else if (size < (1024.0 * 1024.0)) { + return Sprintf("%.1f KiB", size / 1024.0); + } else { + return Sprintf("%.1f MiB", size / 1024.0 / 1024.0); + } } /* ****************************************************************************************************************** */ diff --git a/fpsdk_apps/fpltool/fpltool_extract.hpp b/fpsdk_apps/fpltool/fpltool_extract.hpp index 21ebb7dc..9a39cded 100644 --- a/fpsdk_apps/fpltool/fpltool_extract.hpp +++ b/fpsdk_apps/fpltool/fpltool_extract.hpp @@ -14,10 +14,25 @@ #define __FPSDK_APPS_FPLTOOL_FPLTOOL_EXTRACT_HPP__ /* LIBC/STL */ +#include +#include +#include +#include +#include /* EXTERNAL */ +#include +#if defined(FPSDK_USE_ROS1) +# include +#elif defined(FPSDK_USE_ROS2) +# include +#endif /* Fixposition SDK */ +#include +#include +#include +#include /* PACKAGE */ #include "fpltool_opts.hpp" @@ -27,6 +42,84 @@ namespace apps { namespace fpltool { /* ****************************************************************************************************************** */ +class FplToolExtract +{ + public: + FplToolExtract(FplToolOptions& opts); + bool Run(); + + private: + // Params + FplToolOptions opts_; + std::string output_prefix_; + const char* jsonl_name_ = nullptr; + const char* raw_ext_ = nullptr; + bool do_jsonl_ = false; + bool do_raw_ = false; + bool do_file_ = false; + bool do_ros_ = false; + bool do_cam_ = false; + + // FPL + // clang-format off + enum class ProcRes { OK, ERROR, FATAL }; // clang-format off + ProcRes ProcessLogStatus(const common::fpl::FplMessage& fpl_msg, const bool do_extract, uint32_t& time_into_log); + ProcRes ProcessLogMeta(const common::fpl::FplMessage& fpl_msg, const bool do_extract); + ProcRes ProcessRosMsgDef(const common::fpl::FplMessage& fpl_msg, const bool do_extract); + ProcRes ProcessRosMsgBin(const common::fpl::FplMessage& fpl_msg, const bool do_extract); + ProcRes ProcessStreamMsg(const common::fpl::FplMessage& fpl_msg, const bool do_extract); + ProcRes ProcessFileDump(const common::fpl::FplMessage& fpl_msg, const bool do_extract); + ProcRes ProcessCamData(const common::fpl::FplMessage& fpl_msg, const bool do_extract); + + // LOGMETA + bool have_logmeta_ = false; + + // STREAMMSG + common::parser::Parser parser_; + std::map stream_seq_; + + // ROSMSGDEF, ROSMSGBIN + std::map rosmsgdefs_; + const std::string& FixTopicName(const std::string& in_topic) const; + + // FILEDUMP + std::set files_dumped_; + std::string FileDumpOutName(const common::fpl::FileDump& filedump) const; + + // CAMDATA +#if FPSDK_USE_FFMPEG + struct AsyncDecData + { + common::fpl::CamData camdata_; + std::optional img_; + }; + struct AsyncDecState + { + common::video::VideoFrameDecoderPtr dec_; + std::future fut_; + }; + std::map decoder_states_; + + ProcRes ProcessAsyncDecData(const AsyncDecData& decdata); +#endif + + // Output files +#if defined(FPSDK_USE_ROS1) + ros1::bagwriter::BagWriter bag_; +#elif defined(FPSDK_USE_ROS2) + ros2::bagwriter::BagWriter bag_; +#endif + std::map> files_; + common::path::OutputFile* GetOutputFile(const std::string& name); + bool WriteData(const std::string& name, const std::vector& data); + bool WriteJson(const std::string& name, const nlohmann::json& json); + bool WriteStreamMsg( + const std::string& name, const common::fpl::StreamMsg& streammsg, const common::parser::ParserMsg& parsermsg); + void CloseAll(const bool ok = false); + + std::string OutputSizeStr(const std::string& path) const; +}; + /** * @brief Run FpltoolArgs::Command::EXTRACT * diff --git a/fpsdk_apps/fpltool/fpltool_meta.cpp b/fpsdk_apps/fpltool/fpltool_meta.cpp index ca1d97b8..09ed5ff1 100644 --- a/fpsdk_apps/fpltool/fpltool_meta.cpp +++ b/fpsdk_apps/fpltool/fpltool_meta.cpp @@ -80,6 +80,7 @@ bool DoMeta(const FplToolOptions& opts) case FplType::STREAMMSG: case FplType::LOGSTATUS: case FplType::FILEDUMP: + case FplType::CAMDATA: case FplType::BLOB: case FplType::UNSPECIFIED: case FplType::INT_D: diff --git a/fpsdk_apps/fpltool/fpltool_opts.hpp b/fpsdk_apps/fpltool/fpltool_opts.hpp index 7364ce49..7c8bd545 100644 --- a/fpsdk_apps/fpltool/fpltool_opts.hpp +++ b/fpsdk_apps/fpltool/fpltool_opts.hpp @@ -25,6 +25,7 @@ #include #include #include +#include /* PACKAGE */ @@ -49,7 +50,10 @@ class FplToolOptions : public common::app::ProgramOptions { 'c', false, "compress" }, { 'S', true, "skip" }, { 'D', true, "duration" }, - { 'e', true, "formats" } }) {}; // clang-format on + { 'e', true, "formats" }, + { 's', true, "scale" }, + { 't', true, "pixelfmt" }, + }) {}; // clang-format on /** * @brief Commands, modes of operation @@ -77,12 +81,17 @@ class FplToolOptions : public common::app::ProgramOptions uint32_t skip_ = 0; //!< Skip start [sec] uint32_t duration_ = 0; //!< Duration [sec] std::vector formats_; //!< List of output formats for extraction +#if FPSDK_USE_FFMPEG + double scale_ = 1.0; //!< Scale factor for decoded video frames + common::video::PixelFmt pixelfmt_ = common::video::PixelFmt::RGB24; //!< Pixel format for decoded video frames +#endif // clang-format on static constexpr const char* FORMAT_JSONL = "jsonl"; static constexpr const char* FORMAT_RAW = "raw"; static constexpr const char* FORMAT_FILE = "file"; static constexpr const char* FORMAT_ROS = "ros"; + static constexpr const char* FORMAT_CAM = "cam"; void PrintHelp() override final { @@ -109,6 +118,8 @@ class FplToolOptions : public common::app::ProgramOptions " -S, --skip -- Skip seconds from start of log (default: 0, i.e. no skip)\n" " -D, --duration -- Process seconds of log (default: everything)\n" " -e, --formats -- Comma-separated list of output formats for the extract (default: all)\n" + " -s, --scale -- Scale factor for decoded video frames: 0.1 - 1.0 (default: 1.0)\n" + " -t, --pixelfmt -- Pixel format for decoded video frames: Y8, RGB24, GBRP (default: RGB24)\n" " -- The command, see below\n" " -- The .fpl (or .fpl.gz) file to process\n" " \n" @@ -140,11 +151,11 @@ class FplToolOptions : public common::app::ProgramOptions "\n" " rosbag -- Extract (some of) the data to a ROS bag\n" "\n" - " This is an alias of 'extract -e ros'. See that for details.\n" + " This is an alias of 'extract -e ros,cam'. See that for details.\n" "\n" " extract -- Extract the data in a .fpl file\n" "\n" - " fpltool [-vqpPfocSD] [-e ] extract \n" + " fpltool [-vqpPfocSDst] [-e ] extract \n" "\n" " The data is extracted to different files in the current directory. The files are named like the\n" " with added suffixes and different file extension, depending on the kind of data that\n" @@ -152,18 +163,23 @@ class FplToolOptions : public common::app::ProgramOptions " list of formats in to limit to some formats. The available output data formats are:\n" "\n" " jsonl -- All data in JSONL format (see below)\n" - " raw -- Stream messages (I/O messages, raw messages from GNSS receiver, ...)\n" + " raw -- Stream messages (I/O messages, raw messages from GNSS receiver, raw camera data, ...)\n" " file -- Recorded files (configuration, ...)\n" " ros -- ROS data extracted to a ROS bag. This option is only available when compiled with ROS\n" " (1 or 2) support (see output of 'fpltool -V' to check what your version is). For ROS1\n" - " the standard .bag file format is used. For ROS2 the standard sqlite3 format is used,\n" - " unless compression is used, in which case the mcap format is used.\n" + " the standard .bag file format is used. For ROS2 the standard sqlite3 or, with compression,\n" + " mcap format is used.\n" + " cam -- Camera data, such as encoded video frames (for example, from PBx-A1). With 'ros' and if\n" + " compiled with FFmpeg support (see 'fpltool -V' to check what your version is) encoded\n" + " video is decoded and stored as image to the ROS bag. In this case the mapping of the\n" + " --pixelfmt to ROS image encoding is: RGB24 -> rgb8, Y8 -> mono8, GBRP -> 8UC3.\n" "\n" " The jsonl format consists of one JSON object per line. Depending on the data different fields are\n" " available. The fields starting with a '_' (_type, ...) should always be present. The fields not\n" " starting with a '_' are decoded values. What values can be decoded depends on the data in question,\n" - " the sensor configuration and software version, and the fpltool support for the data. Typically,\n" - " the following kind of JSON objects can be found in the exacted .jsonl file:\n" + " the sensor configuration and software version, and the fpltool support for the data. Note that the\n" + " order of records is the order in the recording (the .fpl file). The data is not ordered by data\n" + " timestamp. Typically, the following kind of JSON objects can be found in the exacted .jsonl file:\n" " \n" " Log meta data: { \"_type\": \"LOGMETA\", \"_yaml\": \"...\", ... }\n" "\n" @@ -182,8 +198,7 @@ class FplToolOptions : public common::app::ProgramOptions " the decoded fields.\n" "\n" " Stream messages: { \"_type\": \"STREAMMSG\", \"_stream\": \"...\", \"_stamp\": ..., \"_raw\": \"...\",\n" - " \"_raw_b64\": \"...\", \"_proto\": \"...\", \"_name\": \"...\", \"_seq\": ...,\n" - " \"_valid\": ..., ... } \n" + " \"_raw_b64\": \"...\", \"_proto\": \"...\", \"_name\": \"...\", \"_seq\": ..., \"_valid\": ..., ... }\n" "\n" " Where _stream is the stream name (userio, gnss1, ...), _stamp is the recording (!) timestamp,\n" " _raw or _raw_b64 is the raw message data (_raw if data is non-binary, _raw_b64 = base64 encoded\n" @@ -194,11 +209,21 @@ class FplToolOptions : public common::app::ProgramOptions " decoded fields.\n" "\n" " Files: { \"_type\": \"FILEDUMP\", \"_filename\": \"...\", \"_mtime\": \"...\", \"_stamp\": ...,\n" - " \"_data\": \"...\", \"_data_b64\": \"...\" } \n" + " \"_data\": \"...\", \"_data_b64\": \"...\" } \n" "\n" " Where _filename is the filename of the file, _mtime its last modification time and _data or\n" " or _data_b64 is the file contents (_data if it is non-binary, _data_b64 = base64 encoded data).\n" "\n" + " Camera data: { \"_type\": \"CAMDATA\", \"cam_id\": \"...\", \"type\": \"...\", \"fmt\": \"...\",\n" + " \"frm\": \"...\", \"height\": ..., \"seq\": ..., \"ts\": ... \"dt\": ..., \"data_b64\": \"...\" }\n" + "\n" + " Where cam_id is the camera ID ('CAM1', ...), type is the data type (HIRES_VID, ...), fmt is the\n" + " data format (H265_NAL, ...), frm is the frame type (I_FRAME, P_FRAME, ...), height and width are\n" + " the image dimensions, seq is the sequence number (independent from other cameras), ts is the\n" + " middle of exposure timestamps [ns] and dt is the exposure duration [ns]. The camera data (encoded\n" + " video frame) is in data as base64 encoded string. This is only available on some products (for\n" + " example, PBx-A1).\n" + "\n" "Examples:\n" "\n" " Print the sensor UID of the sensor that recorded the fpl:\n" @@ -211,14 +236,15 @@ class FplToolOptions : public common::app::ProgramOptions "\n" " Extract all data from a .fpl file:\n" "\n" - " fpltool extract some.fpl" + " fpltool extract some.fpl\n" "\n" " Create a ROS some.bag file (ROS1) resp. some_bag directory (ROS2) from a .fpl file:\n" "\n" " fpltool extract -e ros some.fpl\n" " fpltool robag some.fpl # shortcut\n" "\n" - " Create a compressed another.bag (res. another_bag) with 2 minutes of data starting 60 seconds into some.fpl:\n" + " Create a compressed another.bag (ROS1, another_bag/ for ROS2) with 2 minutes of data starting 60 seconds\n" + " into some.fpl:\n" "\n" " fpltool rosbag some.fpl -c -c -o another.bag -S 60 -D 120\n" "\n" @@ -228,6 +254,11 @@ class FplToolOptions : public common::app::ProgramOptions " ros2 bag info some_bag # ROS 2 (default, see above)\n" " mcap info some_bag/some.mcap # ROS 2 (with compression, see above)\n" "\n" + " Create a ROS bag, convert encoded video (e.g. from PBx-A1 sensor) to greyscale images of half size:\n" + "\n" + " fpltool extract -e ros,cam some.fpl -s 0.5 -t Y8\n" + " fpltool robag some.fpl -s 0.5 -t Y8 # shortcut\n" + "\n" " Check what is in some_userio.raw obtained by 'extract some.fpl':\n" "\n" " parsertool some_userio.raw > some_userio.txt\n" @@ -305,6 +336,19 @@ class FplToolOptions : public common::app::ProgramOptions formats_ = common::string::StrSplit(argument, ","); break; } +#if FPSDK_USE_FFMPEG + case 's': + if (!common::string::StrToValue(argument, scale_) || (scale_ < 0.1) || (scale_ > 1.0)) { + ok = false; + } + break; + case 't': + pixelfmt_ = PixelFmtFromStrOr(argument.c_str(), common::video::PixelFmt::UNSPECIFIED); + if (pixelfmt_ == common::video::PixelFmt::UNSPECIFIED) { + ok = false; + } + break; +#endif default: ok = false; break; @@ -359,7 +403,10 @@ class FplToolOptions : public common::app::ProgramOptions DEBUG("skip = %d", skip_); DEBUG("duration = %d", duration_); DEBUG("formats = %s", common::string::StrJoin(formats_, " ").c_str()); - +#if FPSDK_USE_FFMPEG + DEBUG("scale = %.1f", scale_); + DEBUG("pixelfmt = %s", common::video::PixelFmtToStr(pixelfmt_)); +#endif return ok; } diff --git a/fpsdk_apps/fpltool/fpltool_trim.cpp b/fpsdk_apps/fpltool/fpltool_trim.cpp index 4a4c877b..25a7c409 100644 --- a/fpsdk_apps/fpltool/fpltool_trim.cpp +++ b/fpsdk_apps/fpltool/fpltool_trim.cpp @@ -172,6 +172,7 @@ bool DoTrim(const FplToolOptions& opts) break; } case FplType::STREAMMSG: + case FplType::CAMDATA: case FplType::INT_X: if (use1) { if (fpl.Write(log_msg.Raw())) { diff --git a/fpsdk_apps/fpltool/fpltool_utils.cpp b/fpsdk_apps/fpltool/fpltool_utils.cpp deleted file mode 100644 index e1da7912..00000000 --- a/fpsdk_apps/fpltool/fpltool_utils.cpp +++ /dev/null @@ -1,281 +0,0 @@ -/** - * \verbatim - * ___ ___ - * \ \ / / - * \ \/ / Copyright (c) Fixposition AG (www.fixposition.com) and contributors - * / /\ \ License: see the LICENSE file - * /__/ \__\ - * \endverbatim - * - * @file - * @brief Fixposition SDK: fpltool extract - */ - -/* LIBC/STL */ -#include - -/* EXTERNAL */ - -/* Fixposition SDK */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* PACKAGE */ -#include "fpltool_utils.hpp" - -namespace fpsdk { -namespace apps { -namespace fpltool { -/* ****************************************************************************************************************** */ - -using namespace fpsdk::common::app; -using namespace fpsdk::common::fpl; -using namespace fpsdk::common::parser; -using namespace fpsdk::common::parser::fpa; -using namespace fpsdk::common::parser::fpb; -using namespace fpsdk::common::parser::nmea; -using namespace fpsdk::common::path; -using namespace fpsdk::common::ros1; -using namespace fpsdk::common::string; -using namespace fpsdk::common::types; - -// --------------------------------------------------------------------------------------------------------------------- - -OutputFileHelper::OutputFileHelper(const std::string& prefix, const FplToolOptions& opts) /* clang-format off */ : - prefix_ { prefix }, - opts_ { opts } // clang-format on -{ -} -OutputFileHelper::~OutputFileHelper() -{ - CloseAll(); -} - -bool OutputFileHelper::WriteData(const std::string& name, const std::vector& data) -{ - auto f = GetOutputFile(name); - return f ? f->Write(data) : false; -} - -bool OutputFileHelper::WriteJson(const std::string& name, const nlohmann::json& json) -{ - auto f = GetOutputFile(name); - if (!f) { - return false; - } - return f->Write(json.dump()) && f->Write("\n"); -} - -bool OutputFileHelper::WriteStreamMsg(const std::string& name, const StreamMsg& streammsg, const ParserMsg& parsermsg) -{ - nlohmann::json jdata = streammsg; // magic to_json() (_type, _stamp, _stream, _raw/_raw_b64) - jdata.update(parsermsg); // magic to_json() (_proto, _name, _seq, _info, _raw/_raw_b64) - - // We can decode some messages of some protocols - if (msg_.proto_ == Protocol::FP_A) { - jdata.update(FpaDecodeMessage(msg_.data_)); // magic to_json() - } else if (msg_.proto_ == Protocol::NMEA) { - jdata.update(NmeaDecodeMessage(msg_.data_)); // magic to_json() - } else if (msg_.proto_ == Protocol::FP_B) { - jdata.update(to_json_FP_B(msg_)); // magic to_json() - } - return WriteJson(name, jdata); -} - -void OutputFileHelper::CloseAll(const bool ok) -{ - for (auto& file : files_) { - const std::string path = file.second->Path(); - file.second->Close(); - const auto size_str = OutputSizeStr(path); - if (ok) { - INFO("Wrote file %s (%s)", path.c_str(), size_str.c_str()); - } else { - WARNING("Incomplete file %s (%s)", path.c_str(), size_str.c_str()); - } - } - files_.clear(); -} - -OutputFile* OutputFileHelper::GetOutputFile(const std::string& name) -{ - auto file = files_.find(name); - if (file == files_.end()) { - const std::string path = prefix_ + "_" + name; - file = files_.emplace(name, std::make_unique()).first; - if (!opts_.overwrite_ && PathExists(path)) { - WARNING("Output file %s already exists", path.c_str()); - return nullptr; - } - NOTICE("Extracting to %s", path.c_str()); - if (!file->second->Open(path)) { - return nullptr; - } - } - return file->second.get(); -} - -// --------------------------------------------------------------------------------------------------------------------- - -ParserMsgHelper::ParserMsgHelper() -{ -#if defined(FPSDK_USE_ROS1) || defined(FPSDK_USE_ROS2) - rosmsg_.layout.dim.resize(1); -#endif -} -ParserMsgHelper::~ParserMsgHelper() -{ -} - -void ParserMsgHelper::UpdateParserMsg(const common::fpl::StreamMsg& streammsg) -{ - parser_.Reset(); - if (!parser_.Add(streammsg.msg_data_) || !parser_.Process(msg_) || - (msg_.data_.size() != streammsg.msg_data_.size())) { - msg_.proto_ = Protocol::OTHER; - msg_.name_ = ProtocolStr(Protocol::OTHER); - msg_.data_ = streammsg.msg_data_; - } - msg_.info_.clear(); - auto seq = seq_.find(streammsg.stream_name_); - if (seq == seq_.end()) { - seq = seq_.emplace(streammsg.stream_name_, 1).first; - } - msg_.seq_ = seq->second++; - -#if defined(FPSDK_USE_ROS1) || defined(FPSDK_USE_ROS2) - rosmsg_.layout.dim[0].label = msg_.name_; - rosmsg_.layout.dim[0].size = msg_.data_.size(); - rosmsg_.layout.dim[0].stride = msg_.data_.size(); - rosmsg_.data = { msg_.data_.data(), msg_.data_.data() + msg_.data_.size() }; -# if defined(FPSDK_USE_ROS1) - stamp_ = { streammsg.rec_time_.sec_, streammsg.rec_time_.nsec_ }; -# else - stamp_ = { (int)streammsg.rec_time_.sec_, streammsg.rec_time_.nsec_, RCL_ROS_TIME }; -# endif -#endif -} - -const ParserMsg& ParserMsgHelper::GetParserMsg(const bool make_info) const -{ - if (make_info) { - msg_.MakeInfo(); - } - return msg_; -} - -// --------------------------------------------------------------------------------------------------------------------- - -RosMsgHelper::RosMsgHelper() -{ -} - -RosMsgHelper::~RosMsgHelper() -{ -} - -void RosMsgHelper::AddDef(const RosMsgDef& rosmsgdef) -{ - if (rosmsgdef.valid_ && (defs_.find(rosmsgdef.topic_name_) == defs_.end())) { - DEBUG("RosMsgHelper: %s", rosmsgdef.info_.c_str()); - defs_.emplace(rosmsgdef.topic_name_, rosmsgdef); - } -} - -bool RosMsgHelper::ToJson(const RosMsgBin& rosmsgbin, nlohmann::json& jdata) -{ - if (!rosmsgbin.valid_) { - return false; - } - - const auto& entry = defs_.find(rosmsgbin.topic_name_); - if (entry == defs_.end()) { - WARNING_THR(1000, "Missing ROSMSGDEF for ROSMSGBIN %s", rosmsgbin.info_.c_str()); - return false; - } - - const auto& rosmsgdef = entry->second; - TRACE("ROSMSGBIN %s using ROSMSGDEF %s", rosmsgbin.info_.c_str(), rosmsgdef.info_.c_str()); - - // Try the implemented conversions - bool ok = false; - try { - if (RosMsgToJson(rosmsgdef, rosmsgbin, jdata) || - RosMsgToJson(rosmsgdef, rosmsgbin, jdata) || - RosMsgToJson(rosmsgdef, rosmsgbin, jdata) || - RosMsgToJson(rosmsgdef, rosmsgbin, jdata) || - RosMsgToJson(rosmsgdef, rosmsgbin, jdata)) { - jdata["_type"] = FplTypeStr(FplType::ROSMSGBIN); - jdata["_msg"] = rosmsgdef.msg_name_; - jdata["_topic"] = rosmsgbin.topic_name_; - jdata["_stamp"] = rosmsgbin.rec_time_; - ok = true; - } else { - throw std::runtime_error("conversion not implemented"); - } - } catch (std::exception& ex) { - WARNING( - "ROSMSGBIN %s ROSMSGDEF %s ToJson fail: %s", rosmsgbin.info_.c_str(), rosmsgdef.info_.c_str(), ex.what()); - } - return ok; -} - -// --------------------------------------------------------------------------------------------------------------------- - -std::string FileDumpOutName(const common::fpl::FileDump& filedump) -{ - const auto parts = StrSplit(filedump.filename_, ".", 2); - std::string outname = parts[0]; - const auto m = filedump.mtime_.GetUtcTime(0); - outname += Sprintf("_%04d%02d%02d-%02d%02d%02.0f", m.year_, m.month_, m.day_, m.hour_, m.min_, m.sec_); - if (parts.size() > 1) { - outname += "." + parts[1]; - } - return outname; -} - -// --------------------------------------------------------------------------------------------------------------------- - -const std::string& FixTopicName(const std::string& in_topic) -{ - if (in_topic == "/fusion_optim/imu_biases") { - static std::string str = "/imu/biases"; - return str; - } else { - return in_topic; - } -} - -// --------------------------------------------------------------------------------------------------------------------- - -std::string OutputSizeStr(const std::string& path) -{ - const double size = (double)(PathIsDirectory(path) ? DirSize(path) : FileSize(path)); - if (size < 1024.0) { - return Sprintf("%.0f B", size); - } else if (size < (1024.0 * 1024.0)) { - return Sprintf("%.1f KiB", size / 1024.0); - } else { - return Sprintf("%.1f MiB", size / 1024.0 / 1024.0); - } -} - -/* ****************************************************************************************************************** */ -} // namespace fpltool -} // namespace apps -} // namespace fpsdk diff --git a/fpsdk_apps/fpltool/fpltool_utils.hpp b/fpsdk_apps/fpltool/fpltool_utils.hpp deleted file mode 100644 index e65927cb..00000000 --- a/fpsdk_apps/fpltool/fpltool_utils.hpp +++ /dev/null @@ -1,127 +0,0 @@ -/** - * \verbatim - * ___ ___ - * \ \ / / - * \ \/ / Copyright (c) Fixposition AG (www.fixposition.com) and contributors - * / /\ \ License: see the LICENSE file - * /__/ \__\ - * \endverbatim - * - * @file - * @brief Fixposition SDK: fpltool utils - */ -#ifndef __FPSDK_APPS_FPLTOOL_FPLTOOL_UTILS_HPP__ -#define __FPSDK_APPS_FPLTOOL_FPLTOOL_UTILS_HPP__ - -/* LIBC/STL */ -#include -#include -#include - -/* EXTERNAL */ -#include -#if defined(FPSDK_USE_ROS1) -# include -#elif defined(FPSDK_USE_ROS2) -# include -# include -#endif - -/* Fixposition SDK */ -#include -#include -#include -#include - -/* PACKAGE */ -#include "fpltool_opts.hpp" - -namespace fpsdk { -namespace apps { -namespace fpltool { -/* ****************************************************************************************************************** */ - -class OutputFileHelper -{ - public: - OutputFileHelper(const std::string& prefix, const FplToolOptions& opts); - ~OutputFileHelper(); - - bool WriteData(const std::string& name, const std::vector& data); - bool WriteJson(const std::string& name, const nlohmann::json& json); - bool WriteStreamMsg( - const std::string& name, const common::fpl::StreamMsg& streammsg, const common::parser::ParserMsg& parsermsg); - void CloseAll(const bool ok = false); - - private: - std::string prefix_; - FplToolOptions opts_; - std::map> files_; - common::parser::Parser parser_; - common::parser::ParserMsg msg_; - common::path::OutputFile* GetOutputFile(const std::string& name); -}; - -class ParserMsgHelper -{ - public: - ParserMsgHelper(); - ~ParserMsgHelper(); - void UpdateParserMsg(const common::fpl::StreamMsg& streammsg); - const common::parser::ParserMsg& GetParserMsg(const bool make_info = false) const; -#if defined(FPSDK_USE_ROS1) - const std_msgs::ByteMultiArray& GetRosMsg() const - { - return rosmsg_; - } - const ros::Time& GetRosStamp() const - { - return stamp_; - } -#elif defined(FPSDK_USE_ROS2) - const std_msgs::msg::ByteMultiArray& GetRosMsg() const - { - return rosmsg_; - } - const rclcpp::Time& GetRosStamp() const - { - return stamp_; - } -#endif - - private: - common::parser::Parser parser_; - common::parser::ParserMsg msg_; - std::map seq_; -#if defined(FPSDK_USE_ROS1) - std_msgs::ByteMultiArray rosmsg_; - ros::Time stamp_; -#elif defined(FPSDK_USE_ROS2) - std_msgs::msg::ByteMultiArray rosmsg_; - rclcpp::Time stamp_; -#endif -}; - -class RosMsgHelper -{ - public: - RosMsgHelper(); - ~RosMsgHelper(); - void AddDef(const common::fpl::RosMsgDef& rosmsgdef); - bool ToJson(const common::fpl::RosMsgBin& rosmsgbin, nlohmann::json& jdata); - - private: - std::map defs_; -}; - -std::string FileDumpOutName(const common::fpl::FileDump& filedump); - -const std::string& FixTopicName(const std::string& in_topic); - -std::string OutputSizeStr(const std::string& path); - -/* ****************************************************************************************************************** */ -} // namespace fpltool -} // namespace apps -} // namespace fpsdk -#endif // __FPSDK_APPS_FPLTOOL_FPLTOOL_UTILS_HPP__ diff --git a/fpsdk_common/CMakeLists.txt b/fpsdk_common/CMakeLists.txt index 699fd313..bd3020af 100644 --- a/fpsdk_common/CMakeLists.txt +++ b/fpsdk_common/CMakeLists.txt @@ -3,10 +3,10 @@ message(STATUS "fpsdk: ----- ${CMAKE_CURRENT_SOURCE_DIR} -----") cmake_minimum_required(VERSION 3.16) include(cmake/setup.cmake) -include(cmake/ros.cmake) +include(cmake/utils.cmake) project(fpsdk_common - LANGUAGES CXX + LANGUAGES CXX C VERSION ${FPSDK_VERSION_NUMBER} DESCRIPTION "Fixposition SDK: Common Library" ) @@ -58,23 +58,8 @@ find_package(nlohmann_json REQUIRED) find_package(Threads REQUIRED) find_package(PkgConfig REQUIRED) pkg_search_module(OpenSSL REQUIRED IMPORTED_TARGET openssl) - -# PROJ is optional, unless explicitly requested with -DFPSDK_USE_PROJ=ON -if(FPSDK_USE_PROJ STREQUAL "ON") - find_package(PROJ 9.4 REQUIRED CONFIG) - message(STATUS "fpsdk: Using PROJ (${PROJ_VERSION})") -elseif(FPSDK_USE_PROJ STREQUAL "OFF") - message(STATUS "fpsdk: Not using PROJ") -else() - find_package(PROJ 9.4 CONFIG QUIET) - if(${PROJ_FOUND}) - message(STATUS "fpsdk: Using PROJ (${PROJ_VERSION})") - set(FP_FPSDK_USE_PROJ ON) - else() - message(STATUS "fpsdk: No PROJ found") - set(FP_FPSDK_USE_PROJ OFF) - endif() -endif() +fpsdk_find_package_proj() +fpsdk_find_package_ffmpeg() if(FPSDK_USE_ROS1) fpsdk_cmake_find_ros1_package(std_msgs) @@ -95,7 +80,7 @@ else() endif() fpsdk_save_versions(FILE ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_versions.txt - PACKAGES CMAKE Boost yaml-cpp ZLIB Eigen3 nlohmann_json OpenSSL PROJ) + PACKAGES CMAKE Boost yaml-cpp ZLIB Eigen3 nlohmann_json OpenSSL PROJ libavcodec libavutil libavfilter libswscale) # SHARED LIBRARY ======================================================================================================= @@ -108,6 +93,12 @@ else() target_compile_definitions(${PROJECT_NAME} PUBLIC FPSDK_USE_PROJ=0) endif() +if(FPSDK_USE_FFMPEG) + target_compile_definitions(${PROJECT_NAME} PUBLIC FPSDK_USE_FFMPEG=1) +else() + target_compile_definitions(${PROJECT_NAME} PUBLIC FPSDK_USE_FFMPEG=0) +endif() + target_include_directories(${PROJECT_NAME} PUBLIC $ @@ -141,6 +132,10 @@ target_link_libraries(${PROJECT_NAME} Threads::Threads nlohmann_json::nlohmann_json $<$:PROJ::proj> + $<$:PkgConfig::libavcodec> + $<$:PkgConfig::libavutil> + $<$:PkgConfig::libavfilter> + $<$:PkgConfig::libswscale> ) set_target_properties(${PROJECT_NAME} @@ -156,6 +151,7 @@ set(PROJECT_RUNTIME_DIR ${CMAKE_INSTALL_FULL_BINDIR}) set(PROJECT_LIBRARY_DIR ${CMAKE_INSTALL_FULL_LIBDIR}) set(PROJECT_INCLUDE_DIR ${CMAKE_INSTALL_FULL_INCLUDEDIR}/${PROJECT_NAME}) set(PROJECT_DATA_DIR ${CMAKE_INSTALL_FULL_DATAROOTDIR}/${PROJECT_NAME}) +set(PROJECT_DOC_DIR ${CMAKE_INSTALL_FULL_DOCDIR}) # Headers install(DIRECTORY include/${PROJECT_NAME}/ @@ -218,6 +214,14 @@ install( DESTINATION lib/pkgconfig ) +# Documentation +install( + FILES + ${PROJECT_SOURCE_DIR}/README.md + ${PROJECT_SOURCE_DIR}/LICENSE + DESTINATION ${PROJECT_DOC_DIR} +) + # TESTS ================================================================================================================ @@ -225,6 +229,7 @@ include(cmake/testing.cmake) add_gtest(TARGET app_test SOURCES test/app_test.cpp LINK_LIBS ${PROJECT_NAME}) add_gtest(TARGET can_test SOURCES test/can_test.cpp LINK_LIBS ${PROJECT_NAME}) +add_gtest(TARGET cam_test SOURCES test/cam_test.cpp LINK_LIBS ${PROJECT_NAME}) add_gtest(TARGET fpl_test SOURCES test/fpl_test.cpp LINK_LIBS ${PROJECT_NAME}) add_gtest(TARGET logging_test SOURCES test/logging_test.cpp LINK_LIBS ${PROJECT_NAME}) add_gtest(TARGET gnss_test SOURCES test/gnss_test.cpp LINK_LIBS ${PROJECT_NAME}) @@ -250,6 +255,7 @@ add_gtest(TARGET thread_test SOURCES test/thread_test.cpp LINK add_gtest(TARGET trafo_test SOURCES test/trafo_test.cpp LINK_LIBS ${PROJECT_NAME}) add_gtest(TARGET types_test SOURCES test/types_test.cpp LINK_LIBS ${PROJECT_NAME}) add_gtest(TARGET utils_test SOURCES test/utils_test.cpp LINK_LIBS ${PROJECT_NAME}) +add_gtest(TARGET video_test SOURCES test/video_test.cpp LINK_LIBS ${PROJECT_NAME}) add_gtest(TARGET yaml_test SOURCES test/yaml_test.cpp LINK_LIBS ${PROJECT_NAME}) # ====================================================================================================================== diff --git a/fpsdk_common/LICENSE b/fpsdk_common/LICENSE index 4f27604e..05e4486e 100644 --- a/fpsdk_common/LICENSE +++ b/fpsdk_common/LICENSE @@ -78,3 +78,514 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + + +-------------------------------------------------------------------------------- + +Some of fpsdk_common uses FFmpeg libraries (libavcodec, libavutil and libswscale) +licensed under the GNU Lesser General Public License version 2.1 or later. + +Source code for these libraries is available at: https://ffmpeg.org/ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/fpsdk_common/cmake/ros.cmake b/fpsdk_common/cmake/ros.cmake deleted file mode 100644 index ea1424e8..00000000 --- a/fpsdk_common/cmake/ros.cmake +++ /dev/null @@ -1,21 +0,0 @@ - -# Find ROS1 package and create a target for it -macro(fpsdk_cmake_find_ros1_package ROS_PACKAGE) - if(NOT TARGET ros1::${ROS_PACKAGE}) - # Create a target for the ROS library - add_library(ros1_${ROS_PACKAGE} INTERFACE) - add_library(ros1::${ROS_PACKAGE} ALIAS ros1_${ROS_PACKAGE}) - find_package(${ROS_PACKAGE} REQUIRED) - target_include_directories(ros1_${ROS_PACKAGE} INTERFACE ${${ROS_PACKAGE}_INCLUDE_DIRS}) - target_link_libraries(ros1_${ROS_PACKAGE} INTERFACE ${${ROS_PACKAGE}_LIBRARIES}) - endif() -endmacro() - -# Use shipped ROS1 "packages" (only for fpsdk_common!) -macro(fpsdk_cmake_local_ros1_package ROS_PACKAGE) - if(NOT TARGET ros1::${ROS_PACKAGE}) - add_library(ros1_${ROS_PACKAGE} INTERFACE) - add_library(ros1::${ROS_PACKAGE} ALIAS ros1_${ROS_PACKAGE}) - target_include_directories(ros1_${ROS_PACKAGE} INTERFACE ${PROJECT_SOURCE_DIR}/rosnoros) - endif() -endmacro() diff --git a/fpsdk_common/cmake/testing.cmake b/fpsdk_common/cmake/testing.cmake index 5f7c4f3d..835725ad 100644 --- a/fpsdk_common/cmake/testing.cmake +++ b/fpsdk_common/cmake/testing.cmake @@ -1,42 +1,30 @@ -# Override the standard BUILD_TESTING variable -if (DEFINED FPSDK_BUILD_TESTING) - message(STATUS "fpsdk: FPSDK_BUILD_TESTING=${FPSDK_BUILD_TESTING}") - set(BUILD_TESTING ${FPSDK_BUILD_TESTING}) -endif() - -# User requested to build testing (-DBUILD_TESTING=ON), abort if no suitable version available -if (BUILD_TESTING STREQUAL "ON") - - find_package(GTest 1.12.0 REQUIRED) +# Explicitly requested by user to build tests (-DFPSDK_BUILD_TESTING=ON) +if(FPSDK_BUILD_TESTING STREQUAL "ON") + find_package(GTest 1.11.0 REQUIRED) # GTest doesn't seem to have a normal cmake config file, so we have to check and abort ourselves.. :-/ if ("${GTest_VERSION}" STREQUAL "") message(FATAL_ERROR "Unsupported GTest version") endif() - message(STATUS "fpsdk: Using GTest (${GTest_VERSION})") - set(BUILD_TESTING ON) - -# User requested no testing (-DBUILD_TESTING=OFF) -elseif(BUILD_TESTING STREQUAL "OFF") - message(STATUS "fpsdk: testing disabled") - set(BUILD_TESTING OFF) + # Explicitly requested by user to not build tests (-DFPSDK_BUILD_TESTING=OFF) +elseif(FPSDK_BUILD_TESTING STREQUAL "OFF") + message(STATUS "fpsdk: Not using gtest") -# Automatically detect if a suitable GTest library is available + # Automatic, build tests if a suitable version of gtest is available (-DFPSDK_BUILD_TESTING=) else() - - find_package(GTest 1.12.0 QUIET) + find_package(GTest 1.11.0 QUIET) if ("${GTest_VERSION}" STREQUAL "") - message(STATUS "fpsdk: No GTest found, disable testing") - set(BUILD_TESTING OFF) + set(FPSDK_BUILD_TESTING OFF) + message(STATUS "fpsdk: No gtest found") else() - message(STATUS "fpsdk: Using GTest (${GTest_VERSION})") - set(BUILD_TESTING ON) + set(FPSDK_BUILD_TESTING ON) endif() - endif() +# Setup testing stuff +if(FPSDK_BUILD_TESTING) + message(STATUS "fpsdk: Using gtest (${GTest_VERSION})") -if(BUILD_TESTING) # Make unique test executable names across projects set(GTEST_PREFIX "${PROJECT_NAME}") enable_testing() @@ -71,6 +59,7 @@ if(BUILD_TESTING) endmacro() else() + set(BUILD_TESTING OFF) macro(add_gtest) # nothing... diff --git a/fpsdk_common/cmake/utils.cmake b/fpsdk_common/cmake/utils.cmake new file mode 100644 index 00000000..a0365609 --- /dev/null +++ b/fpsdk_common/cmake/utils.cmake @@ -0,0 +1,146 @@ +include(CheckCSourceRuns) +include(CMakePushCheckState) + +# Find ROS1 package and create a target for it +macro(fpsdk_cmake_find_ros1_package ROS_PACKAGE) + if(NOT TARGET ros1::${ROS_PACKAGE}) + # Create a target for the ROS library + add_library(ros1_${ROS_PACKAGE} INTERFACE) + add_library(ros1::${ROS_PACKAGE} ALIAS ros1_${ROS_PACKAGE}) + find_package(${ROS_PACKAGE} REQUIRED) + target_include_directories(ros1_${ROS_PACKAGE} INTERFACE ${${ROS_PACKAGE}_INCLUDE_DIRS}) + target_link_libraries(ros1_${ROS_PACKAGE} INTERFACE ${${ROS_PACKAGE}_LIBRARIES}) + endif() +endmacro() + +# Use shipped ROS1 "packages" (only for fpsdk_common!) +macro(fpsdk_cmake_local_ros1_package ROS_PACKAGE) + if(NOT TARGET ros1::${ROS_PACKAGE}) + add_library(ros1_${ROS_PACKAGE} INTERFACE) + add_library(ros1::${ROS_PACKAGE} ALIAS ros1_${ROS_PACKAGE}) + target_include_directories(ros1_${ROS_PACKAGE} INTERFACE ${PROJECT_SOURCE_DIR}/rosnoros) + endif() +endmacro() + +######################################################################################################################## + +# PROJ is optional, unless explicitly requested with -DFPSDK_USE_PROJ=ON +macro(fpsdk_find_package_proj) + # message(STATUS "fpsdk: FPSDK_USE_PROJ=${FPSDK_USE_PROJ}") + + # Explicitly requested by user to use proj + if(FPSDK_USE_PROJ STREQUAL "ON") + find_package(PROJ 9.4 REQUIRED CONFIG) + + # Explicitly requested by user to not use proj + elseif(FPSDK_USE_PROJ STREQUAL "OFF") + message(STATUS "fpsdk: Not using PROJ") + + # Automatic, use proj if a suitable version is available + else() + find_package(PROJ 9.4 CONFIG QUIET) + if(${PROJ_FOUND}) + set(FPSDK_USE_PROJ ON) + else() + message(STATUS "fpsdk: No PROJ found") + set(FPSDK_USE_PROJ OFF) + endif() + endif() + + if(FPSDK_USE_PROJ) + message(STATUS "fpsdk: Using PROJ (${PROJ_VERSION}, ${PROJ_INCLUDE_DIRS})") + endif() + +endmacro() + +######################################################################################################################## + +# Check that FFmpeg is not built with --enable-gpl or --enable-nonfree, that is, it is built with +# --disable-gpl and --disable-nonfree. See docker/scripts/install_ffmpeg.sh +macro(fpsdk_check_ffmpeg_is_lgpl) + cmake_push_check_state(RESET) + + set(CMAKE_REQUIRED_LIBRARIES ${libavutil_LIBRARIES}) + set(CMAKE_REQUIRED_INCLUDES ${libavutil_INCLUDEDIR}) + set(CMAKE_REQUIRED_LINK_DIRECTORIES ${libavutil_LIBRARY_DIRS}) + message(STATUS "fpsdk: Checking FFmpeg libraries (${libavutil_LIBRARY_DIRS})") + check_c_source_runs(" + #include + #include + #include + int main() { + const char *config = avutil_configuration(); + printf(\"avutil_configuration: %s%c\", config, 0x0a); + if (strstr(config, \"--enable-gpl\") != NULL) { return 1; } + if (strstr(config, \"--enable-nonfree\") != NULL) { return 1; } + return 0; + } + " FFMPEG_IS_LGPL) + + if (NOT FFMPEG_IS_LGPL) + message(STATUS "fpsdk: The FFmpeg libraries in ${libavutil_LIBRARY_DIRS} are not LGPL.") + else() + message(STATUS "fpsdk: The FFmpeg libraries in ${libavutil_LIBRARY_DIRS} are LGPL.") + endif() + + cmake_pop_check_state() +endmacro() + +# FFmpeg is optional, unless explicitly requested with -DFPSDK_USE_FFMPEG=ON +macro(fpsdk_find_package_ffmpeg) + # message(STATUS "fpsdk: FPSDK_USE_FFMPEG=${FPSDK_USE_FFMPEG}") + + # Explicitly requested by user to use FFmpeg libraries + if(FPSDK_USE_FFMPEG STREQUAL "ON") + find_package(PkgConfig REQUIRED) + pkg_search_module(libavcodec REQUIRED IMPORTED_TARGET libavcodec) + pkg_search_module(libavutil REQUIRED IMPORTED_TARGET libavutil) + pkg_search_module(libavfilter REQUIRED IMPORTED_TARGET libavfilter) + pkg_search_module(libswscale REQUIRED IMPORTED_TARGET libswscale) + fpsdk_check_ffmpeg_is_lgpl() + + # Explicitly requested by user to not use FFmpeg libraries + elseif(FPSDK_USE_FFMPEG STREQUAL "OFF") + message(STATUS "fpsdk: Not using FFmpeg libraries") + set(FPSDK_USE_FFMPEG OFF) + # Automatic, use FFmpeg if available (and free)) + else() + find_package(PkgConfig REQUIRED) + pkg_search_module(libavcodec QUIET IMPORTED_TARGET libavcodec) + pkg_search_module(libavutil QUIET IMPORTED_TARGET libavutil) + pkg_search_module(libavfilter QUIET IMPORTED_TARGET libavfilter) + pkg_search_module(libswscale QUIET IMPORTED_TARGET libswscale) + if(libavcodec_FOUND AND libavutil_FOUND AND libavfilter_FOUND AND libswscale_FOUND) + fpsdk_check_ffmpeg_is_lgpl() + if(NOT FFMPEG_IS_LGPL) + set(FPSDK_USE_FFMPEG OFF) + else() + set(FPSDK_USE_FFMPEG ON) + endif() + else() + set(FPSDK_USE_FFMPEG OFF) + endif() + + if(NOT FPSDK_USE_FFMPEG) + message(STATUS "fpsdk: No (suitable) FFmpeg libraries found") + endif() + + endif() + + # Refuse to continue if FFmpeg is enabled but it's a non-free version + if(FPSDK_USE_FFMPEG STREQUAL "ON") + if(NOT FFMPEG_IS_LGPL) + message(FATAL_ERROR "fpsdk: The FFmpeg libraries in ${libavutil_LIBRARY_DIRS} \ + are not LGPL. Please either disable the use of FFmpeg libraries (-DFPSDK_USE_FFMPEG=OFF) \ + or provide suitably configured and compiled FFmpeg libraries (-DCMAKE_PREFIX_PATH=/path/to/ffmpeg).") + endif() + endif() + + if(FPSDK_USE_FFMPEG) + message(STATUS "fpsdk: Using FFmpeg (libavcodec ${libavcodec_VERSION}, ${libavcodec_INCLUDEDIR})") + message(STATUS "fpsdk: Using FFmpeg (libavutil ${libavutil_VERSION}, ${libavutil_INCLUDEDIR})") + message(STATUS "fpsdk: Using FFmpeg (libavfilter ${libavfilter_VERSION}, ${libavfilter_INCLUDEDIR})") + message(STATUS "fpsdk: Using FFmpeg (libswscale ${libswscale_VERSION}, ${libswscale_INCLUDEDIR})") + endif() + +endmacro() diff --git a/fpsdk_common/doc/doc.hpp b/fpsdk_common/doc/doc.hpp index 0e904e21..980503b0 100644 --- a/fpsdk_common/doc/doc.hpp +++ b/fpsdk_common/doc/doc.hpp @@ -31,7 +31,8 @@ namespace common { @section FPSDK_COMMON_MODULES Modules - - @subpage FPSDK_COMMON_APPS + - @subpage FPSDK_COMMON_APP + - @subpage FPSDK_COMMON_CAM - @subpage FPSDK_COMMON_CAN - @subpage FPSDK_COMMON_FPL - @subpage FPSDK_COMMON_GNSS @@ -41,11 +42,12 @@ namespace common { - @subpage FPSDK_COMMON_PATH - @subpage FPSDK_COMMON_ROS1 - @subpage FPSDK_COMMON_STRING - - @subpage FPSDK_COMMON_TIME - @subpage FPSDK_COMMON_THREAD + - @subpage FPSDK_COMMON_TIME - @subpage FPSDK_COMMON_TRAFO - @subpage FPSDK_COMMON_TYPES - @subpage FPSDK_COMMON_UTILS + - @subpage FPSDK_COMMON_VIDEO - @subpage FPSDK_COMMON_YAML @section FPSDK_COMMON_LICENSE License diff --git a/fpsdk_common/include/fpsdk_common/app.hpp b/fpsdk_common/include/fpsdk_common/app.hpp index 42c229b7..bb0c7e27 100644 --- a/fpsdk_common/include/fpsdk_common/app.hpp +++ b/fpsdk_common/include/fpsdk_common/app.hpp @@ -10,7 +10,7 @@ * @file * @brief Fixposition SDK: Utilities for apps * - * @page FPSDK_COMMON_APPS Utilities for apps + * @page FPSDK_COMMON_APP Utilities for apps * * **API**: fpsdk_common/app.hpp and fpsdk::common::app * diff --git a/fpsdk_common/include/fpsdk_common/cam.hpp b/fpsdk_common/include/fpsdk_common/cam.hpp new file mode 100644 index 00000000..a142a01a --- /dev/null +++ b/fpsdk_common/include/fpsdk_common/cam.hpp @@ -0,0 +1,195 @@ +/** + * \verbatim + * ___ ___ + * \ \ / / + * \ \/ / Copyright (c) Fixposition AG (www.fixposition.com) and contributors + * / /\ \ License: see the LICENSE file + * /__/ \__\ + * \endverbatim + * + * @file + * @brief Fixposition SDK: Camera types and utilities + * + * @page FPSDK_COMMON_CAM Camera types and utilities + * + * **API**: fpsdk_common/cam.hpp and fpsdk::common::cam + * + */ +#ifndef __FPSDK_COMMON_CAM_HPP__ +#define __FPSDK_COMMON_CAM_HPP__ + +/* LIBC/STL */ +#include + +/* EXTERNAL */ + +/* PACKAGE */ + +namespace fpsdk { +namespace common { +/** + * @brief Camera types and utilities + */ +namespace cam { +/* ****************************************************************************************************************** */ + +/** + * @brief Camera ID + */ +enum class CamId : uint8_t // clang-format off +{ + UNSPECIFIED = 0, //!< Unspecified + CAM1 = 1, //!< Camera 1 + CAM2 = 2, //!< Camera 2 + CAM3 = 3, //!< Camera 3 + CAM4 = 4, //!< Camera 4 +}; // clang-format on + +/** + * @brief Stringify camera ID enum + * + * @param[in] camid Camera ID + * + * @returns the stringification of the CameraId, or "?" for bad enum values + */ +const char* CamIdToStr(const CamId camid); + +/** + * @brief Convert camera ID string to enum + * + * @param[in] str Camera ID string + * @param[in] def Default Camer ID, for bad str + * + * @returns the camera ID, or def if str was invalid + */ +CamId CamIdFromStrOr(const char* str, const CamId def); + +/** + * @brief Convert camera ID value to enum + * + * @param[in] val Camera ID value + * @param[in] def Default camera data type, for bad str + * + * @returns the camera data type, or def if str was invalid + */ +CamId CamIdFromValOr(const uint8_t val, const CamId def); + +// --------------------------------------------------------------------------------------------------------------------- + +/** + * @brief Camera data type (type of data) + */ +enum class CamDataType : uint8_t // clang-format off +{ + UNSPECIFIED = 0, //!< Unspecified + HIRES_IMG = 1, //!< High-res image + LORES_IMG = 2, //!< Low-res image + HIRES_VID = 3, //!< High-res video + LORES_VID = 4, //!< Low-res video +}; // clang-format on + +/** + * @brief Stringify camera data type enum + * + * @param[in] type Camera data type + * + * @returns the stringification of the camera data type, or "?" for bad enum values + */ +const char* CamDataTypeToStr(const CamDataType type); + +/** + * @brief Convert camera data type string to enum + * + * @param[in] str Camera data type string + * @param[in] def Default camera data type, for bad str + * + * @returns the camera data type, or def if str was invalid + */ +CamDataType CamDataTypeFromStrOr(const char* str, const CamDataType def); + +/** + * @brief Convert camera data type value to enum + * + * @param[in] val Camera data type value + * @param[in] def Default camera data type, for bad str + * + * @returns the camera data type, or def if str was invalid + */ +CamDataType CamDataTypeFromValOr(const uint8_t val, const CamDataType def); + +// --------------------------------------------------------------------------------------------------------------------- + +/** + * @brief Camera data format + */ +enum class CamDataFmt : uint8_t // clang-format off +{ + UNSPECIFIED = 0, //!< Invalid + // Video + H264_NAL = 1, //!< H.264 NAL (AVC) (video/avc) + H265_NAL = 2, //!< H.265 NAL (HEVC) (video/hevc) + MJPEG = 3, //!< Motion JPEG (image/jpeg) + // Image + JPEG = 4, //!< JPEG (image/jpeg) + Y8 = 5, //!< Planar Y (greyscale), 8bpp + NV12 = 6, //!< Planar YUV 4:2:0, 12bpp + RGB24 = 7, //!< Packed RGB 8:8:8, 24bpp +}; // clang-format on + +/** + * @brief Stringify camera data format enum + * + * @param[in] fmt Camera data format + * + * @returns the stringification of the camera data format, or "?" for bad enum values + */ +const char* CamDataFmtToStr(const CamDataFmt fmt); + +/** + * @brief Convert camera data format value to enum + * + * @param[in] val Camera data format value + * @param[in] def Default camera data format, for bad str + * + * @returns the camera data format, or def if str was invalid + */ +CamDataFmt CamDataFmtFromValOr(const uint8_t val, const CamDataFmt def); + +// --------------------------------------------------------------------------------------------------------------------- + +/** + * @brief Camera frame type + */ +enum class CamDataFrm : uint8_t // clang-format off +{ + UNSPECIFIED = 0, //!< Unspecified, invalid data + I_FRAME = 1, //!< I-frame (= Horizon camsys MC_H26[45]_NALU_TYPE_I) + P_FRAME = 2, //!< P-frame (= Horizon camsys MC_H26[45]_NALU_TYPE_P) + FULL = 3, //!< Full frame (non-encoded, e.g. NV12 or JPEG) + OTHER = 4, //!< Other frame type (unexpected) +}; // clang-format on + +/** + * @brief Stringify camera data frame type enum + * + * @param[in] frm Camera data frame type + * + * @returns the stringification of the camera data frame type, or "?" for bad enum values + */ +const char* CamDataFrmToStr(const CamDataFrm frm); + +/** + * @brief Convert camera data frame type value to enum + * + * @param[in] val Camera data frame type value + * @param[in] def Default camera data frame type, for bad str + * + * @returns the camera data frame type, or def if str was invalid + */ +CamDataFrm CamDataFrmFromValOr(const uint8_t val, const CamDataFrm def); + +/* ****************************************************************************************************************** */ +} // namespace cam +} // namespace common +} // namespace fpsdk +#endif // __FPSDK_COMMON_CAM_HPP__ diff --git a/fpsdk_common/include/fpsdk_common/can.hpp b/fpsdk_common/include/fpsdk_common/can.hpp index 3b0cb715..c0fc8e89 100644 --- a/fpsdk_common/include/fpsdk_common/can.hpp +++ b/fpsdk_common/include/fpsdk_common/can.hpp @@ -12,7 +12,7 @@ * * @page FPSDK_COMMON_CAN CAN bus utilities * - * **API**: fpsdk_common/can.hpp and fpsdk::common::utils + * **API**: fpsdk_common/can.hpp and fpsdk::common::can * * @section FPSDK_COMMON_CAN_FRAMES CAN frames (messages) * diff --git a/fpsdk_common/include/fpsdk_common/fpl.hpp b/fpsdk_common/include/fpsdk_common/fpl.hpp index 04af8c0b..afc9e733 100644 --- a/fpsdk_common/include/fpsdk_common/fpl.hpp +++ b/fpsdk_common/include/fpsdk_common/fpl.hpp @@ -28,6 +28,7 @@ /* EXTERNAL */ /* PACKAGE */ +#include "cam.hpp" #include "time.hpp" namespace fpsdk { @@ -50,6 +51,7 @@ enum class FplType : uint16_t STREAMMSG = 0x0106, //!< Stream message raw data with timestamp LOGSTATUS = 0x0107, //!< Logging status FILEDUMP = 0x0108, //!< Dump of an entire (small) file + CAMDATA = 0x0109, //!< Camera data (image, video, ...) BLOB = 0xbaad, //!< Arbitrary data, not FplMessage framing INT_D = 0xffaa, //!< Fixposition internal use only INT_F = 0xffbb, //!< Fixposition internal use only @@ -414,6 +416,51 @@ struct FileDump std::vector data_; //!< File contents }; +// --------------------------------------------------------------------------------------------------------------------- + +/** + * @brief Meta data for a FplType::CAMDATA message + */ +struct CamDataMeta +{ // clang-format off + uint8_t cam_id_ = 0; //!< Camera (fpsdk::common::cam::CamId) + uint8_t type_ = 0; //!< Data type (fpsdk::common::cam::CamDataType) + uint8_t fmt_ = 0; //!< Data format (fpsdk::common::cam::CamDataFmt) + uint8_t frm_ = 0; //!< Frame type (fpsdk::common::cam::CamDataFrm) + uint8_t reserved0_[4] = { 0 }; //!< Reserved for future use, set to 0 + uint64_t seq_ = 0; //!< Sequence number + uint64_t ts_ = 0; //!< Camera image time (middle of exposure) [CLOCK_REALTIME ns] (0 = invalid) + uint32_t dt_ = 0; //!< Exposure time (duration) [ns] (0 = not available) + uint32_t width_ = 0; //!< Width of the frame [px] + uint32_t height_ = 0; //!< Height of the fram [px] + uint32_t size_ = 0; //!< Size of the data (see LogMessage) + uint64_t rec_time_ = 0; //!< Recording time [CLOCK_REALTIME ns] (for debugging) + uint8_t reserved1_[16] = { 0 }; //!< Reserved for future use, set to 0 +}; // clang-format on + +static_assert(sizeof(CamDataMeta) == 64, ""); + +/** + * @brief Helper for extracting camera data (image, video, ...) + */ +struct CamData +{ + /** + * @brief Constructor + * + * @param[in] log_msg .fpl log message + */ + CamData(const FplMessage& log_msg); + bool valid_; //!< Data valid, successfully extracted from message + std::string info_; //!< Stringification of (some of the) data, for debugging + CamDataMeta meta_; //!< Meta data + cam::CamId cam_id_ = cam::CamId::UNSPECIFIED; //!< = meta_.cam_id_ as enum (if value in range) + cam::CamDataType type_ = cam::CamDataType::UNSPECIFIED; //!< = meta_.type_ as enum (if value in range) + cam::CamDataFmt fmt_ = cam::CamDataFmt::UNSPECIFIED; //!< = meta_.fmt_ as enum (if value in range) + cam::CamDataFrm frm_ = cam::CamDataFrm::UNSPECIFIED; //!< = meta_.frm_ as enum (if value in range) + std::vector data_; //!< Data +}; + /* ****************************************************************************************************************** */ } // namespace fpl } // namespace common diff --git a/fpsdk_common/include/fpsdk_common/parser/rtcm3.hpp b/fpsdk_common/include/fpsdk_common/parser/rtcm3.hpp index 3332e97b..12df5450 100644 --- a/fpsdk_common/include/fpsdk_common/parser/rtcm3.hpp +++ b/fpsdk_common/include/fpsdk_common/parser/rtcm3.hpp @@ -267,7 +267,7 @@ enum class Rtcm3MsmType : uint16_t * * @returns a concise and unique string for the MSM type, "?" for bad value */ -const char* Rtcm3MsmTypeStr(const Rtcm3MsmType gnss); +const char* Rtcm3MsmTypeStr(const Rtcm3MsmType msm); /** * @brief RTMC3 message type to MSM GNSS and type @@ -356,6 +356,8 @@ static constexpr uint16_t RTCM3_TYPE1011_MSGID = 1011; static constexpr const char* RTCM3_TYPE1011_STRID = "RTCM3-TYPE1011"; //!< RTCM3-TYPE1011 message name static constexpr uint16_t RTCM3_TYPE1012_MSGID = 1012; //!< RTCM3-TYPE1012 message ID static constexpr const char* RTCM3_TYPE1012_STRID = "RTCM3-TYPE1012"; //!< RTCM3-TYPE1012 message name +static constexpr uint16_t RTCM3_TYPE1029_MSGID = 1029; //!< RTCM3-TYPE1029 message ID +static constexpr const char* RTCM3_TYPE1029_STRID = "RTCM3-TYPE1029"; //!< RTCM3-TYPE1029 message name static constexpr uint16_t RTCM3_TYPE1030_MSGID = 1030; //!< RTCM3-TYPE1030 message ID static constexpr const char* RTCM3_TYPE1030_STRID = "RTCM3-TYPE1030"; //!< RTCM3-TYPE1030 message name static constexpr uint16_t RTCM3_TYPE1031_MSGID = 1031; //!< RTCM3-TYPE1031 message ID diff --git a/fpsdk_common/include/fpsdk_common/to_json/fpl.hpp b/fpsdk_common/include/fpsdk_common/to_json/fpl.hpp index aa874969..c0559390 100644 --- a/fpsdk_common/include/fpsdk_common/to_json/fpl.hpp +++ b/fpsdk_common/include/fpsdk_common/to_json/fpl.hpp @@ -19,6 +19,7 @@ #include /* PACKAGE */ +#include "../cam.hpp" #include "../fpl.hpp" #include "../string.hpp" #include "time.hpp" @@ -82,9 +83,9 @@ inline void to_json(nlohmann::json& j, const StreamMsg& m) if (std::all_of(m.msg_data_.data(), m.msg_data_.data() + m.msg_data_.size(), [](const uint8_t c) { return std::isprint(c) || std::isspace(c); })) { - j["_raw"] = string::BufToStr(m.msg_data_); + j["_data"] = string::BufToStr(m.msg_data_); } else { - j["_raw_b64"] = string::Base64Enc(m.msg_data_); + j["_data_b64"] = string::Base64Enc(m.msg_data_); } } @@ -106,6 +107,25 @@ inline void to_json(nlohmann::json& j, const FileDump& m) } } +// --------------------------------------------------------------------------------------------------------------------- + +inline void to_json(nlohmann::json& j, const CamData& m) +{ + j = nlohmann::json::object({ + { "_type", FplTypeStr(FplType::CAMDATA) }, + { "cam_id", cam::CamIdToStr(m.cam_id_) }, + { "type", cam::CamDataTypeToStr(m.type_) }, + { "fmt", cam::CamDataFmtToStr(m.fmt_) }, + { "frm", cam::CamDataFrmToStr(m.frm_) }, + { "seq", m.meta_.seq_ }, + { "ts", m.meta_.ts_ }, + { "dt", m.meta_.dt_ }, + { "width", m.meta_.width_ }, + { "height", m.meta_.height_ }, + { "data_b64", string::Base64Enc(m.data_) }, + }); +} + } // namespace fpsdk::common::fpl /* ****************************************************************************************************************** */ #endif // !_DOXYGEN_ diff --git a/fpsdk_common/include/fpsdk_common/video.hpp b/fpsdk_common/include/fpsdk_common/video.hpp new file mode 100644 index 00000000..a6012689 --- /dev/null +++ b/fpsdk_common/include/fpsdk_common/video.hpp @@ -0,0 +1,284 @@ +/** + * \verbatim + * ___ ___ + * \ \ / / + * \ \/ / Copyright (c) Fixposition AG (www.fixposition.com) and contributors + * / /\ \ License: see the LICENSE file + * /__/ \__\ + * \endverbatim + * + * @file + * @brief Fixposition SDK: Video frame decoding + * + * @page FPSDK_COMMON_VIDEO Video frame decoding + * + * **API**: fpsdk_common/video.hpp and fpsdk::common::video + * + */ +#ifndef __FPSDK_COMMON_VIDEO_HPP__ +#define __FPSDK_COMMON_VIDEO_HPP__ + +/* LIBC/STL */ +#include +#include +#include +#include + +/* EXTERNAL */ + +/* PACKAGE */ + +namespace fpsdk { +namespace common { +/** + * @brief Video frame decoding + */ +namespace video { +/* ****************************************************************************************************************** */ +#if FPSDK_USE_FFMPEG + +/** + * @brief Video codec + */ +enum class VideoCodec : int +{ + UNSPECIFIED = 0, //!< Unspecified + H264, //!< H.264 Advanced Video Coding (AVC) + H265, //!< H.265 High Efficiency Video Coding (HEVC) +}; + +/** + * @brief Stringify video codec enum + * + * @param[in] codec Codec ID + * + * @returns the stringification of the codec, or "?" for bad enum values + */ +const char* VideoCodecToStr(const VideoCodec codec); + +/** + * @brief Convert codec ID string to enum + * + * @param[in] str Codec ID string + * @param[in] def Default codec ID, for bad str + * + * @returns the codec ID, or def if str was invalid + */ +VideoCodec VideoCodecFromStrOr(const char* str, const VideoCodec def); + +/** + * @brief Pixel format + */ +enum class PixelFmt : int +{ + UNSPECIFIED = 0, //!< Invalid + Y8, //!< Planar Y (greyscale), 8bpp (= FFmpeg AV_PIX_FMT_GRAY8) + RGB24, //!< Packed RGB 8:8:8, 24bpp, RGBRGB... (= FFmpeg AV_PIX_FMT_RGB24) + GBRP, //!< Planar GBR, 24bpp [R][R][R]...[G][G][G]...[B][B][B]... (~ FFmpeg AV_PIX_FMT_GBRP/AV_PIX_FMT_GBR24P) +}; + +/** + * @brief Stringify pixel format enum + * + * @param[in] fmt Pixel format + * + * @returns the stringification of the fmt, or "?" for bad enum values + */ +const char* PixelFmtToStr(const PixelFmt fmt); + +/** + * @brief Convert pixel format string to enum + * + * @param[in] str Pixel format string + * @param[in] def Default pixel format, for bad str + * + * @returns the pixel format, or def if str was invalid + */ +PixelFmt PixelFmtFromStrOr(const char* str, const PixelFmt def); + +/** + * @brief Scaling method (only for sw decoding) + */ +enum class ScalingQual : int +{ + UNSPECIFIED = 0, //!< Unspecified + BILINEAR, //!< Low quality, very fast + BICUBIC, //!< Medium/high quality, fast (B=0.0, C=0.5, "Catmull-Rom") + LANCZOS, //!< High quality, slower (radius=3.0) + SINC, //!< Very high quality, very slow (radius=3.0) +}; + +/** + * @brief Stringify scaling method enum + * + * @param[in] qual Scaling method + * + * @returns the stringification of the qual, or "?" for bad enum values + */ +const char* ScalingQualToStr(const ScalingQual qual); + +/** + * @brief Convert scaling method string to enum + * + * @param[in] str Scaling method string + * @param[in] def Default pixel format, for bad str + * + * @returns the scaling method, or def if str was invalid + */ +ScalingQual ScalingQualFromStrOr(const char* str, const ScalingQual def); + +/** + * @brief Hw acceleration + */ +enum class HwAccel : uint8_t +{ + UNSPECIFIED = 0, //!< Unspecified + AUTO = 1, //!< Use hw accel if possible, fallback to SW + SW = 2, //!< Do not use any hw acceleration, use pure software implementation + HW = 3, //!< Use hw acceleration (currently, Linux Video Acceleration API, VA-API) +}; + +/** + * @brief Stringify hw accel method enum + * + * @param[in] accel HW acceleration + * + * @returns the stringification of the accel, or "?" for bad enum values + */ +const char* HwAccelToStr(const HwAccel accel); + +/** + * @brief Convert scaling method string to enum + * + * @param[in] str HW acceleration string + * @param[in] def Default HW acceleration, for bad str + * + * @returns the HW acceleration, or def if str was invalid + */ +HwAccel HwAccelFromStrOr(const char* str, const HwAccel def); + +/** + * @brief VideoFrameDecoder params and their defaults + */ +struct VideoDecoderParams +{ // clang-format off + std::string name_; //!< Name (for debug logging) + VideoCodec codec_ = VideoCodec::UNSPECIFIED; //!< Codec + PixelFmt fmt_ = PixelFmt::UNSPECIFIED; //!< Pixel format (of decoded image) + double scale_ = 1.0; //!< Scale factor (of decoded image), range 0.1-1.0 + HwAccel accel_ = HwAccel::AUTO; //!< Hw acceleration + ScalingQual qual_ = ScalingQual::BICUBIC; //!< Scaling method +}; // clang-format on + +// --------------------------------------------------------------------------------------------------------------------- + +/** + * @brief Decoded, converted and scaled image + */ +struct ImageData +{ + PixelFmt fmt_ = PixelFmt::UNSPECIFIED; //!< Pixel format (of data_) + int width_ = 0; //!< Width [px] + int height_ = 0; //!< Height [px] + std::vector data_; //!< Image data, memory layout depends on fmt_ + +# ifndef NDEBUG + double t_dec_ = 0.0; + double t_conv_ = 0.0; +# endif +}; + +/** + * @brief Helper for decoding video frames + * + * Note that separate instances of this should be used to process different video streams. + */ +class VideoFrameDecoder +{ + public: + /** + * @brief Constructor + * + * Don't use, use the CreateVideoFrameDecoder() factory function. + * + * @param[in] params The parameters + */ + VideoFrameDecoder(const VideoDecoderParams& params); + + /** + * @brief Destructor + */ + virtual ~VideoFrameDecoder(); + + /** + * @brief Initialise video decoder + * + * @returns true on success, false otherwise (bad params, failed to initialse stuff, ...) + */ + bool Init(); + + /** + * @brief Decode one frame + * + * - This works for data that is available frame by frame. That is, it does not work for regular video streams where + * the data is chunked arbitrarily. Ideally, start by supplying an I frame incl. all the necessary NAL units + * (NALUs) to decode a first frame. After that, the following P frames should decode fine. + * - For example, for HEVC (H.265) a full set of data for a I frame should contain: + * - NALU type 32 (VPS, Video Parameter Set) + * - NALU type 33 (SPS, Sequence Parameter Set) + * - NALU type 34 (PPS, Picture Parameter Set) + * - NALU type 19 (IDR, Instantaneous Decoding Refresh, and RADL, Random Access Decodable Leading) (possibly other + * types 16-23 (IRAP, Intra Random Access Point) NALUs might work) + * - The video encoder should be configured accordingly (to repeat the VPS/SPS/PPS for each I-frame, and possibly to + * produce all/only I frames. + * + * @param[in] data Data for one (not more, not less) video frame + * @param[in] size Size of data + * + * @returns the decoded, converted and scaled image, nullptr otherwise (not enough data, bad data, ...) + */ + virtual std::optional DecodeFrame(const uint8_t* data, const std::size_t size) = 0; + + /** + * @brief Decode video data + * + * @param[in] data Data for one (recommended) or more video frame(s) + * + * @returns the decoded, converted and scaled image, nullptr otherwise (not enough data, bad data, ...) + */ + std::optional DecodeFrame(const std::vector& data); + + /** + * @brief Check if decoder is in error state + * + * This is useful to check when DecodeFrame() didn't return a frame. If the decoder is in error state, it cannot + * be used anymore and must be discarded. + * + * @returns true if the decoder is okay, false if it is in error state + */ + virtual bool IsOkay() const = 0; + + protected: + VideoDecoderParams params_; //!< Params +}; + +/** + * @brief Pointer to a VideoFrameDecoder instance, see CreateVideoFrameDecoder() + */ +using VideoFrameDecoderPtr = std::unique_ptr; + +/** + * @brief Create a video frame decoder + * + * @param[in] params The parameters + * + * @returns the video frame decoder instances, or nullptr if it failed (bad params) + */ +VideoFrameDecoderPtr CreateVideoFrameDecoder(const VideoDecoderParams& params); + +#endif // FPSDK_USE_FFMPEG +/* ****************************************************************************************************************** */ +} // namespace video +} // namespace common +} // namespace fpsdk +#endif // __FPSDK_COMMON_VIDEO_HPP__ diff --git a/fpsdk_common/src/app.cpp b/fpsdk_common/src/app.cpp index c7868f06..89dd324f 100644 --- a/fpsdk_common/src/app.cpp +++ b/fpsdk_common/src/app.cpp @@ -368,18 +368,30 @@ static constexpr const char* BUILDSTR = #endif static constexpr const char* ROSSTR = #if defined(FPSDK_USE_ROS1) - "ROS1"; + ", ROS1"; #elif defined(FPSDK_USE_ROS2) - "ROS2"; + ", ROS2"; #else - "no ROS"; + ", noros"; +#endif +static constexpr const char* PROJSTR = +#if FPSDK_USE_PROJ + ", PROJ"; +#else + ", noproj"; +#endif +static constexpr const char* FFMPEGSTR = +#if FPSDK_USE_FFMPEG + ", FFmpeg"; +#else + ", noffmpeg"; #endif void ProgramOptions::PrintVersion() const { - std::fprintf(stdout, "%s%s%s (fpsdk: %s, %s, %s)\n%s\n%s\n", app_name_.c_str(), version_str_.empty() ? "" : " ", - version_str_.empty() ? "" : version_str_.c_str(), BUILDSTR, ROSSTR, utils::GetVersionString(), - copy_str_.empty() ? utils::GetCopyrightString() : copy_str_.c_str(), + std::fprintf(stdout, "%s%s%s (fpsdk: %s%s%s%s, %s)\n%s\n%s\n", app_name_.c_str(), version_str_.empty() ? "" : " ", + version_str_.empty() ? "" : version_str_.c_str(), BUILDSTR, ROSSTR, PROJSTR, FFMPEGSTR, + utils::GetVersionString(), copy_str_.empty() ? utils::GetCopyrightString() : copy_str_.c_str(), lic_str_.empty() ? utils::GetLicenseString() : lic_str_.c_str()); } diff --git a/fpsdk_common/src/cam.cpp b/fpsdk_common/src/cam.cpp new file mode 100644 index 00000000..82b01de6 --- /dev/null +++ b/fpsdk_common/src/cam.cpp @@ -0,0 +1,162 @@ +/** + * \verbatim + * ___ ___ + * \ \ / / + * \ \/ / Copyright (c) Fixposition AG (www.fixposition.com) and contributors + * / /\ \ License: see the LICENSE file + * /__/ \__\ + * \endverbatim + * + * @file + * @brief Fixposition SDK: Camera types and utilities + */ + +/* LIBC/STL */ +#include + +/* EXTERNAL */ + +/* PACKAGE */ +#include "fpsdk_common/cam.hpp" +#include "fpsdk_common/logging.hpp" +#include "fpsdk_common/types.hpp" + +namespace fpsdk { +namespace common { +namespace cam { +/* ****************************************************************************************************************** */ + +const char* CamIdToStr(const CamId camid) +{ + switch (camid) { // clang-format off + case CamId::UNSPECIFIED: return "UNSPECIFIED"; + case CamId::CAM1: return "CAM1"; + case CamId::CAM2: return "CAM2"; + case CamId::CAM3: return "CAM3"; + case CamId::CAM4: return "CAM4"; + } // clang-format on + return "?"; +} + +CamId CamIdFromStrOr(const char* str, const CamId def) +{ // clang-format off + if (strcmp(str, "UNSPECIFIED") == 0) { return CamId::UNSPECIFIED; } + if (strcmp(str, "CAM1") == 0) { return CamId::CAM1; } + if (strcmp(str, "CAM2") == 0) { return CamId::CAM2; } + if (strcmp(str, "CAM3") == 0) { return CamId::CAM3; } + if (strcmp(str, "CAM4") == 0) { return CamId::CAM4; } + return def; // clang-format on +} + +CamId CamIdFromValOr(const uint8_t val, const CamId def) +{ // clang-format off + switch (val) { + case types::EnumToVal(CamId::UNSPECIFIED): return CamId::UNSPECIFIED; + case types::EnumToVal(CamId::CAM1): return CamId::CAM1; + case types::EnumToVal(CamId::CAM2): return CamId::CAM2; + case types::EnumToVal(CamId::CAM3): return CamId::CAM3; + case types::EnumToVal(CamId::CAM4): return CamId::CAM4; + } + return def; // clang-format on +} + +// --------------------------------------------------------------------------------------------------------------------- + +const char* CamDataTypeToStr(const CamDataType type) +{ + switch (type) { // clang-format off + case CamDataType::UNSPECIFIED: return "UNSPECIFIED"; + case CamDataType::HIRES_IMG: return "HIRES_IMG"; + case CamDataType::LORES_IMG: return "LORES_IMG"; + case CamDataType::HIRES_VID: return "HIRES_VID"; + case CamDataType::LORES_VID: return "LORES_VID"; + } // clang-format on + return "?"; +} + +CamDataType CamDataTypeFromStrOr(const char* str, const CamDataType def) +{ // clang-format off + if (strcmp(str, "UNSPECIFIED") == 0) { return CamDataType::UNSPECIFIED; } + if (strcmp(str, "HIRES_IMG") == 0) { return CamDataType::HIRES_IMG; } + if (strcmp(str, "LORES_IMG") == 0) { return CamDataType::LORES_IMG; } + if (strcmp(str, "HIRES_VID") == 0) { return CamDataType::HIRES_VID; } + if (strcmp(str, "LORES_VID") == 0) { return CamDataType::LORES_VID; } + return def; // clang-format on +} + +CamDataType CamDataTypeFromValOr(const uint8_t val, const CamDataType def) +{ // clang-format off + switch (val) { + case types::EnumToVal(CamDataType::UNSPECIFIED): return CamDataType::UNSPECIFIED; + case types::EnumToVal(CamDataType::HIRES_IMG): return CamDataType::HIRES_IMG; + case types::EnumToVal(CamDataType::LORES_IMG): return CamDataType::LORES_IMG; + case types::EnumToVal(CamDataType::HIRES_VID): return CamDataType::HIRES_VID; + case types::EnumToVal(CamDataType::LORES_VID): return CamDataType::LORES_VID; + } + return def; // clang-format on +} + +// --------------------------------------------------------------------------------------------------------------------- + +const char* CamDataFmtToStr(const CamDataFmt fmt) +{ + switch (fmt) { // clang-format off + case CamDataFmt::UNSPECIFIED: return "UNSPECIFIED"; + case CamDataFmt::H264_NAL: return "H264_NAL"; + case CamDataFmt::H265_NAL: return "H265_NAL"; + case CamDataFmt::MJPEG: return "MJPEG"; + case CamDataFmt::JPEG: return "JPEG"; + case CamDataFmt::Y8: return "Y8"; + case CamDataFmt::NV12: return "NV12"; + case CamDataFmt::RGB24: return "RGB24"; + } // clang-format on + return "?"; +} + +CamDataFmt CamDataFmtFromValOr(const uint8_t val, const CamDataFmt def) +{ // clang-format off + switch (val) { + case types::EnumToVal(CamDataFmt::UNSPECIFIED): return CamDataFmt::UNSPECIFIED; + case types::EnumToVal(CamDataFmt::H264_NAL): return CamDataFmt::H264_NAL; + case types::EnumToVal(CamDataFmt::H265_NAL): return CamDataFmt::H265_NAL; + case types::EnumToVal(CamDataFmt::MJPEG): return CamDataFmt::MJPEG; + case types::EnumToVal(CamDataFmt::JPEG): return CamDataFmt::JPEG; + case types::EnumToVal(CamDataFmt::Y8): return CamDataFmt::Y8; + case types::EnumToVal(CamDataFmt::NV12): return CamDataFmt::NV12; + case types::EnumToVal(CamDataFmt::RGB24): return CamDataFmt::RGB24; + } + return def; // clang-format on +} + +// --------------------------------------------------------------------------------------------------------------------- + +const char* CamDataFrmToStr(const CamDataFrm frm) +{ + switch (frm) { // clang-format off + case CamDataFrm::UNSPECIFIED: return "UNSPECIFIED"; + case CamDataFrm::I_FRAME: return "I_FRAME"; + case CamDataFrm::P_FRAME: return "P_FRAME"; + case CamDataFrm::FULL: return "FULL"; + case CamDataFrm::OTHER: return "OTHER"; + } // clang-format on + return "?"; +} + +CamDataFrm CamDataFrmFromValOr(const uint8_t val, const CamDataFrm def) +{ // clang-format off + switch (val) { + case types::EnumToVal(CamDataFrm::UNSPECIFIED): return CamDataFrm::UNSPECIFIED; + case types::EnumToVal(CamDataFrm::I_FRAME): return CamDataFrm::I_FRAME; + case types::EnumToVal(CamDataFrm::P_FRAME): return CamDataFrm::P_FRAME; + case types::EnumToVal(CamDataFrm::FULL): return CamDataFrm::FULL; + case types::EnumToVal(CamDataFrm::OTHER): return CamDataFrm::OTHER; + } + return def; // clang-format on +} + +// --------------------------------------------------------------------------------------------------------------------- + +/* ****************************************************************************************************************** */ +} // namespace cam +} // namespace common +} // namespace fpsdk diff --git a/fpsdk_common/src/codegen.pl b/fpsdk_common/src/codegen.pl index 1181660c..3f80e46b 100755 --- a/fpsdk_common/src/codegen.pl +++ b/fpsdk_common/src/codegen.pl @@ -362,6 +362,8 @@ { type => 1011, desc => "L1/L2 GLONASS RTK observables" }, { type => 1012, desc => "Extended L1/L2 GLONASS RTK observables" }, # + { type => 1029, desc => "Unicode text string" }, + # { type => 1030, desc => "GPS network RTK residual message" }, { type => 1031, desc => "GLONASS network RTK residual message" }, { type => 1032, desc => "Physical reference station position message" }, diff --git a/fpsdk_common/src/fpl.cpp b/fpsdk_common/src/fpl.cpp index 4a233ce0..5b94e7cf 100644 --- a/fpsdk_common/src/fpl.cpp +++ b/fpsdk_common/src/fpl.cpp @@ -45,6 +45,7 @@ const char* FplTypeStr(const FplType type) case FplType::STREAMMSG: return "STREAMMSG"; case FplType::LOGSTATUS: return "LOGSTATUS"; case FplType::FILEDUMP: return "FILEDUMP"; + case FplType::CAMDATA: return "CAMDATA"; case FplType::BLOB: return "BLOB"; case FplType::INT_D: return "INT_D"; case FplType::INT_F: return "INT_F"; @@ -121,6 +122,7 @@ int FplMessage::Parse(const uint8_t* data, const uint32_t size) case FplType::STREAMMSG: case FplType::LOGSTATUS: case FplType::FILEDUMP: + case FplType::CAMDATA: payload_type_ = (FplType)payload_type; break; case FplType::BLOB: @@ -689,7 +691,46 @@ FileDump::FileDump(const FplMessage& log_msg) info_ = string::Sprintf( "filename=%s size=%" PRIuMAX " mtime=%s", filename_.c_str(), data_.size(), mtime_.StrUtcTime(0).c_str()); } else { - WARNING("LogFileDump: invalid message"); + WARNING("FileDump: invalid message"); + info_ = ""; + } + valid_ = ok; +} + +// --------------------------------------------------------------------------------------------------------------------- + +CamData::CamData(const FplMessage& log_msg) +{ + bool ok = true; + if (log_msg.PayloadType() == FplType::CAMDATA) { + const uint8_t* payload = log_msg.PayloadData(); + const uint32_t payload_size = log_msg.PayloadSize(); + + if (payload_size >= sizeof(meta_)) { + std::memcpy(&meta_, payload, sizeof(meta_)); + } else { + ok = false; + } + + if (ok && (payload_size >= (sizeof(meta_) + meta_.size_))) { + data_ = { &payload[sizeof(meta_)], &payload[sizeof(meta_)] + meta_.size_ }; + } else { + ok = false; + } + } else { + ok = false; + } + if (ok) { + cam_id_ = CamIdFromValOr(meta_.cam_id_, cam::CamId::UNSPECIFIED); + type_ = CamDataTypeFromValOr(meta_.type_, cam::CamDataType::UNSPECIFIED); + fmt_ = CamDataFmtFromValOr(meta_.fmt_, cam::CamDataFmt::UNSPECIFIED); + frm_ = CamDataFrmFromValOr(meta_.frm_, cam::CamDataFrm::UNSPECIFIED); + info_ = string::Sprintf("rec_time=%.3f cam_id=%s type=%s fmt=%s frm=%s %" PRIu32 "x%" PRIu32 " seq=%" PRIuMAX + " ts=%.3f size=%" PRIuMAX, + (double)meta_.rec_time_ * 1e-9, CamIdToStr(cam_id_), CamDataTypeToStr(type_), CamDataFmtToStr(fmt_), + CamDataFrmToStr(frm_), meta_.width_, meta_.height_, meta_.seq_, (double)meta_.ts_ * 1e-9, data_.size()); + } else { + WARNING("CamData: invalid message"); info_ = ""; } valid_ = ok; diff --git a/fpsdk_common/src/parser/rtcm3.cpp b/fpsdk_common/src/parser/rtcm3.cpp index f21a1efb..a3f321a0 100644 --- a/fpsdk_common/src/parser/rtcm3.cpp +++ b/fpsdk_common/src/parser/rtcm3.cpp @@ -44,7 +44,7 @@ struct MsgInfo // clang-format off // @fp_codegen_begin{FPSDK_COMMON_PARSER_RTCM3_MSGINFO} -static constexpr std::array MSG_INFO = +static constexpr std::array MSG_INFO = {{ { RTCM3_TYPE1001_MSGID, 0, RTCM3_TYPE1001_STRID, "L1-only GPS RTK observables" }, { RTCM3_TYPE1002_MSGID, 0, RTCM3_TYPE1002_STRID, "Extended L1-only GPS RTK observables" }, @@ -58,6 +58,7 @@ static constexpr std::array MSG_INFO = { RTCM3_TYPE1010_MSGID, 0, RTCM3_TYPE1010_STRID, "Extended L1-only GLONASS RTK observables" }, { RTCM3_TYPE1011_MSGID, 0, RTCM3_TYPE1011_STRID, "L1/L2 GLONASS RTK observables" }, { RTCM3_TYPE1012_MSGID, 0, RTCM3_TYPE1012_STRID, "Extended L1/L2 GLONASS RTK observables" }, + { RTCM3_TYPE1029_MSGID, 0, RTCM3_TYPE1029_STRID, "Unicode text string" }, { RTCM3_TYPE1030_MSGID, 0, RTCM3_TYPE1030_STRID, "GPS network RTK residual message" }, { RTCM3_TYPE1031_MSGID, 0, RTCM3_TYPE1031_STRID, "GLONASS network RTK residual message" }, { RTCM3_TYPE1032_MSGID, 0, RTCM3_TYPE1032_STRID, "Physical reference station position message" }, @@ -319,9 +320,9 @@ const char* Rtcm3MsmGnssStr(const Rtcm3MsmGnss gnss) return "?"; } -const char* Rtcm3MsmTypeStr(const Rtcm3MsmType gnss) +const char* Rtcm3MsmTypeStr(const Rtcm3MsmType msm) { - switch (gnss) { // clang-format off + switch (msm) { // clang-format off case Rtcm3MsmType::MSM1: return "MSM1"; case Rtcm3MsmType::MSM2: return "MSM2"; case Rtcm3MsmType::MSM3: return "MSM3"; @@ -542,7 +543,23 @@ bool Rtcm3GetMessageInfo(char* info, const std::size_t size, const uint8_t* msg, msm.num_cell_, msm.multi_msg_bit_ ? "more" : "last", exp_size); } - const char* type_desc = Rtcm3GetTypeDesc(Rtcm3Type(msg)); + const uint16_t type = Rtcm3Type(msg); + const uint8_t* data = &msg[RTCM3_HEAD_SIZE]; + + if (type == RTCM3_TYPE1029_MSGID) { // clang-format off + const uint16_t sta = Rtcm3GetUnsigned(data, 12, 12); // DF003 + const uint16_t mjd = Rtcm3GetUnsigned(data, 24, 16); // DF051 + const uint32_t sod = Rtcm3GetUnsigned(data, 40, 17); // DF052 + const uint8_t nu = Rtcm3GetUnsigned(data, 64, 8); // DF139 + // clang-format off + char str[1000]; + const auto sl = std::min({nu, msg_size - RTCM3_FRAME_SIZE - (72 / 8), sizeof(str) - 1}); + std::memcpy(str, &msg[RTCM3_HEAD_SIZE + (72 / 8)], sl); + str[sl] = '\0'; + len += snprintf(info, size, "(#%" PRIu16 ") %" PRIu16 ":%06" PRIu32 " %s", sta, mjd, sod, str); + } + + const char* type_desc = Rtcm3GetTypeDesc(type); if ((len == 0) && (len < size) && (type_desc != NULL)) { len += snprintf(&info[len], size - len, "%s%s", len > 0 ? " - " : "", type_desc); } diff --git a/fpsdk_common/src/video.cpp b/fpsdk_common/src/video.cpp new file mode 100644 index 00000000..7e9a4cde --- /dev/null +++ b/fpsdk_common/src/video.cpp @@ -0,0 +1,906 @@ +/** + * \verbatim + * ___ ___ + * \ \ / / + * \ \/ / Copyright (c) Fixposition AG (www.fixposition.com) and contributors + * / /\ \ License: see the LICENSE file + * /__/ \__\ + * \endverbatim + * + * @file + * @brief Fixposition SDK: Video frame decoding + */ + +/* LIBC/STL */ +#include +#include +#include +#include + +/* EXTERNAL */ +extern "C" { +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +} + +/* PACKAGE */ +#include "fpsdk_common/logging.hpp" +#include "fpsdk_common/string.hpp" +#include "fpsdk_common/time.hpp" +#include "fpsdk_common/types.hpp" +#include "fpsdk_common/video.hpp" + +namespace fpsdk { +namespace common { +namespace video { +/* ****************************************************************************************************************** */ +#if FPSDK_USE_FFMPEG + +using namespace fpsdk::common::logging; +using namespace fpsdk::common::types; +using namespace fpsdk::common::time; +using namespace fpsdk::common::string; + +const char* VideoCodecToStr(const VideoCodec codec_id) +{ + switch (codec_id) { // clang-format off + case VideoCodec::UNSPECIFIED: return "UNSPECIFIED"; + case VideoCodec::H264: return "H264"; + case VideoCodec::H265: return "H265"; + } // clang-format on + return "?"; +} + +VideoCodec VideoCodecFromStrOr(const char* str, const VideoCodec def) +{ // clang-format off + if (strcmp(str, "UNSPECIFIED") == 0) { return VideoCodec::UNSPECIFIED; } + if (strcmp(str, "H264") == 0) { return VideoCodec::H264; } + if (strcmp(str, "H265") == 0) { return VideoCodec::H265; } + return def; // clang-format on +} + +const char* PixelFmtToStr(const PixelFmt fmt) +{ + switch (fmt) { // clang-format off + case PixelFmt::UNSPECIFIED: return "UNSPECIFIED"; + case PixelFmt::Y8: return "Y8"; + case PixelFmt::RGB24: return "RGB24"; + case PixelFmt::GBRP: return "GBRP"; + } // clang-format on + return "?"; +} + +static const char* PixelFmtToFlag(const PixelFmt fmt) +{ + switch (fmt) { // clang-format off + case PixelFmt::UNSPECIFIED: break; + case PixelFmt::Y8: return "gray8"; + case PixelFmt::RGB24: return "rgb24"; + case PixelFmt::GBRP: return "gbrp"; + } // clang-format on + return "?"; +} + +PixelFmt PixelFmtFromStrOr(const char* str, const PixelFmt def) +{ // clang-format off + if (strcmp(str, "UNSPECIFIED") == 0) { return PixelFmt::UNSPECIFIED; } + if (strcmp(str, "Y8") == 0) { return PixelFmt::Y8; } + if (strcmp(str, "RGB24") == 0) { return PixelFmt::RGB24; } + if (strcmp(str, "GBRP") == 0) { return PixelFmt::GBRP; } + return def; // clang-format on +} + +const char* ScalingQualToStr(const ScalingQual qual) +{ + switch (qual) { // clang-format off + case ScalingQual::UNSPECIFIED: return "UNSPECIFIED"; + case ScalingQual::BILINEAR: return "BILINEAR"; + case ScalingQual::BICUBIC: return "BICUBIC"; + case ScalingQual::LANCZOS: return "LANCZOS"; + case ScalingQual::SINC: return "SINC"; + } // clang-format on + return "?"; +} + +static const char* ScalingQualToFlag(const ScalingQual qual) +{ + switch (qual) { // clang-format off + case ScalingQual::UNSPECIFIED: break; + case ScalingQual::BILINEAR: return "bilinear"; + case ScalingQual::BICUBIC: return "bicubic"; + case ScalingQual::LANCZOS: return "lanczos"; + case ScalingQual::SINC: return "sinc"; + } // clang-format on + return "?"; +} + +ScalingQual ScalingQualFromStrOr(const char* str, const ScalingQual def) +{ // clang-format off + if (strcmp(str, "UNSPECIFIED") == 0) { return ScalingQual::UNSPECIFIED; } + if (strcmp(str, "BILINEAR") == 0) { return ScalingQual::BILINEAR; } + if (strcmp(str, "BICUBIC") == 0) { return ScalingQual::BICUBIC; } + if (strcmp(str, "LANCZOS") == 0) { return ScalingQual::LANCZOS; } + if (strcmp(str, "SINC") == 0) { return ScalingQual::SINC; } + return def; // clang-format on +} + +const char* HwAccelToStr(const HwAccel accel) +{ + switch (accel) { // clang-format off + case HwAccel::UNSPECIFIED: return "UNSPECIFIED"; + case HwAccel::AUTO: return "AUTO"; + case HwAccel::SW: return "SW"; + case HwAccel::HW: return "HW"; + } // clang-format on + return "?"; +} + +HwAccel HwAccelFromStrOr(const char* str, const HwAccel def) +{ // clang-format off + if (strcmp(str, "UNSPECIFIED") == 0) { return HwAccel::UNSPECIFIED; } + if (strcmp(str, "AUTO") == 0) { return HwAccel::AUTO; } + if (strcmp(str, "SW") == 0) { return HwAccel::SW; } + if (strcmp(str, "HW") == 0) { return HwAccel::HW; } + return def; // clang-format on +} + +// --------------------------------------------------------------------------------------------------------------------- + +static std::string VideoDecoderParamsToStr(const VideoDecoderParams& params) +{ + return Sprintf("%s %s %.1f %s %s", VideoCodecToStr(params.codec_), PixelFmtToStr(params.fmt_), params.scale_, + ScalingQualToStr(params.qual_), HwAccelToStr(params.accel_)); +} + +// ********************************************************************************************************************* + +VideoFrameDecoder::VideoFrameDecoder(const VideoDecoderParams& params) /* clang-format off */ : + params_ { params } // clang-format on + +{ + DEBUG("VideoFrameDecoder(%s) %s", params_.name_.c_str(), VideoDecoderParamsToStr(params_).c_str()); + + // Setup FFmpeg logging. Note that this is global... :-/. We could set a custom handler (av_log_set_callback()) and + // in there check which context it has been called from and then selectively output messages. However, that would be + // quite involved to make it work across all instances of VideoFrameDecoder (and possibly other things that use + // FFmpeg stuff in the same app). So we globally silence it (it's quite chatty otherwise, also at warning and error + // levels!). +# ifdef NDEBUG + av_log_set_level(AV_LOG_FATAL); +# else + if (LoggingIsLevel(LoggingLevel::TRACE)) { + av_log_set_level(AV_LOG_DEBUG); + } else if (LoggingIsLevel(LoggingLevel::DEBUG)) { + av_log_set_level(AV_LOG_WARNING); + } else { + av_log_set_level(AV_LOG_ERROR); + } +# endif +} + +VideoFrameDecoder::~VideoFrameDecoder() +{ +} + +// --------------------------------------------------------------------------------------------------------------------- + +std::optional VideoFrameDecoder::DecodeFrame(const std::vector& data) +{ + return DecodeFrame(data.data(), data.size()); +} + +// ********************************************************************************************************************* + +// Software decoder +class VideoFrameDecoderImpl : public VideoFrameDecoder, private NoCopyNoMove +{ + public: + VideoFrameDecoderImpl(const VideoDecoderParams& params); + ~VideoFrameDecoderImpl(); + std::optional DecodeFrame(const uint8_t* data, const std::size_t size) override final; + bool IsOkay() const override final; + + enum AVPixelFormat GetHwFormat(const enum AVPixelFormat* fmts) const; + + private: + // clang-format off + enum class State { DEINIT, INIT_AV, INIT_GRAPH, ERROR }; + State state_ = State::DEINIT; + AVHWDeviceType hw_type_ = AV_HWDEVICE_TYPE_NONE; // AV_HWDEVICE_TYPE_VAAPI + enum AVPixelFormat src_fmt_ = AV_PIX_FMT_YUVJ420P; // AV_PIX_FMT_VAAPI + const AVCodec* codec_ = nullptr; + AVCodecContext* ctx_ = nullptr; + AVBufferRef* hw_ctx_ = nullptr; // hwdevice_ctx + AVPacket* pkt_ = nullptr; + AVFrame* frame_ = nullptr; + AVFrame* filt_frame_ = nullptr; + AVFilterGraph* graph_ = nullptr; + AVFilterContext* src_ctx_ = nullptr; + AVFilterContext* dst_ctx_ = nullptr; + const AVFilter* src_filt_ = nullptr; + const AVFilter* dst_filt_ = nullptr; + // clang-format off + + bool InitAv(); + bool InitGraph(); + void Cleanup(); + + // Buffer and helper for stringifying FFmpeg errors + char av_errbuf_[1000] = {0}; + const char* AvErrStr(const int errnum); +}; + +VideoFrameDecoderImpl::VideoFrameDecoderImpl(const VideoDecoderParams& params) /* clang-format off */ : + VideoFrameDecoder(params) // clang-format on +{ +} + +VideoFrameDecoderImpl::~VideoFrameDecoderImpl() +{ + Cleanup(); +} + +// --------------------------------------------------------------------------------------------------------------------- + +const char* VideoFrameDecoderImpl::AvErrStr(const int errnum) +{ + av_strerror(errnum, av_errbuf_, sizeof(av_errbuf_)); + return av_errbuf_; +} + +bool VideoFrameDecoderImpl::IsOkay() const +{ + return state_ != State::ERROR; +} + +// --------------------------------------------------------------------------------------------------------------------- + +static enum AVPixelFormat GetHwFormatCb(AVCodecContext* ctx, const enum AVPixelFormat* fmts) +{ + return static_cast(ctx->opaque)->GetHwFormat(fmts); +} + +enum AVPixelFormat VideoFrameDecoderImpl::GetHwFormat(const enum AVPixelFormat* fmts) const +{ + for (const enum AVPixelFormat* p = fmts; *p != AV_PIX_FMT_NONE; ++p) { + if (*p == src_fmt_) { + return *p; + } + } + return AV_PIX_FMT_NONE; +} + +bool VideoFrameDecoderImpl::InitAv() +{ + DEBUG("VideoFrameDecoderImpl(%s) init", params_.name_.c_str()); + + bool ok = (state_ == State::DEINIT); + while (ok) { + // ----- Get/check codec ----- + + switch (params_.codec_) { // clang-format off + case VideoCodec::H264: codec_ = avcodec_find_decoder(AV_CODEC_ID_H264); break; + case VideoCodec::H265: codec_ = avcodec_find_decoder(AV_CODEC_ID_H265); break; + case VideoCodec::UNSPECIFIED: + break; + } // clang-format on + if (!codec_) { + WARNING("VideoFrameDecoder(%s) avcodec_find_decoder fail", params_.name_.c_str()); + ok = false; + break; + } + + // ----- Try to use hw acceleration ----- + +# if 0 // Debugging, dev help + // Check supported hw devices + + // AVHWDeviceType device_type = AV_HWDEVICE_TYPE_NONE; + // while (true) { + // device_type = av_hwdevice_iterate_types(device_type); + // if (device_type == AV_HWDEVICE_TYPE_NONE) { + // break; + // } + // DEBUG("VideoDecoder(%s) hw device: %s", name_.c_str(), av_hwdevice_get_type_name(device_type)); + // } + // hw device: vdpau + // hw device: cuda + // hw device: vaapi + // hw device: qsv + // hw device: drm + // hw device: opencl + // hw device: vulkan + + for (int ix = 0; ix < 100; ix++) { + const AVCodecHWConfig* config = avcodec_get_hw_config(codec_, ix); + if (!config) { + DEBUG("nope %d", ix); + break; + } + DEBUG("VideoFrameDecoder(%s) hw config %d: type %s, pixfmt %s, methods 0x%02x", params_.name_.c_str(), ix, + av_hwdevice_get_type_name(config->device_type), av_get_pix_fmt_name(config->pix_fmt), config->methods); + } + // hw config 0: type cuda, pixfmt cuda, methods 0x03 + // hw config 1: type vaapi, pixfmt vaapi, methods 0x0b + // hw config 2: type vdpau, pixfmt vdpau, methods 0x0b + // hw config 3: type vulkan, pixfmt vulkan, methods 0x0b +# endif + const bool use_hw_accel = ((params_.accel_ == HwAccel::AUTO) || (params_.accel_ == HwAccel::HW)); + const bool req_hw_accel = (params_.accel_ == HwAccel::HW); + + if (use_hw_accel) { + // We currently only support VA-API. We could probe for other kinds of hw accel here. + AVBufferRef* hw = nullptr; + const int hw_res = av_hwdevice_ctx_create(&hw, AV_HWDEVICE_TYPE_VAAPI, nullptr, nullptr, 0); + if (hw_res < 0) { + if (req_hw_accel) { + WARNING("VideoFrameDecoder(%s) Could not create hw device (%s)", params_.name_.c_str(), + AvErrStr(hw_res)); + ok = false; + break; + } else { + DEBUG("VideoFrameDecoder(%s) Could not create hw device (%s), using sw decoding", + params_.name_.c_str(), AvErrStr(hw_res)); + } + hw = nullptr; + } else { + // hw_ctx_ = reinterpret_cast(hw); + hw_ctx_ = av_buffer_ref(hw); + hw_type_ = AV_HWDEVICE_TYPE_VAAPI; + src_fmt_ = AV_PIX_FMT_VAAPI; + // @todo this crashes.. + // ctx_->opaque = this; + // ctx_->get_format = GetHwFormatCb; + UNUSED(GetHwFormatCb); + } + } + + // ----- Setup codec context ----- + + ctx_ = avcodec_alloc_context3(codec_); + if (!ctx_) { + WARNING("VideoFrameDecoder(%s) avcodec_alloc_context3 fail", params_.name_.c_str()); + ok = false; + break; + } + if (hw_ctx_) { + ctx_->hw_device_ctx = av_buffer_ref(hw_ctx_); + } + + if (avcodec_open2(ctx_, codec_, nullptr) < 0) { + WARNING("VideoFrameDecoder(%s) avcodec_open2 fail", params_.name_.c_str()); + ok = false; + break; + } + + // ----- Packet and frames for later use ----- + + pkt_ = av_packet_alloc(); + if (!pkt_) { + WARNING("VideoFrameDecoder(%s) av_packet_alloc fail", params_.name_.c_str()); + ok = false; + break; + } + + frame_ = av_frame_alloc(); + if (!frame_) { + WARNING("VideoFrameDecoder(%s) av_frame_alloc fail", params_.name_.c_str()); + ok = false; + break; + } + + filt_frame_ = av_frame_alloc(); + if (!filt_frame_) { + WARNING("VideoFrameDecoder(%s) av_frame_alloc fail", params_.name_.c_str()); + ok = false; + break; + } + + // ----- Filter graph handles ----- + + graph_ = avfilter_graph_alloc(); + if (!graph_) { + WARNING("VideoFrameDecoder(%s) avfilter_graph_alloc fail", params_.name_.c_str()); + ok = false; + break; + } + + src_filt_ = avfilter_get_by_name("buffer"); + if (!src_filt_) { + WARNING("VideoFrameDecoder(%s) avfilter_get_by_name(buffer) fail", params_.name_.c_str()); + ok = false; + break; + } + + dst_filt_ = avfilter_get_by_name("buffersink"); + if (!dst_filt_) { + WARNING("VideoFrameDecoder(%s) avfilter_get_by_name(buffersink) fail", params_.name_.c_str()); + ok = false; + break; + } + + break; + } + + if (!ok) { + WARNING("VideoFrameDecoder(%s) init fail", params_.name_.c_str()); + return false; + } + + state_ = State::INIT_AV; + return ok; +} + +// --------------------------------------------------------------------------------------------------------------------- + +bool VideoFrameDecoderImpl::InitGraph() +{ + bool ok = true; + + int src_width = 0; + int src_height = 0; + AVPixelFormat src_format = AV_PIX_FMT_NONE; // TODO check <-> src_fmt_ + AVRational src_time_base = { 1, 25 }; + AVRational src_sar = { 1, 1 }; + + src_width = frame_->width; + src_height = frame_->height; + src_format = static_cast(frame_->format); // yuvj420p, check that + if ((ctx_->time_base.num > 0) && (ctx_->time_base.den > 0)) { + src_time_base = ctx_->time_base; + } else { + src_time_base.num = 1; + src_time_base.den = 25; + } + if ((frame_->sample_aspect_ratio.num > 0) && (frame_->sample_aspect_ratio.den > 0)) { + src_sar = frame_->sample_aspect_ratio; + } else { + src_sar.num = 1; + src_sar.den = 1; + } + + const double scale = std::clamp(params_.scale_, 0.1, 1.0); + const int dst_width = std::round(src_width * scale); + const int dst_height = std::round(src_height * scale); + + DEBUG("VideoFrameDecoderImpl(%s) graph (frame %dx%d %s, sar %d/%d, tb %d/%d) -> (%dx%d %s)", + params_.name_.c_str(), // + src_width, src_height, av_get_pix_fmt_name(src_format), // + src_sar.num, src_sar.den, src_time_base.num, src_time_base.den, // + dst_width, dst_height, PixelFmtToStr(params_.fmt_)); + + while (ok) { + // Source buffer +# if 0 + char src_args[256]; + std::snprintf(src_args, sizeof(src_args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", + src_width, src_height, src_format, src_time_base.num, src_time_base.den, src_sar.num, src_sar.den); + TRACE("VideoFrameDecoderImpl(%s) avfilter_graph_create_filter(src) %s", params_.name_.c_str(), src_args); + const int res_src = avfilter_graph_create_filter(&src_ctx_, src_filt_, "src", src_args, nullptr, graph_); + if (res_src < 0) { + WARNING("VideoFrameDecoder(%s) avfilter_graph_create_filter(src) fail: %s", params_.name_.c_str(), AvErrStr(res_src)); + ok = false; + break; + } +# else + src_ctx_ = avfilter_graph_alloc_filter(graph_, src_filt_, "src"); + if (!src_ctx_) { + WARNING("VideoFrameDecoder(%s) avfilter_graph_alloc_filter(src) fail", params_.name_.c_str()); + ok = false; + break; + } + + AVBufferSrcParameters* par = av_buffersrc_parameters_alloc(); + if (!par) { + WARNING("VideoFrameDecoder(%s) av_buffersrc_parameters_alloc(src) fail", params_.name_.c_str()); + ok = false; + break; + } + par->format = src_fmt_; + par->width = src_width; + par->height = src_height; + par->time_base = src_time_base; + par->sample_aspect_ratio = src_sar; + if (hw_ctx_) { + if (frame_->hw_frames_ctx) { // Should be present now that we have decoded the first frame + par->hw_frames_ctx = av_buffer_ref(frame_->hw_frames_ctx); + } else { + WARNING("VideoFrameDecoder(%s) hw_frames_ctx missing", params_.name_.c_str()); + ok = false; + break; + } + } + const int res_par = av_buffersrc_parameters_set(src_ctx_, par); + av_free(par); + if (res_par < 0) { + WARNING("VideoFrameDecoder(%s) av_buffersrc_parameters_set(src) fail: %s", params_.name_.c_str(), + AvErrStr(res_par)); + ok = false; + break; + } + const int res_str = avfilter_init_str(src_ctx_, nullptr); + if (res_str < 0) { + WARNING( + "VideoFrameDecoderImpl(%s) avfilter_init_str(src) fail: %s", params_.name_.c_str(), AvErrStr(res_str)); + return false; + } +# endif + // Dest buffer +# if LIBAVFILTER_VERSION_INT < AV_VERSION_INT(10, 6, 100) + TRACE("VideoFrameDecoderImpl(%s) avfilter_graph_create_filter(sink)", params_.name_.c_str()); + const int res_sink = avfilter_graph_create_filter(&dst_ctx_, dst_filt_, "dst", nullptr, nullptr, graph_); + if (res_sink < 0) { + WARNING("VideoFrameDecoder(%s) avfilter_graph_create_filter(sink) fail: %s", params_.name_.c_str(), + AvErrStr(res_sink)); + return false; + } + // Constrain a buffersink to a single output pixel format + enum AVPixelFormat out_pix_fmts[2] = { AV_PIX_FMT_NONE, AV_PIX_FMT_NONE }; + switch (params_.fmt_) { + case PixelFmt::Y8: + out_pix_fmts[0] = AV_PIX_FMT_GRAY8; + break; + case PixelFmt::RGB24: + out_pix_fmts[0] = AV_PIX_FMT_RGB24; + break; + case PixelFmt::GBRP: + out_pix_fmts[0] = AV_PIX_FMT_GBRP; + break; + case PixelFmt::UNSPECIFIED: + break; + } + const int res_pix_fmts = + av_opt_set_int_list(dst_ctx_, "pix_fmts", out_pix_fmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN); + if (res_pix_fmts < 0) { + WARNING("VideoFrameDecoder(%s) av_opt_set_int_list(sink, pix_fmts) fail: %s", params_.name_.c_str(), + AvErrStr(res_pix_fmts)); + ok = false; + break; + } +# else // >= 10.6.100 + char sink_args[256]; + std::snprintf(sink_args, sizeof(sink_args), "pixel_formats=%s", PixelFmtToFlag(params_.fmt_)); + TRACE("VideoFrameDecoderImpl(%s) avfilter_graph_create_filter(sink) %s", params_.name_.c_str(), sink_args); + const int res_sink = avfilter_graph_create_filter(&dst_ctx_, dst_filt_, "dst", sink_args, nullptr, graph_); + if (res_sink < 0) { + WARNING("VideoFrameDecoder(%s) avfilter_graph_create_filter(sink) fail: %s", params_.name_.c_str(), + AvErrStr(res_sink)); + ok = false; + break; + } +# endif + + char filter_desc[256]; + if (hw_ctx_) { + std::snprintf(filter_desc, sizeof(filter_desc), "scale_vaapi=w=%d:h=%d:format=nv12,hwdownload,format=nv12", + dst_width, dst_height); + } else { + std::snprintf(filter_desc, sizeof(filter_desc), "scale=w=%d:h=%d:flags=%s,format=pix_fmts=%s", dst_width, + dst_height, ScalingQualToFlag(params_.qual_), PixelFmtToFlag(params_.fmt_)); + } + + // Parse filter_desc connecting src_ctx -> sink_ctx into graph, then configure the graph + AVFilterInOut* outputs = avfilter_inout_alloc(); + AVFilterInOut* inputs = avfilter_inout_alloc(); + if (!outputs || !inputs) { + if (outputs) { + avfilter_inout_free(&outputs); + } + if (inputs) { + avfilter_inout_free(&inputs); + } + WARNING("VideoFrameDecoder(%s) avfilter_inout_alloc() fail", params_.name_.c_str()); + ok = false; + break; + } + + outputs->name = av_strdup("in"); + outputs->filter_ctx = src_ctx_; + outputs->pad_idx = 0; + outputs->next = nullptr; + + inputs->name = av_strdup("out"); + inputs->filter_ctx = dst_ctx_; + inputs->pad_idx = 0; + inputs->next = nullptr; + + TRACE("VideoFrameDecoderImpl(%s) avfilter_graph_parse_ptr(sink) %s", params_.name_.c_str(), filter_desc); + + const int res_graph = avfilter_graph_parse_ptr(graph_, filter_desc, &inputs, &outputs, nullptr); + avfilter_inout_free(&inputs); + avfilter_inout_free(&outputs); + if (res_graph < 0) { + WARNING( + "VideoFrameDecoder(%s): avfilter_graph_parse_ptr fail: %s", params_.name_.c_str(), AvErrStr(res_graph)); + ok = false; + break; + } + + const int res_config = avfilter_graph_config(graph_, nullptr); + if (res_config < 0) { + WARNING("%s: avfilter_graph_config failed: %s", params_.name_.c_str(), AvErrStr(res_config)); + ok = false; + break; + } + + break; + } + + if (!ok) { + WARNING("VideoFrameDecoder(%s) filter fail", params_.name_.c_str()); + return false; + } + + state_ = State::INIT_GRAPH; + return ok; +} + +// --------------------------------------------------------------------------------------------------------------------- + +void VideoFrameDecoderImpl::Cleanup() +{ + if (graph_) { + avfilter_graph_free(&graph_); + graph_ = nullptr; + } + if (filt_frame_) { + av_frame_free(&filt_frame_); + filt_frame_ = nullptr; + } + if (frame_) { + av_frame_free(&frame_); + frame_ = nullptr; + } + if (pkt_) { + av_packet_free(&pkt_); + pkt_ = nullptr; + } + if (ctx_) { + avcodec_free_context(&ctx_); + ctx_ = nullptr; + } + if (hw_ctx_) { + av_buffer_unref(&hw_ctx_); + hw_ctx_ = nullptr; + } + + codec_ = nullptr; + src_filt_ = nullptr; + dst_filt_ = nullptr; + src_ctx_ = nullptr; // @todo av_free(src_ctx_) ? + dst_ctx_ = nullptr; // @todo av_free(dst_ctx_) ? + + state_ = State::DEINIT; +} + +// --------------------------------------------------------------------------------------------------------------------- + +std::optional VideoFrameDecoderImpl::DecodeFrame(const uint8_t* data, const std::size_t size) +{ + TRACE("VideoFrameDecoderImpl(%s) decode %" PRIuMAX, params_.name_.c_str(), size); + + if ((data == nullptr) || (size == 0) || (size > 10000000) || (state_ == State::ERROR)) { + return std::nullopt; + } + + // Initialise on first call + if ((state_ == State::DEINIT) && !InitAv()) { + state_ = State::ERROR; + return std::nullopt; + } + +# ifndef NDEBUG + TicToc tt; +# endif + + // One chunk of data is one frame. Copy the data, as FFmpeg wants to own (and modify?) it (pkt->data is + // non-const...). + av_packet_unref(pkt_); // cleanup from previous use + pkt_->data = reinterpret_cast(av_memdup(data, size)); + if (!pkt_->data) { + TRACE("VideoFrameDecoderImpl(%s) av_memdup fail", params_.name_.c_str()); + return std::nullopt; + } + pkt_->size = size; + pkt_->flags = 0; // Let FFmpeg work out what's inside. Could be AV_PKT_FLAG_KEY, AV_PKT_FLAG_TRUSTED, ... + + // Tell FFmpeg it owns this buffer and should av_free() it when done + pkt_->buf = av_buffer_create(pkt_->data, pkt_->size, av_buffer_default_free, nullptr, 0); + if (!pkt_->buf) { + av_freep(&pkt_->data); + TRACE("VideoFrameDecoderImpl(%s) av_buffer_create fail", params_.name_.c_str()); + return std::nullopt; + } + + // Send packet to decoder algorithm + const int res_send = avcodec_send_packet(ctx_, pkt_); + // This should give FFmpeg debug output (when enabled) something like this: + // [hevc @ 0x56050a13e2c0] nal_unit_type: 32(VPS), nuh_layer_id: 0, temporal_id: 0 + // [hevc @ 0x56050a13e2c0] nal_unit_type: 33(SPS), nuh_layer_id: 0, temporal_id: 0 + // [hevc @ 0x56050a13e2c0] nal_unit_type: 34(PPS), nuh_layer_id: 0, temporal_id: 0 + // [hevc @ 0x56050a13e2c0] nal_unit_type: 19(IDR_W_RADL), nuh_layer_id: 0, temporal_id: 0 + // [hevc @ 0x56050a13e2c0] Decoding SPS + // [hevc @ 0x56050a13e2c0] Main profile bitstream + // [hevc @ 0x56050a13e2c0] Decoding VUI + // [hevc @ 0x56050a13e2c0] Decoding PPS + // [hevc @ 0x56050a13e2c0] Output frame with POC 0/0. + // [hevc @ 0x56050a13e2c0] Decoded frame with POC 0/0. + TRACE("VideoFrameDecoderImpl(%s) avcodec_send_packet -> res_send=%d (%s)", params_.name_.c_str(), res_send, + AvErrStr(res_send)); + // - 0 Success + // - AVERROR(EAGAIN) Input not accepted in current state, must do avcodec_receive_frame() first + // - AVERROR_EOF Decoder has been flushed, no new packets can be sent to it + // - AVERROR(EINVAL) Codec not opened, not a decoder, or requires flush + // - AVERROR(ENOMEM) Failed to add packet to internal queue, or similar + if (res_send < 0) { // Or other negative error codes... + WARNING("VideoFrameDecoder(%s) avcodec_send_packet fail: %s", params_.name_.c_str(), AvErrStr(res_send)); + return std::nullopt; + } + +# ifndef NDEBUG + const double t_send = tt.TocMs(true); +# endif + + // Receive decoded frame + const int res_recv = avcodec_receive_frame(ctx_, reinterpret_cast(frame_)); + TRACE("VideoFrameDecoderImpl(%s) avcodec_receive_frame -> res_recv=%d (%s)", params_.name_.c_str(), res_recv, + AvErrStr(res_recv)); + // AVERROR(EAGAIN) Output is not available in this state - user must try to send new/more input + // AVERROR_EOF The codec has been fully flushed, and there will be no more output frames + if ((res_recv == AVERROR(EAGAIN)) || (res_recv == AVERROR_EOF)) { + return std::nullopt; + } + // AVERROR(EINVAL) Codec not opened + // other < 0 value Other error + else if (res_recv < 0) { + WARNING("VideoFrameDecoder(%s) avcodec_receive_frame fail: %s", params_.name_.c_str(), AvErrStr(res_recv)); + return std::nullopt; + } + // else: 0 = Success, a frame was returned + +# ifndef NDEBUG + const double t_recv = tt.TocMs(true); +# endif + + // Initialise filter graph now that we know the frame size and pixel format + if ((state_ == State::INIT_AV) && !InitGraph()) { + state_ = State::ERROR; + return std::nullopt; + } + +# ifndef NDEBUG + tt.Tic(); +# endif + + // Convert (pixel format, scale) + + // Run frame through filter + const int res_add = av_buffersrc_add_frame_flags(src_ctx_, frame_, AV_BUFFERSRC_FLAG_KEEP_REF); + av_frame_unref(frame_); + if (res_add < 0) { + WARNING("VideoFrameDecoder(%s) av_buffersrc_add_frame fail: %s", params_.name_.c_str(), AvErrStr(res_add)); + return std::nullopt; + } + const int res_sink = av_buffersink_get_frame(dst_ctx_, filt_frame_); + if ((res_sink == AVERROR(EAGAIN)) || (res_sink == AVERROR_EOF)) { + return std::nullopt; + } else if (res_sink < 0) { + WARNING( + "VideoFrameDecoderImpl(%s) av_buffersink_get_frame fail: %s", params_.name_.c_str(), AvErrStr(res_sink)); + // state_ = State::ERROR; // @todo ??? + return std::nullopt; + } + + // Copy out image data + ImageData img; + img.fmt_ = params_.fmt_; + img.width_ = filt_frame_->width; + img.height_ = filt_frame_->height; + int img_size = 0; + switch (params_.fmt_) { // clang-format off + case PixelFmt::Y8: img_size = img.width_ * img.height_; break; + case PixelFmt::RGB24: + case PixelFmt::GBRP: img_size = img.width_ * img.height_ * 3; break; + case PixelFmt::UNSPECIFIED: break; + } // clang-format on + img.data_.resize(img_size); + const enum AVPixelFormat frame_fmt = static_cast(filt_frame_->format); + TRACE("VideoFrameDecoderImpl(%s) av_image_copy_to_buffer %dx%d %s %d", params_.name_.c_str(), filt_frame_->width, + filt_frame_->height, av_get_pix_fmt_name(frame_fmt), img_size); + + const int res_copy = av_image_copy_to_buffer(img.data_.data(), img_size, filt_frame_->data, filt_frame_->linesize, + frame_fmt, filt_frame_->width, filt_frame_->height, /*align=*/1); + av_frame_unref(filt_frame_); + if (res_copy < 0) { + WARNING( + "VideoFrameDecoderImpl(%s) av_image_copy_to_buffer fail: %s", params_.name_.c_str(), AvErrStr(res_copy)); + // state_ = State::ERROR; // @todo ??? + return std::nullopt; + } else if (res_copy != img_size) { + WARNING( + "VideoFrameDecoder(%s) av_image_copy_to_buffer size %d != %d", params_.name_.c_str(), res_copy, img_size); + // state_ = State::ERROR; // @todo ??? + return std::nullopt; + } + +# ifndef NDEBUG + const double t_conv = tt.TocMs(); + TRACE("VideoFrameDecoderImpl(%s) t_send=%.1f t_recv=%.1f t_conv=%.1f total=%.1f", params_.name_.c_str(), t_send, + t_recv, t_conv, t_send + t_recv + t_conv); + img.t_dec_ = t_send + t_recv; + img.t_conv_ = t_conv; +# endif + + return img; +} + +/* ****************************************************************************************************************** */ + +std::unique_ptr CreateVideoFrameDecoder(const VideoDecoderParams& params) +{ + // Some sanity checks.. + bool ok = true; + + switch (params.fmt_) { + case PixelFmt::RGB24: + case PixelFmt::Y8: + case PixelFmt::GBRP: + break; + case PixelFmt::UNSPECIFIED: + ok = false; + break; + } + + const AVCodec* codec = nullptr; + switch (params.codec_) { // clang-format off + case VideoCodec::H264: codec = avcodec_find_decoder(AV_CODEC_ID_H264); break; + case VideoCodec::H265: codec = avcodec_find_decoder(AV_CODEC_ID_H265); break; + case VideoCodec::UNSPECIFIED: break; + } // clang-format on + if (!codec) { + ok = false; + } + + switch (params.accel_) { + case HwAccel::AUTO: + case HwAccel::SW: + case HwAccel::HW: + break; + case HwAccel::UNSPECIFIED: + ok = false; + break; + } + + switch (params.qual_) { + case ScalingQual::BILINEAR: + case ScalingQual::BICUBIC: + case ScalingQual::LANCZOS: + case ScalingQual::SINC: + break; + case ScalingQual::UNSPECIFIED: + ok = false; + break; + } + + if (!ok) { + WARNING( + "Bad params for VideoFrameDecoder(%s) %s", params.name_.c_str(), VideoDecoderParamsToStr(params).c_str()); + return nullptr; + } + + return std::make_unique(params); +} + +#endif // FPSDK_USE_FFMPEG +/* ****************************************************************************************************************** */ +} // namespace video +} // namespace common +} // namespace fpsdk diff --git a/fpsdk_common/test/cam_test.cpp b/fpsdk_common/test/cam_test.cpp new file mode 100644 index 00000000..7f55ad01 --- /dev/null +++ b/fpsdk_common/test/cam_test.cpp @@ -0,0 +1,47 @@ +/** + * \verbatim + * ___ ___ + * \ \ / / + * \ \/ / Copyright (c) Fixposition AG (www.fixposition.com) and contributors + * / /\ \ License: see the LICENSE file + * /__/ \__\ + * \endverbatim + * + * @file + * @brief Fixposition SDK: tests for fpsdk::common::cam + */ + +/* LIBC/STL */ +#include + +/* EXTERNAL */ +#include + +/* PACKAGE */ +#include +#include + +namespace { +/* ****************************************************************************************************************** */ +using namespace fpsdk::common::cam; + +TEST(CamTest, Dummy) +{ + EXPECT_TRUE(true); +} + +/* ****************************************************************************************************************** */ +} // namespace + +int main(int argc, char** argv) +{ + testing::InitGoogleTest(&argc, argv); + auto level = fpsdk::common::logging::LoggingLevel::WARNING; + for (int ix = 0; ix < argc; ix++) { + if ((argv[ix][0] == '-') && argv[ix][1] == 'v') { + level++; + } + } + fpsdk::common::logging::LoggingSetParams(level); + return RUN_ALL_TESTS(); +} diff --git a/fpsdk_common/test/data/hevc_i_frame.bin b/fpsdk_common/test/data/hevc_i_frame.bin new file mode 100644 index 00000000..2c7f5488 Binary files /dev/null and b/fpsdk_common/test/data/hevc_i_frame.bin differ diff --git a/fpsdk_common/test/data/hevc_i_frame.cpp b/fpsdk_common/test/data/hevc_i_frame.cpp new file mode 100644 index 00000000..429844e8 --- /dev/null +++ b/fpsdk_common/test/data/hevc_i_frame.cpp @@ -0,0 +1,782 @@ +// clang-format off +static const std::vector HEVC_I_FRAME_BIN = { + 0x00, 0x00, 0x00, 0x01, 0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x5d, 0xf0, 0x30, 0x00, 0x00, 0x3e, 0x80, + 0x00, 0x06, 0x1a, 0x85, 0x00, 0x00, 0x00, 0x01, 0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x5d, 0xa0, 0x02, 0x00, 0x80, 0x30, + 0x16, 0x5f, 0x92, 0x4c, 0x9e, 0xcc, 0x88, 0x10, 0x00, 0x00, 0x3e, 0x80, 0x00, 0x06, 0x1a, 0x80, 0x80, 0x00, 0x00, 0x00, 0x01, 0x44, 0x01, 0xe0, 0x71, 0x81, 0x32, 0x00, 0x00, 0x00, 0x01, 0x26, + 0x01, 0xaf, 0x09, 0x60, 0xba, 0x3f, 0x15, 0x1e, 0x3d, 0xe3, 0xfc, 0x83, 0xab, 0x4e, 0x60, 0xa9, 0xd6, 0xbd, 0xf9, 0xe9, 0x00, 0xc2, 0x28, 0xd5, 0x4c, 0x50, 0xf1, 0x1a, 0x2d, 0xc1, 0xaf, 0x7f, + 0x7e, 0x63, 0xd1, 0xed, 0x85, 0x30, 0x67, 0xf4, 0xc4, 0x6b, 0x07, 0xfe, 0xa9, 0xe0, 0x99, 0x97, 0x0b, 0x40, 0x35, 0xda, 0x0e, 0x56, 0xb0, 0xa7, 0x12, 0x8a, 0xa4, 0x8d, 0x68, 0xf5, 0xa4, 0x0d, + 0x4b, 0xdb, 0x05, 0x71, 0x64, 0x69, 0xd8, 0x91, 0x56, 0xd7, 0x4e, 0x50, 0x82, 0xfb, 0xd6, 0x0d, 0x84, 0xdc, 0x5f, 0x61, 0xc6, 0x26, 0xe1, 0xb3, 0x2b, 0x8d, 0xdd, 0x3a, 0x5d, 0x00, 0x70, 0x62, + 0xd4, 0xd4, 0x2d, 0x8e, 0xa0, 0xe5, 0x07, 0x7b, 0xe5, 0x91, 0x5c, 0xb7, 0x00, 0x1c, 0x27, 0xb6, 0x07, 0xf2, 0xde, 0xff, 0x8d, 0x63, 0x9d, 0xbf, 0x0f, 0x2c, 0xff, 0x4c, 0xf6, 0xcd, 0x4c, 0xb0, + 0x87, 0xb8, 0xac, 0x8f, 0x81, 0x30, 0x24, 0x52, 0x48, 0xfd, 0x02, 0x78, 0x04, 0x00, 0x71, 0xfa, 0x9f, 0xe6, 0xb1, 0x33, 0x2d, 0x17, 0xe7, 0xd3, 0x6f, 0x0d, 0xeb, 0x44, 0xb8, 0xb4, 0x25, 0x73, + 0xf8, 0x59, 0x72, 0x6b, 0x89, 0xa4, 0x3f, 0x96, 0xe5, 0xc5, 0xcd, 0x0f, 0x7a, 0x84, 0x21, 0x05, 0x5e, 0xb4, 0x49, 0xba, 0xe7, 0x25, 0x50, 0xc3, 0xd4, 0x7f, 0x8b, 0xf1, 0x93, 0x8b, 0x11, 0x2e, + 0x96, 0x83, 0x54, 0x84, 0xb3, 0x52, 0xe8, 0xc5, 0xb5, 0xfd, 0x68, 0xc7, 0x83, 0xcb, 0x1f, 0xaa, 0x64, 0x9e, 0x56, 0x66, 0x1d, 0x60, 0x1a, 0x3b, 0x05, 0x1e, 0x6e, 0x81, 0x32, 0xfa, 0xbd, 0xbb, + 0x42, 0xa4, 0xe3, 0xbe, 0x53, 0xa9, 0x18, 0x2e, 0x14, 0x4c, 0xe6, 0x7d, 0x1d, 0x90, 0x23, 0x44, 0x5d, 0x69, 0xa4, 0xcf, 0x23, 0x19, 0xbb, 0xa2, 0x21, 0x51, 0x1c, 0x83, 0xb1, 0x76, 0x2f, 0x1a, + 0xdc, 0x73, 0x2c, 0xf5, 0x47, 0x00, 0xbb, 0xd0, 0xab, 0x67, 0x3b, 0xe8, 0x01, 0x29, 0x1f, 0xbe, 0x4c, 0x80, 0x96, 0xe7, 0xc0, 0x22, 0xc1, 0x45, 0x84, 0x3d, 0x71, 0x59, 0x72, 0x23, 0x06, 0x8a, + 0x17, 0xc8, 0xa6, 0xe3, 0xff, 0xb8, 0x07, 0xbc, 0xda, 0xc3, 0x83, 0xf5, 0x93, 0xd9, 0xc6, 0x8a, 0x76, 0x9b, 0xb2, 0x05, 0xfc, 0x37, 0x68, 0x53, 0xdd, 0x35, 0xf6, 0x79, 0xf3, 0x1c, 0x8d, 0x2e, + 0x8d, 0x2c, 0x2c, 0x32, 0x1c, 0xdd, 0x6c, 0x84, 0xdb, 0xad, 0x55, 0x39, 0x6d, 0x6f, 0x4d, 0x70, 0xbc, 0xf7, 0x0c, 0xcd, 0xf1, 0xf9, 0xb1, 0xaf, 0x52, 0x0a, 0x85, 0x8a, 0x78, 0xf7, 0x14, 0x5c, + 0xf1, 0xd3, 0xd1, 0x3f, 0xfa, 0x96, 0xba, 0x91, 0xaf, 0x24, 0x4a, 0x6f, 0x73, 0x5a, 0x12, 0xf2, 0x3a, 0x33, 0x06, 0xe9, 0xf7, 0xad, 0xf0, 0xf0, 0x7a, 0x14, 0xe5, 0x35, 0x22, 0x28, 0x51, 0xa5, + 0x3a, 0xad, 0xbd, 0xcb, 0x36, 0x3c, 0xc9, 0x3f, 0x05, 0xff, 0xbb, 0x1a, 0x37, 0x8a, 0xed, 0xe9, 0xb9, 0x0e, 0x96, 0xa9, 0x04, 0x99, 0xb6, 0xe2, 0x5e, 0x92, 0x30, 0x86, 0xfa, 0xc6, 0xdc, 0xc2, + 0x73, 0xa8, 0x6b, 0x5c, 0x8f, 0xde, 0x9c, 0x26, 0x16, 0x2f, 0xd6, 0x9a, 0x6a, 0x8c, 0x48, 0x87, 0x2b, 0x85, 0x75, 0x50, 0x84, 0xf3, 0x4a, 0x3f, 0xc6, 0x69, 0x9e, 0xf5, 0xea, 0xc8, 0x08, 0x85, + 0xfc, 0x56, 0x9b, 0x63, 0x6e, 0x4d, 0xa4, 0x7c, 0x81, 0x7e, 0x21, 0xc3, 0xbe, 0x4b, 0xd2, 0x1e, 0xbe, 0xe8, 0x36, 0x40, 0xa6, 0x21, 0x3b, 0x4d, 0x3f, 0x9a, 0xe3, 0xd1, 0x3e, 0xa6, 0x78, 0x83, + 0xb7, 0xb4, 0xa3, 0xef, 0x5f, 0x42, 0x02, 0x0e, 0xb8, 0x31, 0x78, 0x3e, 0xe9, 0x8b, 0xf1, 0x1f, 0x93, 0x7f, 0x1a, 0x17, 0x6a, 0xbf, 0x4f, 0xb3, 0xa9, 0x92, 0x75, 0x76, 0x0f, 0x2e, 0x20, 0xc0, + 0x92, 0x68, 0x8e, 0x66, 0x72, 0x28, 0xcc, 0x15, 0x73, 0x47, 0xa7, 0x7a, 0x9c, 0xb5, 0xfd, 0x3e, 0xa6, 0xc1, 0x44, 0x4e, 0x36, 0xed, 0x94, 0xf6, 0x5a, 0x8d, 0x9a, 0xc6, 0xe3, 0xa3, 0xef, 0xc4, + 0x3a, 0x3a, 0x77, 0xf4, 0x16, 0xbb, 0x7e, 0xd0, 0xbd, 0xa9, 0x9f, 0x56, 0xc6, 0x82, 0xe8, 0xa8, 0xb3, 0x6b, 0x90, 0x42, 0x74, 0x73, 0xed, 0x56, 0x54, 0x05, 0x46, 0x98, 0xe1, 0xb2, 0xaa, 0x92, + 0xc8, 0xb8, 0x76, 0x22, 0x6b, 0x07, 0x87, 0xeb, 0x25, 0x8d, 0xef, 0x93, 0xba, 0x7b, 0xeb, 0xf6, 0x2a, 0x2d, 0xe1, 0x5c, 0x92, 0x25, 0x88, 0xe1, 0xeb, 0x80, 0x94, 0x78, 0x19, 0xa3, 0xf7, 0xe5, + 0xcc, 0xda, 0x6d, 0xca, 0xe4, 0xf8, 0xb7, 0x50, 0xaa, 0x78, 0xba, 0x68, 0x85, 0xb0, 0xf8, 0x85, 0xc3, 0x72, 0x28, 0x8c, 0xf6, 0x24, 0x27, 0xe6, 0x28, 0x29, 0xc1, 0xef, 0xa7, 0x20, 0xa9, 0x1d, + 0xc2, 0x4c, 0xb0, 0x84, 0x77, 0x74, 0x45, 0x65, 0x17, 0x70, 0x6f, 0xb6, 0x1a, 0xaf, 0xcf, 0x3c, 0x08, 0x64, 0xdb, 0x48, 0x0c, 0x93, 0x9d, 0x2d, 0x4d, 0x96, 0x2f, 0x57, 0x9a, 0x03, 0xd4, 0xc8, + 0x0e, 0xf3, 0x6f, 0xa0, 0x1d, 0x85, 0x9a, 0xaa, 0xcf, 0x39, 0x0e, 0x12, 0xbe, 0xac, 0x76, 0x28, 0x41, 0x99, 0xa5, 0x5a, 0x2b, 0x81, 0xaa, 0x60, 0xe6, 0x2a, 0x12, 0x12, 0x98, 0xb8, 0xee, 0x98, + 0x37, 0x91, 0x26, 0x88, 0x18, 0x2b, 0xea, 0xf2, 0x1f, 0x0e, 0x11, 0x99, 0xfc, 0x89, 0xc1, 0xb1, 0x82, 0xbc, 0xbf, 0x8c, 0xe5, 0x15, 0x8b, 0xaa, 0x55, 0x04, 0x31, 0x73, 0x04, 0x9d, 0x88, 0xac, + 0xa4, 0x91, 0x4d, 0x8e, 0xfc, 0x06, 0xc4, 0xd1, 0x45, 0xb3, 0xab, 0x81, 0xa1, 0x6f, 0x3f, 0x31, 0x42, 0x97, 0x55, 0xa6, 0x1f, 0x8a, 0xce, 0x03, 0x2d, 0x73, 0x40, 0x64, 0x4e, 0x65, 0x9e, 0x51, + 0x88, 0x92, 0x75, 0xd2, 0x2d, 0xb9, 0x11, 0x88, 0x6c, 0xaf, 0x96, 0xbf, 0xf3, 0x12, 0x73, 0x44, 0xf0, 0x52, 0x24, 0x69, 0xb8, 0x09, 0x65, 0xea, 0x0e, 0x3e, 0xe2, 0x79, 0x55, 0x03, 0x7d, 0x6b, + 0x90, 0xa4, 0x4b, 0xb8, 0xf8, 0x4b, 0x9e, 0xf3, 0x75, 0x9e, 0x36, 0x61, 0x3e, 0x38, 0xab, 0x0b, 0x9f, 0x0f, 0x3c, 0xfc, 0x34, 0x33, 0xfd, 0x34, 0x0b, 0xeb, 0x9a, 0xd9, 0x1e, 0x0e, 0xe7, 0xbf, + 0x7f, 0x62, 0xd7, 0xe0, 0xb5, 0xc5, 0x28, 0x52, 0xc7, 0x4d, 0xdc, 0xf3, 0xdf, 0x6f, 0xbc, 0x27, 0xf6, 0xd0, 0x47, 0xc3, 0x2d, 0x90, 0x1f, 0xf2, 0xf9, 0xcb, 0x56, 0x63, 0xbb, 0xa0, 0xaa, 0xbb, + 0xd6, 0xba, 0x61, 0x44, 0x5d, 0x56, 0x31, 0xca, 0x95, 0xeb, 0xaf, 0x39, 0xd7, 0x7f, 0x73, 0x06, 0xf0, 0x3c, 0x8e, 0x51, 0x38, 0x08, 0x16, 0x09, 0x60, 0x76, 0x16, 0x84, 0x2b, 0x19, 0x09, 0x30, + 0x1a, 0xf5, 0x7d, 0xa0, 0xb9, 0x22, 0x63, 0x52, 0x56, 0x83, 0x9e, 0xb6, 0x61, 0x39, 0xeb, 0x98, 0x3a, 0x8d, 0xcd, 0xb0, 0x82, 0x60, 0x9f, 0x56, 0x54, 0xff, 0x73, 0xf1, 0xdf, 0x45, 0xc8, 0xe4, + 0x1e, 0xc5, 0x7b, 0xa6, 0x3b, 0xef, 0xd7, 0x48, 0x4f, 0x94, 0x5c, 0x03, 0xd1, 0x64, 0x56, 0x75, 0x60, 0x9c, 0xd5, 0xe4, 0x1c, 0x17, 0xca, 0x24, 0x7f, 0x88, 0xd8, 0x96, 0xbc, 0x71, 0x60, 0xe7, + 0x04, 0xb1, 0xf4, 0x86, 0x8a, 0xd1, 0x24, 0x37, 0x3a, 0xb0, 0xae, 0x52, 0x92, 0xf4, 0xbd, 0x21, 0x2e, 0x0a, 0x5b, 0x7b, 0x94, 0xb3, 0xd5, 0x6e, 0x51, 0xe9, 0x36, 0x17, 0xc0, 0x88, 0x5b, 0x29, + 0xc8, 0xfc, 0x88, 0xcc, 0x63, 0x3b, 0x25, 0x3e, 0x13, 0xb5, 0xcf, 0x2b, 0x6f, 0xf9, 0x42, 0x65, 0x39, 0xc5, 0x39, 0xb8, 0x45, 0xcd, 0x28, 0xf2, 0xe9, 0xc2, 0x78, 0xaa, 0xb7, 0x1b, 0xdc, 0xf5, + 0xce, 0x10, 0x76, 0xe9, 0x48, 0x4a, 0x25, 0x72, 0x02, 0xf5, 0x5c, 0xfd, 0x76, 0xf0, 0xfc, 0x0a, 0x24, 0xef, 0xb3, 0x41, 0x43, 0xde, 0xc2, 0x6d, 0xac, 0xcf, 0x54, 0x39, 0x96, 0xca, 0x6d, 0x17, + 0x3c, 0x66, 0x60, 0xc9, 0xa1, 0x73, 0xbb, 0x26, 0x4d, 0x65, 0x64, 0x9c, 0x8e, 0x28, 0x80, 0x53, 0xd0, 0xb5, 0xc4, 0x88, 0xed, 0x97, 0x4b, 0xe7, 0x0d, 0x29, 0xef, 0x0a, 0xf7, 0x24, 0xcf, 0x07, + 0x09, 0xd7, 0xf1, 0x36, 0x9a, 0x6b, 0xbd, 0x03, 0x62, 0x0e, 0xb4, 0xc3, 0x30, 0x75, 0xd6, 0x97, 0xec, 0x0c, 0x79, 0xd6, 0xef, 0xc5, 0x34, 0x18, 0x1b, 0x02, 0xf8, 0xe3, 0xf8, 0xb3, 0x96, 0x76, + 0xfb, 0x70, 0x5f, 0xc1, 0xbb, 0xe5, 0xc8, 0x63, 0xa6, 0x5c, 0x8e, 0x44, 0xe4, 0x3c, 0x64, 0x39, 0x0b, 0x17, 0x05, 0xbb, 0xab, 0xde, 0xda, 0x8d, 0x6d, 0xe8, 0xb7, 0xff, 0x82, 0x01, 0xe5, 0x8e, + 0x36, 0xd9, 0x48, 0x11, 0xc3, 0xb9, 0xc1, 0x7c, 0xee, 0x71, 0xae, 0xbb, 0xe0, 0x1f, 0xda, 0x37, 0xcc, 0x66, 0x7e, 0x56, 0xb7, 0x0d, 0x11, 0xf7, 0xd4, 0x2e, 0xe2, 0x0e, 0x7b, 0x6f, 0x5c, 0xd0, + 0xa9, 0x92, 0xcf, 0x00, 0x56, 0xd7, 0x91, 0xb9, 0xe9, 0x2e, 0x28, 0x22, 0xc7, 0x67, 0xd1, 0x3f, 0x3d, 0x4e, 0xb1, 0x93, 0x55, 0x88, 0x2d, 0xdf, 0xd0, 0x0d, 0x6c, 0x83, 0xb2, 0xfc, 0x93, 0xe3, + 0x46, 0xed, 0x8f, 0x36, 0x4a, 0x2f, 0xf5, 0x54, 0x91, 0x40, 0x44, 0x9c, 0xe3, 0x4a, 0x2a, 0x0e, 0x36, 0x4f, 0x97, 0x2a, 0x82, 0x5e, 0x3a, 0x45, 0xca, 0x02, 0x41, 0xaa, 0x4d, 0x06, 0x59, 0x20, + 0x49, 0xaf, 0x86, 0x50, 0x3d, 0x7f, 0xbe, 0xd9, 0x85, 0x1a, 0xfd, 0x85, 0xe1, 0x87, 0xde, 0x91, 0x85, 0x6e, 0x4e, 0x05, 0x93, 0x9a, 0xda, 0xd3, 0xb1, 0x2f, 0x6a, 0x92, 0x51, 0x75, 0xbd, 0x58, + 0x14, 0x85, 0x51, 0xa9, 0x09, 0x9e, 0xcf, 0x30, 0xe4, 0xcf, 0x7d, 0x0c, 0xcf, 0x99, 0x0e, 0xe2, 0x3e, 0x22, 0x3c, 0xd0, 0xc5, 0x2a, 0x30, 0x02, 0x2f, 0xe6, 0x6b, 0xf3, 0x9b, 0x53, 0x32, 0xbb, + 0xe8, 0x24, 0x04, 0x06, 0xf4, 0x07, 0x05, 0x85, 0xed, 0xb9, 0x31, 0xfc, 0xb9, 0xac, 0x29, 0xf7, 0xd1, 0xa4, 0x0c, 0x78, 0xa6, 0xe0, 0x72, 0x87, 0x6d, 0x14, 0xee, 0x74, 0xf7, 0x32, 0xbf, 0x1e, + 0xe7, 0x81, 0x9c, 0xea, 0x90, 0xa2, 0xe1, 0x7e, 0x5d, 0xa7, 0x5c, 0x54, 0x4f, 0x6a, 0xe5, 0xcc, 0xa9, 0xab, 0x4e, 0x26, 0xd9, 0x91, 0xa1, 0x32, 0x4e, 0x54, 0x38, 0x3a, 0x7a, 0xcc, 0xd0, 0x31, + 0xff, 0x01, 0x84, 0x5e, 0x01, 0x44, 0xf9, 0x71, 0x17, 0x6d, 0xe6, 0xe2, 0x18, 0xef, 0xeb, 0x30, 0xf4, 0x4b, 0xf6, 0x33, 0x96, 0xed, 0x21, 0xcc, 0xdf, 0x32, 0xcd, 0xf0, 0x9a, 0xb8, 0x85, 0x0a, + 0x7a, 0x02, 0x06, 0x1c, 0x87, 0xf9, 0xba, 0xbf, 0xb8, 0x4d, 0x02, 0xe6, 0xdf, 0xf6, 0xc9, 0x4b, 0x14, 0x98, 0x8c, 0x54, 0xcd, 0xae, 0x3c, 0xe6, 0xd4, 0x2a, 0x70, 0x62, 0xf8, 0xa9, 0xeb, 0xa8, + 0x0a, 0x16, 0x4e, 0x1b, 0x18, 0xbe, 0x29, 0xe7, 0x57, 0xc5, 0xc0, 0x02, 0xd7, 0x15, 0x2b, 0x27, 0x3a, 0xdd, 0xe7, 0x30, 0xba, 0x1a, 0xe2, 0xae, 0x36, 0xf6, 0x95, 0x31, 0x80, 0xcd, 0x7b, 0x0b, + 0x88, 0x06, 0xba, 0x0f, 0xab, 0x57, 0x42, 0x27, 0x90, 0x34, 0x5a, 0x7e, 0x9c, 0x8e, 0xb2, 0x8a, 0x0e, 0x3c, 0xf5, 0x90, 0xea, 0xb6, 0xfa, 0xf5, 0xf9, 0x84, 0xdb, 0x32, 0x66, 0x66, 0x9b, 0x7c, + 0x75, 0xf6, 0x4f, 0x7d, 0x6b, 0xb9, 0x73, 0x40, 0x9d, 0x89, 0x43, 0xbf, 0x2b, 0x2a, 0xf9, 0x0d, 0x29, 0xa6, 0x56, 0x8f, 0xfb, 0xbf, 0x1e, 0x39, 0x07, 0x0a, 0xd9, 0x65, 0x0a, 0x55, 0x6b, 0x34, + 0xf4, 0x63, 0x9b, 0x9a, 0x25, 0x49, 0x51, 0x11, 0x69, 0x80, 0xa3, 0x56, 0xea, 0xe4, 0xfd, 0x9a, 0x43, 0x7d, 0x60, 0x72, 0xe3, 0x56, 0x27, 0x61, 0x01, 0x47, 0x3d, 0xe3, 0xb1, 0x88, 0x39, 0x7b, + 0xd1, 0x25, 0x2d, 0x5d, 0x9a, 0xa6, 0xf8, 0x92, 0x52, 0xca, 0x51, 0x9b, 0xcd, 0xb4, 0x0c, 0xc1, 0xfa, 0xb7, 0x9f, 0x48, 0x5a, 0xf6, 0x6a, 0xe5, 0x0d, 0x83, 0x49, 0xb8, 0xe1, 0x19, 0x01, 0xdd, + 0x4b, 0x87, 0x53, 0x3e, 0xa3, 0x1a, 0x0a, 0xbc, 0x3b, 0xa4, 0x29, 0xed, 0x40, 0xa6, 0xbb, 0x4e, 0xee, 0xc0, 0x74, 0xae, 0x78, 0x26, 0xaa, 0xd0, 0xc7, 0x0f, 0xd1, 0x3c, 0xbf, 0x13, 0xbd, 0x58, + 0xd1, 0x6d, 0x51, 0x52, 0x72, 0xf3, 0x01, 0x4b, 0x27, 0x98, 0x45, 0x09, 0x75, 0xd4, 0xbe, 0xc1, 0x37, 0xa3, 0x87, 0xd8, 0xac, 0x97, 0x80, 0xac, 0x59, 0xa4, 0xa8, 0x86, 0x14, 0x2c, 0xed, 0xaa, + 0xbb, 0x62, 0xcf, 0xba, 0x7c, 0xdf, 0x4e, 0x95, 0xb6, 0xcc, 0x48, 0xde, 0x4b, 0x29, 0xd0, 0xfc, 0x79, 0x9d, 0x07, 0xbb, 0x2c, 0xe6, 0xc1, 0x90, 0x6c, 0x67, 0x7b, 0x0d, 0x34, 0x6c, 0x90, 0xad, + 0xfa, 0x18, 0x92, 0x5d, 0x44, 0xdb, 0x33, 0xaf, 0x73, 0x66, 0x23, 0xb8, 0xdd, 0x4d, 0x3d, 0x0d, 0xd3, 0xea, 0xf7, 0x37, 0x85, 0x84, 0x25, 0xea, 0x7a, 0xd6, 0xf5, 0x5e, 0xb9, 0x76, 0x8b, 0x72, + 0x80, 0x99, 0x6f, 0x9f, 0x10, 0xea, 0xd0, 0xa7, 0xca, 0x63, 0x29, 0x66, 0x7f, 0xf9, 0xb3, 0x12, 0x0b, 0x6f, 0x23, 0xfa, 0xa2, 0x52, 0x67, 0x78, 0xfc, 0xd6, 0xda, 0x14, 0x35, 0x26, 0x08, 0x8a, + 0x72, 0x61, 0x4c, 0x50, 0x17, 0x2f, 0x77, 0x6b, 0x6b, 0x8c, 0x5f, 0x93, 0xbc, 0x6a, 0x74, 0x05, 0x44, 0x18, 0x2e, 0x23, 0x97, 0x3f, 0x84, 0x6b, 0x5a, 0x86, 0xbc, 0x22, 0x49, 0x0c, 0x98, 0xb9, + 0x29, 0x6d, 0x51, 0x4f, 0x40, 0x1a, 0x04, 0x00, 0x89, 0xaf, 0x08, 0xb3, 0xa6, 0x1a, 0xcc, 0x0b, 0x1b, 0x69, 0x90, 0x97, 0xf9, 0x91, 0xb5, 0xd8, 0x3a, 0xe3, 0x63, 0xc3, 0xc0, 0x4d, 0x78, 0x29, + 0xfd, 0x6b, 0x1d, 0x83, 0x4b, 0xed, 0x2c, 0xa7, 0xe2, 0x93, 0x1d, 0x62, 0xdc, 0x03, 0x06, 0x44, 0xbe, 0xcb, 0x24, 0x89, 0x60, 0xb6, 0x30, 0x53, 0x66, 0xb6, 0xa3, 0x9a, 0xc9, 0xde, 0xdb, 0xbc, + 0x39, 0x31, 0x1d, 0x8d, 0x39, 0x4e, 0xce, 0x29, 0x53, 0xcb, 0xb2, 0xdc, 0x85, 0x00, 0x70, 0xd6, 0x00, 0x16, 0x0f, 0x44, 0x76, 0x80, 0x60, 0x42, 0xc5, 0xa9, 0xfe, 0x9c, 0x2b, 0x4e, 0xc3, 0x03, + 0xa9, 0x64, 0x7c, 0xea, 0xc5, 0x8f, 0x10, 0xff, 0x4e, 0xdb, 0x81, 0x1d, 0x19, 0xa9, 0xc2, 0x93, 0x67, 0x7a, 0xda, 0x18, 0x20, 0x32, 0xe3, 0x44, 0xc0, 0xa3, 0x5e, 0xc4, 0x4f, 0x3a, 0x52, 0x63, + 0x6f, 0xb5, 0x17, 0x53, 0xa2, 0xa3, 0x9a, 0x53, 0x17, 0x63, 0x92, 0x3b, 0x8b, 0xa3, 0xc1, 0x94, 0x1a, 0x20, 0x3b, 0x1c, 0xfc, 0x34, 0x3f, 0x05, 0x1a, 0x42, 0x34, 0xfc, 0x7f, 0x96, 0x35, 0x49, + 0x8c, 0x40, 0x4e, 0x9f, 0x9d, 0x4a, 0x54, 0x4c, 0x61, 0x0d, 0xa5, 0xf2, 0xda, 0x7b, 0xe6, 0xd7, 0xb3, 0x32, 0xb8, 0xf4, 0x88, 0x5c, 0x39, 0x84, 0x08, 0xe6, 0x20, 0xf4, 0x04, 0x05, 0x58, 0xa4, + 0x54, 0x54, 0xf8, 0x29, 0xf0, 0x7f, 0x47, 0xf4, 0xfd, 0x16, 0xf4, 0x17, 0x53, 0x15, 0x07, 0xf7, 0x91, 0x73, 0xc8, 0xe3, 0x87, 0x6b, 0x9b, 0x1c, 0x45, 0xb4, 0x44, 0x39, 0x63, 0xab, 0x6d, 0x17, + 0x34, 0x3e, 0x0a, 0x93, 0x81, 0x6d, 0x58, 0xa1, 0xb7, 0x73, 0xd9, 0xc9, 0xeb, 0x43, 0x0b, 0x37, 0xdd, 0x97, 0x06, 0x60, 0x0c, 0xba, 0x04, 0x8d, 0x1f, 0xc5, 0xb4, 0x6b, 0x34, 0x80, 0x36, 0x89, + 0x7d, 0x75, 0x7c, 0x27, 0xad, 0x9b, 0x18, 0xaf, 0x9f, 0x77, 0x17, 0xb7, 0x62, 0xd7, 0xd7, 0xaf, 0xda, 0xfd, 0x0f, 0x7b, 0xbd, 0x78, 0xb5, 0xc4, 0xd9, 0x73, 0x72, 0xfe, 0x49, 0x3b, 0xb1, 0x39, + 0xc4, 0xee, 0x0e, 0x99, 0xa2, 0x0a, 0x72, 0x33, 0xbf, 0x9c, 0x1c, 0x7e, 0x8f, 0x1d, 0x0f, 0xb1, 0x57, 0xea, 0xa0, 0xfb, 0x2f, 0xba, 0x4f, 0xb2, 0x67, 0xeb, 0xb7, 0x6d, 0xef, 0x06, 0x13, 0x6a, + 0xdc, 0xd5, 0xd7, 0x45, 0xaa, 0x4b, 0x4f, 0xe4, 0xbb, 0x1f, 0x05, 0x4d, 0xdd, 0x5f, 0x83, 0xb3, 0x96, 0xfe, 0xf9, 0x51, 0x05, 0xe9, 0x0b, 0x6b, 0xff, 0xbe, 0xd4, 0x3c, 0x4e, 0x2f, 0xf7, 0x69, + 0xe4, 0xd1, 0x1c, 0xd3, 0xbb, 0x9a, 0x50, 0x6a, 0x63, 0xd8, 0x4a, 0xdd, 0x8f, 0x93, 0xd5, 0xb3, 0x03, 0x1c, 0x76, 0xcb, 0xbe, 0x34, 0xcc, 0x03, 0x1e, 0xca, 0xc8, 0xcb, 0xa1, 0xc3, 0x11, 0x4e, + 0xc4, 0x39, 0xfb, 0x90, 0xfc, 0x1e, 0x27, 0x10, 0x0e, 0xe3, 0xaf, 0x1f, 0x61, 0xb8, 0x19, 0xaa, 0x3c, 0x07, 0xfd, 0x8c, 0xe5, 0xe5, 0x2c, 0xd6, 0x17, 0x89, 0x52, 0x04, 0x89, 0x85, 0xef, 0x22, + 0x5b, 0x5b, 0x6b, 0x96, 0xeb, 0x4a, 0x76, 0xdc, 0x68, 0xc3, 0x33, 0xe2, 0x43, 0xcd, 0x71, 0x51, 0x52, 0x9c, 0x8f, 0xd4, 0x46, 0x33, 0x4f, 0x65, 0xb5, 0x87, 0xd8, 0x60, 0x38, 0xab, 0x66, 0x3e, + 0x95, 0x3f, 0x31, 0xb9, 0xc6, 0x48, 0x40, 0x25, 0xc9, 0x2e, 0xe6, 0x58, 0xef, 0xeb, 0xfd, 0xbf, 0x85, 0xee, 0x64, 0x0d, 0x1d, 0x1f, 0x02, 0xb8, 0x6e, 0x56, 0x0e, 0x00, 0x54, 0xe3, 0x13, 0x3f, + 0xaa, 0x5e, 0x1f, 0x5a, 0xd8, 0xb6, 0xc4, 0xdf, 0x70, 0xdc, 0xa6, 0xe8, 0x62, 0xbd, 0x30, 0xcd, 0xee, 0xdc, 0x05, 0x7d, 0x07, 0x4d, 0xa9, 0xca, 0x1a, 0x72, 0x62, 0x95, 0x1f, 0xe6, 0x89, 0xbe, + 0x0e, 0x0b, 0xc1, 0xf9, 0x6b, 0x85, 0xd3, 0x6e, 0x3d, 0x24, 0xd1, 0x34, 0xdf, 0x71, 0x83, 0xf6, 0x42, 0xa4, 0xca, 0x81, 0xf1, 0xe0, 0x85, 0x0b, 0x91, 0xce, 0x94, 0xb3, 0xd0, 0x4a, 0xd7, 0x3c, + 0xe5, 0xf9, 0xa9, 0x83, 0x17, 0x6c, 0x69, 0x76, 0x58, 0x2d, 0x8f, 0xe0, 0x35, 0x05, 0x18, 0x91, 0xd5, 0x1f, 0xb5, 0x37, 0x6d, 0xeb, 0x95, 0xa4, 0xed, 0xa7, 0x0c, 0x26, 0xfd, 0xd3, 0x8e, 0x80, + 0x74, 0x91, 0xf0, 0x80, 0xde, 0x69, 0x6a, 0x83, 0xa2, 0x84, 0xce, 0x11, 0xb0, 0xa6, 0x8c, 0x63, 0x0d, 0xf4, 0x17, 0xa4, 0x6f, 0x9d, 0x22, 0xba, 0xb7, 0xd6, 0xc9, 0x42, 0x62, 0x2d, 0xd2, 0x0d, + 0x7c, 0x9b, 0x88, 0xad, 0x2d, 0xaf, 0x33, 0xda, 0x10, 0x24, 0x46, 0xe6, 0x29, 0x4b, 0xfc, 0x7f, 0x85, 0x81, 0xc6, 0x0a, 0x3f, 0xfc, 0x40, 0xbe, 0xde, 0xa3, 0x6c, 0xeb, 0x17, 0xa9, 0xf4, 0x4f, + 0xb5, 0x79, 0xc6, 0xee, 0x29, 0x85, 0x0c, 0x4a, 0x57, 0xc6, 0x0f, 0x4b, 0x2c, 0x7c, 0x4d, 0x55, 0x9e, 0x98, 0x3b, 0x77, 0x76, 0xbf, 0xb6, 0xbf, 0x60, 0xbd, 0xe7, 0x89, 0xe7, 0x8c, 0xe6, 0x45, + 0x62, 0x83, 0x00, 0x7a, 0x6d, 0xc9, 0x1a, 0x78, 0xf1, 0x84, 0x1b, 0x48, 0xee, 0xf1, 0xb5, 0x9e, 0x96, 0x7f, 0x13, 0x5c, 0x0c, 0x9d, 0x57, 0x32, 0xaf, 0x43, 0xaf, 0x27, 0x10, 0xf8, 0xdf, 0x3b, + 0xb7, 0xa5, 0x62, 0x76, 0x4c, 0x0c, 0x79, 0xd0, 0x80, 0xa0, 0xb6, 0x00, 0xaa, 0x3d, 0x2a, 0x8f, 0x1d, 0x1a, 0xb5, 0x3c, 0x50, 0x97, 0xe7, 0x7e, 0x0e, 0x7a, 0x85, 0x8d, 0xd5, 0x97, 0x7a, 0x4e, + 0xe7, 0x22, 0x97, 0xa6, 0xcd, 0xdd, 0x95, 0x35, 0x18, 0x88, 0x09, 0x8e, 0x13, 0x42, 0x5a, 0xe5, 0xeb, 0x03, 0xd4, 0xa8, 0x74, 0x3c, 0x12, 0x85, 0x19, 0xb1, 0xf0, 0x50, 0x6b, 0xda, 0xf5, 0x30, + 0xc8, 0x0f, 0x9f, 0xc6, 0x32, 0x55, 0xaa, 0xbe, 0x18, 0xcd, 0x7f, 0x7f, 0x11, 0x7d, 0xbd, 0xc1, 0xc5, 0x25, 0xf2, 0x2d, 0x2b, 0xde, 0xa8, 0xf6, 0xfc, 0xdb, 0x16, 0xdf, 0x05, 0x66, 0xdc, 0x3e, + 0xa4, 0x57, 0x71, 0x6d, 0x49, 0x43, 0x9d, 0xb9, 0x29, 0xd4, 0xf4, 0xe7, 0xb2, 0x49, 0x03, 0x5f, 0x10, 0x15, 0x1f, 0x30, 0xc3, 0x94, 0x8b, 0x2a, 0xc1, 0xe3, 0xaa, 0x60, 0xca, 0xea, 0xb0, 0xa1, + 0x95, 0x05, 0x23, 0xe3, 0x93, 0x7b, 0x3e, 0x6a, 0xb5, 0x5a, 0xb6, 0xbd, 0x27, 0x6e, 0x90, 0xa7, 0xe0, 0x56, 0x4f, 0xeb, 0xd3, 0xbd, 0xdd, 0x39, 0x72, 0x4f, 0x9b, 0x26, 0xff, 0x8c, 0x58, 0x44, + 0xca, 0x3c, 0x70, 0x33, 0x64, 0x96, 0x97, 0xed, 0x0f, 0xae, 0x2a, 0x5a, 0xaa, 0x05, 0x16, 0xb3, 0xee, 0x65, 0x8f, 0x3c, 0xe0, 0x60, 0xd0, 0x31, 0xed, 0x7f, 0x02, 0x08, 0x59, 0x1e, 0xad, 0xe9, + 0x12, 0xd3, 0x31, 0xf3, 0x95, 0xa4, 0x19, 0xf7, 0x5d, 0x98, 0xe4, 0x13, 0x59, 0x3d, 0xc7, 0x80, 0xb0, 0xb3, 0x5d, 0xcc, 0x42, 0x1c, 0x60, 0x92, 0x2b, 0xa8, 0x4b, 0x41, 0x2a, 0x40, 0xff, 0x15, + 0xbc, 0x19, 0xda, 0x2e, 0x80, 0xcb, 0x63, 0xd9, 0x97, 0x62, 0x2e, 0x2a, 0x44, 0x56, 0xe5, 0xf9, 0xe8, 0x37, 0xcd, 0x55, 0x2e, 0xe1, 0x94, 0xbf, 0xb2, 0xd1, 0x3c, 0x1c, 0xb2, 0x39, 0x10, 0xb1, + 0xe2, 0xa7, 0x37, 0x78, 0x22, 0xa3, 0xb4, 0x51, 0x00, 0x1a, 0x30, 0x66, 0x81, 0xe7, 0xda, 0xa5, 0xa5, 0x04, 0xe0, 0x01, 0xf2, 0xe2, 0xf1, 0xa8, 0x43, 0xa3, 0x54, 0x2c, 0x8d, 0xa9, 0x57, 0x55, + 0x4f, 0x50, 0xde, 0x6a, 0x41, 0xe0, 0x95, 0x49, 0x8b, 0x30, 0x71, 0x13, 0xb4, 0x39, 0xa6, 0xa4, 0x19, 0xb8, 0x9a, 0x34, 0x21, 0xc5, 0x32, 0xd9, 0x3c, 0x5f, 0x49, 0x38, 0xf0, 0xe8, 0x90, 0xa6, + 0xc9, 0x67, 0xd1, 0x85, 0x7e, 0x22, 0x03, 0x18, 0xa8, 0xc0, 0x69, 0x30, 0x80, 0xc3, 0x42, 0x1b, 0x4b, 0x66, 0xfe, 0x9c, 0x3f, 0x05, 0x5b, 0x50, 0x99, 0x0c, 0xab, 0x9c, 0xd0, 0xf3, 0x03, 0x5f, + 0x48, 0xe1, 0xda, 0x65, 0xc5, 0xef, 0x8d, 0xca, 0xbe, 0xd4, 0x80, 0xfa, 0x43, 0x99, 0x4e, 0xc3, 0xfc, 0x69, 0x34, 0x33, 0xfe, 0x4f, 0x7c, 0x2f, 0x76, 0x3e, 0xd7, 0x81, 0xd2, 0xce, 0xc4, 0xae, + 0xea, 0xd6, 0x99, 0x75, 0xa0, 0xc2, 0x4b, 0x18, 0x75, 0xed, 0xd2, 0x24, 0x5e, 0x08, 0x09, 0x9d, 0x19, 0x6f, 0x9c, 0xe7, 0xd6, 0xff, 0x4f, 0x96, 0x6a, 0x9f, 0x27, 0x5c, 0x0d, 0x37, 0x44, 0x83, + 0xd8, 0x29, 0x99, 0xd5, 0x9e, 0x92, 0xb4, 0xf2, 0xd7, 0x0f, 0x7f, 0x20, 0xc3, 0x25, 0xda, 0x88, 0xe6, 0x9e, 0x3f, 0x2c, 0x24, 0x4d, 0x17, 0xdd, 0x6e, 0xfa, 0x57, 0xba, 0x09, 0x63, 0xe8, 0xe5, + 0xb2, 0x2d, 0x52, 0x58, 0x49, 0x47, 0x64, 0x7e, 0x8b, 0x04, 0x87, 0xae, 0x7b, 0xde, 0xef, 0xbf, 0xe1, 0x1c, 0x0b, 0xf3, 0x00, 0xff, 0xe5, 0xdb, 0x15, 0x83, 0xd5, 0xcc, 0x64, 0x5a, 0xba, 0xa6, + 0xb8, 0xdb, 0x38, 0x03, 0x97, 0x6d, 0xb3, 0xc9, 0x2e, 0x0b, 0x6c, 0x8a, 0x76, 0xd8, 0xd1, 0xc1, 0xa3, 0xb5, 0xf3, 0x63, 0xcf, 0x82, 0xe3, 0x93, 0x73, 0x6b, 0x02, 0xe6, 0x98, 0x1f, 0xff, 0x48, + 0x1d, 0x1e, 0x7c, 0x6c, 0x99, 0x76, 0x80, 0xee, 0xde, 0xf4, 0x1c, 0xbe, 0xe7, 0x0f, 0x55, 0x60, 0xb3, 0x34, 0x6b, 0xd7, 0x5b, 0x29, 0x97, 0xdd, 0x26, 0x50, 0xb6, 0x32, 0xa3, 0xe5, 0xfb, 0xa8, + 0xfa, 0x5b, 0xe1, 0x15, 0x6c, 0x50, 0x4b, 0x05, 0x9d, 0xd2, 0x68, 0x7a, 0x5b, 0x37, 0x63, 0x51, 0xb9, 0xb0, 0x88, 0x5a, 0xb2, 0x61, 0xcf, 0x4b, 0x36, 0x27, 0x57, 0xd4, 0x76, 0x52, 0xcf, 0xa8, + 0xa2, 0x85, 0x17, 0x8a, 0xa4, 0x87, 0xdd, 0xcc, 0x51, 0xba, 0x87, 0x7f, 0x2c, 0x2a, 0x08, 0xa6, 0xcd, 0x87, 0xb8, 0x80, 0xbb, 0x9a, 0xdf, 0x93, 0xd8, 0xcf, 0x76, 0x1b, 0xa6, 0xd3, 0xbf, 0x6a, + 0xfe, 0x9e, 0x8e, 0x87, 0xdb, 0xa1, 0xd0, 0x4a, 0x78, 0x23, 0xa1, 0xbc, 0x81, 0xc4, 0x0f, 0x0f, 0xf0, 0x26, 0xbf, 0xbc, 0x2e, 0x39, 0x1c, 0xf9, 0x77, 0xa1, 0xaa, 0x23, 0x28, 0x19, 0xf2, 0x07, + 0xfc, 0xe8, 0xe9, 0x34, 0x11, 0x78, 0x44, 0xc3, 0xc6, 0xfc, 0x03, 0x6f, 0xde, 0xdc, 0x97, 0x66, 0x44, 0x4c, 0x31, 0x83, 0x05, 0x2d, 0xca, 0xbe, 0x3f, 0xb7, 0xe5, 0xe1, 0x27, 0xfc, 0xa7, 0x20, + 0x05, 0x53, 0x60, 0x50, 0xe6, 0xf4, 0x7f, 0xe6, 0x16, 0x85, 0x4b, 0x19, 0x88, 0x11, 0xc3, 0x82, 0xfa, 0x64, 0xe4, 0xb0, 0xde, 0x72, 0xc1, 0xb6, 0xd3, 0x6c, 0xd9, 0x3e, 0x67, 0x70, 0x9b, 0xa3, + 0xe3, 0xac, 0x55, 0x2d, 0x69, 0x12, 0x9f, 0x61, 0x5c, 0x4c, 0x9f, 0x39, 0x37, 0x9e, 0x91, 0x79, 0x39, 0x87, 0x66, 0x06, 0xdb, 0xe9, 0x3a, 0x4d, 0x54, 0x99, 0x23, 0x56, 0xc2, 0x9a, 0x20, 0x7f, + 0x48, 0x86, 0x45, 0x40, 0x5b, 0x9a, 0x68, 0xd6, 0x19, 0xca, 0x28, 0x20, 0x40, 0x1e, 0x9b, 0xb4, 0xbb, 0x3f, 0xaf, 0x93, 0xba, 0x30, 0x65, 0x83, 0x13, 0x77, 0x45, 0x22, 0x1e, 0xba, 0x63, 0x50, + 0x79, 0xd9, 0x04, 0x8e, 0xbe, 0x81, 0x40, 0x8a, 0x60, 0xf9, 0xe0, 0x1a, 0x23, 0xe6, 0xb0, 0x7c, 0xf7, 0xab, 0xf5, 0xbe, 0x62, 0x72, 0xb8, 0x79, 0x0a, 0x36, 0xf3, 0x34, 0xc8, 0x57, 0x5c, 0x96, + 0x55, 0xff, 0xdd, 0xb3, 0xab, 0x93, 0x10, 0x53, 0xde, 0x05, 0x00, 0xf2, 0x67, 0x1f, 0x47, 0x0e, 0x53, 0x87, 0x28, 0xe5, 0x6c, 0x07, 0x40, 0x60, 0xc9, 0x7c, 0x07, 0xbd, 0x0b, 0x23, 0x26, 0xa1, + 0x72, 0x15, 0x8b, 0x40, 0xed, 0x1f, 0xc1, 0x69, 0x2c, 0x34, 0x03, 0x33, 0xd1, 0x93, 0x8b, 0x15, 0xdb, 0x99, 0x81, 0x26, 0x8f, 0xfd, 0x73, 0x6d, 0x64, 0x81, 0xe2, 0xa3, 0x5f, 0xd1, 0xe2, 0x40, + 0xa4, 0x24, 0xea, 0x1b, 0x03, 0x69, 0x30, 0x35, 0xcf, 0xd8, 0x52, 0x0e, 0x5f, 0x7f, 0x7a, 0x3d, 0xd2, 0x27, 0x8a, 0x59, 0x82, 0xa4, 0x12, 0x9e, 0xf7, 0x8c, 0x98, 0xb8, 0x25, 0x38, 0x15, 0x35, + 0xe0, 0xdc, 0x67, 0x86, 0x44, 0xf8, 0x27, 0x21, 0x30, 0x7f, 0xad, 0x8f, 0xb8, 0x9f, 0xc6, 0x47, 0xb7, 0x9c, 0x4f, 0x07, 0x06, 0x4a, 0xf2, 0x1a, 0xde, 0x98, 0xef, 0x36, 0xc7, 0x1d, 0x0d, 0x58, + 0x0a, 0xcb, 0x1f, 0x18, 0x5c, 0x01, 0x23, 0xed, 0xdd, 0xd4, 0x8d, 0xb8, 0xda, 0x03, 0xca, 0x61, 0x56, 0x29, 0xce, 0x82, 0xde, 0x16, 0x3c, 0xf8, 0x9c, 0x72, 0xd3, 0x43, 0x27, 0x8d, 0x72, 0xde, + 0x3d, 0xac, 0xdd, 0x2b, 0xb6, 0xf7, 0xbb, 0xad, 0x8f, 0xf2, 0x19, 0x71, 0xd6, 0xd9, 0xc1, 0x4e, 0x34, 0xc5, 0x35, 0x1c, 0x93, 0x97, 0xd2, 0xc7, 0xcd, 0x5e, 0x0a, 0x07, 0xd0, 0xf6, 0x2a, 0x5f, + 0x33, 0xe0, 0x53, 0xf0, 0x19, 0x94, 0x82, 0x35, 0x46, 0xd6, 0x0e, 0x40, 0x39, 0x59, 0xec, 0x97, 0xca, 0x1d, 0x90, 0x8c, 0x6e, 0xc6, 0x39, 0x39, 0xea, 0xde, 0x6f, 0x79, 0xf4, 0x23, 0xdf, 0x0a, + 0x02, 0x3a, 0x3b, 0x8a, 0x5d, 0x83, 0x3d, 0x88, 0xa0, 0xb0, 0x36, 0x01, 0xfb, 0x15, 0xd0, 0x03, 0xd7, 0x59, 0x8f, 0x5c, 0xf5, 0x04, 0xd4, 0x30, 0xeb, 0x9c, 0xdc, 0x3f, 0xc1, 0x93, 0xb9, 0x24, + 0x70, 0x7e, 0xd7, 0x08, 0x67, 0xe3, 0x72, 0x59, 0x6b, 0xbb, 0x1b, 0x2d, 0x90, 0x26, 0xd6, 0xf7, 0xd4, 0x76, 0xbe, 0xbb, 0xeb, 0x7f, 0x7f, 0x2a, 0x3d, 0x8f, 0xd7, 0x44, 0xc6, 0xa0, 0xcf, 0xd0, + 0xcb, 0x66, 0xe1, 0x09, 0xae, 0xc7, 0xc4, 0x58, 0x9a, 0x90, 0xa8, 0xfc, 0x0c, 0x1a, 0x07, 0xb3, 0x87, 0xa7, 0xa9, 0xa4, 0x75, 0x77, 0x57, 0x48, 0xc4, 0x30, 0x04, 0x0e, 0x33, 0x74, 0xad, 0x6b, + 0x54, 0xbd, 0x32, 0x52, 0x39, 0xa0, 0xa3, 0x99, 0xd7, 0x32, 0x80, 0x24, 0x0b, 0x64, 0xbd, 0x60, 0x28, 0xa1, 0x58, 0xa1, 0x58, 0x19, 0xeb, 0xbe, 0x05, 0x3d, 0xf3, 0x26, 0x2d, 0xaf, 0xe5, 0xb5, + 0x3d, 0x3f, 0x34, 0x8b, 0xd8, 0xf1, 0x7d, 0x43, 0xa8, 0xed, 0xd3, 0xa5, 0x44, 0xe4, 0xb7, 0x49, 0x39, 0x8d, 0x5f, 0x5b, 0x58, 0x4d, 0x0e, 0xbb, 0x12, 0x57, 0x5a, 0x4d, 0x7c, 0x11, 0x6f, 0x90, + 0x70, 0x47, 0xf7, 0xda, 0x45, 0x72, 0x06, 0x04, 0x5f, 0x45, 0xaa, 0x5f, 0xa5, 0xad, 0x09, 0x05, 0x24, 0x74, 0x01, 0x2d, 0x2f, 0x05, 0x5c, 0x3d, 0x72, 0x0d, 0x1b, 0xad, 0xa8, 0xce, 0x32, 0x76, + 0x6d, 0xe3, 0xee, 0xf2, 0x84, 0x0a, 0x5b, 0x27, 0xe9, 0x3b, 0x67, 0xca, 0xec, 0xed, 0x3c, 0xa6, 0xc8, 0x25, 0x28, 0x0d, 0x0f, 0xde, 0x80, 0xd2, 0x95, 0x46, 0x4d, 0xb3, 0xa6, 0x21, 0xcc, 0x1f, + 0x4f, 0xc7, 0x49, 0x55, 0x57, 0x65, 0x9c, 0x4b, 0xa7, 0x5a, 0x4c, 0xf3, 0x94, 0x43, 0xe0, 0xc3, 0x75, 0x25, 0x34, 0x25, 0x5e, 0xec, 0x5e, 0xa2, 0x49, 0x2c, 0x7c, 0x7c, 0x3b, 0x1b, 0x54, 0x96, + 0xee, 0x29, 0xfa, 0x6a, 0xf7, 0xfb, 0x56, 0x90, 0x02, 0x07, 0x8a, 0xa1, 0x5b, 0xda, 0xa7, 0xe7, 0x4b, 0x2d, 0xc1, 0x3c, 0xa3, 0xc4, 0x74, 0xf6, 0x98, 0x15, 0x5c, 0x36, 0xe4, 0x75, 0xba, 0xb7, + 0x3b, 0x88, 0xaa, 0x2f, 0x1a, 0x8c, 0xfd, 0x04, 0xd1, 0xf0, 0xe8, 0xad, 0x06, 0x59, 0xce, 0xa8, 0x45, 0xfc, 0x58, 0xc2, 0x9b, 0xa6, 0x03, 0x9b, 0x35, 0x1b, 0x8d, 0xb4, 0x3e, 0x27, 0x29, 0x1b, + 0x2e, 0x4c, 0x38, 0xe3, 0xd2, 0x6a, 0x65, 0xff, 0x15, 0x5b, 0xcc, 0x77, 0x72, 0x69, 0xb5, 0x50, 0x78, 0xcf, 0x1f, 0x21, 0x74, 0xde, 0x63, 0x78, 0xd4, 0x02, 0x01, 0xaf, 0x03, 0x32, 0xdf, 0x54, + 0x40, 0x54, 0xb0, 0x89, 0x39, 0xef, 0x41, 0x05, 0x0d, 0xa9, 0x83, 0x8a, 0xe6, 0x70, 0xf8, 0xe4, 0x5d, 0x57, 0x5a, 0xc3, 0x8e, 0x1e, 0x78, 0x8e, 0xb6, 0x51, 0x7b, 0xd6, 0xf0, 0xb2, 0x88, 0x76, + 0xaf, 0xea, 0x6f, 0x3d, 0x79, 0xd9, 0x60, 0x22, 0x73, 0xc8, 0xe6, 0x5d, 0x16, 0xec, 0x1e, 0x68, 0x29, 0x38, 0x3d, 0xcb, 0xac, 0x08, 0x52, 0x3a, 0xba, 0x52, 0x63, 0x00, 0xd0, 0x35, 0x7e, 0x5d, + 0xd5, 0x12, 0x88, 0x25, 0x19, 0x7e, 0x24, 0x77, 0x39, 0xcc, 0x04, 0x3d, 0xff, 0xec, 0x43, 0xa8, 0x91, 0xf8, 0xf9, 0x40, 0x7f, 0x22, 0xd0, 0x40, 0x35, 0xad, 0x7a, 0xb0, 0x08, 0x9f, 0xe0, 0x5e, + 0xbd, 0x1a, 0x30, 0xf6, 0x4a, 0x33, 0x1e, 0x03, 0xff, 0x45, 0xdd, 0x17, 0x2d, 0x4f, 0xb1, 0xfb, 0xbd, 0xdb, 0xb9, 0x21, 0x30, 0xad, 0xdb, 0x13, 0x78, 0x05, 0xeb, 0x39, 0xbb, 0x17, 0x18, 0x3d, + 0x8b, 0x7e, 0x33, 0x37, 0x07, 0xe6, 0xd9, 0xac, 0x6b, 0xd1, 0x06, 0xb8, 0x99, 0xb8, 0x94, 0x28, 0xe0, 0xab, 0x01, 0x47, 0x0f, 0xa2, 0x07, 0x7f, 0x6e, 0x37, 0x32, 0x1c, 0x0a, 0x35, 0x48, 0x8f, + 0x40, 0x73, 0x6e, 0xc6, 0x67, 0xe0, 0x5e, 0xb0, 0xbc, 0x1f, 0xd1, 0x55, 0x0b, 0x87, 0x15, 0x0e, 0x25, 0xbe, 0xf4, 0xfc, 0xd1, 0x15, 0xdf, 0xcb, 0x0f, 0x43, 0x55, 0xb8, 0x7d, 0x6b, 0xf3, 0xda, + 0x5c, 0xe5, 0x3d, 0xd0, 0x9d, 0xfb, 0x41, 0x74, 0xed, 0xd0, 0xd7, 0xba, 0xd3, 0xac, 0xd9, 0xd3, 0xa7, 0x22, 0x2d, 0xce, 0x92, 0xf9, 0xf8, 0xf9, 0x6a, 0xa4, 0x2a, 0x6a, 0x4c, 0x51, 0x2c, 0xe8, + 0xea, 0xcc, 0xa6, 0xe0, 0x05, 0x9d, 0x43, 0xd5, 0xba, 0xf6, 0x84, 0x64, 0x1a, 0xf6, 0x61, 0x8a, 0x5e, 0xad, 0xfd, 0x9a, 0x58, 0x9b, 0xc4, 0xc6, 0x42, 0xa7, 0x01, 0x9d, 0x57, 0x6e, 0x3f, 0xf7, + 0x45, 0xb8, 0xd4, 0xf5, 0xba, 0xf7, 0x85, 0x08, 0xa1, 0x43, 0xd1, 0xe7, 0x21, 0x43, 0xb9, 0xaf, 0x6c, 0xdd, 0x9d, 0x15, 0x66, 0xb9, 0xf2, 0xa5, 0x76, 0x04, 0xc7, 0x5b, 0x41, 0x81, 0xc7, 0x17, + 0x99, 0xe4, 0x40, 0x01, 0x77, 0x60, 0x55, 0x97, 0x46, 0x2a, 0x7e, 0xc9, 0xe1, 0xfd, 0xe6, 0xdc, 0xe4, 0x8d, 0x07, 0x13, 0x06, 0x12, 0x19, 0xd3, 0x40, 0x56, 0x94, 0x81, 0xad, 0xf2, 0x82, 0x47, + 0xcc, 0xfc, 0xc6, 0x13, 0x5b, 0xc3, 0x43, 0x4f, 0x84, 0xc8, 0x71, 0x40, 0x5a, 0x71, 0xb8, 0xaa, 0xc8, 0x98, 0x85, 0xb9, 0x28, 0x14, 0x1b, 0xea, 0xb6, 0x6b, 0x52, 0x6e, 0xd1, 0x86, 0x25, 0x71, + 0x5e, 0x6c, 0xf4, 0x3a, 0x4b, 0x87, 0x65, 0x0d, 0x6a, 0xf2, 0x01, 0x85, 0x6c, 0xdd, 0xc5, 0x9c, 0x3d, 0xef, 0x17, 0x03, 0xa0, 0x7c, 0x59, 0xe0, 0x92, 0x27, 0x58, 0x68, 0x8e, 0x09, 0x57, 0x4e, + 0xfe, 0x48, 0x7f, 0x0a, 0xf3, 0x6a, 0x61, 0x94, 0x4e, 0xec, 0x5f, 0xca, 0xec, 0x65, 0x56, 0x8b, 0x54, 0xcd, 0x0f, 0x61, 0xb8, 0xb5, 0x2a, 0x7f, 0xee, 0x5a, 0xee, 0x56, 0x4d, 0x86, 0x6d, 0xe6, + 0x74, 0x7c, 0x97, 0x03, 0x36, 0x6b, 0xe2, 0x55, 0x13, 0x08, 0xd7, 0xf9, 0xb3, 0x1d, 0x76, 0x3f, 0x15, 0x83, 0x4c, 0xc5, 0xd5, 0x18, 0x45, 0x10, 0xb6, 0xbf, 0x0d, 0xc3, 0x88, 0xde, 0x59, 0xe7, + 0xed, 0xec, 0xae, 0x06, 0x6a, 0xe3, 0x9b, 0xf2, 0x57, 0xc7, 0x8f, 0x8b, 0xf2, 0xf2, 0xd4, 0xd8, 0x45, 0x8f, 0xce, 0x3b, 0x9e, 0xcb, 0x1f, 0x43, 0x0e, 0x6b, 0x88, 0xff, 0x01, 0x43, 0xe6, 0x06, + 0x0d, 0x00, 0xd1, 0x6f, 0xa7, 0xdd, 0x3d, 0xdb, 0xfa, 0x5a, 0x14, 0x2a, 0xd5, 0xe2, 0xe5, 0x7b, 0xeb, 0x54, 0x2d, 0x8d, 0x7b, 0x1b, 0x56, 0xaf, 0xd2, 0x33, 0xba, 0xbc, 0x00, 0xf2, 0x8a, 0xaf, + 0x26, 0x58, 0x53, 0x25, 0x8f, 0xb1, 0x54, 0xda, 0xed, 0x48, 0xd0, 0xa3, 0x8a, 0x25, 0x2c, 0x4e, 0x58, 0xda, 0xba, 0xd4, 0x09, 0x09, 0x97, 0xdd, 0xcf, 0xf3, 0x27, 0xe7, 0x73, 0x15, 0xa7, 0x51, + 0x7b, 0x57, 0xaa, 0x87, 0xe8, 0x45, 0x0e, 0x39, 0x45, 0xc0, 0xc6, 0x3a, 0xe5, 0x86, 0x7e, 0xf7, 0x41, 0x88, 0xc6, 0xf8, 0x4c, 0xde, 0x5b, 0xd0, 0xa9, 0xcb, 0xe8, 0x42, 0xac, 0xd2, 0x2d, 0x5b, + 0x9c, 0x36, 0x13, 0xfc, 0xe2, 0xa3, 0xbe, 0xd5, 0xb5, 0xf9, 0x92, 0xc7, 0x69, 0x84, 0x36, 0xa0, 0x4c, 0x18, 0x61, 0xcb, 0x4e, 0x61, 0xc9, 0xb9, 0x4e, 0xbe, 0x8f, 0x13, 0xf2, 0x2f, 0x3a, 0xf4, + 0x18, 0xde, 0xab, 0xdd, 0x8d, 0x97, 0xa0, 0xc5, 0xcb, 0xcc, 0x8f, 0xcd, 0x96, 0x19, 0x57, 0x48, 0x66, 0xbc, 0xb8, 0x92, 0xa4, 0x4b, 0xc7, 0x01, 0xe5, 0x32, 0x5d, 0xee, 0x3a, 0x41, 0x4f, 0x19, + 0x4f, 0x1b, 0x54, 0xfd, 0x30, 0xcb, 0xeb, 0x63, 0x97, 0x52, 0x3a, 0xef, 0xc6, 0x0d, 0x90, 0xa9, 0xaa, 0x49, 0x64, 0xd6, 0x1e, 0x60, 0x9d, 0x78, 0xd3, 0x7a, 0xbe, 0x86, 0x6a, 0x93, 0x45, 0xd0, + 0xa4, 0xe7, 0xd5, 0x98, 0x2d, 0xe6, 0x56, 0xa8, 0x3f, 0xc8, 0x0d, 0x00, 0x0b, 0xd9, 0x20, 0x04, 0x82, 0xb1, 0x2f, 0xc9, 0xcc, 0x03, 0x9f, 0x2c, 0x0a, 0xc2, 0x95, 0x3a, 0x18, 0x2b, 0x1d, 0xf6, + 0x25, 0x12, 0x86, 0x79, 0x85, 0x25, 0xe8, 0x15, 0xdf, 0x66, 0x20, 0xff, 0x11, 0xeb, 0x45, 0xfb, 0xd1, 0x7c, 0x26, 0x38, 0x74, 0x26, 0x60, 0xd2, 0x13, 0x36, 0xdb, 0xb1, 0x49, 0x58, 0x97, 0xf0, + 0x44, 0x78, 0x45, 0xc4, 0x6a, 0x42, 0xc3, 0x62, 0xf6, 0x8c, 0x5e, 0x70, 0x2d, 0xdd, 0x03, 0x60, 0xdc, 0xe9, 0xc6, 0x29, 0xe4, 0x18, 0x38, 0x67, 0x13, 0x09, 0xf1, 0x51, 0x22, 0x75, 0x07, 0xe2, + 0xfd, 0x8c, 0xb4, 0x39, 0x03, 0x4d, 0x54, 0xa6, 0xdd, 0x10, 0xb3, 0xc2, 0xcd, 0x48, 0x9b, 0x7e, 0x6b, 0x98, 0xdb, 0xd9, 0xb5, 0xfd, 0xe2, 0x2f, 0xd1, 0xa5, 0x5a, 0x9e, 0xe8, 0x71, 0xc9, 0xbb, + 0x29, 0xd3, 0x91, 0x6e, 0x3a, 0x68, 0x7f, 0x31, 0xab, 0xa3, 0xf6, 0xa2, 0xde, 0x4f, 0xc5, 0x55, 0x5d, 0x06, 0xa6, 0xdc, 0x0c, 0xfd, 0x2d, 0xf7, 0xa1, 0xb0, 0xdf, 0x21, 0x50, 0xd3, 0xad, 0xf0, + 0xea, 0x9b, 0x17, 0x4c, 0x1d, 0x19, 0x56, 0x12, 0x08, 0xce, 0x75, 0x2f, 0x07, 0x3f, 0x66, 0x1b, 0x9e, 0xa9, 0x63, 0x40, 0xdf, 0x99, 0xd5, 0x57, 0x5a, 0x42, 0xe7, 0x1f, 0xc1, 0xbd, 0x09, 0x73, + 0xc2, 0x5f, 0xbb, 0xa6, 0xa7, 0x86, 0xc9, 0x0f, 0x17, 0x8d, 0x26, 0xe3, 0xdd, 0xbe, 0xfc, 0x7f, 0xbd, 0x13, 0xf1, 0x5d, 0x3b, 0x00, 0x59, 0x0e, 0x37, 0xa3, 0x84, 0x69, 0x79, 0xc7, 0x56, 0x7f, + 0xdc, 0xa4, 0xef, 0xb9, 0x4d, 0x59, 0xf7, 0x3c, 0x19, 0x56, 0x19, 0x1f, 0x63, 0x92, 0x2d, 0x8f, 0xe2, 0xa4, 0xff, 0xb2, 0x4c, 0xfa, 0x6a, 0x32, 0xb4, 0x4e, 0x74, 0xd6, 0x9b, 0x2d, 0xe1, 0x6e, + 0xe5, 0x7b, 0xf8, 0xad, 0xf7, 0xf4, 0x42, 0x62, 0x58, 0xec, 0x7c, 0x18, 0x82, 0x68, 0x09, 0x88, 0xea, 0x41, 0xa5, 0xec, 0x42, 0x54, 0xcd, 0xe0, 0xfe, 0x3b, 0x3b, 0x98, 0x8f, 0xb7, 0x1d, 0x4f, + 0x21, 0xd6, 0x3c, 0x09, 0xf3, 0xa8, 0xdc, 0x8d, 0x8f, 0x52, 0xa6, 0x96, 0x84, 0xac, 0xce, 0xd2, 0x0b, 0xb1, 0xbc, 0xf3, 0x69, 0x72, 0xa4, 0xfc, 0xdc, 0xfa, 0xf4, 0x54, 0xd2, 0x69, 0x08, 0xe2, + 0xc3, 0x72, 0x99, 0x3c, 0xa3, 0x4c, 0x8d, 0x3b, 0xca, 0x60, 0x85, 0x28, 0x02, 0x19, 0x47, 0x97, 0x71, 0x9c, 0x07, 0xd9, 0x64, 0x89, 0xff, 0x06, 0xa3, 0xd2, 0x2a, 0x2a, 0x93, 0x8e, 0x53, 0xd8, + 0xd7, 0x44, 0x0e, 0xaa, 0x9f, 0xf4, 0x5b, 0x78, 0xa3, 0x7e, 0x86, 0x6a, 0xf9, 0x09, 0x1e, 0xd4, 0xa4, 0xd0, 0x28, 0xc2, 0xf8, 0x7c, 0x95, 0xd2, 0xd8, 0x25, 0x27, 0x76, 0x0b, 0x56, 0x42, 0x4d, + 0x34, 0x30, 0xf4, 0x3c, 0x61, 0x31, 0x78, 0xbb, 0xed, 0x7d, 0x0a, 0x0d, 0x77, 0x6d, 0x6b, 0x48, 0x0d, 0xa6, 0x02, 0x97, 0xb3, 0xd4, 0x33, 0xbc, 0x0b, 0xd2, 0x84, 0xe2, 0x2f, 0x90, 0x63, 0x7c, + 0x03, 0x74, 0x50, 0x31, 0x1e, 0x50, 0x73, 0xd2, 0x85, 0x9b, 0x3f, 0xc2, 0xf8, 0xc2, 0xd5, 0x77, 0x37, 0x83, 0x04, 0xee, 0x3f, 0xb1, 0x81, 0x45, 0x37, 0x27, 0xda, 0x87, 0x5a, 0x5c, 0xef, 0x1a, + 0x36, 0xde, 0xb9, 0x79, 0x74, 0x29, 0x44, 0x0d, 0xe5, 0x92, 0x5e, 0x3d, 0xe3, 0x4f, 0x32, 0xf4, 0xf4, 0x04, 0x88, 0x80, 0x77, 0x96, 0x68, 0xb1, 0xc1, 0xda, 0x24, 0xe1, 0xa7, 0x76, 0x34, 0xb0, + 0xfa, 0x91, 0x18, 0x42, 0x86, 0xd9, 0xd2, 0x73, 0x1e, 0x79, 0x05, 0xed, 0xa0, 0x49, 0x0b, 0xf8, 0x2f, 0x12, 0xd7, 0x10, 0xfc, 0xee, 0x43, 0x0f, 0x29, 0x67, 0x85, 0x57, 0x5b, 0xfa, 0x82, 0xca, + 0xe6, 0xc6, 0xd9, 0x39, 0x46, 0xc6, 0x87, 0x1a, 0x3c, 0x12, 0x0e, 0xa6, 0x01, 0x49, 0x1c, 0x22, 0x3c, 0xf6, 0x67, 0x5e, 0x8c, 0xec, 0xee, 0x07, 0x20, 0x26, 0x30, 0x23, 0x51, 0x56, 0x92, 0x24, + 0x0b, 0x9c, 0x9c, 0xe3, 0x3b, 0x07, 0x26, 0x95, 0xfc, 0x12, 0x3f, 0x56, 0xf5, 0xf1, 0x6c, 0xfd, 0x1e, 0x49, 0x77, 0xbe, 0x60, 0x24, 0x0f, 0xaf, 0x70, 0xe8, 0x9d, 0x4d, 0xb5, 0x81, 0x70, 0xc8, + 0x06, 0x7d, 0x43, 0x83, 0x66, 0xef, 0x1d, 0xd8, 0x22, 0xf1, 0x05, 0xb4, 0xac, 0xeb, 0x57, 0xd8, 0x72, 0xbd, 0xf1, 0x79, 0xf2, 0x42, 0x0b, 0xd4, 0x80, 0x93, 0xb3, 0x2a, 0x88, 0x02, 0x40, 0x79, + 0x71, 0xa6, 0xd8, 0x9b, 0x8c, 0x55, 0x23, 0x85, 0xb6, 0x1f, 0x24, 0x69, 0x7e, 0x8f, 0xe1, 0x29, 0x97, 0xe7, 0x22, 0xc5, 0xbf, 0xb8, 0xc5, 0xb2, 0x29, 0xed, 0x44, 0xa6, 0x83, 0xe5, 0x71, 0x72, + 0x76, 0x42, 0x1d, 0xab, 0xd3, 0x89, 0x08, 0x62, 0x0d, 0xb5, 0x8a, 0x20, 0x3c, 0xeb, 0x61, 0x4e, 0x59, 0xe0, 0xdc, 0x93, 0x08, 0x6d, 0x06, 0xcc, 0x5d, 0x8a, 0x59, 0xf1, 0x1e, 0xea, 0x96, 0x1e, + 0x71, 0x1c, 0xc7, 0x4f, 0x04, 0x26, 0x48, 0x2e, 0x8a, 0x44, 0x18, 0x84, 0x9f, 0x81, 0xd8, 0xf1, 0x28, 0x3c, 0xf9, 0x3a, 0x11, 0x4b, 0xbc, 0xe0, 0xf7, 0x1c, 0xe3, 0x6c, 0xa0, 0x3d, 0xfb, 0x9a, + 0x8c, 0x0c, 0x5c, 0x8e, 0xa3, 0xc4, 0x81, 0xfc, 0x5b, 0x6a, 0x66, 0x62, 0x03, 0x51, 0x0c, 0x09, 0xeb, 0x15, 0x99, 0xdd, 0x4b, 0xa4, 0x16, 0x7c, 0x37, 0x40, 0x4a, 0xc7, 0x21, 0x1a, 0x2d, 0x8e, + 0xaa, 0xe1, 0x67, 0x7e, 0x9f, 0x0b, 0xe7, 0xc7, 0x64, 0x5d, 0xbb, 0xb8, 0x3d, 0x7f, 0x4a, 0x60, 0x8f, 0x82, 0xe7, 0x17, 0x2e, 0xc3, 0x3b, 0x9f, 0xe8, 0x5c, 0xb8, 0xff, 0x91, 0x7b, 0xb3, 0xe6, + 0xce, 0xfa, 0x5e, 0x45, 0xdc, 0xc6, 0x76, 0x32, 0xb2, 0x1d, 0x18, 0x86, 0x26, 0x83, 0xee, 0x8f, 0x75, 0xf5, 0x90, 0xcf, 0x40, 0x5c, 0xeb, 0x16, 0xfb, 0xf0, 0xad, 0x5e, 0x03, 0x99, 0x11, 0x16, + 0xb8, 0xdd, 0xf5, 0xdf, 0x85, 0x4a, 0x46, 0x44, 0xf9, 0xd8, 0x3c, 0x1b, 0xd3, 0xfb, 0xee, 0x9f, 0xcb, 0x3c, 0x80, 0xcc, 0xf3, 0xfa, 0x4f, 0xfb, 0xd4, 0x29, 0xc3, 0x6a, 0xca, 0x26, 0x1d, 0x81, + 0x74, 0x28, 0x31, 0x39, 0xfb, 0x32, 0x85, 0x6c, 0x09, 0xae, 0xac, 0x61, 0x1d, 0xd8, 0x3d, 0x48, 0x25, 0xc5, 0x48, 0xe4, 0xc2, 0x0a, 0x0a, 0x31, 0xb9, 0xd6, 0xfc, 0xa6, 0x58, 0xd2, 0x3f, 0x82, + 0x53, 0xad, 0xe6, 0x0d, 0xa4, 0xb1, 0xe1, 0x86, 0x91, 0x45, 0x64, 0xec, 0xf6, 0x3e, 0x51, 0x11, 0xc9, 0xe4, 0x9e, 0x0f, 0x66, 0x56, 0x44, 0xcc, 0xda, 0x1b, 0x02, 0x29, 0x08, 0x99, 0xe2, 0xeb, + 0x45, 0x07, 0x3d, 0x6b, 0xea, 0xaf, 0x2e, 0x57, 0x0e, 0x6b, 0xcf, 0x0a, 0xef, 0x09, 0x8d, 0xe1, 0x73, 0xdf, 0x72, 0x13, 0xd5, 0x8b, 0x1a, 0x70, 0x81, 0x10, 0xa6, 0x79, 0xeb, 0x5c, 0x30, 0xea, + 0x9d, 0x45, 0x78, 0x13, 0x15, 0x45, 0x46, 0xec, 0x7f, 0x1f, 0xfe, 0x01, 0xb7, 0x13, 0x91, 0x06, 0x1e, 0xbf, 0x02, 0x3a, 0xde, 0x33, 0xe8, 0x48, 0x2a, 0x99, 0x92, 0x05, 0x79, 0x9f, 0xb0, 0x2d, + 0xd7, 0x6f, 0xee, 0x75, 0x26, 0x01, 0xb7, 0x23, 0xa1, 0x3f, 0x28, 0x31, 0x47, 0xe2, 0x97, 0x45, 0xa6, 0x1d, 0x9b, 0x21, 0x50, 0x0a, 0xc1, 0x85, 0xd5, 0x65, 0xda, 0x64, 0xd6, 0xfb, 0xf8, 0x33, + 0x90, 0x53, 0x48, 0x29, 0x37, 0x88, 0xd1, 0xb5, 0x9d, 0x0a, 0x43, 0xbe, 0x84, 0x8d, 0x12, 0x95, 0xa6, 0x18, 0x5b, 0xa8, 0x6f, 0x1c, 0xa7, 0x00, 0x07, 0x92, 0x8e, 0x49, 0x24, 0x8a, 0x84, 0xbf, + 0x9b, 0xaa, 0xd3, 0x8d, 0xfb, 0x1c, 0x71, 0x6e, 0xa8, 0x25, 0x0e, 0x89, 0x7c, 0xf4, 0x66, 0x60, 0x84, 0x44, 0x59, 0x43, 0x1f, 0x26, 0x2b, 0x08, 0x8a, 0xa2, 0xbc, 0xcb, 0xae, 0x6d, 0xdf, 0x5d, + 0xfa, 0xb5, 0x94, 0x8b, 0x57, 0x0c, 0x1f, 0x5d, 0xae, 0x3e, 0x94, 0x46, 0xd9, 0x8b, 0xef, 0x52, 0xdb, 0xd2, 0xb6, 0x77, 0x34, 0x8e, 0x1a, 0x22, 0x02, 0x88, 0x8a, 0x4c, 0x3a, 0x4d, 0x40, 0x76, + 0xd4, 0x6d, 0x2c, 0xba, 0x58, 0x68, 0xe4, 0xde, 0x5e, 0xc0, 0xfd, 0xee, 0x3c, 0x80, 0x15, 0x20, 0x90, 0xb4, 0x38, 0x54, 0x32, 0xb3, 0xde, 0xc1, 0x59, 0xa2, 0xe4, 0xa1, 0x0e, 0xe9, 0x44, 0xff, + 0x2c, 0x3d, 0x97, 0x7f, 0x5d, 0xd0, 0x13, 0xbd, 0x2c, 0x21, 0xd7, 0xc1, 0xd6, 0x5e, 0x53, 0xf2, 0xc5, 0x51, 0x8f, 0xca, 0x48, 0x9f, 0xf6, 0xdb, 0x12, 0x85, 0xda, 0x3d, 0x96, 0x41, 0x38, 0xa8, + 0x22, 0x6b, 0x4f, 0x52, 0x51, 0x84, 0xa2, 0x30, 0xb5, 0x2e, 0x33, 0xad, 0xa4, 0xfb, 0x45, 0x92, 0x54, 0x94, 0x6c, 0x1d, 0xe5, 0xd9, 0x62, 0x26, 0x02, 0x94, 0x57, 0x25, 0x17, 0xf4, 0xff, 0xdc, + 0xb0, 0xf1, 0xb9, 0xd9, 0x20, 0x92, 0x6e, 0x10, 0x37, 0x0d, 0xe7, 0xf4, 0xe8, 0x5b, 0xd8, 0xd7, 0x99, 0x3c, 0xe0, 0x74, 0x8b, 0x7a, 0x42, 0x45, 0x7c, 0x14, 0xb6, 0x56, 0x76, 0x65, 0x87, 0x6e, + 0x89, 0x8a, 0x53, 0x50, 0xa9, 0xbf, 0x03, 0x88, 0x25, 0xcd, 0x8a, 0xb8, 0x59, 0xdd, 0xfa, 0xf0, 0xef, 0x99, 0x20, 0x1a, 0x88, 0xf9, 0x11, 0xd8, 0x83, 0x4d, 0xa8, 0x05, 0xff, 0xe3, 0x2f, 0xde, + 0x2f, 0xa1, 0x2c, 0x8c, 0x5a, 0x2c, 0x96, 0x34, 0x98, 0x31, 0xe3, 0x8a, 0xf9, 0xe8, 0xb0, 0x72, 0xd5, 0xab, 0xe0, 0x53, 0xc7, 0x62, 0x47, 0x57, 0x46, 0x5b, 0x1b, 0x40, 0x3e, 0xff, 0x91, 0x4e, + 0x80, 0xd8, 0xe3, 0x10, 0xb9, 0x58, 0xa0, 0xc6, 0xf8, 0x1e, 0x73, 0x62, 0x9f, 0x8b, 0x1d, 0x91, 0xdc, 0xc7, 0x20, 0x67, 0x41, 0x63, 0x69, 0x15, 0x43, 0xc1, 0x29, 0xda, 0x98, 0xab, 0xcc, 0xf8, + 0x06, 0x8b, 0xec, 0xd4, 0x14, 0x79, 0xf8, 0x17, 0x12, 0x94, 0x3c, 0xda, 0xe3, 0x4f, 0x22, 0xba, 0x08, 0x38, 0x67, 0x38, 0xb3, 0xbc, 0xc3, 0x9f, 0x36, 0x30, 0x38, 0x50, 0x29, 0x6b, 0x3e, 0x81, + 0x95, 0xb6, 0x57, 0x01, 0xdc, 0x66, 0x1b, 0xc2, 0xb3, 0xea, 0xde, 0x99, 0x5d, 0x11, 0x8b, 0x68, 0x65, 0xd9, 0xd4, 0x1e, 0x77, 0xf9, 0xa1, 0x2d, 0x81, 0xb6, 0x8e, 0x9a, 0xf3, 0xad, 0x88, 0xe7, + 0x54, 0xa3, 0x84, 0x0a, 0x79, 0x3c, 0x20, 0x64, 0x58, 0x66, 0xae, 0x4c, 0xe6, 0x57, 0xaa, 0xf4, 0xcb, 0x93, 0x3e, 0x75, 0xd2, 0xe6, 0x6b, 0xa2, 0xe0, 0xa5, 0xe3, 0xf4, 0x49, 0x02, 0x48, 0xe8, + 0xff, 0x39, 0xb5, 0xf9, 0x60, 0x2e, 0x2f, 0xf7, 0x04, 0x7d, 0xff, 0xa7, 0xb9, 0x0f, 0xaa, 0xfb, 0x7d, 0x58, 0xb0, 0x94, 0x54, 0x69, 0x74, 0x2c, 0x33, 0xdf, 0x23, 0x14, 0xbb, 0x48, 0x5d, 0x9a, + 0xa5, 0x44, 0xf9, 0x44, 0xd1, 0xd9, 0x75, 0x55, 0x82, 0x60, 0x71, 0x46, 0x6e, 0x52, 0x46, 0x2f, 0x94, 0x35, 0x60, 0xaa, 0x29, 0x63, 0xa7, 0xe8, 0x60, 0x6c, 0x65, 0x4a, 0x89, 0x60, 0x39, 0xaa, + 0xb6, 0xae, 0x14, 0xf9, 0x72, 0x9d, 0x81, 0xc1, 0x39, 0xe0, 0x8d, 0x07, 0x21, 0xaf, 0xa7, 0x09, 0xc2, 0xb7, 0x1f, 0xcd, 0x64, 0xc6, 0x8a, 0x7d, 0x76, 0x21, 0x76, 0x8b, 0x84, 0x72, 0xab, 0x16, + 0xd5, 0x2e, 0xa4, 0xc1, 0x34, 0x4f, 0xd8, 0x4d, 0x1b, 0x56, 0x8c, 0x71, 0x6b, 0xdd, 0xea, 0x6c, 0xc9, 0xcb, 0xdf, 0xd8, 0x97, 0x50, 0xcc, 0xca, 0x9e, 0x3f, 0xbe, 0xc1, 0x13, 0x4e, 0x92, 0x6c, + 0xa7, 0x3e, 0x11, 0xb6, 0x44, 0xc9, 0xee, 0x06, 0x16, 0xd8, 0xa8, 0x8b, 0x00, 0xf1, 0x19, 0xfe, 0xa6, 0x29, 0x69, 0x65, 0xb2, 0xb2, 0xff, 0x87, 0x66, 0x20, 0xe1, 0x14, 0x9d, 0x36, 0x95, 0xd1, + 0x95, 0x50, 0xbd, 0xce, 0xd8, 0x36, 0xc5, 0x82, 0xa3, 0x34, 0x60, 0x1e, 0x9c, 0xa7, 0x73, 0xe9, 0x98, 0xf7, 0xb0, 0x8f, 0x16, 0xcf, 0x10, 0xa5, 0xaf, 0x2d, 0x84, 0x33, 0x4f, 0xbb, 0xe0, 0x64, + 0xf0, 0x3f, 0x8f, 0x0e, 0xc1, 0x84, 0x65, 0x6b, 0x7d, 0xaf, 0x68, 0x96, 0x00, 0x63, 0x44, 0x76, 0xff, 0x7f, 0x77, 0x8b, 0xcc, 0xd0, 0xfe, 0xae, 0x3f, 0x17, 0xa0, 0x04, 0xf7, 0x22, 0x8b, 0x09, + 0x62, 0xaf, 0x34, 0x89, 0x48, 0xb1, 0x41, 0x3f, 0xed, 0xff, 0x75, 0x50, 0x3b, 0xd1, 0xcf, 0x0a, 0xeb, 0xfc, 0xd6, 0xdc, 0xa0, 0x8a, 0xe1, 0x38, 0xdc, 0x2f, 0x14, 0x70, 0x75, 0x4f, 0x02, 0x34, + 0x8f, 0x42, 0xca, 0xe3, 0x72, 0xb0, 0x23, 0x43, 0xdd, 0x6a, 0x20, 0x8c, 0x9a, 0x10, 0xd5, 0xbd, 0xce, 0xc9, 0xed, 0x11, 0x8e, 0x21, 0x48, 0x30, 0xd6, 0x69, 0x94, 0xca, 0xbd, 0x26, 0xc8, 0x0c, + 0xe3, 0x7f, 0xe0, 0xdd, 0x98, 0xd8, 0x18, 0x96, 0x22, 0xfb, 0xff, 0x05, 0x02, 0x20, 0xa0, 0xed, 0xd0, 0x1d, 0x54, 0xa1, 0xee, 0x2c, 0xcd, 0xfb, 0x16, 0x94, 0xf6, 0x47, 0xe1, 0xd0, 0x56, 0x71, + 0x6a, 0x6a, 0xb9, 0xa5, 0x4b, 0x74, 0x8b, 0xb4, 0x76, 0xff, 0x1b, 0x6c, 0x88, 0xf0, 0x9b, 0x3c, 0xe8, 0x61, 0xb7, 0x5a, 0x44, 0xfc, 0xdc, 0xc3, 0xf4, 0x84, 0x23, 0xac, 0x1e, 0x49, 0x43, 0xb0, + 0x0d, 0x60, 0x11, 0x34, 0xef, 0x3b, 0x42, 0x12, 0x1b, 0x95, 0xb9, 0x77, 0xce, 0xe2, 0x14, 0xcd, 0x49, 0xd9, 0x9b, 0xaa, 0x20, 0x60, 0xd2, 0xd6, 0x14, 0xe0, 0xc4, 0x54, 0x48, 0x35, 0x4f, 0xfa, + 0xcf, 0x96, 0xf4, 0x85, 0xde, 0x7a, 0x18, 0xd9, 0x0b, 0xd1, 0xad, 0xfa, 0x39, 0x3d, 0x95, 0x6f, 0x24, 0xd7, 0xe9, 0x93, 0x66, 0x03, 0xda, 0xe0, 0x2c, 0x5d, 0x0d, 0x8e, 0x42, 0xd0, 0x74, 0x8f, + 0xe3, 0x11, 0x7f, 0xe5, 0xee, 0xef, 0x40, 0x68, 0x3e, 0x86, 0x13, 0x75, 0x81, 0xff, 0x5b, 0x4d, 0x84, 0x06, 0x4e, 0x17, 0x97, 0x2c, 0xc5, 0x4d, 0x21, 0xf9, 0xe7, 0x04, 0xe2, 0x63, 0xa9, 0x26, + 0x10, 0xc3, 0x9f, 0x27, 0x8c, 0x18, 0xf2, 0xc1, 0x57, 0x33, 0x62, 0xf6, 0x4d, 0xde, 0xa6, 0x52, 0x08, 0x3d, 0x3a, 0x2a, 0x18, 0xf4, 0x35, 0x2e, 0x62, 0x7e, 0x34, 0x09, 0x1c, 0x5a, 0x0a, 0xd4, + 0xe4, 0x67, 0x18, 0xff, 0x26, 0xa9, 0xb2, 0x3a, 0x8e, 0xdb, 0x7d, 0x11, 0x28, 0x0c, 0x20, 0x67, 0xd4, 0x8b, 0x75, 0xd0, 0x38, 0x9c, 0x09, 0x55, 0x8e, 0x23, 0x5c, 0x79, 0x5e, 0x10, 0xe1, 0xdc, + 0x95, 0x4f, 0xe5, 0xcd, 0x48, 0x36, 0x81, 0x0a, 0x8c, 0xd0, 0x58, 0x88, 0xd7, 0xc1, 0x4e, 0x8b, 0x92, 0xdf, 0x6a, 0xeb, 0x2d, 0x4f, 0x4a, 0x0d, 0x28, 0x20, 0xaa, 0xca, 0xfc, 0x6e, 0x83, 0xb6, + 0x3d, 0x00, 0x96, 0x95, 0xfd, 0xd3, 0xae, 0x53, 0xd3, 0x2b, 0xfa, 0xc0, 0x71, 0x95, 0xda, 0xef, 0xa3, 0x54, 0x26, 0xdf, 0x81, 0x52, 0x74, 0x52, 0x6f, 0x0e, 0x30, 0x96, 0x25, 0x45, 0x70, 0x6e, + 0x09, 0x7e, 0x08, 0x2e, 0x16, 0x6a, 0x9f, 0x17, 0xb0, 0x1f, 0x86, 0xee, 0x82, 0x31, 0x72, 0x61, 0x6b, 0x28, 0x36, 0x6d, 0xfa, 0xb6, 0x2b, 0x35, 0x82, 0xb6, 0x01, 0xc7, 0x7e, 0x42, 0x8a, 0xcb, + 0x06, 0x14, 0xe3, 0xcf, 0x69, 0xb6, 0x30, 0xd0, 0x4f, 0x92, 0x46, 0x16, 0x31, 0x96, 0xc8, 0x6d, 0x4a, 0xf3, 0xb3, 0xb0, 0x88, 0x89, 0x7b, 0x78, 0x39, 0x99, 0xae, 0x99, 0xb6, 0xbc, 0x58, 0x60, + 0x15, 0x74, 0xb2, 0x40, 0x81, 0xf7, 0xda, 0xda, 0xf6, 0x5f, 0x44, 0xe0, 0xa0, 0xcf, 0x7d, 0xfa, 0xd0, 0x08, 0xb7, 0x22, 0x92, 0x69, 0xcc, 0xf8, 0x29, 0x09, 0xa4, 0x0a, 0x58, 0xd8, 0x03, 0x4d, + 0xfd, 0x9e, 0xd7, 0x60, 0x4e, 0xc4, 0x8b, 0xf0, 0x50, 0xac, 0xf0, 0xdd, 0x65, 0x59, 0xe0, 0xa9, 0x89, 0xd3, 0x95, 0xf8, 0x7e, 0x1a, 0x56, 0x37, 0x9f, 0x17, 0x9b, 0xdc, 0xb7, 0x45, 0x5a, 0xeb, + 0xb0, 0x9f, 0x6c, 0xb7, 0xa4, 0xd7, 0x3c, 0x4c, 0xad, 0xd3, 0x5d, 0x0b, 0xc0, 0x0b, 0xd3, 0xa5, 0x3e, 0x6e, 0x22, 0x07, 0xde, 0x63, 0xda, 0x05, 0x63, 0xc8, 0xe3, 0x3a, 0x95, 0x26, 0xff, 0x61, + 0x3b, 0xc5, 0x97, 0x58, 0xe8, 0x92, 0x03, 0x9d, 0x3f, 0xd6, 0x80, 0x62, 0xbb, 0xf7, 0x94, 0x16, 0x8c, 0xc3, 0xbd, 0xf1, 0x2a, 0xd8, 0x1b, 0x8a, 0x43, 0xad, 0xdc, 0x38, 0x17, 0xae, 0xab, 0x5f, + 0x02, 0xb7, 0xe6, 0x78, 0x53, 0xdf, 0x7a, 0x95, 0xc6, 0xb5, 0x52, 0xc6, 0xa0, 0xa7, 0x1b, 0xc3, 0xef, 0x57, 0x39, 0x72, 0xa0, 0x5e, 0x47, 0x04, 0x5d, 0x0d, 0x79, 0x94, 0x3b, 0xd2, 0xef, 0x5c, + 0xa3, 0x19, 0xec, 0xab, 0xe3, 0xd9, 0x6c, 0xbf, 0x18, 0x2a, 0x3b, 0xdb, 0xdc, 0xab, 0xa6, 0xa3, 0xb8, 0x0f, 0xa3, 0x66, 0x9c, 0x18, 0x8c, 0x57, 0x4d, 0x5c, 0x52, 0xc2, 0x99, 0xff, 0xec, 0x2f, + 0x64, 0x84, 0xf8, 0xbf, 0x29, 0x98, 0x92, 0xfd, 0x5b, 0x35, 0xfc, 0x16, 0x9d, 0xcd, 0xc7, 0xd3, 0xc2, 0xb8, 0x20, 0x37, 0x15, 0x6a, 0x92, 0x2c, 0x80, 0x90, 0xc2, 0x28, 0xab, 0x15, 0xdd, 0x3a, + 0x1f, 0xf0, 0xb5, 0xde, 0xa7, 0x6b, 0x41, 0x17, 0x19, 0xeb, 0xdb, 0x84, 0xd0, 0x66, 0x2a, 0xa2, 0xab, 0xb7, 0xfe, 0xc8, 0xf6, 0xe3, 0xcf, 0x70, 0x4b, 0x41, 0xea, 0x5f, 0x19, 0xa1, 0xc5, 0x0a, + 0xd3, 0xc2, 0x92, 0x1a, 0xae, 0x27, 0x08, 0x13, 0x86, 0x38, 0x1d, 0x03, 0xee, 0x21, 0x4b, 0xf6, 0x22, 0x1f, 0xca, 0xb1, 0xa8, 0x7e, 0xcb, 0x18, 0x91, 0x98, 0x00, 0xc5, 0xb3, 0x53, 0x4d, 0x32, + 0xf9, 0xf5, 0x5a, 0xc4, 0x6f, 0x37, 0xd6, 0x49, 0x92, 0x6f, 0xc0, 0x6b, 0xe9, 0x03, 0x62, 0x3d, 0x97, 0x63, 0xf4, 0x38, 0xdc, 0xa4, 0x02, 0x58, 0xb8, 0xb0, 0x20, 0x77, 0xa9, 0x13, 0xad, 0x54, + 0x76, 0x5d, 0x4f, 0x25, 0x4c, 0x35, 0xcc, 0x70, 0xb5, 0x4b, 0x97, 0x55, 0xbc, 0xd5, 0x42, 0x2f, 0xc9, 0xf5, 0xee, 0x8a, 0x8f, 0x85, 0x52, 0xfd, 0xef, 0x55, 0x67, 0x51, 0x23, 0x71, 0x12, 0x13, + 0xac, 0x6a, 0xd2, 0xfd, 0x88, 0x3d, 0x47, 0x98, 0xc1, 0x0a, 0x22, 0xd9, 0xed, 0x2b, 0x4d, 0xf2, 0x4f, 0xfd, 0xb8, 0x31, 0x46, 0xfb, 0x54, 0xe4, 0xa1, 0xe4, 0x38, 0x88, 0xf5, 0xd2, 0x0c, 0xc0, + 0xdc, 0x99, 0xad, 0x28, 0x5a, 0xdf, 0x4b, 0x2d, 0x93, 0x33, 0x36, 0x57, 0x66, 0x2a, 0x4c, 0xef, 0xd1, 0xbf, 0x24, 0x1e, 0x9f, 0x00, 0x61, 0xc6, 0xd7, 0x8a, 0x3b, 0xfb, 0xa2, 0x17, 0x09, 0x35, + 0x5b, 0xf3, 0xfd, 0xda, 0x08, 0xa2, 0x49, 0x61, 0x52, 0xdb, 0x5f, 0x4e, 0xaa, 0xb2, 0x21, 0x17, 0x4d, 0x56, 0xc1, 0x37, 0x26, 0x12, 0xf1, 0x90, 0x5c, 0xfb, 0x70, 0x68, 0x6b, 0x83, 0x01, 0x8e, + 0x93, 0x02, 0xb0, 0xe8, 0x60, 0xa6, 0x7d, 0x21, 0xf6, 0x42, 0x4a, 0xbc, 0xf8, 0x81, 0xbe, 0x20, 0x57, 0x3a, 0xcc, 0xbd, 0x86, 0x1c, 0xce, 0xd1, 0xef, 0x52, 0x29, 0xe4, 0x97, 0x81, 0xd6, 0x0d, + 0x9a, 0xe8, 0x4a, 0x90, 0x55, 0x84, 0x66, 0xca, 0x4a, 0xb4, 0xdd, 0xe9, 0x27, 0x27, 0x84, 0x5d, 0x7a, 0xe0, 0xae, 0x16, 0xcd, 0x99, 0xe5, 0x3e, 0x6e, 0x22, 0x69, 0x86, 0x99, 0xfb, 0xb3, 0xb8, + 0x53, 0xad, 0xb3, 0x29, 0x56, 0x00, 0x9e, 0xdb, 0x09, 0xc6, 0x46, 0x41, 0xb4, 0x8a, 0x77, 0x01, 0xff, 0x25, 0x86, 0x97, 0xb5, 0x4d, 0xe2, 0x18, 0xb0, 0xc4, 0x7a, 0x81, 0x18, 0xee, 0xe8, 0x60, + 0x2b, 0x36, 0x75, 0x24, 0x10, 0x64, 0x7a, 0x6d, 0xa0, 0x20, 0x58, 0xc3, 0x2a, 0x53, 0xcd, 0xda, 0x1d, 0x7e, 0x5d, 0x71, 0x5d, 0x55, 0xe5, 0xc8, 0x8c, 0xdb, 0x0a, 0xfb, 0x9a, 0x31, 0x08, 0x1e, + 0x64, 0x4f, 0xa8, 0xda, 0x25, 0x27, 0x73, 0x83, 0xb3, 0x57, 0xde, 0x30, 0x51, 0x45, 0x78, 0xb9, 0xe3, 0xa5, 0x6a, 0x84, 0x1b, 0xb6, 0x14, 0x59, 0xd2, 0x11, 0xfa, 0x08, 0x77, 0x99, 0x93, 0xb8, + 0x3c, 0x53, 0xaf, 0xe0, 0x0c, 0x3a, 0x81, 0x8b, 0x53, 0x27, 0xa5, 0xac, 0xef, 0x2c, 0x78, 0x2e, 0xb3, 0xa1, 0xa6, 0x13, 0x1c, 0x79, 0x09, 0x9b, 0x9a, 0x35, 0x0e, 0xe7, 0xcc, 0x7a, 0xb6, 0x87, + 0x64, 0x44, 0xba, 0x06, 0x01, 0x76, 0x46, 0x92, 0x83, 0x9f, 0x5e, 0xde, 0x80, 0xf8, 0x5c, 0xa8, 0xde, 0x41, 0xfc, 0xae, 0xd5, 0x4f, 0xfb, 0x1e, 0xb2, 0xa0, 0xaf, 0x2f, 0xcb, 0xd6, 0x37, 0x0b, + 0xe3, 0x7e, 0xc5, 0x4d, 0x60, 0x0b, 0x63, 0xf7, 0x8d, 0xa6, 0x9b, 0x37, 0xcd, 0x53, 0x5d, 0xe4, 0xef, 0xce, 0xd9, 0xf2, 0xb3, 0xf7, 0x9f, 0x5f, 0x5f, 0xaa, 0x13, 0xc9, 0xca, 0x22, 0x47, 0xcd, + 0xbc, 0x43, 0x0f, 0x7b, 0x62, 0x71, 0x7e, 0x25, 0x33, 0x6b, 0xd9, 0x5b, 0x1e, 0xbc, 0xcd, 0x45, 0x0b, 0xa3, 0x9d, 0xef, 0x01, 0x5c, 0xb7, 0x43, 0x75, 0x6e, 0x02, 0xb0, 0x1a, 0x57, 0x12, 0xda, + 0x3f, 0xeb, 0x40, 0x78, 0xe7, 0xda, 0xa7, 0x82, 0x0c, 0x06, 0xfd, 0xff, 0xfa, 0xab, 0x35, 0xdd, 0x4e, 0x97, 0x0e, 0x75, 0xc7, 0x07, 0xa7, 0x0d, 0xc7, 0xb2, 0x0b, 0xb7, 0xe7, 0xcb, 0x0d, 0x8a, + 0x3a, 0x61, 0x4d, 0x0c, 0xfb, 0xed, 0x7f, 0x66, 0xa3, 0x3c, 0xac, 0x58, 0xfb, 0xa7, 0xa8, 0x3f, 0x2f, 0xe7, 0xff, 0x73, 0xaf, 0xa0, 0xa8, 0x62, 0x99, 0x4b, 0x05, 0xcb, 0x0b, 0x7d, 0x85, 0x4c, + 0x3f, 0x1e, 0x53, 0x11, 0xfe, 0x2e, 0x18, 0x11, 0x54, 0xfe, 0x9e, 0xcb, 0x08, 0xff, 0x54, 0xa4, 0x54, 0xa8, 0x31, 0xbc, 0xd0, 0xf6, 0xb1, 0xda, 0x9a, 0xe6, 0x60, 0x97, 0x5c, 0xfb, 0x3c, 0x90, + 0x5b, 0xd5, 0xf0, 0xb7, 0x1a, 0x40, 0xf1, 0x7c, 0xcc, 0x34, 0xcc, 0x1a, 0x39, 0xc6, 0xa2, 0xb5, 0xfd, 0xb9, 0x6d, 0x22, 0x10, 0xfa, 0xd6, 0x2d, 0xe7, 0x46, 0xf4, 0xb0, 0x2a, 0xcb, 0x72, 0x76, + 0x31, 0xae, 0xa9, 0xad, 0x97, 0xd9, 0x7c, 0x6e, 0xc5, 0x1f, 0x8c, 0x05, 0x2c, 0xb4, 0xa9, 0x46, 0xa3, 0x94, 0xf4, 0xaf, 0x26, 0xbc, 0xae, 0xd9, 0xfb, 0x96, 0xd0, 0x63, 0xed, 0x28, 0x1d, 0xb7, + 0xdb, 0x3a, 0xd9, 0xcb, 0xba, 0x66, 0x83, 0x43, 0xe0, 0xaa, 0x38, 0x3a, 0xcc, 0x84, 0x90, 0x98, 0xbb, 0xa7, 0xb2, 0x69, 0x7c, 0x3b, 0x4c, 0xd3, 0x1b, 0xfc, 0xa7, 0x7d, 0x01, 0xed, 0x30, 0xd9, + 0x18, 0x04, 0xd9, 0x51, 0xd0, 0xfe, 0x61, 0x2d, 0x52, 0xed, 0xa3, 0xe1, 0x37, 0xd3, 0x6e, 0x52, 0x6a, 0xd9, 0x6d, 0xfe, 0x72, 0x1e, 0xc7, 0x20, 0x31, 0x93, 0x3d, 0x7a, 0xe6, 0x3a, 0x49, 0x58, + 0x67, 0xa9, 0xca, 0x03, 0x09, 0x63, 0xfd, 0xdb, 0x91, 0xd3, 0x15, 0x0f, 0xf5, 0xdf, 0x18, 0xfa, 0x03, 0x6e, 0xc3, 0xad, 0x89, 0xa1, 0x59, 0x07, 0x80, 0xce, 0x04, 0xb6, 0xa1, 0xa7, 0xfa, 0x8e, + 0x61, 0xab, 0xcf, 0x8a, 0x5b, 0xae, 0xdc, 0x0c, 0x55, 0xad, 0x3e, 0x73, 0x05, 0x4b, 0xac, 0x50, 0xf4, 0xc8, 0xa6, 0xfb, 0x23, 0xbb, 0x80, 0x84, 0xb4, 0xe4, 0x24, 0x85, 0xe2, 0x53, 0x76, 0x38, + 0x97, 0xc4, 0x98, 0xff, 0xa3, 0xac, 0x42, 0x26, 0xd4, 0x59, 0x5e, 0x10, 0x52, 0xbd, 0x89, 0x97, 0xdd, 0x09, 0x0f, 0x23, 0xdc, 0x02, 0x0b, 0xb9, 0x52, 0xc2, 0x11, 0xe6, 0x67, 0x7c, 0x7e, 0x66, + 0x3b, 0x91, 0xa5, 0x32, 0xce, 0x98, 0xef, 0x68, 0x6c, 0xd0, 0xff, 0xdd, 0x7c, 0x49, 0xc2, 0x94, 0x18, 0x1f, 0x6c, 0x2a, 0x96, 0xea, 0x37, 0x48, 0x65, 0x92, 0x9d, 0x67, 0xbf, 0x4e, 0x5e, 0xf2, + 0x70, 0x79, 0x80, 0xe3, 0xc7, 0x66, 0x09, 0x3b, 0xd0, 0x87, 0xa5, 0x32, 0xef, 0xf6, 0x9c, 0xd5, 0xf6, 0xa2, 0x18, 0xbd, 0x64, 0x7b, 0x27, 0xeb, 0x06, 0x4f, 0xb3, 0x33, 0xcd, 0x2b, 0x89, 0x6b, + 0xac, 0x22, 0x0a, 0x1f, 0x0e, 0x2e, 0xe7, 0xdf, 0xf3, 0xb1, 0xa2, 0x4d, 0xe3, 0x03, 0xab, 0x81, 0x33, 0x20, 0x77, 0x55, 0x10, 0xea, 0xbc, 0x69, 0xab, 0xbe, 0xfd, 0xb0, 0x3e, 0xec, 0x50, 0xaf, + 0x8f, 0x28, 0x80, 0xf6, 0x6b, 0x1c, 0x34, 0x31, 0x78, 0xf6, 0xda, 0xee, 0x64, 0x20, 0xe4, 0x3e, 0x75, 0x6a, 0x56, 0x33, 0x20, 0x0c, 0xe2, 0xb9, 0x14, 0x73, 0x8a, 0xcb, 0xe8, 0xa1, 0x26, 0xb6, + 0x07, 0xce, 0x52, 0x19, 0x19, 0xe5, 0xc4, 0x96, 0x1b, 0x51, 0xf9, 0x2f, 0x83, 0xef, 0xfc, 0xd9, 0x33, 0x37, 0x98, 0x05, 0x1a, 0x8c, 0x27, 0x62, 0x53, 0x4d, 0xd8, 0xc6, 0x37, 0x47, 0xd8, 0xf5, + 0x72, 0x29, 0x93, 0xe1, 0xc8, 0x8a, 0x24, 0xab, 0xc1, 0x8e, 0x6f, 0xd9, 0x44, 0xfb, 0x69, 0x0f, 0x02, 0xd9, 0xc6, 0xa6, 0x04, 0x0e, 0x17, 0xaf, 0x46, 0x5e, 0x27, 0x84, 0xcc, 0xc2, 0xa6, 0x2b, + 0x43, 0x48, 0xb2, 0xc9, 0x25, 0x7d, 0x91, 0x8d, 0x53, 0xd5, 0x62, 0x12, 0x6e, 0xa4, 0x53, 0xd3, 0x80, 0xdc, 0x5c, 0xb1, 0x9d, 0x68, 0x8d, 0xbe, 0x62, 0xbf, 0x4e, 0x71, 0xec, 0xa8, 0x64, 0x62, + 0x5e, 0x58, 0xb1, 0xe9, 0x58, 0x15, 0xf9, 0x8d, 0xe2, 0x3c, 0x0c, 0xf5, 0x30, 0xd6, 0xf5, 0xfb, 0x93, 0x01, 0xd2, 0xc0, 0xbb, 0xaf, 0x06, 0x6f, 0xbf, 0x70, 0xc2, 0x43, 0xd0, 0x29, 0x79, 0xd9, + 0x99, 0x10, 0x72, 0xe3, 0xd0, 0x87, 0xba, 0x59, 0x3c, 0xd7, 0x9a, 0xa2, 0x82, 0x64, 0x9e, 0xc4, 0xa5, 0xfb, 0x8f, 0x11, 0xab, 0x03, 0x07, 0xb6, 0x3a, 0xf6, 0x58, 0xb1, 0xa3, 0x85, 0x53, 0x22, + 0xf9, 0x22, 0x58, 0xa4, 0x41, 0x4f, 0x5d, 0xa0, 0xd3, 0x0a, 0x05, 0x24, 0x59, 0x00, 0x35, 0xb1, 0x31, 0x52, 0x4b, 0x17, 0x1d, 0x66, 0x6b, 0xcf, 0x5b, 0x21, 0x97, 0x24, 0x8e, 0x68, 0x6c, 0x4c, + 0x58, 0x4d, 0xd8, 0xbb, 0x03, 0x92, 0x8d, 0xf3, 0x12, 0x66, 0x47, 0x61, 0xd9, 0xf2, 0xb6, 0xd4, 0x53, 0x2c, 0x30, 0xe2, 0x40, 0x54, 0x91, 0x63, 0xc2, 0x60, 0xbd, 0xac, 0x8f, 0x63, 0x3c, 0x09, + 0x09, 0x75, 0x2d, 0xdc, 0xd5, 0xaf, 0xa1, 0xb2, 0xec, 0x23, 0x62, 0x4a, 0x6a, 0x16, 0x5b, 0x60, 0xd5, 0x38, 0x2c, 0xaa, 0x93, 0x9c, 0xc0, 0x54, 0x14, 0x4e, 0x63, 0x5c, 0x15, 0xfb, 0xa9, 0xe8, + 0x57, 0xf8, 0xbb, 0x6f, 0xe0, 0xef, 0x23, 0x6f, 0xcd, 0xac, 0xfc, 0x00, 0x2d, 0xa7, 0x92, 0xb3, 0x30, 0xcf, 0xb1, 0xe2, 0x85, 0xf4, 0x52, 0x30, 0xd0, 0x10, 0xf7, 0x93, 0x70, 0x4b, 0xd4, 0xff, + 0x55, 0x81, 0x4f, 0x49, 0xe6, 0x01, 0x3f, 0x2f, 0x06, 0x41, 0x9b, 0x3a, 0xa5, 0x30, 0xee, 0xc0, 0x18, 0x29, 0xa5, 0x7e, 0x41, 0x69, 0xab, 0xba, 0x96, 0x01, 0x53, 0x42, 0x2c, 0x81, 0x63, 0x67, + 0x0a, 0x26, 0xe6, 0x13, 0x58, 0xcb, 0x97, 0x9b, 0xb3, 0x74, 0x3d, 0xc7, 0x31, 0xcd, 0x67, 0xcf, 0x49, 0xe7, 0x03, 0xc9, 0x50, 0x78, 0x6a, 0x0b, 0x41, 0x6f, 0x2e, 0x66, 0x6d, 0xf7, 0x96, 0xd7, + 0x93, 0x16, 0x0b, 0xe7, 0xda, 0xf3, 0xa1, 0xfd, 0x24, 0x3b, 0x7b, 0x6f, 0xee, 0xd6, 0xc9, 0x26, 0xdc, 0x2f, 0xb3, 0x00, 0x98, 0x49, 0xc3, 0x10, 0x5f, 0xe9, 0x9a, 0x43, 0x21, 0x11, 0xc3, 0xb4, + 0x6e, 0xe8, 0x5f, 0x1b, 0x53, 0xa8, 0x6c, 0x05, 0x90, 0x3f, 0xa6, 0x3d, 0x1a, 0x6f, 0x52, 0x77, 0x4f, 0x58, 0x63, 0x96, 0x8b, 0x9a, 0x3f, 0x3c, 0x57, 0x5b, 0x89, 0x2c, 0x38, 0x2e, 0x92, 0x04, + 0xa6, 0xa1, 0x37, 0x69, 0x16, 0xc3, 0x5c, 0x23, 0xb5, 0x4d, 0xfb, 0x02, 0xbc, 0x9a, 0x3d, 0x07, 0x03, 0x0e, 0x26, 0x20, 0x75, 0x84, 0x0b, 0x13, 0xfc, 0x13, 0xe8, 0xee, 0x97, 0x2b, 0x4f, 0xeb, + 0x76, 0x5b, 0xeb, 0x4f, 0x9d, 0xce, 0x08, 0xb2, 0xef, 0xb2, 0xef, 0xdd, 0xa4, 0xb5, 0x63, 0xf7, 0x5f, 0x65, 0xbf, 0x7d, 0x1a, 0xa7, 0x22, 0x34, 0x3e, 0xbd, 0xcc, 0xef, 0xd7, 0xde, 0x21, 0x23, + 0x2e, 0x21, 0xb0, 0x3d, 0xb0, 0xf5, 0xdd, 0x06, 0xbb, 0x78, 0x32, 0xa4, 0xae, 0x72, 0xf2, 0x62, 0x1d, 0x96, 0x18, 0x05, 0xd9, 0x46, 0x24, 0x1a, 0x58, 0xb9, 0x0f, 0xb6, 0x7f, 0x35, 0xae, 0x05, + 0x4e, 0xbc, 0x79, 0xf0, 0xbb, 0xd8, 0x0d, 0x84, 0x8b, 0x2b, 0x4d, 0x6d, 0x2e, 0x59, 0x52, 0xb1, 0x44, 0x29, 0xd1, 0xe1, 0xa0, 0x1a, 0xbd, 0x3a, 0xc5, 0xb5, 0x0d, 0x89, 0xc1, 0x17, 0x4f, 0x13, + 0x66, 0xc2, 0x87, 0xde, 0x1a, 0xa5, 0x6f, 0xdc, 0x54, 0xc0, 0x16, 0xa4, 0xfb, 0x3d, 0x2c, 0xad, 0xa7, 0xcb, 0xe6, 0x39, 0xe7, 0xfe, 0x91, 0x8f, 0x57, 0xbd, 0xea, 0xc9, 0xd4, 0x1d, 0x95, 0xa9, + 0xc3, 0x52, 0xe8, 0x5c, 0x85, 0x1d, 0xef, 0xcf, 0x4f, 0xa1, 0x28, 0x29, 0x26, 0xbe, 0x8f, 0x7a, 0x64, 0xb4, 0x2b, 0x63, 0x10, 0x34, 0xc3, 0x07, 0x78, 0x05, 0x9d, 0x61, 0xbc, 0xe8, 0xaf, 0x15, + 0x7b, 0x2c, 0x25, 0x6a, 0x78, 0x63, 0x81, 0x47, 0xfc, 0x36, 0x8b, 0x78, 0x89, 0x29, 0xa9, 0x04, 0x19, 0x30, 0x34, 0xa2, 0xe4, 0xe5, 0x08, 0x8b, 0x7f, 0x65, 0x90, 0xf4, 0xc1, 0x1c, 0x76, 0x3c, + 0x63, 0x68, 0x29, 0x1a, 0x44, 0x1e, 0x9c, 0x65, 0xc0, 0x04, 0x59, 0x50, 0x44, 0xac, 0xe0, 0x97, 0x40, 0xbb, 0x35, 0x3e, 0x1b, 0x16, 0xfa, 0x1b, 0xeb, 0x81, 0x12, 0x1c, 0xa3, 0x71, 0xf7, 0xa1, + 0xa4, 0x2f, 0x91, 0x07, 0x08, 0x12, 0xb2, 0xc6, 0xa5, 0x4d, 0xc6, 0xaa, 0x1c, 0x7a, 0x80, 0x13, 0xbd, 0x57, 0x53, 0x50, 0x63, 0xe7, 0xce, 0x50, 0x2f, 0x94, 0xe1, 0x6c, 0x78, 0x29, 0x4f, 0x02, + 0x9c, 0x4a, 0x10, 0x52, 0x3b, 0x14, 0x1f, 0xda, 0x85, 0x6c, 0x8f, 0x16, 0x62, 0x6e, 0xbe, 0x24, 0x8e, 0x3c, 0xe7, 0xa8, 0xbc, 0x51, 0x80, 0x01, 0xa6, 0xdc, 0x19, 0x74, 0xa9, 0x29, 0x00, 0x65, + 0x70, 0x7f, 0x2f, 0x72, 0xc1, 0x57, 0x6a, 0x33, 0x9f, 0xd8, 0xa2, 0x4d, 0x56, 0x61, 0x49, 0x9d, 0xdd, 0xde, 0x35, 0x6a, 0xd0, 0x39, 0xb5, 0x05, 0x58, 0x87, 0x57, 0x2d, 0x49, 0xf7, 0xc4, 0x97, + 0x18, 0x52, 0x85, 0xc2, 0x1b, 0x99, 0xcb, 0xfd, 0x39, 0x23, 0x29, 0x1b, 0x9e, 0xb0, 0x2c, 0xc4, 0xa4, 0x30, 0x99, 0xc2, 0xbf, 0xb0, 0x5a, 0x24, 0xaf, 0x5c, 0x24, 0x9c, 0x77, 0xbf, 0x32, 0xc2, + 0xfb, 0xce, 0x9e, 0x2d, 0x6b, 0x57, 0x4c, 0x3f, 0x15, 0x03, 0xc9, 0x2f, 0x78, 0xbc, 0xf4, 0xa3, 0x75, 0x37, 0x6e, 0x09, 0x4d, 0x1f, 0xbd, 0xfa, 0x5f, 0x54, 0xdd, 0xe8, 0x1b, 0x05, 0x1b, 0xa5, + 0xcc, 0x32, 0xc8, 0xef, 0x41, 0xc3, 0xae, 0x66, 0xb4, 0xd9, 0xd7, 0xeb, 0x50, 0x0c, 0x64, 0x93, 0xb6, 0x1b, 0xcf, 0x2e, 0x04, 0x73, 0xb9, 0x0d, 0x85, 0xd4, 0xde, 0x10, 0x0f, 0x7b, 0xad, 0xa5, + 0x8f, 0x6b, 0x89, 0x76, 0x3f, 0x2e, 0xe8, 0x4d, 0x9d, 0xe0, 0x8a, 0x36, 0x7c, 0xe6, 0x83, 0x97, 0xf3, 0xb3, 0x95, 0x1a, 0xbf, 0x5c, 0xa3, 0xec, 0x90, 0x92, 0x36, 0x44, 0xdb, 0x3e, 0x6c, 0x40, + 0x58, 0xd5, 0x28, 0x8c, 0x95, 0x15, 0xaf, 0x82, 0x54, 0xf9, 0xa8, 0x10, 0xb0, 0x81, 0x2f, 0x5b, 0xe7, 0x94, 0xc7, 0x82, 0x6c, 0x03, 0xe9, 0x7d, 0xc1, 0x73, 0xd4, 0xa6, 0x68, 0xd9, 0x2c, 0x79, + 0x50, 0xa2, 0x58, 0xc0, 0xca, 0xfc, 0x03, 0xce, 0xac, 0x50, 0x91, 0x13, 0x50, 0x4f, 0x75, 0x58, 0xc4, 0x0e, 0x34, 0xf6, 0x05, 0x6e, 0xfb, 0x72, 0xc4, 0xb2, 0xef, 0xff, 0xbc, 0xd4, 0x20, 0x31, + 0xb8, 0x36, 0xf3, 0xc9, 0xed, 0x84, 0x6f, 0x74, 0x65, 0x12, 0x76, 0x05, 0x65, 0xaf, 0x2e, 0xe4, 0x9e, 0xae, 0x7a, 0x12, 0xd9, 0xe8, 0x81, 0x77, 0xb8, 0x64, 0x2f, 0x28, 0x7e, 0x21, 0xdf, 0x38, + 0x00, 0x4d, 0xef, 0x17, 0x23, 0x9a, 0x00, 0x8c, 0xbe, 0x49, 0x46, 0xb8, 0xe9, 0xc4, 0xb6, 0xd0, 0x46, 0xf0, 0x0f, 0x39, 0xe6, 0xac, 0x06, 0xce, 0xfa, 0x5e, 0x07, 0xc9, 0x00, 0x52, 0x4f, 0x99, + 0x10, 0x80, 0x89, 0xcb, 0xa3, 0xe8, 0xaa, 0x91, 0xce, 0xd5, 0xe8, 0xd9, 0x5f, 0xa7, 0xc0, 0x9c, 0x74, 0xea, 0x42, 0x60, 0x37, 0x03, 0xc3, 0x6f, 0x5e, 0xbc, 0x77, 0x1f, 0xb7, 0xe2, 0x5e, 0x2e, + 0xa7, 0xa4, 0x28, 0x95, 0x13, 0x46, 0x35, 0xfe, 0x40, 0xe9, 0xe3, 0x41, 0x93, 0x8d, 0x1a, 0x4a, 0x24, 0x7c, 0x11, 0xcd, 0xe4, 0xd7, 0xe9, 0x59, 0x72, 0x34, 0x17, 0xc1, 0x64, 0x14, 0xa1, 0xdc, + 0x26, 0x97, 0x57, 0x96, 0xcb, 0x1c, 0xde, 0x56, 0xba, 0x14, 0x2c, 0x17, 0xe0, 0x39, 0xdc, 0x7f, 0x95, 0xe9, 0xf7, 0x0b, 0x7a, 0x99, 0x2e, 0x9c, 0x4d, 0x12, 0x06, 0x67, 0xe2, 0x78, 0x3b, 0x3c, + 0x44, 0x44, 0x3f, 0x93, 0x21, 0xf5, 0x7b, 0x36, 0x97, 0x40, 0x9e, 0xdd, 0x76, 0xe6, 0x8d, 0x43, 0x8b, 0x33, 0x33, 0xdb, 0x4d, 0xd6, 0x8b, 0x9e, 0x11, 0xe0, 0x96, 0x9d, 0xd1, 0xe3, 0x34, 0x84, + 0x99, 0x14, 0x92, 0x54, 0x89, 0x20, 0x82, 0x3e, 0xaa, 0xb8, 0x1f, 0xd5, 0xbc, 0x08, 0xac, 0xad, 0x21, 0x2b, 0x35, 0x04, 0x49, 0xda, 0xea, 0x4c, 0xd5, 0x1f, 0x62, 0x16, 0x30, 0xee, 0xfe, 0x0f, + 0x09, 0x32, 0x5e, 0xb5, 0xda, 0x73, 0x20, 0x50, 0xe2, 0x1c, 0x2e, 0x52, 0x2f, 0x43, 0x19, 0x75, 0xc9, 0xbc, 0x4f, 0xb5, 0xd2, 0xd8, 0x46, 0x27, 0x6f, 0x4b, 0x95, 0x14, 0xed, 0xac, 0x92, 0x64, + 0xed, 0xb6, 0x92, 0x89, 0x57, 0xba, 0x66, 0xed, 0xd4, 0x77, 0xcc, 0xa7, 0x64, 0x79, 0x21, 0xc3, 0x40, 0xd4, 0xd8, 0xa1, 0x4f, 0x2b, 0x3f, 0xc1, 0x02, 0x80, 0x83, 0xc1, 0x81, 0xbb, 0x0c, 0x72, + 0xe9, 0xaa, 0x85, 0x2d, 0xc0, 0xe1, 0x3e, 0xc6, 0x04, 0xeb, 0x2f, 0x13, 0x89, 0xb4, 0x48, 0xbe, 0x7a, 0x19, 0xc2, 0x18, 0x70, 0xfa, 0x65, 0x89, 0xaa, 0xf0, 0x1a, 0xdc, 0x74, 0xcd, 0xca, 0x04, + 0xc6, 0x39, 0x89, 0xa5, 0xf5, 0xf2, 0x6e, 0x93, 0x22, 0xcd, 0xbb, 0x6e, 0x8a, 0xc7, 0x71, 0xa9, 0x87, 0x7a, 0xc5, 0xaf, 0x4f, 0xf8, 0x40, 0xa5, 0xc9, 0x91, 0xee, 0xc8, 0x43, 0xc5, 0x59, 0x0b, + 0x93, 0x72, 0xad, 0xe9, 0x86, 0x64, 0x27, 0xc0, 0xa1, 0x4f, 0x4e, 0x0a, 0x86, 0x8f, 0xcf, 0x6d, 0x52, 0xe9, 0xb4, 0x95, 0xf9, 0x92, 0x49, 0xf5, 0xc6, 0xa8, 0xf6, 0x63, 0x59, 0x9c, 0xf2, 0x76, + 0x94, 0xe2, 0xab, 0x67, 0x44, 0xea, 0x1c, 0x81, 0xa8, 0x3a, 0x43, 0x60, 0x8f, 0x54, 0xf6, 0x3d, 0x93, 0x8d, 0xf1, 0x74, 0x9b, 0xc9, 0x3a, 0x97, 0x5f, 0x22, 0xc3, 0x4a, 0xbe, 0xa7, 0x3d, 0x1d, + 0x5e, 0x42, 0x09, 0x81, 0x82, 0x14, 0x03, 0x3c, 0xc8, 0x01, 0xfe, 0x99, 0x09, 0xb4, 0xf9, 0xd4, 0x49, 0x98, 0xec, 0x87, 0x82, 0x95, 0xdb, 0xd9, 0x96, 0x68, 0xdc, 0x19, 0x68, 0xd4, 0x9d, 0x7e, + 0x66, 0xc4, 0x1e, 0x3b, 0xe5, 0x9c, 0xf5, 0xdf, 0xa1, 0xfa, 0x6c, 0x55, 0x3e, 0x3d, 0x5e, 0xd0, 0x2a, 0x38, 0xd2, 0x49, 0x52, 0xef, 0x68, 0x00, 0x62, 0x8f, 0x31, 0xb5, 0x79, 0x08, 0x1d, 0x63, + 0x2d, 0xca, 0x2b, 0x66, 0x9c, 0xad, 0xe2, 0x72, 0xf6, 0x24, 0xb4, 0x20, 0x72, 0xc7, 0xd8, 0x8e, 0x16, 0xe2, 0x0e, 0xa7, 0xda, 0xc3, 0x50, 0xd5, 0xb4, 0x79, 0x8c, 0x7d, 0x3c, 0x57, 0x39, 0xd0, + 0x01, 0xdf, 0xd0, 0x79, 0x92, 0xdc, 0xa3, 0xd3, 0xe1, 0xf8, 0x77, 0x7a, 0xd7, 0x4f, 0x6b, 0xb1, 0x7f, 0x14, 0x07, 0x83, 0xc2, 0x6e, 0x6f, 0x59, 0xa2, 0x7f, 0xfa, 0x33, 0x2c, 0xc3, 0x89, 0x27, + 0x19, 0x59, 0x5e, 0xec, 0x8e, 0x11, 0x2a, 0xd9, 0xd6, 0x10, 0x6d, 0xe4, 0x67, 0x84, 0x8f, 0xf9, 0x50, 0xe7, 0x82, 0x98, 0xbf, 0xf0, 0x77, 0x5f, 0x80, 0xe8, 0x99, 0x76, 0x2d, 0x0d, 0xaf, 0x0a, + 0x8a, 0x3c, 0x3d, 0x9a, 0xbc, 0x0a, 0x93, 0x75, 0xdd, 0x7e, 0xcc, 0x77, 0x38, 0xab, 0x4d, 0xd3, 0x5c, 0xf4, 0x61, 0xe7, 0x38, 0x12, 0x75, 0x54, 0x6c, 0x55, 0xd2, 0x2c, 0x66, 0x6e, 0x46, 0x22, + 0x14, 0xca, 0x6b, 0x0c, 0xc3, 0x18, 0x34, 0x55, 0x9e, 0x99, 0x56, 0xdc, 0x77, 0x2a, 0x25, 0x33, 0x49, 0x63, 0xd5, 0x71, 0x7c, 0x35, 0xa6, 0x31, 0xb2, 0xb8, 0xb9, 0xb2, 0xda, 0x21, 0x90, 0x70, + 0xdd, 0x97, 0xd9, 0xc2, 0x72, 0x0b, 0xcd, 0x37, 0x4f, 0xa5, 0xd2, 0xf3, 0xed, 0x46, 0xc0, 0xa8, 0x2d, 0x6d, 0x11, 0x93, 0xb8, 0x6b, 0xd2, 0xd2, 0x69, 0x41, 0x5c, 0xc1, 0x27, 0x16, 0x1e, 0x09, + 0x56, 0x75, 0xa0, 0x41, 0x63, 0x99, 0xe4, 0x6e, 0xaf, 0x1a, 0x68, 0xc8, 0x89, 0xb1, 0x15, 0x66, 0xa8, 0x70, 0x9e, 0xbb, 0xda, 0x8f, 0xde, 0xde, 0x39, 0x22, 0xf0, 0xc2, 0x89, 0x43, 0xc6, 0xa8, + 0x75, 0x02, 0x5d, 0x02, 0xb4, 0x48, 0x26, 0x94, 0xa3, 0x72, 0x28, 0x9e, 0x82, 0xa3, 0xdc, 0xb6, 0x2e, 0xe7, 0x7c, 0xa8, 0x20, 0x79, 0x6c, 0x20, 0x5f, 0xdf, 0xaa, 0xd1, 0xdb, 0xe6, 0x9b, 0xfe, + 0xd4, 0xcc, 0xbe, 0x29, 0xe5, 0x69, 0x52, 0xb8, 0x48, 0x39, 0x5a, 0x92, 0x1a, 0x63, 0x06, 0xe9, 0x9a, 0x42, 0x87, 0x72, 0x7e, 0x0c, 0x33, 0x75, 0x3d, 0x35, 0xd5, 0x7f, 0x29, 0x36, 0x73, 0xd5, + 0x89, 0xbe, 0xe5, 0x26, 0x0a, 0x34, 0x47, 0xce, 0x31, 0x9c, 0xe7, 0xde, 0xdf, 0x7c, 0x22, 0x0b, 0x13, 0x8c, 0xab, 0x5b, 0x6d, 0x2f, 0x54, 0x47, 0x38, 0x09, 0x2d, 0xeb, 0x7b, 0x59, 0xc9, 0xe6, + 0xbd, 0x28, 0xbc, 0x8b, 0xb0, 0x80, 0xc5, 0x72, 0x19, 0xe1, 0x44, 0xc4, 0x86, 0xfc, 0x25, 0x8a, 0x0f, 0xe4, 0xbd, 0x7e, 0xd1, 0x72, 0x48, 0x86, 0xa2, 0x4c, 0x78, 0xc5, 0xd5, 0x7e, 0xa4, 0x80, + 0xa8, 0x9a, 0x66, 0xba, 0x8a, 0xf8, 0x62, 0x50, 0xb3, 0xda, 0xa7, 0xbb, 0x53, 0x0f, 0xb0, 0x47, 0x10, 0xba, 0x0b, 0x0d, 0xcf, 0x1b, 0x47, 0xaf, 0xdb, 0xea, 0x69, 0xe2, 0xc9, 0xfb, 0x58, 0x44, + 0xa4, 0xb4, 0x17, 0x86, 0xb9, 0xc9, 0x01, 0x26, 0x47, 0x38, 0x7f, 0x97, 0x97, 0x8f, 0x56, 0x5d, 0x64, 0x19, 0xe3, 0xf0, 0x0b, 0x88, 0xe8, 0xcf, 0x7c, 0xdf, 0xa2, 0xb2, 0x23, 0x45, 0x69, 0xa7, + 0x80, 0x89, 0xd8, 0xa8, 0x94, 0x85, 0x74, 0xf0, 0x5a, 0xf8, 0x98, 0xed, 0x7a, 0x72, 0x15, 0x8a, 0xa3, 0xce, 0xf1, 0xfa, 0x73, 0x36, 0x70, 0x29, 0xff, 0x25, 0x7c, 0x21, 0x43, 0x0b, 0x67, 0x73, + 0x63, 0x90, 0x65, 0x71, 0xe5, 0x61, 0x7e, 0x97, 0xf0, 0x3c, 0x7e, 0xef, 0xc3, 0xaf, 0x60, 0x60, 0xeb, 0x92, 0x64, 0x86, 0x35, 0x55, 0x4b, 0x35, 0x80, 0x7e, 0x56, 0xab, 0x62, 0xff, 0xef, 0xff, + 0xee, 0x4e, 0x3f, 0x73, 0x60, 0x87, 0xb4, 0x95, 0xf1, 0x78, 0x3b, 0x2b, 0x56, 0xda, 0x96, 0x5b, 0xe9, 0xee, 0xb8, 0xc2, 0x58, 0x4c, 0x28, 0x4e, 0x84, 0x11, 0x2f, 0x87, 0xf0, 0x17, 0x1d, 0x06, + 0xbf, 0xd0, 0x3a, 0x93, 0x81, 0x70, 0xa1, 0x5d, 0x80, 0x71, 0x27, 0xfb, 0xf5, 0x38, 0x35, 0xa2, 0x12, 0x84, 0x4a, 0xe5, 0x88, 0x1a, 0xf5, 0x4c, 0xb8, 0x96, 0x4b, 0x24, 0x6b, 0x92, 0xe2, 0x1f, + 0x9c, 0xeb, 0x9d, 0x58, 0x9c, 0x9a, 0x37, 0xb1, 0x85, 0x32, 0x5f, 0x75, 0x9c, 0x29, 0x5c, 0x27, 0x3e, 0x1c, 0x00, 0x39, 0x7b, 0x29, 0xeb, 0x2a, 0x60, 0xf7, 0x33, 0xc3, 0x38, 0xcc, 0x40, 0xc6, + 0x0e, 0x83, 0xe7, 0xa1, 0x2e, 0xc6, 0x6c, 0xbb, 0x8e, 0x10, 0x6b, 0x16, 0x38, 0x4d, 0x22, 0x45, 0x39, 0x42, 0xec, 0xa2, 0x85, 0x44, 0x8b, 0x03, 0x70, 0x23, 0x5d, 0x8a, 0x20, 0xcf, 0xad, 0x68, + 0xbf, 0xa0, 0x52, 0x19, 0xca, 0xb2, 0x6c, 0xe9, 0x72, 0xa6, 0x07, 0xef, 0x7f, 0x9a, 0xfd, 0x6b, 0x4f, 0x23, 0x20, 0xd5, 0xd0, 0xf9, 0x71, 0x21, 0x02, 0xa9, 0x14, 0xf9, 0xa5, 0x18, 0x7f, 0x59, + 0x97, 0xb3, 0x74, 0x5c, 0xe6, 0x9c, 0xca, 0x4b, 0x11, 0x00, 0x02, 0x85, 0xa7, 0x92, 0xf4, 0xf7, 0x24, 0xc9, 0x3e, 0x1d, 0xae, 0x7b, 0x5d, 0x19, 0x0e, 0xc3, 0xf7, 0xd2, 0x6f, 0xcd, 0xe0, 0x00, + 0xdd, 0xb9, 0x62, 0x99, 0xc9, 0xde, 0x6d, 0xba, 0xad, 0x43, 0x23, 0xb8, 0x8e, 0x06, 0x84, 0xa7, 0x70, 0x13, 0xc8, 0x67, 0x64, 0x6e, 0x80, 0xcf, 0x47, 0x3a, 0xf7, 0x49, 0xaa, 0x4b, 0xef, 0xa3, + 0xb7, 0xc1, 0x90, 0xf7, 0xee, 0x10, 0xe5, 0xba, 0x7a, 0xb9, 0x17, 0xbc, 0x3d, 0xa3, 0xaa, 0x42, 0xcb, 0x2f, 0x51, 0xd6, 0xe6, 0x86, 0x19, 0x05, 0x46, 0x36, 0xce, 0xda, 0x6e, 0x03, 0x8d, 0x7e, + 0x11, 0xd4, 0xe1, 0xf3, 0x3a, 0x9b, 0x9a, 0x9c, 0x39, 0x67, 0x7c, 0x3b, 0xab, 0xe2, 0xa5, 0xb4, 0x98, 0x34, 0x2d, 0x87, 0x3e, 0x6b, 0x60, 0xc7, 0xf7, 0x20, 0x01, 0xd6, 0xbc, 0x61, 0xa4, 0xf6, + 0x14, 0x1e, 0xbb, 0xc5, 0x17, 0x3a, 0x38, 0x5d, 0x0b, 0x2d, 0xf1, 0x59, 0x1d, 0x51, 0xe4, 0x3f, 0x58, 0x2b, 0x8c, 0xe7, 0xfe, 0x0d, 0x5c, 0x2e, 0x4c, 0xaa, 0x2a, 0x6e, 0x54, 0x55, 0x49, 0xe5, + 0xc6, 0x25, 0xcb, 0x8a, 0x34, 0xac, 0x19, 0x75, 0x3e, 0x48, 0x2e, 0x62, 0x06, 0x17, 0x1e, 0x8a, 0xec, 0x60, 0xb2, 0x7a, 0x8b, 0x21, 0x04, 0xd1, 0xcc, 0x62, 0x31, 0xa7, 0xbd, 0xe3, 0x78, 0xc2, + 0xd2, 0x32, 0x50, 0x6d, 0xff, 0x2a, 0x12, 0x23, 0xe0, 0x29, 0xc4, 0xa9, 0x36, 0xfc, 0x9a, 0xd3, 0x08, 0xe8, 0xbe, 0x7b, 0xe5, 0xac, 0xa7, 0x49, 0xa1, 0x12, 0xd1, 0x43, 0x0d, 0x8f, 0xfa, 0x74, + 0xb3, 0xc0, 0x11, 0xb6, 0x87, 0x28, 0xaf, 0x71, 0xa5, 0xff, 0x69, 0x35, 0x9e, 0xe8, 0xef, 0x33, 0x5d, 0x48, 0xca, 0xc1, 0xa5, 0xa0, 0x69, 0x85, 0x98, 0x95, 0x25, 0xde, 0x83, 0xdd, 0x02, 0xdd, + 0x75, 0x78, 0x1e, 0xb1, 0x10, 0x4a, 0xa9, 0x8a, 0xfe, 0x2d, 0x89, 0x4e, 0xd6, 0x91, 0xb0, 0x4b, 0x4f, 0x9e, 0x25, 0x19, 0xbc, 0x0f, 0x70, 0x47, 0xb6, 0x13, 0x8d, 0xd6, 0xa2, 0xa4, 0xeb, 0x14, + 0x0a, 0xd4, 0x8e, 0x3a, 0x11, 0xbb, 0xf9, 0x35, 0x87, 0xf9, 0x73, 0x15, 0x45, 0x95, 0xdb, 0x03, 0x91, 0xa8, 0x48, 0x8a, 0xbf, 0x5b, 0xea, 0x24, 0x4a, 0x8d, 0xcf, 0x6f, 0xc9, 0xef, 0x82, 0xb6, + 0x1a, 0x39, 0xe5, 0xe9, 0xdc, 0x64, 0xf8, 0x5d, 0x04, 0x30, 0xf0, 0x7f, 0xe5, 0x56, 0x44, 0x6c, 0x12, 0x2a, 0x20, 0xfd, 0xe3, 0x55, 0xf4, 0xd8, 0x2a, 0x9a, 0x10, 0xeb, 0xc7, 0xb5, 0x35, 0xc2, + 0xd4, 0x9b, 0x9c, 0xe7, 0xe8, 0x3a, 0xfb, 0x9c, 0xbf, 0x60, 0x46, 0x6f, 0xed, 0x95, 0x16, 0x60, 0xd0, 0x32, 0x9a, 0x81, 0x2c, 0x2f, 0xe7, 0xaf, 0x9e, 0x9c, 0x95, 0xc4, 0x98, 0x4a, 0x1b, 0x09, + 0xdb, 0x4e, 0x48, 0xcf, 0x0e, 0x26, 0x43, 0x98, 0x39, 0xc7, 0x36, 0x37, 0x5e, 0x5e, 0x96, 0x38, 0x89, 0xa4, 0xfa, 0x4a, 0xae, 0xf6, 0xf7, 0x7e, 0xd3, 0xe0, 0x9d, 0xb6, 0xf6, 0x8e, 0x8d, 0x24, + 0xa3, 0x5e, 0x42, 0x87, 0xda, 0x09, 0x9d, 0x7e, 0x20, 0xf6, 0xcc, 0xa4, 0x5e, 0xa2, 0xab, 0x21, 0x0b, 0x41, 0x9e, 0x94, 0x4e, 0x3c, 0x4e, 0xb4, 0x5c, 0x95, 0xb1, 0x98, 0xd1, 0x1e, 0x23, 0x1a, + 0xd5, 0xed, 0xa9, 0x14, 0x4a, 0x2b, 0x45, 0x86, 0x6a, 0xef, 0xd4, 0x8c, 0x62, 0xa4, 0xee, 0xc3, 0x92, 0x33, 0x12, 0x84, 0xaa, 0x98, 0x54, 0x8d, 0xe5, 0xd6, 0x8c, 0xc5, 0x23, 0x46, 0x29, 0xc2, + 0x02, 0x02, 0xd0, 0xf4, 0x10, 0x7f, 0x50, 0xce, 0x7a, 0x68, 0x84, 0xba, 0x74, 0xfa, 0x06, 0xba, 0x43, 0x56, 0xfe, 0x54, 0x7c, 0xc6, 0xbe, 0xfd, 0x94, 0x91, 0x5d, 0x3e, 0x4a, 0xb5, 0x9e, 0x08, + 0xa2, 0xb8, 0x3d, 0x80, 0x88, 0xac, 0xdc, 0xf2, 0xfa, 0x7e, 0xc5, 0xd0, 0xd4, 0xc7, 0x07, 0x42, 0x57, 0x37, 0xb0, 0x66, 0x1a, 0x68, 0xaf, 0x29, 0xf4, 0x28, 0x55, 0x70, 0x14, 0x2c, 0x9d, 0xb3, + 0x90, 0x53, 0x43, 0x73, 0xcf, 0xb6, 0x88, 0x9b, 0xd4, 0xe8, 0x92, 0x04, 0x92, 0x2a, 0xe6, 0x12, 0x59, 0xa6, 0x0f, 0x0a, 0x8b, 0xaa, 0xbf, 0x70, 0x34, 0x7e, 0x68, 0xbf, 0xdc, 0x97, 0xcc, 0xbf, + 0x02, 0x2f, 0xfc, 0xdb, 0xa2, 0x63, 0x63, 0xe9, 0xcb, 0xdc, 0x40, 0x08, 0xb0, 0x09, 0xcd, 0x81, 0xfa, 0xed, 0x8c, 0xef, 0x84, 0x5c, 0xc1, 0x55, 0xe1, 0xa7, 0x73, 0x36, 0x41, 0xac, 0x22, 0xe3, + 0x16, 0x0b, 0x84, 0x1c, 0x63, 0x6d, 0x02, 0xf6, 0xbb, 0x98, 0xcd, 0xb1, 0x73, 0x6b, 0x30, 0x0a, 0xd8, 0x95, 0x6f, 0x9f, 0x04, 0xea, 0x70, 0x0c, 0xa4, 0xbb, 0x7a, 0xe4, 0xc0, 0xdc, 0xa3, 0xa6, + 0xcf, 0xcc, 0xe3, 0xfc, 0xdf, 0xdc, 0xad, 0x61, 0x0d, 0xad, 0x3d, 0xf7, 0x76, 0xd3, 0x21, 0xd3, 0xc3, 0x7c, 0x33, 0x63, 0xa7, 0x1f, 0xf9, 0x2d, 0xdb, 0x3d, 0x71, 0xe4, 0xfd, 0x0c, 0xe3, 0xfa, + 0xd4, 0xa2, 0x00, 0xf9, 0xaa, 0xf0, 0x9e, 0x44, 0xa8, 0xd6, 0x00, 0x9a, 0x86, 0x03, 0x97, 0xef, 0x1c, 0x77, 0x24, 0xdc, 0x58, 0x3f, 0x19, 0x2e, 0x59, 0x65, 0xe2, 0xb2, 0x19, 0xf5, 0x58, 0x78, + 0xe5, 0x4e, 0xd7, 0x35, 0x3a, 0xa2, 0x97, 0x2e, 0xd9, 0xf4, 0x06, 0x0e, 0xab, 0x97, 0x05, 0x33, 0x82, 0xa9, 0x23, 0xa8, 0x8f, 0x0e, 0x87, 0xad, 0xbb, 0xde, 0xce, 0xcf, 0x56, 0x3c, 0x23, 0x5d, + 0x46, 0x7e, 0x0a, 0xcd, 0x13, 0x6e, 0x2e, 0x04, 0x7f, 0x0d, 0x7b, 0x73, 0x3b, 0x1b, 0x03, 0x97, 0x37, 0xa7, 0xae, 0x31, 0x4e, 0xaf, 0xc4, 0xcd, 0xfb, 0x12, 0xdb, 0x38, 0x86, 0xf9, 0x0a, 0xc3, + 0x30, 0xa8, 0x8c, 0xd8, 0xb3, 0x40, 0xd2, 0x0a, 0x08, 0x17, 0x88, 0xd6, 0x07, 0x1e, 0xc7, 0x55, 0x98, 0x4c, 0xb0, 0xb7, 0x26, 0xea, 0xb6, 0x25, 0xe1, 0x47, 0x84, 0x42, 0x3d, 0xaa, 0x4a, 0x1a, + 0x7c, 0x97, 0xc9, 0xb6, 0x92, 0x0f, 0x67, 0x7b, 0xcc, 0x78, 0xda, 0x87, 0xba, 0x6f, 0x7b, 0x30, 0x4c, 0x33, 0x83, 0x46, 0x9d, 0x14, 0x20, 0x30, 0x5d, 0x4b, 0x45, 0xf9, 0x3e, 0xbb, 0x0a, 0x2a, + 0x1b, 0x41, 0x19, 0x51, 0x39, 0xd2, 0x63, 0x04, 0x5e, 0x18, 0x14, 0x70, 0xcc, 0x2c, 0xc7, 0x86, 0x07, 0x4d, 0x7b, 0x72, 0x50, 0x35, 0xce, 0x21, 0x3e, 0x3b, 0x30, 0x73, 0xb1, 0x7c, 0xb8, 0x50, + 0x21, 0x09, 0xdd, 0x1b, 0x68, 0x5d, 0xc8, 0xe8, 0xfa, 0x94, 0x04, 0x91, 0xe1, 0x6a, 0xf0, 0x4e, 0xa6, 0xef, 0xfd, 0x31, 0xc4, 0xd0, 0x8e, 0x87, 0x04, 0xa6, 0xde, 0x19, 0x0d, 0xf8, 0x23, 0x3c, + 0x84, 0x39, 0xb7, 0x10, 0x5f, 0x2b, 0xd3, 0xe8, 0x69, 0xd7, 0x44, 0x95, 0x94, 0x20, 0x35, 0x9f, 0xc8, 0x84, 0xd6, 0xdf, 0x0d, 0xc9, 0x0f, 0x35, 0x2d, 0x93, 0x80, 0x54, 0x32, 0x3b, 0xb2, 0x29, + 0x42, 0xb5, 0x08, 0xfd, 0x39, 0x3b, 0x25, 0x7c, 0x45, 0x64, 0x2f, 0x7f, 0xac, 0x96, 0x0c, 0x36, 0x1c, 0x9d, 0x1c, 0xe6, 0x26, 0x44, 0x03, 0xef, 0xe9, 0x0b, 0x40, 0xf9, 0xb1, 0xca, 0xda, 0x64, + 0x7d, 0xb9, 0x02, 0xe8, 0x9b, 0xb2, 0xa9, 0x71, 0x48, 0x09, 0xb7, 0x1c, 0x2d, 0xd2, 0x01, 0x4a, 0x89, 0x73, 0x03, 0x64, 0xf9, 0xb7, 0xee, 0xac, 0xd5, 0x08, 0x62, 0x68, 0xcb, 0x2a, 0xa9, 0x5f, + 0xd3, 0x1a, 0x92, 0x75, 0xf7, 0x99, 0x01, 0xb8, 0x9d, 0x65, 0xfe, 0xff, 0x6c, 0x8b, 0xad, 0x7b, 0xf9, 0x55, 0x77, 0xb2, 0x99, 0x36, 0xb0, 0x98, 0x55, 0xde, 0xd7, 0x64, 0xf7, 0x2f, 0x1f, 0x35, + 0xd6, 0x86, 0x16, 0x5c, 0xf8, 0x7a, 0x52, 0x73, 0x54, 0x4c, 0x97, 0x14, 0xc6, 0x96, 0x4c, 0xd6, 0xfc, 0x6b, 0xa2, 0x91, 0x19, 0x40, 0x9a, 0x11, 0x6c, 0x06, 0x89, 0x1d, 0x0d, 0xdc, 0xda, 0xab, + 0xfe, 0x5e, 0x30, 0xe6, 0xf6, 0xd5, 0xc0, 0x2f, 0x59, 0x5f, 0xbc, 0xaf, 0xb6, 0x79, 0xae, 0xf4, 0x25, 0xe1, 0xdb, 0xa7, 0x0b, 0x69, 0x94, 0x56, 0x7b, 0xf2, 0x12, 0x09, 0xd3, 0x0c, 0x02, 0x7f, + 0x34, 0x9c, 0xec, 0x0a, 0x7c, 0x6a, 0xcb, 0xa7, 0xe7, 0xa4, 0x87, 0x60, 0x35, 0x8c, 0x32, 0xbf, 0xcf, 0xdc, 0x5d, 0x84, 0x88, 0x38, 0x77, 0x6d, 0x28, 0x86, 0x50, 0x3d, 0xa6, 0xdb, 0xb0, 0xa9, + 0x64, 0xce, 0xb1, 0x0d, 0xb0, 0x06, 0x58, 0x36, 0xba, 0x76, 0x23, 0x3f, 0x4f, 0xee, 0x35, 0x73, 0xe1, 0x2d, 0xe8, 0x7c, 0x91, 0x1a, 0x58, 0x05, 0xb2, 0xd2, 0x1b, 0x5b, 0xeb, 0xe3, 0xeb, 0x73, + 0x01, 0x3b, 0xf3, 0xad, 0x2b, 0xda, 0xe4, 0x74, 0x9c, 0x18, 0x1d, 0x3d, 0x1f, 0x18, 0x42, 0x19, 0x03, 0xbd, 0xb2, 0xb3, 0xcb, 0xf8, 0xc1, 0xd2, 0xa8, 0xf5, 0x4b, 0xbb, 0xcf, 0xa7, 0x35, 0x85, + 0xdc, 0x7d, 0x1b, 0xb5, 0x71, 0x6a, 0x02, 0x7e, 0x0c, 0x1b, 0x66, 0x72, 0xfd, 0x4b, 0x16, 0x21, 0x6a, 0x20, 0x67, 0xd9, 0xaa, 0xc3, 0x29, 0x7c, 0xb1, 0xf6, 0x36, 0x1d, 0xb9, 0xd7, 0x3e, 0xa9, + 0xd4, 0xdc, 0x9b, 0x66, 0x12, 0x3d, 0x44, 0x6a, 0xda, 0x59, 0xe6, 0x0d, 0x99, 0x93, 0x2c, 0x4f, 0xe1, 0xc4, 0x23, 0xf4, 0xc6, 0xac, 0x7b, 0x7a, 0xd9, 0x10, 0xf2, 0x75, 0x1c, 0xbb, 0xa2, 0xed, + 0xf7, 0xfa, 0x77, 0x9f, 0x50, 0x51, 0x1d, 0x53, 0x7f, 0x74, 0x18, 0xfe, 0xb8, 0x91, 0x3a, 0xcd, 0x51, 0x95, 0x7f, 0xf2, 0x04, 0xf0, 0x40, 0x5b, 0xf9, 0x2a, 0xb9, 0xf7, 0xc9, 0x4c, 0x93, 0x3f, + 0x8f, 0xc4, 0xa9, 0x9c, 0x9e, 0x42, 0xb7, 0x99, 0x8e, 0x08, 0x33, 0x34, 0x05, 0xe0, 0x3f, 0xad, 0xb9, 0x7f, 0xa7, 0xb6, 0xfc, 0xf5, 0xf1, 0xed, 0x42, 0x3e, 0xca, 0xcd, 0xe8, 0x92, 0x20, 0xa9, + 0x98, 0x12, 0x82, 0x34, 0x4d, 0x0e, 0x22, 0x4d, 0x7c, 0xc9, 0x07, 0x2b, 0x0d, 0x21, 0x4a, 0x9e, 0xfd, 0xa6, 0xd8, 0x1b, 0xef, 0x06, 0x9a, 0x11, 0x5b, 0x05, 0xc1, 0x43, 0xc2, 0xd7, 0x1e, 0x4d, + 0xe5, 0xa0, 0x17, 0x0c, 0xaf, 0xdb, 0xda, 0x82, 0x3e, 0x7a, 0x08, 0xd9, 0xd3, 0x5c, 0xb6, 0x7e, 0x4f, 0xb4, 0x25, 0x62, 0xdc, 0x4e, 0x68, 0x95, 0xf4, 0x18, 0x93, 0x50, 0xd9, 0x39, 0x40, 0xc4, + 0x8c, 0x8e, 0x6e, 0x2b, 0x56, 0x51, 0x0f, 0x1c, 0x91, 0x39, 0xa1, 0x8b, 0x01, 0x4e, 0x89, 0xe4, 0xb3, 0xb7, 0x4a, 0xa5, 0x59, 0x00, 0xd1, 0x73, 0x26, 0x5b, 0x73, 0x6a, 0xb4, 0x29, 0x53, 0x25, + 0x69, 0x7a, 0x0c, 0x67, 0x32, 0x56, 0x3c, 0xc3, 0x71, 0xbb, 0x33, 0x7e, 0xe7, 0xc5, 0xd7, 0xfe, 0xc0, 0x96, 0xa8, 0xd1, 0xed, 0x64, 0x28, 0x51, 0x10, 0x31, 0x82, 0xed, 0x02, 0xba, 0xe2, 0x4f, + 0x9d, 0x67, 0xe1, 0x77, 0xd4, 0xd0, 0x51, 0xda, 0x3b, 0x39, 0x37, 0x3d, 0x60, 0xe6, 0x2a, 0x46, 0x3a, 0x24, 0xf6, 0x1e, 0xd3, 0xba, 0x4d, 0x2e, 0xcf, 0xc6, 0xc9, 0x50, 0xa2, 0x3e, 0x88, 0x24, + 0x8c, 0x9b, 0xaf, 0x54, 0xd2, 0x1c, 0x3c, 0x4a, 0x80, 0xb8, 0xeb, 0x73, 0x1b, 0xe6, 0x6b, 0x36, 0x7d, 0xc6, 0x82, 0x26, 0x7d, 0x6d, 0x4c, 0x1b, 0x83, 0x89, 0x84, 0xea, 0x1f, 0xb3, 0x35, 0xa3, + 0xc3, 0x27, 0x03, 0x54, 0x82, 0x4e, 0x18, 0xb2, 0x00, 0xf1, 0x48, 0xc5, 0x15, 0x97, 0x05, 0xd1, 0xb5, 0x1f, 0xc3, 0xdc, 0xaa, 0xc4, 0x2f, 0xd4, 0x15, 0x6f, 0xa4, 0x5d, 0xf7, 0xb3, 0xfd, 0x33, + 0xf8, 0x97, 0x1a, 0x9d, 0xca, 0x12, 0xff, 0x22, 0xfb, 0xc2, 0x83, 0xd7, 0x46, 0x1f, 0xff, 0xbb, 0x22, 0x75, 0x27, 0x20, 0xc4, 0x1f, 0x2a, 0xbd, 0x90, 0xc9, 0xf7, 0x65, 0x33, 0x1d, 0xd5, 0x76, + 0xce, 0xef, 0x52, 0x5a, 0x55, 0x76, 0x76, 0x61, 0x6f, 0x24, 0x68, 0xd0, 0xef, 0xe6, 0x2d, 0x00, 0xe2, 0x81, 0xe6, 0x85, 0x70, 0xda, 0x25, 0xe9, 0xff, 0x53, 0x3c, 0x1a, 0xd9, 0x6a, 0x02, 0x70, + 0xd1, 0x40, 0xf6, 0x3d, 0x09, 0xd8, 0x3d, 0xc7, 0xe2, 0xbe, 0x0c, 0xa7, 0x57, 0x1b, 0x9d, 0xf3, 0xda, 0xc9, 0x32, 0xee, 0x5c, 0xab, 0xc2, 0x6f, 0x7f, 0xdb, 0x2a, 0xd9, 0x49, 0x02, 0xf0, 0x61, + 0x6b, 0xfc, 0x44, 0xb5, 0xbd, 0x81, 0x1c, 0x96, 0xc0, 0xf2, 0x2e, 0x7f, 0xe0, 0x2c, 0x79, 0x4c, 0x31, 0x34, 0x8d, 0xe9, 0xf2, 0xa6, 0x69, 0x5c, 0x98, 0x11, 0xf7, 0x65, 0x0b, 0xf0, 0xaf, 0xf9, + 0x11, 0x4e, 0xfb, 0x99, 0xb5, 0x40, 0xd2, 0x1b, 0xf7, 0x27, 0x2b, 0x92, 0xde, 0x06, 0xb5, 0x9f, 0xd8, 0x41, 0x8c, 0x34, 0xb6, 0x83, 0xc6, 0xc8, 0x4b, 0x94, 0x0b, 0x8c, 0xac, 0xa1, 0x48, 0x60, + 0x71, 0xf0, 0x22, 0xeb, 0x14, 0x67, 0xe5, 0x43, 0x90, 0x1f, 0x04, 0xac, 0x5a, 0xc6, 0x66, 0x01, 0x58, 0x2f, 0x57, 0x2d, 0xa3, 0x9a, 0x4f, 0xe5, 0x55, 0xb0, 0xe4, 0xbc, 0x72, 0x16, 0x4c, 0xaa, + 0xfc, 0xb6, 0x28, 0xc6, 0xe0, 0x06, 0xf2, 0xea, 0xd5, 0xf2, 0xce, 0xdf, 0x9e, 0xb3, 0x6b, 0x73, 0xd2, 0x3d, 0xf7, 0x56, 0x99, 0x3c, 0x9b, 0xc6, 0x6e, 0x5b, 0x33, 0x85, 0x56, 0xcd, 0xba, 0x1e, + 0x69, 0x91, 0xf4, 0x5e, 0xae, 0x62, 0xd3, 0x05, 0x5a, 0x31, 0x3b, 0xed, 0xd9, 0xda, 0xbd, 0x94, 0x10, 0xb7, 0x9d, 0xe3, 0x9b, 0xd6, 0xe6, 0x45, 0x68, 0x89, 0x86, 0xde, 0x7c, 0xde, 0xde, 0x5b, + 0x4f, 0x75, 0x7a, 0x2c, 0xc1, 0x5f, 0xbf, 0xce, 0xbf, 0x27, 0xb7, 0x9b, 0x2a, 0xa3, 0x87, 0x0c, 0xc0, 0x1e, 0xaf, 0x4a, 0x16, 0xfc, 0x7a, 0x8b, 0x97, 0xef, 0x21, 0xd9, 0x77, 0x72, 0xf6, 0xdc, + 0x36, 0x7c, 0x4d, 0xfa, 0x5b, 0x1d, 0x25, 0x8c, 0x03, 0x18, 0x0a, 0x35, 0x4f, 0x93, 0x26, 0xea, 0x3a, 0x3a, 0xf8, 0x45, 0x39, 0x50, 0xbf, 0xa6, 0x6f, 0x70, 0xd2, 0xb5, 0xb0, 0xdd, 0x35, 0x41, + 0x7b, 0x0a, 0x76, 0x99, 0x38, 0x8f, 0x0f, 0x30, 0x27, 0xf5, 0xfe, 0x45, 0xe8, 0x2c, 0x66, 0xc9, 0xd6, 0x27, 0x33, 0x0e, 0xb4, 0x20, 0xae, 0xf6, 0x8d, 0x9d, 0x16, 0x64, 0x50, 0x28, 0x7f, 0xcb, + 0xde, 0x0b, 0x7b, 0x1e, 0x00, 0x33, 0x0d, 0x09, 0x86, 0xc3, 0x99, 0xf9, 0x15, 0x3d, 0x3e, 0x2e, 0xcf, 0xca, 0x7e, 0x7a, 0x2c, 0xf4, 0x39, 0xc1, 0x87, 0x88, 0x6d, 0x5b, 0x46, 0xa4, 0xbd, 0x0b, + 0x24, 0x06, 0x65, 0x75, 0xb7, 0x00, 0x75, 0x5e, 0xab, 0x8a, 0xf5, 0x69, 0xf0, 0x32, 0xe2, 0xc7, 0x86, 0xeb, 0x68, 0xa3, 0xde, 0x5d, 0xe5, 0xe3, 0x94, 0x15, 0xc4, 0x03, 0x7b, 0x0b, 0x91, 0x8a, + 0x6f, 0xe9, 0x1f, 0x64, 0x1e, 0xaa, 0xc7, 0xa1, 0x8f, 0xa7, 0xa6, 0x1f, 0x30, 0xbd, 0x1e, 0xa3, 0xe9, 0x80, 0x9b, 0x76, 0xeb, 0xa7, 0x95, 0xba, 0xed, 0xaf, 0x8f, 0xc2, 0x7d, 0xb9, 0xb5, 0x7f, + 0x02, 0x92, 0xe3, 0x9f, 0xf5, 0xca, 0xaf, 0xc6, 0x02, 0x5d, 0xa2, 0xaf, 0x3c, 0xf7, 0xb2, 0xa4, 0x47, 0xc3, 0x69, 0xcb, 0x0b, 0x5b, 0xb6, 0x1d, 0xef, 0xbe, 0xfb, 0x52, 0xbe, 0x03, 0xea, 0x2d, + 0x4a, 0x1f, 0xea, 0x2f, 0x60, 0x41, 0x53, 0x92, 0xa7, 0xe4, 0xde, 0x34, 0xc2, 0x3b, 0x83, 0x76, 0x8d, 0x6f, 0x2b, 0xa4, 0x2f, 0xcb, 0x72, 0x2c, 0x27, 0xb4, 0x3e, 0x67, 0x83, 0xe3, 0x27, 0xbf, + 0x9e, 0xa2, 0x3f, 0x39, 0x15, 0x12, 0x86, 0x4e, 0x8b, 0x7d, 0xfd, 0xf4, 0x17, 0xe0, 0x2e, 0x5e, 0xd0, 0xc9, 0x5a, 0xbb, 0x14, 0x02, 0x2b, 0xa5, 0xa7, 0x27, 0xef, 0x47, 0x15, 0xc9, 0x72, 0xa0, + 0x19, 0x88, 0xf5, 0xab, 0xc1, 0x2b, 0x14, 0x30, 0x0c, 0xde, 0x69, 0x8b, 0x7d, 0x86, 0x19, 0x86, 0xb8, 0x29, 0x3f, 0x9c, 0xce, 0x48, 0x7c, 0x62, 0x3a, 0x40, 0x4e, 0x7c, 0x2b, 0x21, 0xeb, 0x39, + 0xf7, 0xca, 0xb4, 0x52, 0xbf, 0x4a, 0xa6, 0x48, 0xcb, 0x1e, 0xe9, 0xc0, 0xe8, 0x20, 0x6d, 0xd5, 0x6c, 0xb2, 0x75, 0xc1, 0x50, 0x09, 0x1b, 0x5f, 0x14, 0xe9, 0x1b, 0x70, 0xb0, 0x4b, 0xd1, 0xeb, + 0xbd, 0x2d, 0x57, 0x44, 0xa0, 0x0b, 0x36, 0x98, 0x66, 0x98, 0xb0, 0xaf, 0x6e, 0xb5, 0x72, 0x62, 0xf4, 0x47, 0x0f, 0x81, 0x66, 0x72, 0x68, 0xc5, 0x4b, 0x69, 0xcb, 0x7b, 0xef, 0xe0, 0x06, 0x2a, + 0xc0, 0x26, 0x97, 0x6b, 0xb3, 0x7b, 0x1b, 0x65, 0xe1, 0x5c, 0x66, 0x7c, 0xb6, 0x86, 0x6d, 0x7b, 0xe7, 0xb8, 0x14, 0xdb, 0x72, 0x59, 0x41, 0x08, 0x7d, 0xe9, 0xba, 0x23, 0x63, 0x28, 0x07, 0x37, + 0xd5, 0x8e, 0x51, 0x06, 0x2c, 0x24, 0x7e, 0x1f, 0x60, 0x84, 0x6b, 0x87, 0x74, 0x89, 0x68, 0xdb, 0x79, 0xc4, 0x5d, 0xf3, 0xca, 0x4a, 0xe1, 0xc2, 0x81, 0xba, 0x3f, 0x6c, 0x08, 0xd3, 0x58, 0xf9, + 0x5d, 0xbc, 0xbe, 0xe1, 0x63, 0x71, 0xc6, 0x4f, 0xed, 0x76, 0xd3, 0x2a, 0x70, 0x65, 0xe6, 0x2a, 0xb1, 0x7a, 0xb3, 0x55, 0x63, 0x21, 0x0d, 0x12, 0xd4, 0x73, 0x67, 0xbe, 0xbf, 0x4a, 0x80, 0xa7, + 0x00, 0x68, 0xc1, 0x09, 0x92, 0xa8, 0x7b, 0x4c, 0xb4, 0xac, 0xc1, 0x54, 0x87, 0x94, 0x81, 0xfc, 0xc8, 0x96, 0xe2, 0xa9, 0x73, 0x00, 0xc4, 0x9d, 0x3b, 0x40, 0xda, 0xc1, 0x6e, 0x5f, 0x23, 0x28, + 0xe1, 0xb6, 0x82, 0x07, 0x60, 0x9b, 0x40, 0xd4, 0xa6, 0x29, 0x14, 0xfc, 0x74, 0xc8, 0xe5, 0xc0, 0x1c, 0x64, 0xef, 0x5b, 0x9a, 0xf9, 0xae, 0x57, 0x34, 0x78, 0x8e, 0xf3, 0xd6, 0x9c, 0x76, 0x68, + 0x38, 0xea, 0x07, 0x0a, 0x79, 0xca, 0xe8, 0xc0, 0x16, 0xfa, 0x2b, 0x9d, 0x95, 0x97, 0x59, 0x53, 0xdd, 0xa9, 0xf7, 0xeb, 0xc6, 0x8b, 0x28, 0x1c, 0xe7, 0xaf, 0xa9, 0x0e, 0x38, 0x6f, 0xd3, 0xed, + 0x58, 0x76, 0x2f, 0xb8, 0x5e, 0xe2, 0x5f, 0x56, 0x96, 0x12, 0x4e, 0xd7, 0xba, 0x93, 0x49, 0x62, 0xef, 0x5e, 0xcc, 0x72, 0x55, 0x81, 0xf4, 0xb9, 0x88, 0xc5, 0xfb, 0x54, 0x05, 0x3c, 0xc8, 0xd7, + 0x13, 0x56, 0x75, 0x53, 0xe9, 0x8c, 0x69, 0xf7, 0x2a, 0xf7, 0x08, 0x0c, 0x90, 0x2e, 0xe5, 0xa7, 0x0f, 0xb6, 0x82, 0xd0, 0x13, 0x26, 0xfd, 0xa4, 0x82, 0xbf, 0xdb, 0xc7, 0x68, 0xb0, 0x38, 0x31, + 0x43, 0x1f, 0xd0, 0x64, 0x82, 0x4d, 0x98, 0x5a, 0x57, 0xdd, 0xd4, 0x1b, 0xea, 0x24, 0x7e, 0x63, 0x30, 0x90, 0x7d, 0xe2, 0x37, 0xed, 0xb7, 0x29, 0x3e, 0x4b, 0xf4, 0x86, 0xe1, 0xde, 0x75, 0x7c, + 0x15, 0x0d, 0x3b, 0xff, 0xc2, 0x99, 0xb4, 0xe7, 0x30, 0x7e, 0x30, 0x3f, 0xbf, 0x20, 0x20, 0x86, 0xe3, 0x43, 0xa8, 0x0a, 0x18, 0x7e, 0xf5, 0xca, 0xda, 0x8a, 0x46, 0x4b, 0x4a, 0x06, 0xed, 0x56, + 0x7a, 0x44, 0xc8, 0xe3, 0x16, 0x27, 0xb7, 0x21, 0xd9, 0xf1, 0xb1, 0xf5, 0x59, 0x6e, 0xc9, 0x94, 0xd5, 0xaf, 0xe2, 0xaf, 0x63, 0x61, 0xb3, 0x69, 0x7f, 0x2e, 0x35, 0x39, 0xd9, 0x34, 0x05, 0x03, + 0xe9, 0x24, 0xd9, 0xec, 0x31, 0xdc, 0x01, 0x11, 0x59, 0x72, 0x01, 0x53, 0x03, 0xaf, 0x59, 0x0f, 0x2b, 0x85, 0x1f, 0x03, 0x76, 0x37, 0x25, 0x7d, 0x91, 0x29, 0x38, 0x40, 0x1f, 0xe9, 0xd7, 0x1b, + 0xb0, 0x00, 0x7a, 0xdd, 0x19, 0xbd, 0xdb, 0x15, 0x0c, 0x49, 0x63, 0xad, 0x3d, 0x95, 0xf8, 0xe0, 0x7c, 0xe0, 0x18, 0x34, 0x8a, 0xf4, 0xd7, 0x2f, 0x6a, 0x6c, 0xef, 0x27, 0x02, 0xbd, 0x79, 0xca, + 0x0e, 0xad, 0x53, 0x39, 0xe3, 0xdf, 0xf6, 0x57, 0x97, 0x24, 0x63, 0x6d, 0x74, 0x3f, 0xa4, 0x96, 0xfb, 0xe5, 0xf1, 0x5a, 0x52, 0xc4, 0x93, 0xda, 0xe8, 0xd8, 0x7c, 0xed, 0xe4, 0x7b, 0x60, 0xdf, + 0x6c, 0x4e, 0xb7, 0x55, 0xb5, 0x05, 0xf6, 0xcf, 0xb8, 0x02, 0x7e, 0x0e, 0x45, 0x9c, 0x55, 0x23, 0xcf, 0x2f, 0xe6, 0x44, 0x1c, 0xfc, 0x4c, 0x6f, 0xc6, 0x2c, 0x45, 0x26, 0xb0, 0xf1, 0xf6, 0x80, + 0x94, 0xcb, 0xcd, 0x4e, 0x9a, 0x89, 0x35, 0xe1, 0x4f, 0x3c, 0x82, 0xa9, 0x2d, 0x80, 0xd9, 0x1b, 0x01, 0x8d, 0x0c, 0xea, 0xe8, 0xf4, 0xf9, 0xd4, 0x17, 0x18, 0xfc, 0xec, 0xe8, 0x57, 0xee, 0x33, + 0x22, 0x0e, 0x08, 0x5f, 0x20, 0x2b, 0xba, 0x76, 0xf2, 0x96, 0x97, 0x05, 0x3f, 0xf1, 0x8c, 0xbe, 0x6e, 0x94, 0x7b, 0x6d, 0x1f, 0x2a, 0x4c, 0xf9, 0xf7, 0xb4, 0x7d, 0xfb, 0xeb, 0x06, 0x1a, 0x3e, + 0x40, 0xa6, 0xa7, 0x25, 0x2e, 0xdf, 0x5a, 0xe9, 0x02, 0xa6, 0xc9, 0x82, 0x80, 0xea, 0x53, 0x85, 0x86, 0x57, 0xc3, 0x4c, 0xe7, 0xb0, 0x18, 0x95, 0x90, 0xf6, 0xfd, 0xe3, 0x5e, 0x55, 0xb4, 0xde, + 0x88, 0x73, 0xd3, 0x51, 0xd0, 0xa5, 0x43, 0x9c, 0x15, 0xa2, 0xed, 0x8f, 0x12, 0x7f, 0x64, 0xfc, 0xfe, 0x74, 0x9a, 0xfc, 0x6c, 0xf9, 0xc7, 0xf4, 0xfb, 0x83, 0x2d, 0xe2, 0x88, 0xb0, 0x09, 0x38, + 0xf9, 0xc0, 0xc4, 0x57, 0x70, 0x5c, 0x53, 0x35, 0xa9, 0xac, 0x8a, 0x94, 0x28, 0xef, 0xe2, 0xb5, 0x68, 0x56, 0xeb, 0x03, 0xb8, 0x5b, 0x3e, 0x8c, 0xaa, 0x4c, 0xbe, 0xcd, 0x3e, 0xba, 0x9d, 0xf4, + 0xd2, 0x1d, 0x60, 0xb5, 0xc8, 0x28, 0x90, 0xf8, 0x30, 0x42, 0x7e, 0xb1, 0x1c, 0x97, 0x74, 0x69, 0xeb, 0x99, 0x5d, 0xf8, 0x24, 0x35, 0x7d, 0x27, 0x2d, 0xa6, 0x89, 0x5b, 0x8d, 0x7b, 0x26, 0x6f, + 0xa4, 0x36, 0xea, 0xb1, 0x13, 0xd5, 0xf1, 0xff, 0x30, 0x10, 0xe5, 0x77, 0x6b, 0xa4, 0x79, 0x74, 0x86, 0x54, 0x39, 0xfd, 0xdb, 0x6b, 0xca, 0xe4, 0xd1, 0x52, 0x1b, 0x66, 0xc6, 0x6d, 0xe4, 0x50, + 0xb1, 0x51, 0xed, 0xdb, 0x63, 0x3d, 0x48, 0xf8, 0x34, 0xf7, 0xd7, 0x1e, 0x92, 0xb0, 0x9a, 0x8b, 0x2e, 0x15, 0x0b, 0x26, 0xee, 0x6e, 0x40, 0x3b, 0xe3, 0xdf, 0xf7, 0xdc, 0xcd, 0x6b, 0xa3, 0x00, + 0xb9, 0x3e, 0x3c, 0x99, 0xb9, 0x16, 0xa3, 0x59, 0xc6, 0xd4, 0x25, 0x28, 0x2a, 0xba, 0x40, 0xba, 0x22, 0xf7, 0x31, 0x20, 0x21, 0xaf, 0x2f, 0x48, 0x01, 0x85, 0x97, 0x75, 0x08, 0x91, 0x16, 0x3b, + 0x73, 0xab, 0x80, 0x01, 0xac, 0x37, 0x28, 0xc3, 0xa0, 0xb7, 0x25, 0xdc, 0xb8, 0x95, 0xfa, 0x7f, 0x67, 0xe5, 0x85, 0x7c, 0xc1, 0xb3, 0xe7, 0x27, 0xad, 0x79, 0x46, 0x30, 0x9d, 0xdb, 0xe6, 0x1d, + 0x3d, 0xcc, 0x07, 0x2f, 0x01, 0xd9, 0xde, 0xb5, 0x82, 0x9c, 0xed, 0x05, 0xc3, 0x7b, 0x2f, 0x8f, 0x57, 0xc8, 0x86, 0xa1, 0x64, 0xab, 0x87, 0x29, 0x87, 0xd0, 0x77, 0xd1, 0x31, 0xf0, 0x2e, 0xdf, + 0x3f, 0xf8, 0x96, 0xe4, 0xbd, 0xc7, 0xd5, 0xd1, 0x5b, 0xfa, 0x18, 0xfd, 0xff, 0xc7, 0x8d, 0x93, 0x4f, 0xdf, 0x29, 0x48, 0x9d, 0x83, 0x29, 0x1b, 0xce, 0xb4, 0x7a, 0x86, 0x27, 0x85, 0x42, 0x90, + 0x9b, 0x90, 0xcf, 0xc6, 0xea, 0xd1, 0xe7, 0x42, 0x58, 0x3a, 0x4e, 0x5b, 0x0b, 0x2a, 0x7e, 0x3a, 0x42, 0x17, 0xe7, 0xe8, 0x16, 0x8c, 0x83, 0x13, 0x37, 0xf1, 0xa9, 0xc1, 0x63, 0x80, 0xd0, 0xb6, + 0x81, 0x83, 0x2c, 0xdb, 0x70, 0xc8, 0xd2, 0xfb, 0x8e, 0x43, 0xcf, 0x48, 0xe8, 0x40, 0xa0, 0x19, 0xe2, 0x4f, 0x7f, 0x3f, 0x21, 0xb6, 0x0f, 0xaf, 0x2a, 0x1e, 0xfb, 0xdb, 0x11, 0x6c, 0x6e, 0xca, + 0xf2, 0xc3, 0x7f, 0x94, 0x14, 0x15, 0xde, 0x97, 0x06, 0xba, 0x66, 0x6b, 0x08, 0x5e, 0x7f, 0x51, 0xaa, 0xa6, 0x48, 0x10, 0x4b, 0x34, 0xf2, 0x1e, 0xdf, 0xf7, 0x0b, 0xd6, 0xdc, 0x85, 0x57, 0x90, + 0x35, 0x34, 0x0e, 0x82, 0x7f, 0x6d, 0xe4, 0x03, 0x09, 0x7a, 0xff, 0xd8, 0x3a, 0x4a, 0x1f, 0x20, 0xe9, 0x59, 0x65, 0xbf, 0x64, 0xd9, 0x9e, 0xf1, 0xbb, 0x14, 0xf4, 0x3d, 0xe5, 0x51, 0x35, 0xb6, + 0xac, 0x9d, 0x40, 0x9c, 0xa8, 0x3a, 0x2a, 0x7a, 0xbc, 0xa3, 0x37, 0xc5, 0x1c, 0x62, 0x30, 0x08, 0x4c, 0x71, 0x68, 0x2a, 0x6b, 0x8c, 0x4f, 0x41, 0xc7, 0xed, 0x20, 0x20, 0xd5, 0xa9, 0xcd, 0x46, + 0x13, 0x0b, 0x8a, 0x0f, 0xbe, 0xcf, 0x1e, 0xb4, 0xa6, 0xce, 0x07, 0x82, 0x50, 0x15, 0x7f, 0xce, 0xfa, 0x05, 0xce, 0x4e, 0x9e, 0x73, 0xa3, 0x14, 0x7b, 0x7a, 0xda, 0xf5, 0x73, 0x7d, 0x43, 0x69, + 0x86, 0x0e, 0x0e, 0xa3, 0x7a, 0x6e, 0x50, 0x10, 0x12, 0x2d, 0x07, 0x9b, 0x30, 0x31, 0xbc, 0x63, 0x98, 0x16, 0x8c, 0x68, 0x19, 0x3f, 0xc5, 0x2c, 0x55, 0x87, 0x6e, 0x43, 0x76, 0xb7, 0x56, 0x1e, + 0x08, 0x14, 0x07, 0x4e, 0x63, 0xa0, 0xfc, 0xc7, 0xd1, 0xcb, 0xc5, 0x6b, 0x19, 0x24, 0xb7, 0x61, 0x9f, 0x0f, 0x70, 0xf0, 0xfb, 0x12, 0xb5, 0xc5, 0xf2, 0x51, 0x51, 0x47, 0xf2, 0x03, 0x75, 0x1f, + 0x1c, 0xa9, 0xac, 0x2d, 0xc0, 0x24, 0xbd, 0x1f, 0x69, 0xde, 0x90, 0x00, 0xe9, 0x7e, 0x32, 0x8a, 0xd7, 0x73, 0xad, 0x72, 0x84, 0x75, 0xcf, 0x9c, 0x5b, 0xcd, 0x24, 0xa4, 0xc8, 0x61, 0xed, 0x36, + 0x43, 0xf3, 0xe2, 0x30, 0xcc, 0x8a, 0x88, 0xa7, 0x1e, 0xc2, 0x08, 0xb6, 0xac, 0xaa, 0xd5, 0x45, 0x4c, 0x26, 0x65, 0xc9, 0xb6, 0x94, 0x0d, 0x19, 0x48, 0xfa, 0xaa, 0xd6, 0x6d, 0xab, 0x08, 0x11, + 0x1a, 0x41, 0x01, 0xa8, 0x7f, 0xf9, 0x68, 0x62, 0xb6, 0x63, 0xb6, 0xd2, 0xe5, 0x79, 0x1c, 0xc3, 0x13, 0x37, 0xba, 0xb1, 0x2a, 0xd0, 0x45, 0x46, 0xda, 0xce, 0x4f, 0x23, 0x22, 0x67, 0xb1, 0x21, + 0xbe, 0x9f, 0xb3, 0x63, 0x4d, 0xa7, 0x3d, 0x9d, 0x78, 0xbc, 0xcc, 0x61, 0xce, 0x01, 0x71, 0x8a, 0x10, 0x28, 0x73, 0x69, 0x5a, 0xa7, 0xff, 0x62, 0x2d, 0xa8, 0xb6, 0x4e, 0x58, 0x18, 0xba, 0x18, + 0xc5, 0x87, 0x28, 0xbf, 0xf5, 0xff, 0x4d, 0x02, 0x86, 0x9c, 0x4e, 0x74, 0x30, 0x48, 0xeb, 0x0c, 0x94, 0x3a, 0x3e, 0xb4, 0x04, 0xe8, 0x7f, 0x5c, 0x45, 0x08, 0x3e, 0xb8, 0x79, 0xed, 0x7e, 0x32, + 0x98, 0x9c, 0xe3, 0xec, 0x31, 0x33, 0x83, 0x40, 0x6a, 0x92, 0x2c, 0x08, 0x16, 0x16, 0x9d, 0xee, 0x6b, 0xd7, 0x51, 0x6d, 0xd1, 0xa2, 0xe2, 0xbd, 0x85, 0x24, 0x9d, 0x81, 0xd2, 0x13, 0x64, 0x7e, + 0xaf, 0x30, 0xda, 0x85, 0x07, 0x94, 0x46, 0x49, 0x7b, 0xd8, 0x4b, 0xcb, 0x08, 0xdc, 0x0d, 0x21, 0x9d, 0x56, 0x3d, 0xb2, 0x58, 0x70, 0xd8, 0x3e, 0x48, 0xf1, 0xd5, 0xb3, 0xba, 0xda, 0x71, 0x1e, + 0x98, 0x03, 0x59, 0x8b, 0x90, 0xe1, 0x76, 0xa3, 0x03, 0x08, 0x08, 0x69, 0xb7, 0x83, 0x2e, 0xd0, 0x48, 0xee, 0xe4, 0x6c, 0xeb, 0x6b, 0xe5, 0x63, 0x06, 0xf6, 0xf0, 0x3f, 0x0c, 0xdc, 0x28, 0x67, + 0xda, 0xad, 0xde, 0x10, 0x78, 0x4c, 0x2e, 0x81, 0x24, 0x2f, 0x92, 0xe0, 0xbc, 0x4b, 0x53, 0xd1, 0x69, 0x14, 0xaf, 0x8a, 0xd6, 0xf3, 0xd7, 0xbf, 0x95, 0xcb, 0xd4, 0x04, 0x92, 0xd8, 0xd4, 0x2b, + 0xdb, 0xb0, 0xf5, 0x64, 0xa8, 0xee, 0x9e, 0x96, 0x03, 0xbc, 0x7f, 0x57, 0x86, 0x92, 0x3b, 0xb0, 0x73, 0x5e, 0x1e, 0xdd, 0x52, 0x4c, 0xd9, 0x48, 0xb7, 0x7f, 0xaf, 0x53, 0xc0, 0x16, 0x03, 0x42, + 0x29, 0xbc, 0x62, 0x65, 0x2a, 0x52, 0x9d, 0x7d, 0xa0, 0x71, 0x29, 0x9a, 0xca, 0xcd, 0x1b, 0xb2, 0x89, 0x79, 0x32, 0x7a, 0xe5, 0xfb, 0xc9, 0xf4, 0x16, 0x2a, 0x7d, 0x40, 0x74, 0x0a, 0x3d, 0xa3, + 0x9b, 0x5b, 0x44, 0x80, 0xab, 0x51, 0x58, 0x51, 0x4b, 0x7e, 0xb3, 0x99, 0x12, 0x0b, 0xf0, 0xcd, 0x19, 0xce, 0xba, 0x7f, 0x83, 0xc6, 0x0f, 0x28, 0xe7, 0x36, 0x29, 0x9f, 0x32, 0x50, 0x5f, 0x8d, + 0xbb, 0x83, 0xf1, 0xa7, 0x37, 0x7a, 0x55, 0xb5, 0xd6, 0xbf, 0x27, 0xa8, 0x68, 0xd8, 0xff, 0xbb, 0xe8, 0x25, 0x22, 0xe4, 0x24, 0xa6, 0xd7, 0xf1, 0x52, 0x4f, 0x9e, 0xb9, 0xc8, 0x91, 0x6c, 0x60, + 0x1d, 0xb2, 0x72, 0xa5, 0xec, 0x91, 0x48, 0x5e, 0xd9, 0x59, 0x8a, 0x25, 0xc6, 0xa1, 0x2f, 0xe8, 0xed, 0xed, 0xf7, 0x0c, 0xc2, 0x4a, 0x85, 0x45, 0x80, 0xe6, 0xb3, 0xae, 0x5a, 0x09, 0x60, 0xbb, + 0xce, 0x34, 0xe5, 0x3b, 0xc4, 0x17, 0xc9, 0x36, 0x7c, 0x22, 0xa3, 0x23, 0xea, 0xaa, 0x30, 0x3e, 0x1b, 0xae, 0x89, 0x32, 0x1d, 0xa6, 0xbe, 0xad, 0xf2, 0xd3, 0x3b, 0x82, 0x92, 0x29, 0x61, 0x26, + 0x26, 0xaf, 0x2f, 0xcd, 0xe8, 0x0f, 0xe2, 0xd0, 0xf4, 0x4e, 0x54, 0x50, 0xdb, 0x59, 0xa6, 0xa7, 0x1c, 0x1f, 0xd6, 0xea, 0x89, 0x43, 0xd9, 0x6c, 0xbd, 0x82, 0xe0, 0x85, 0xbb, 0x79, 0xf8, 0x71, + 0xbc, 0x8b, 0x18, 0x2e, 0xcc, 0xae, 0x30, 0x7b, 0xb3, 0xe3, 0x54, 0x19, 0xf6, 0xc6, 0x0d, 0x56, 0x3b, 0xf9, 0xcf, 0x41, 0xef, 0xa1, 0xb0, 0x07, 0x89, 0x6f, 0x84, 0xa9, 0x01, 0xae, 0x13, 0xe3, + 0xbc, 0x64, 0x25, 0xda, 0x6b, 0x1d, 0x48, 0x13, 0x0c, 0x2b, 0x07, 0x4d, 0xac, 0x48, 0x89, 0x0b, 0x58, 0x22, 0xac, 0x90, 0xdb, 0x94, 0x32, 0xc4, 0x03, 0x87, 0x1e, 0xb9, 0xfa, 0x6d, 0x54, 0x80, + 0x1e, 0x73, 0x13, 0x62, 0x01, 0xa5, 0x66, 0x36, 0x13, 0xc6, 0xd5, 0xf3, 0xd0, 0x05, 0xad, 0x75, 0x5b, 0x7a, 0xb0, 0x76, 0x8d, 0xd2, 0x9e, 0x4e, 0x47, 0x4e, 0x01, 0x71, 0x36, 0x6b, 0xd7, 0x1c, + 0x94, 0xde, 0xc4, 0x1c, 0x0d, 0x8e, 0xb2, 0x86, 0x88, 0x05, 0xf3, 0xa2, 0xa6, 0x35, 0xf1, 0xa9, 0x3e, 0x0d, 0x7b, 0x2d, 0x97, 0xf6, 0xfb, 0x92, 0xe7, 0xd7, 0x61, 0x5f, 0x93, 0x74, 0x9d, 0x15, + 0x47, 0x3e, 0xa4, 0x99, 0xb1, 0x7e, 0x2a, 0xdb, 0x6f, 0x35, 0xcb, 0xd3, 0x42, 0x35, 0x0e, 0x1a, 0xf0, 0x3f, 0xa6, 0x43, 0x20, 0x7e, 0x09, 0x5a, 0xdc, 0xc5, 0xe2, 0xc0, 0x44, 0x18, 0x46, 0x00, + 0x36, 0x63, 0xdd, 0x88, 0x84, 0x24, 0x97, 0x84, 0xe0, 0xd4, 0xc0, 0x06, 0xe2, 0x4f, 0x35, 0xf2, 0x11, 0x11, 0x85, 0xed, 0x57, 0x5a, 0xb9, 0xcc, 0xa9, 0x55, 0xfa, 0x99, 0x9f, 0xaa, 0x1f, 0x91, + 0xc9, 0x83, 0x2f, 0x55, 0x1b, 0x11, 0xa4, 0xda, 0x1b, 0x7d, 0x91, 0x49, 0x5f, 0x9f, 0x2d, 0xbc, 0xd9, 0xe9, 0x7e, 0xd3, 0xf2, 0x52, 0x40, 0xf4, 0x92, 0xda, 0x9a, 0x69, 0x0c, 0x1e, 0x75, 0xea, + 0x0f, 0x09, 0x2e, 0x83, 0x52, 0x8b, 0x1e, 0x0e, 0x1c, 0x05, 0x99, 0x06, 0x4d, 0x47, 0x67, 0x53, 0xa4, 0xdc, 0x1b, 0x6e, 0x3e, 0xdc, 0xaf, 0x36, 0x16, 0x20, 0x3d, 0x0f, 0x3e, 0xbe, 0x81, 0xd8, + 0xd6, 0xaf, 0x79, 0x80, 0x26, 0x5c, 0x3c, 0x14, 0xf2, 0xdf, 0x75, 0xe1, 0x3c, 0xc2, 0x4f, 0xc3, 0xa7, 0xef, 0x14, 0x43, 0x09, 0xa8, 0xb3, 0xa4, 0x5b, 0x37, 0x5c, 0x48, 0x40, 0x25, 0xca, 0x4f, + 0x60, 0xff, 0x04, 0x3b, 0x2e, 0x0a, 0xf6, 0x13, 0x2b, 0x0c, 0x25, 0xc0, 0x29, 0xfa, 0xee, 0x39, 0x77, 0xc5, 0x8f, 0xf2, 0x1e, 0xa4, 0xbc, 0x45, 0xf1, 0xc7, 0xac, 0x6f, 0x1c, 0xf1, 0x06, 0xc6, + 0x06, 0x27, 0x16, 0x4a, 0x34, 0x1d, 0x76, 0xc5, 0x21, 0xc2, 0xf1, 0x11, 0x2a, 0xe8, 0xb3, 0x1c, 0x61, 0xee, 0x1d, 0xb0, 0x0c, 0x61, 0x50, 0xbd, 0x8d, 0xbf, 0xb7, 0xe0, 0x42, 0x43, 0x71, 0x34, + 0xe9, 0x87, 0x0e, 0x99, 0xdb, 0x04, 0x46, 0x32, 0x7c, 0xc3, 0xbd, 0x9c, 0x1a, 0xc2, 0x04, 0xce, 0xbd, 0x8d, 0xc8, 0x41, 0xc3, 0x46, 0x24, 0x4c, 0x48, 0x1e, 0xf3, 0x32, 0xd1, 0x2c, 0x9b, 0x7e, + 0xb4, 0x14, 0x20, 0x19, 0x47, 0xdb, 0x95, 0x7e, 0xbb, 0xe0, 0x22, 0xf2, 0xb5, 0x45, 0xc9, 0x31, 0xb9, 0xfe, 0x55, 0x4c, 0x11, 0x1a, 0xcf, 0x31, 0x3e, 0x5d, 0x33, 0x4a, 0x5b, 0xcc, 0xb8, 0x81, + 0x57, 0x5b, 0x5e, 0x94, 0xa8, 0x1a, 0x09, 0x97, 0xa3, 0xc5, 0x02, 0x56, 0x14, 0xdf, 0xf4, 0xc1, 0xf9, 0x9c, 0x51, 0xae, 0x68, 0xf6, 0xba, 0xdf, 0x47, 0xe7, 0xbd, 0x52, 0x25, 0x70, 0x38, 0x36, + 0x88, 0x54, 0x55, 0xb1, 0xd7, 0xf9, 0x59, 0x08, 0x74, 0x08, 0x46, 0xb8, 0x10, 0x7e, 0x1c, 0xf8, 0xcc, 0x20, 0x86, 0x40, 0x61, 0x80, 0xf9, 0xcd, 0xdf, 0x2f, 0x5e, 0x2e, 0xe5, 0xe6, 0x7e, 0x6c, + 0xe5, 0xb8, 0x21, 0xf2, 0x7d, 0x10, 0xad, 0x45, 0x5f, 0xe4, 0xea, 0x0f, 0x8e, 0xc4, 0x1d, 0xb8, 0x5d, 0x7e, 0xbe, 0xb4, 0x02, 0x95, 0x10, 0xa3, 0xca, 0x0c, 0xe5, 0x7b, 0xd1, 0xdf, 0xd0, 0xa5, + 0x60, 0xfa, 0x39, 0x7e, 0xef, 0x01, 0x65, 0x48, 0x7a, 0x35, 0xed, 0x31, 0x84, 0x40, 0xba, 0x5b, 0xb8, 0x7b, 0xa4, 0x1e, 0xc3, 0xd8, 0x52, 0x63, 0x21, 0xb6, 0x78, 0x03, 0xa5, 0xb7, 0x91, 0x0f, + 0xf7, 0xdb, 0x3b, 0xcd, 0x78, 0x72, 0x76, 0x28, 0xb2, 0x27, 0xf2, 0x91, 0x08, 0x82, 0xce, 0xd2, 0x61, 0x67, 0xd6, 0x7e, 0xd1, 0xc4, 0x56, 0xc2, 0x8e, 0xfe, 0xb2, 0xac, 0x3f, 0x8f, 0x77, 0x0b, + 0xe7, 0x53, 0xe4, 0xc3, 0x42, 0xe9, 0x9e, 0xc9, 0xd2, 0x04, 0x71, 0xf8, 0xcf, 0xb2, 0xf5, 0x10, 0x33, 0xa1, 0xba, 0x0b, 0x23, 0x02, 0x31, 0xef, 0xdd, 0xb1, 0x4f, 0x0d, 0x55, 0xf1, 0x5c, 0x0c, + 0x08, 0xa5, 0x58, 0xb5, 0x05, 0x9f, 0x2a, 0xcf, 0xae, 0x96, 0xaf, 0x83, 0x68, 0x07, 0x55, 0xee, 0x11, 0xfc, 0xff, 0x60, 0x26, 0xab, 0x9a, 0xec, 0x4a, 0x3c, 0x5c, 0xf1, 0xc7, 0x04, 0x88, 0x5b, + 0x50, 0x62, 0xa1, 0xac, 0x27, 0x4f, 0xf6, 0xff, 0xdd, 0x83, 0x70, 0x97, 0x46, 0x67, 0x42, 0x6b, 0xed, 0x83, 0x1b, 0xe6, 0xcc, 0xb3, 0x19, 0x56, 0xe9, 0x5f, 0xf4, 0xb7, 0xf1, 0xd1, 0x04, 0x87, + 0x22, 0x31, 0x68, 0x89, 0xdf, 0xf5, 0x9e, 0x86, 0xb0, 0x8b, 0x91, 0x1a, 0x6a, 0x11, 0x81, 0x5c, 0x7d, 0x41, 0xaf, 0xa2, 0x29, 0xd4, 0xb8, 0x71, 0xca, 0xd0, 0xa1, 0x37, 0x58, 0x0d, 0xce, 0x32, + 0x34, 0x4b, 0xe5, 0xa6, 0x74, 0x89, 0xef, 0xb7, 0xe1, 0xda, 0x82, 0xc2, 0x12, 0xeb, 0x47, 0x2e, 0x88, 0x1a, 0x4c, 0x50, 0xee, 0xea, 0x75, 0x74, 0x63, 0xc3, 0xca, 0x31, 0x27, 0x8c, 0x45, 0x8c, + 0xfb, 0xf3, 0x18, 0x93, 0xa2, 0xec, 0x89, 0x62, 0xf0, 0x48, 0xf0, 0x0e, 0xf1, 0x16, 0xb1, 0x80, 0x7c, 0x4b, 0x5f, 0x7c, 0x75, 0x78, 0x5d, 0x52, 0x5d, 0x3f, 0xb6, 0xae, 0x67, 0x2c, 0xba, 0x40, + 0xa4, 0x37, 0x15, 0xa0, 0xef, 0x0e, 0xfc, 0x25, 0xda, 0x92, 0xf0, 0x17, 0x7f, 0x8e, 0xf3, 0x03, 0x4f, 0xa1, 0xa6, 0x8a, 0x49, 0x80, 0x78, 0xf3, 0xdd, 0xb5, 0x25, 0x92, 0x62, 0x62, 0xed, 0xa9, + 0x1b, 0x83, 0x19, 0x5b, 0xd2, 0xa5, 0x0a, 0x28, 0xbd, 0xac, 0xe3, 0xbb, 0xc6, 0x8a, 0xe9, 0x47, 0xe4, 0xda, 0x6c, 0xdf, 0x8c, 0xdf, 0x7a, 0x20, 0xd2, 0x19, 0x6d, 0xa0, 0x9a, 0x16, 0x6d, 0x81, + 0xa3, 0x11, 0xd9, 0xd9, 0xf4, 0xac, 0x7e, 0xc7, 0x26, 0x64, 0x5d, 0x12, 0x4f, 0x8b, 0x9a, 0x24, 0x5d, 0xde, 0xd2, 0x2e, 0x3b, 0x17, 0x21, 0x23, 0xda, 0xf4, 0x80, 0x24, 0x8c, 0xe6, 0xb9, 0xd4, + 0x14, 0xc6, 0x5a, 0x99, 0xcc, 0xbe, 0x6b, 0x6e, 0xef, 0x48, 0x3a, 0xef, 0xcb, 0xdf, 0x2a, 0xac, 0xa3, 0x9e, 0xae, 0xb1, 0x89, 0x53, 0x4e, 0xfe, 0x2d, 0x19, 0xcb, 0x51, 0x3d, 0xda, 0x31, 0x41, + 0x0a, 0x1b, 0x7a, 0x5e, 0xa2, 0xee, 0x54, 0x04, 0x4d, 0x38, 0xdf, 0x80, 0xa7, 0x15, 0x5a, 0x3f, 0xc3, 0x59, 0x3d, 0x12, 0x84, 0xea, 0x4f, 0xb7, 0x54, 0x45, 0x44, 0x41, 0x42, 0x53, 0x1e, 0x02, + 0x5e, 0x47, 0xbb, 0x68, 0x5b, 0x9e, 0xbb, 0x56, 0x36, 0x0e, 0xc3, 0xc7, 0x8d, 0x0f, 0x95, 0x2d, 0xc0, 0xb7, 0xec, 0xc7, 0xed, 0x89, 0xef, 0x34, 0x1b, 0x20, 0xf6, 0x88, 0xbc, 0xcb, 0xaa, 0x95, + 0x9e, 0x9e, 0xd5, 0x06, 0x75, 0x61, 0x35, 0xcd, 0xc9, 0x7f, 0x11, 0xd3, 0x86, 0xb1, 0x62, 0x71, 0xf1, 0xfc, 0x25, 0x36, 0x0d, 0xce, 0xa1, 0x9c, 0x59, 0x66, 0xec, 0xa5, 0xca, 0xca, 0xc4, 0xec, + 0x56, 0x19, 0x33, 0xda, 0x26, 0xdc, 0x67, 0x11, 0x39, 0xa8, 0xef, 0xe7, 0xf7, 0x5d, 0xc9, 0x6d, 0x12, 0x8a, 0x9e, 0xb9, 0x45, 0x3d, 0xef, 0x68, 0x4a, 0x77, 0xf8, 0xdd, 0xcb, 0x61, 0x36, 0x35, + 0xe3, 0xf4, 0x51, 0x1b, 0x29, 0xa0, 0xec, 0x58, 0x92, 0xc7, 0x4e, 0xc0, 0x9a, 0xa1, 0xab, 0xd4, 0x79, 0x50, 0xc7, 0x31, 0xf4, 0x98, 0x87, 0x9a, 0xde, 0x91, 0x23, 0x1e, 0x4b, 0x72, 0x97, 0x7c, + 0x30, 0xb9, 0x39, 0xff, 0x91, 0x38, 0x38, 0x6a, 0xdf, 0xfd, 0xf9, 0xc9, 0xf8, 0x79, 0x00, 0x50, 0x83, 0x50, 0x96, 0x67, 0xab, 0xe7, 0x67, 0xdf, 0xa8, 0x83, 0x0f, 0x01, 0xd8, 0xce, 0x84, 0x4f, + 0x7e, 0x62, 0xd2, 0x14, 0xf0, 0x46, 0xa7, 0x00, 0x54, 0x5f, 0x56, 0x1d, 0xf7, 0x63, 0x6a, 0x52, 0x26, 0x3c, 0x58, 0x7b, 0x62, 0x12, 0x9b, 0x49, 0x77, 0x59, 0x8c, 0x30, 0x2c, 0x61, 0xc1, 0x13, + 0x27, 0x60, 0xf2, 0xb5, 0xd5, 0xe2, 0x04, 0xf1, 0x7f, 0x71, 0x5f, 0x2b, 0xcd, 0x39, 0x7d, 0x4a, 0xc8, 0xf0, 0x96, 0xf2, 0x30, 0xdb, 0xee, 0x8e, 0xd0, 0xa8, 0xfb, 0x6a, 0x86, 0x0d, 0x85, 0xeb, + 0xd6, 0x8a, 0xeb, 0x48, 0xa1, 0x81, 0xa1, 0x21, 0xae, 0x01, 0xc9, 0xbc, 0x73, 0x11, 0x70, 0x55, 0x23, 0x98, 0x9d, 0xa6, 0x4d, 0xba, 0x1f, 0xd9, 0xf5, 0x8d, 0xd5, 0xaf, 0xda, 0x9e, 0x3b, 0x8c, + 0x5e, 0xa3, 0x88, 0x0d, 0x5d, 0xb8, 0x89, 0x09, 0x14, 0x3b, 0x98, 0x84, 0x4e, 0xd7, 0xba, 0xf5, 0x00, 0x8d, 0xee, 0x81, 0x0a, 0x51, 0x19, 0x40, 0xdf, 0x3a, 0xab, 0x23, 0x8a, 0x43, 0x80, 0x37, + 0xab, 0x33, 0x2e, 0x4c, 0x04, 0x30, 0x96, 0x85, 0x52, 0xdc, 0xe1, 0xe3, 0xfc, 0x38, 0x1f, 0xe1, 0xab, 0x25, 0x23, 0xde, 0x55, 0x4a, 0xc0, 0xdc, 0xae, 0xb2, 0xb2, 0x57, 0xbb, 0xc9, 0xe5, 0x1c, + 0x01, 0x23, 0xf8, 0x29, 0x2c, 0xc9, 0x61, 0xb4, 0xb8, 0x60, 0x3e, 0x27, 0x1f, 0xcd, 0x06, 0xaf, 0x57, 0x88, 0x34, 0x97, 0x79, 0xe5, 0xf8, 0x96, 0x89, 0x94, 0x8c, 0x25, 0x02, 0x50, 0xec, 0x94, + 0xdc, 0x01, 0xa4, 0x26, 0xe9, 0xf2, 0x6f, 0x6d, 0xf8, 0x1f, 0x46, 0xb1, 0xde, 0x59, 0xb8, 0x8e, 0x4b, 0x15, 0xb1, 0xce, 0x7f, 0x4a, 0x2d, 0x69, 0x19, 0xc2, 0x57, 0xf0, 0x75, 0x72, 0x02, 0x21, + 0x44, 0x14, 0x90, 0xf5, 0x9c, 0x5d, 0x64, 0xee, 0x95, 0x7a, 0x47, 0xc7, 0x87, 0x7f, 0x1e, 0x6e, 0x92, 0x76, 0x44, 0x59, 0xa9, 0xa9, 0xc0, 0xbf, 0x97, 0x87, 0x72, 0xdc, 0xe0, 0x70, 0xcb, 0x17, + 0x83, 0xad, 0xdf, 0x20, 0x8a, 0x0d, 0xfe, 0x15, 0x92, 0xeb, 0x73, 0x87, 0x99, 0x9f, 0x2f, 0x4e, 0x53, 0x76, 0xd4, 0x8a, 0x3b, 0xb1, 0xdf, 0x66, 0x02, 0xd8, 0x0b, 0xc4, 0xda, 0xf4, 0x25, 0x31, + 0x8c, 0xcb, 0xbc, 0xbd, 0xfc, 0x46, 0x75, 0xf7, 0x93, 0xbd, 0x31, 0x01, 0xdd, 0x4c, 0xae, 0x98, 0x37, 0x95, 0x51, 0xfe, 0xd5, 0x40, 0x91, 0x0e, 0xb9, 0xaa, 0xb9, 0xc6, 0xf9, 0x74, 0x10, 0xe3, + 0xa2, 0x71, 0xf3, 0xc3, 0xcc, 0x5c, 0xae, 0xcf, 0x72, 0xb0, 0x80, 0x8e, 0xc8, 0x2d, 0x91, 0xce, 0x1c, 0xe5, 0xa6, 0xf9, 0x8f, 0x3d, 0xbc, 0x0a, 0x81, 0x27, 0xa6, 0xc2, 0x48, 0x84, 0x01, 0xf0, + 0x1a, 0x35, 0x6a, 0x92, 0xd3, 0x74, 0x79, 0x3e, 0xe5, 0x33, 0x7b, 0x57, 0x52, 0x56, 0x3e, 0x4e, 0x6e, 0x52, 0xc9, 0x83, 0x42, 0x7d, 0xa5, 0xbd, 0xe0, 0x49, 0x10, 0x73, 0x86, 0xe9, 0x23, 0xa5, + 0xb8, 0xb5, 0x6a, 0xff, 0x42, 0xfe, 0xff, 0xb8, 0xc3, 0x21, 0xd7, 0x31, 0x1a, 0x91, 0x0a, 0x41, 0x9c, 0xf7, 0x4b, 0x56, 0x0a, 0x09, 0x9d, 0x9a, 0x8d, 0xdc, 0x1d, 0xd5, 0x5a, 0xc3, 0xed, 0x8a, + 0x97, 0xd5, 0x7b, 0xf6, 0xad, 0x44, 0xd6, 0x5d, 0x11, 0x68, 0x1c, 0x09, 0x2a, 0x60, 0x78, 0xab, 0x9c, 0xad, 0x68, 0xb3, 0xc9, 0x3c, 0xc1, 0x37, 0x8f, 0xff, 0xc0, 0x53, 0x41, 0xcd, 0xdc, 0x6d, + 0x21, 0x29, 0x18, 0x8e, 0x51, 0xfa, 0x60, 0x0d, 0x9c, 0xd4, 0xad, 0x59, 0xef, 0x5c, 0xc9, 0xad, 0x49, 0x5b, 0x46, 0xf6, 0x9d, 0x2d, 0x32, 0x75, 0x96, 0xed, 0xf9, 0x9c, 0xa8, 0x84, 0x9b, 0x83, + 0x1c, 0x6f, 0x26, 0x7d, 0xbd, 0x27, 0x9b, 0x4f, 0xd8, 0x24, 0xa1, 0xce, 0xb5, 0xe6, 0x3b, 0xbb, 0x5c, 0x3c, 0x46, 0x87, 0x5b, 0x32, 0xd5, 0x90, 0x43, 0xa9, 0xdc, 0x6b, 0x37, 0x11, 0x6c, 0xbd, + 0x39, 0x5e, 0xec, 0xcc, 0x47, 0x24, 0x72, 0xcd, 0x96, 0x2d, 0xec, 0x53, 0x16, 0x8d, 0xd3, 0xa1, 0xd0, 0xc8, 0xf4, 0x64, 0xfe, 0x8e, 0x5c, 0xd9, 0x4f, 0x41, 0xef, 0x2e, 0x38, 0x5d, 0xa8, 0xa5, + 0xb4, 0x7a, 0x82, 0xa8, 0x84, 0x26, 0x36, 0x75, 0x9d, 0x3a, 0x75, 0x53, 0xe4, 0xaa, 0x5b, 0xde, 0x4c, 0x2c, 0x3d, 0x8c, 0xc8, 0xd7, 0x2e, 0x70, 0x7d, 0xd4, 0x81, 0x4e, 0xe6, 0x3d, 0xa2, 0xe3, + 0x94, 0xe0, 0x58, 0xd0, 0xde, 0xb4, 0x40, 0x7a, 0x10, 0xa2, 0x82, 0x83, 0xc9, 0xd3, 0xda, 0x3f, 0x85, 0x31, 0x98, 0xe4, 0x8f, 0x73, 0xf6, 0x79, 0xee, 0xe8, 0xe2, 0x68, 0x78, 0x8a, 0xd7, 0x00, + 0xce, 0x9e, 0xf1, 0x92, 0x6e, 0x4e, 0xe8, 0x2c, 0xb9, 0xb3, 0xf7, 0xbf, 0xc0, 0x73, 0xdf, 0x0b, 0xa8, 0xf8, 0x57, 0x0a, 0xfd, 0x39, 0xc4, 0x78, 0x5b, 0xc6, 0xb9, 0x40, 0xd4, 0x12, 0x0e, 0x10, + 0x83, 0x11, 0xe2, 0xfd, 0x4e, 0xd9, 0x21, 0x87, 0x6e, 0x99, 0xb7, 0x15, 0x39, 0x8c, 0x5e, 0xef, 0xc7, 0xa7, 0xdb, 0xca, 0x3d, 0x85, 0x7a, 0x93, 0xbc, 0x5f, 0x84, 0xf9, 0x8b, 0x8d, 0x44, 0x3e, + 0xab, 0x12, 0x55, 0x48, 0x06, 0x5b, 0x89, 0x18, 0xe1, 0x85, 0x12, 0x40, 0xdf, 0x11, 0x8e, 0x93, 0x68, 0x75, 0xf0, 0x41, 0x8d, 0xea, 0xdc, 0x91, 0x3b, 0x84, 0x60, 0x18, 0x9a, 0x1c, 0x1b, 0xe4, + 0xb4, 0x9a, 0xa6, 0xc5, 0x1b, 0x6c, 0xd7, 0x75, 0x40, 0x1a, 0x08, 0x5b, 0x22, 0x1a, 0x91, 0x06, 0x9c, 0xab, 0xe2, 0xd5, 0x58, 0x3b, 0xaf, 0x72, 0xe5, 0xf5, 0x0d, 0x09, 0x20, 0x56, 0xfe, 0x67, + 0x54, 0x60, 0xea, 0xde, 0x80, 0xf2, 0x97, 0x31, 0x6d, 0x30, 0xe5, 0xe1, 0x40, 0x83, 0x31, 0x26, 0xb6, 0x2c, 0xd2, 0xe7, 0x40, 0x6e, 0x99, 0xb6, 0xab, 0x1b, 0x36, 0x20, 0x06, 0xf7, 0xeb, 0x10, + 0x33, 0x3f, 0xf3, 0x1d, 0x52, 0xf5, 0xb2, 0x99, 0x8d, 0xcc, 0xb2, 0xa9, 0x0f, 0x17, 0x24, 0xc0, 0x5a, 0x39, 0xe4, 0xae, 0x1b, 0xc6, 0xe6, 0xd6, 0x73, 0x06, 0x66, 0xd1, 0x9c, 0x73, 0x9e, 0x44, + 0x14, 0xb6, 0xf0, 0x25, 0xa1, 0xb5, 0x7f, 0x65, 0x46, 0xdb, 0xe9, 0xe9, 0xb2, 0x30, 0xa3, 0xb1, 0x46, 0x2b, 0x8e, 0xff, 0xc7, 0x85, 0x02, 0xb7, 0xd5, 0xcc, 0x1c, 0xae, 0x3a, 0x70, 0x54, 0x7f, + 0x02, 0x34, 0x09, 0x0f, 0x24, 0x1d, 0xd3, 0xa6, 0xf0, 0x63, 0x38, 0xc0, 0x62, 0x73, 0x6e, 0x00, 0x06, 0xb0, 0xd4, 0x8f, 0x13, 0x64, 0x61, 0xc7, 0x28, 0xe5, 0x40, 0xb9, 0x97, 0x84, 0xb3, 0x73, + 0x5c, 0xe9, 0x7f, 0xf1, 0xc7, 0xe5, 0x8f, 0xf9, 0xc9, 0x5d, 0x22, 0xa4, 0x67, 0x37, 0x1d, 0xab, 0x77, 0x64, 0x0f, 0xd6, 0xd0, 0xc1, 0x02, 0x28, 0x30, 0xcd, 0x14, 0x12, 0xe9, 0x3d, 0x71, 0x47, + 0x22, 0x6d, 0x95, 0x72, 0x3b, 0x69, 0xb2, 0xb4, 0x6f, 0x6e, 0x57, 0x6c, 0xde, 0x5c, 0xcf, 0xdc, 0xee, 0xc9, 0xd2, 0xff, 0xc7, 0x58, 0x3b, 0x8e, 0x6e, 0x93, 0xdc, 0xa9, 0x93, 0xc5, 0xdb, 0x73, + 0xea, 0x95, 0xcd, 0x3e, 0xad, 0x13, 0xe3, 0x25, 0x31, 0x1a, 0x3a, 0x7f, 0x88, 0xcf, 0x8d, 0x26, 0x5b, 0x25, 0x6e, 0xc2, 0x42, 0xfb, 0x1e, 0xf5, 0xeb, 0x83, 0x60, 0x18, 0x89, 0xa0, 0x1a, 0xf5, + 0xfb, 0xe5, 0xc0, 0x63, 0xff, 0x06, 0xb8, 0x8b, 0xeb, 0x6e, 0x8a, 0x30, 0xa3, 0x04, 0x15, 0x95, 0x20, 0x3d, 0x01, 0x09, 0x56, 0xc7, 0xa8, 0xf0, 0xb9, 0xad, 0x41, 0x5e, 0xf4, 0xdf, 0x10, 0x44, + 0xa2, 0x5e, 0x0a, 0x9f, 0xe8, 0xfa, 0x67, 0x5f, 0x5c, 0xe0, 0x95, 0x1c, 0xd6, 0x84, 0x4b, 0xf4, 0xa9, 0x57, 0xbe, 0xe0, 0xd7, 0xec, 0x69, 0x4f, 0x9d, 0x72, 0xd8, 0x32, 0xce, 0x93, 0x90, 0x57, + 0xf7, 0x27, 0x70, 0x90, 0x3d, 0x91, 0x64, 0x13, 0x96, 0x25, 0x6f, 0x66, 0xde, 0x0c, 0x21, 0x0f, 0x50, 0x6b, 0x85, 0x92, 0x13, 0xcd, 0x4e, 0x32, 0xb5, 0x8d, 0x84, 0xaf, 0xd4, 0x78, 0xe6, 0x9c, + 0xd5, 0x0e, 0x24, 0x27, 0x24, 0x23, 0xe3, 0xff, 0xef, 0x0e, 0xad, 0x8b, 0xa8, 0x31, 0xec, 0x8f, 0x62, 0x81, 0x1f, 0xb0, 0xaf, 0xd2, 0x9b, 0x65, 0x06, 0xbf, 0x06, 0x45, 0xc2, 0x80, 0x2d, 0xf5, + 0xbe, 0xcd, 0xa5, 0x03, 0x7b, 0x87, 0xb6, 0x17, 0x07, 0x5d, 0x2f, 0x2a, 0xd5, 0x75, 0xf9, 0xb5, 0x2f, 0xef, 0xc6, 0xfb, 0x41, 0xab, 0xdd, 0x0a, 0x5e, 0xfe, 0x7c, 0x17, 0x45, 0x7a, 0x89, 0x33, + 0x00, 0x2c, 0x5b, 0x46, 0xec, 0xb5, 0x3e, 0x8b, 0x95, 0x2b, 0x3a, 0xe1, 0x67, 0x6c, 0x27, 0x42, 0x31, 0x1d, 0xd6, 0xe0, 0xfa, 0x57, 0x5d, 0x37, 0x0c, 0xb0, 0x09, 0x3f, 0xcb, 0x4f, 0x50, 0xc5, + 0x48, 0x1e, 0xd1, 0xf6, 0x5d, 0xdf, 0x7e, 0xa8, 0x78, 0xea, 0x28, 0x62, 0x9e, 0x71, 0xb3, 0xc3, 0xd3, 0xad, 0x33, 0x75, 0x8a, 0x35, 0xcf, 0x26, 0xc9, 0x1e, 0xfd, 0x69, 0x69, 0x7f, 0xe1, 0x71, + 0xa6, 0x88, 0xaf, 0xb9, 0xff, 0xad, 0xfb, 0x43, 0x9a, 0xb5, 0x61, 0x09, 0x5f, 0x3a, 0x96, 0x5d, 0x57, 0x32, 0xf6, 0x8d, 0xd2, 0x57, 0x9c, 0x8d, 0x5c, 0xa0, 0xff, 0x80, 0x24, 0x12, 0x02, 0x08, + 0x94, 0xba, 0xb2, 0xd3, 0x3c, 0x04, 0x94, 0xda, 0x5d, 0x03, 0x5f, 0x83, 0xb7, 0xc6, 0x6e, 0x2f, 0x57, 0x0f, 0x22, 0xa2, 0x6f, 0xac, 0x9e, 0x26, 0xb2, 0x0d, 0xb5, 0x5e, 0x09, 0x96, 0x33, 0xf4, + 0xcf, 0x96, 0xe8, 0xad, 0xd7, 0xa6, 0xef, 0x79, 0xf8, 0x1f, 0x7d, 0x9f, 0x84, 0x89, 0x0e, 0x66, 0x00, 0xe9, 0xdd, 0xed, 0x4a, 0x36, 0x1b, 0x4b, 0x0f, 0x07, 0xf2, 0x84, 0x06, 0x6c, 0x1f, 0x8e, + 0x9f, 0xee, 0x4d, 0x62, 0x01, 0x99, 0x4f, 0xcd, 0xf4, 0x9b, 0x6a, 0xfe, 0x73, 0x09, 0xf2, 0xf3, 0x68, 0x27, 0x3f, 0xd3, 0x3e, 0xae, 0x43, 0xc7, 0x40, 0x32, 0x63, 0x26, 0x27, 0x70, 0x28, 0xe9, + 0x42, 0x95, 0x90, 0xb6, 0xf9, 0xbd, 0x9b, 0x17, 0x5b, 0xa1, 0x75, 0x63, 0xff, 0xc0, 0x7b, 0x00, 0xb5, 0x8d, 0x98, 0xb5, 0x25, 0x81, 0x9d, 0xeb, 0x03, 0x42, 0x33, 0xf9, 0x72, 0xee, 0x13, 0x7a, + 0x39, 0x71, 0xca, 0x7f, 0xbe, 0xec, 0x6f, 0x8a, 0xe2, 0x66, 0x35, 0x69, 0xc1, 0x77, 0x9b, 0xb8, 0x48, 0x2d, 0x41, 0xa6, 0x24, 0xcf, 0x23, 0xdd, 0xb4, 0x43, 0x5d, 0x97, 0x4f, 0x11, 0xfb, 0x78, + 0x45, 0x8f, 0x3e, 0x3c, 0xa0, 0xe5, 0x10, 0x55, 0xa9, 0x37, 0x42, 0x54, 0x6e, 0x13, 0x41, 0x84, 0x8b, 0xf4, 0xeb, 0x32, 0xc8, 0x56, 0xbc, 0xba, 0x65, 0x7c, 0x25, 0xa6, 0x36, 0x48, 0xd8, 0xe6, + 0xbb, 0x94, 0x1c, 0xd6, 0xf4, 0xe6, 0xe0, 0x21, 0xd0, 0x01, 0x5d, 0xda, 0x7e, 0xe1, 0x0b, 0x7a, 0xb4, 0x8c, 0xc4, 0xdc, 0x6e, 0x01, 0xfa, 0x4b, 0xa4, 0x6c, 0x2d, 0x70, 0xca, 0x99, 0x88, 0x3a, + 0x58, 0x9e, 0xb9, 0xee, 0xcb, 0xb2, 0x6f, 0x62, 0x40, 0x01, 0xff, 0xef, 0x0a, 0x15, 0x7b, 0x5f, 0x7b, 0xa2, 0x42, 0xb4, 0xd0, 0x0a, 0x5c, 0x26, 0xd6, 0x07, 0x56, 0xac, 0x84, 0x85, 0x58, 0x8a, + 0x30, 0x24, 0xfb, 0x07, 0x8b, 0x6b, 0xd5, 0xf4, 0x4f, 0xc4, 0xf3, 0xf3, 0xcd, 0xbb, 0x81, 0xde, 0x66, 0xb2, 0x1d, 0x02, 0x90, 0x61, 0xdc, 0x60, 0x34, 0xc7, 0x9e, 0x4c, 0xf0, 0xb6, 0xbe, 0x00, + 0x50, 0x72, 0x82, 0x88, 0x07, 0xa1, 0x2b, 0x03, 0x82, 0xa7, 0x5e, 0x74, 0x32, 0x46, 0x69, 0x8d, 0xb6, 0xef, 0x65, 0xbb, 0xa4, 0x6d, 0x3b, 0x32, 0xd8, 0x2d, 0x85, 0x85, 0xd6, 0xc2, 0x65, 0xc4, + 0x8a, 0x40, 0xf1, 0x1f, 0x63, 0xa3, 0xed, 0xe4, 0x61, 0x8c, 0x6e, 0xf7, 0x19, 0xb3, 0x53, 0x1c, 0xc9, 0xd9, 0x1b, 0x45, 0x6d, 0xd0, 0x19, 0x93, 0xee, 0x5e, 0x14, 0x7d, 0xab, 0xf9, 0xff, 0x9c, + 0x4e, 0x0a, 0x32, 0xc7, 0x52, 0x16, 0x4f, 0x14, 0x93, 0xb1, 0xc6, 0x61, 0x71, 0x32, 0xca, 0x34, 0x92, 0x65, 0x14, 0x25, 0xf7, 0xca, 0xc9, 0x31, 0x76, 0xcd, 0x2d, 0xf7, 0xe6, 0xd5, 0x9e, 0x96, + 0xff, 0x33, 0xee, 0xfe, 0xd0, 0xa0, 0x31, 0xa2, 0x87, 0x4e, 0x59, 0x6a, 0x38, 0x60, 0x8f, 0x0c, 0xcb, 0x22, 0x38, 0xf6, 0x1f, 0x06, 0x5f, 0xf7, 0xa0, 0x42, 0x4c, 0xc4, 0x24, 0x8e, 0x83, 0xe5, + 0x6f, 0xbd, 0x4d, 0x7c, 0x17, 0xe2, 0x92, 0xd9, 0x11, 0xf6, 0x96, 0x8b, 0x1d, 0x3a, 0x32, 0x4b, 0xb2, 0x36, 0xa4, 0x8f, 0x2d, 0xb8, 0x61, 0xb8, 0x05, 0x66, 0xba, 0xe8, 0x81, 0x72, 0xc3, 0x35, + 0xb2, 0x58, 0xd3, 0xf9, 0x64, 0x99, 0x8c, 0x04, 0xe5, 0x7b, 0xae, 0xec, 0x83, 0xd8, 0xef, 0xb2, 0xe3, 0xfb, 0x92, 0x99, 0xc7, 0xb7, 0x21, 0xf7, 0x57, 0x65, 0xbd, 0xf5, 0xb9, 0xf4, 0xef, 0x07, + 0xf6, 0xe3, 0x59, 0x2b, 0xf9, 0xfc, 0xb4, 0xe5, 0xdd, 0xe1, 0x3d, 0x2a, 0x20, 0x2b, 0x61, 0x3c, 0x42, 0xb0, 0xc4, 0xe3, 0xc4, 0x85, 0x95, 0x0c, 0x73, 0xdd, 0xee, 0x85, 0x40, 0x91, 0x9d, 0x10, + 0xb3, 0x97, 0x92, 0x48, 0x23, 0xc1, 0x3f, 0x88, 0x4b, 0xb9, 0x51, 0x52, 0x19, 0xaa, 0x48, 0x9f, 0xc9, 0x94, 0xd5, 0xce, 0x5d, 0xf9, 0x1a, 0x26, 0x7d, 0x96, 0x1b, 0x0e, 0x60, 0x2c, 0x9f, 0xa7, + 0xfe, 0x45, 0x92, 0x7f, 0x28, 0xab, 0x74, 0x48, 0x6a, 0xd8, 0x9a, 0x78, 0x7e, 0x3e, 0x12, 0xa3, 0x7d, 0x9a, 0x03, 0x4a, 0xfe, 0xdf, 0x6b, 0x9b, 0x8b, 0xc7, 0x03, 0xac, 0xa4, 0x66, 0x99, 0xbc, + 0x02, 0x8f, 0x23, 0xe7, 0x7c, 0x69, 0x1a, 0xaf, 0xf0, 0xbb, 0xa4, 0x4d, 0x8a, 0xe3, 0x49, 0xaa, 0xc3, 0x65, 0x21, 0x82, 0x34, 0xcd, 0xd2, 0x4c, 0xa3, 0xc5, 0xc1, 0x64, 0xc1, 0x30, 0x35, 0x18, + 0x6e, 0x42, 0xf4, 0xa6, 0xe0, 0x5b, 0xc0, 0x46, 0x46, 0xc3, 0x56, 0x56, 0x5f, 0x0a, 0x53, 0xc7, 0x3e, 0x90, 0x9a, 0x67, 0x31, 0x52, 0x95, 0x78, 0x5a, 0x9d, 0x3a, 0x7b, 0x9e, 0xb6, 0x0d, 0xa2, + 0xf3, 0xbb, 0x1b, 0xa9, 0x83, 0x4c, 0xa6, 0xee, 0x5b, 0x8b, 0x0d, 0xa2, 0x02, 0x47, 0x12, 0xde, 0x4f, 0x61, 0x7f, 0xaf, 0x30, 0x42, 0x92, 0x6f, 0x98, 0x96, 0x4a, 0x91, 0x8b, 0xf9, 0x39, 0xd3, + 0x37, 0xcc, 0x59, 0xb5, 0xc5, 0x3e, 0x97, 0x05, 0x8d, 0x91, 0x97, 0x4b, 0xfb, 0x1b, 0x59, 0xf5, 0x2c, 0x2c, 0x60, 0x42, 0xbd, 0x8e, 0x9f, 0x12, 0x45, 0xf1, 0xd0, 0x88, 0xe6, 0x9c, 0x80, 0xf7, + 0xf4, 0x93, 0xf4, 0xa3, 0x3e, 0xcc, 0x91, 0x56, 0x97, 0xa0, 0xe4, 0xe4, 0xca, 0xaf, 0xdf, 0xef, 0x75, 0x41, 0x47, 0x3b, 0xa1, 0x63, 0x67, 0x46, 0x7a, 0xdd, 0xaf, 0xba, 0x9e, 0x77, 0xb5, 0x7e, + 0x99, 0xf9, 0xa4, 0x65, 0x4e, 0x00, 0x49, 0x82, 0x48, 0xf8, 0x1a, 0xd2, 0x33, 0x17, 0xde, 0x6b, 0x5c, 0x0f, 0xd8, 0xca, 0x09, 0x37, 0xdc, 0xcf, 0xa2, 0xa8, 0xe2, 0x01, 0xde, 0x26, 0xf6, 0xd6, + 0x69, 0xb8, 0x3e, 0x51, 0x24, 0x81, 0x03, 0x81, 0x05, 0x3a, 0xc1, 0x61, 0x89, 0x10, 0x23, 0x64, 0xef, 0x54, 0x16, 0xfc, 0xcc, 0x3d, 0x80, 0x14, 0xdc, 0x4c, 0x1b, 0x09, 0x1d, 0xcb, 0x52, 0xed, + 0x30, 0xa8, 0x46, 0x37, 0x22, 0x10, 0x1a, 0x47, 0x4f, 0xed, 0xb4, 0xf8, 0xd8, 0x42, 0xa0, 0x6c, 0x53, 0x6d, 0xef, 0xd2, 0x31, 0x50, 0xbe, 0xee, 0xad, 0x69, 0x71, 0xf0, 0x39, 0xd9, 0x44, 0x2a, + 0xf0, 0x76, 0xd7, 0xf7, 0x26, 0x4f, 0xd1, 0xd9, 0xa0, 0x7a, 0xc5, 0xea, 0x23, 0x01, 0xe1, 0x24, 0xc5, 0xce, 0xda, 0x69, 0x6e, 0x80, 0x65, 0x43, 0x0d, 0xa2, 0xc8, 0x41, 0xdc, 0xc7, 0x18, 0xad, + 0x16, 0xb5, 0xa3, 0x92, 0x83, 0xde, 0xd8, 0x23, 0x29, 0x68, 0xff, 0x42, 0xd3, 0xe8, 0xbd, 0x31, 0xba, 0x00, 0xa9, 0xdb, 0x75, 0xf5, 0x7e, 0x45, 0x76, 0x5c, 0x48, 0xa8, 0x59, 0xb4, 0xca, 0x59, + 0x7b, 0x13, 0x61, 0x7e, 0xcf, 0xcb, 0xf2, 0x4d, 0xfb, 0x7b, 0x7e, 0xc8, 0x39, 0xdd, 0xb8, 0x72, 0xaf, 0x25, 0x37, 0x1f, 0x29, 0xf2, 0xf9, 0xab, 0x0e, 0xdd, 0x53, 0xad, 0x55, 0x81, 0xef, 0xb2, + 0x1f, 0x60, 0xbf, 0xc4, 0x49, 0xbe, 0x6e, 0x06, 0x28, 0x22, 0xfe, 0x9f, 0x99, 0x26, 0x38, 0x5e, 0x1c, 0xb2, 0x22, 0x6c, 0x9d, 0xa9, 0xdd, 0xb4, 0x37, 0x7b, 0x59, 0x6c, 0x32, 0xae, 0x96, 0x42, + 0xc1, 0x32, 0xd0, 0x8f, 0x66, 0x0d, 0x1d, 0xca, 0xbe, 0xec, 0x8e, 0x3f, 0xe9, 0x4a, 0xdc, 0x0e, 0xe2, 0x5a, 0xde, 0x45, 0x08, 0x38, 0x39, 0x6f, 0x39, 0x92, 0x06, 0x89, 0x2b, 0xb7, 0xcf, 0x87, + 0x50, 0xda, 0x80, 0xa5, 0xa6, 0xc7, 0xef, 0x76, 0x40, 0xb9, 0x20, 0xdc, 0xfe, 0xf1, 0xb3, 0x9a, 0x8f, 0x48, 0x2e, 0xd9, 0xf5, 0x12, 0xd2, 0xb6, 0x7b, 0x05, 0x74, 0xf1, 0x88, 0x6e, 0x52, 0x22, + 0x01, 0x23, 0xce, 0x1d, 0x5b, 0x60, 0x32, 0xfb, 0x7d, 0xda, 0x8f, 0xa9, 0xd5, 0x14, 0xb6, 0x4e, 0x1d, 0x34, 0xbc, 0x94, 0x41, 0x20, 0x6d, 0xf3, 0x8d, 0xb1, 0x74, 0x65, 0x0f, 0xa4, 0x9e, 0xc7, + 0x50, 0x27, 0x8e, 0x64, 0x64, 0x52, 0x5b, 0x09, 0x50, 0xe8, 0x6f, 0x3e, 0xff, 0x85, 0x34, 0xce, 0x84, 0x0f, 0x60, 0x66, 0xa3, 0xac, 0x08, 0x8b, 0xa4, 0x5a, 0x55, 0xcb, 0xf0, 0xca, 0x4b, 0x6a, + 0xae, 0x73, 0x68, 0xbd, 0xbd, 0x3e, 0xf9, 0x76, 0xff, 0xc2, 0xcb, 0x5f, 0x6b, 0x21, 0xd8, 0xce, 0xda, 0xe1, 0x06, 0xb0, 0x59, 0x20, 0x33, 0xbd, 0x41, 0x92, 0x30, 0x36, 0x4c, 0xad, 0x71, 0xc8, + 0xa9, 0xb3, 0x72, 0xdb, 0x9c, 0x7c, 0x28, 0x07, 0x82, 0xc5, 0x03, 0xfa, 0x84, 0xd4, 0xaf, 0x8d, 0x3b, 0x06, 0x67, 0xd7, 0x81, 0x0d, 0x0a, 0x09, 0x81, 0x3b, 0x34, 0x6b, 0xd3, 0xb5, 0xb3, 0xc2, + 0x6a, 0x83, 0x4b, 0x87, 0xda, 0x8d, 0x73, 0x4b, 0x1f, 0xc3, 0x2a, 0xfb, 0xaa, 0xb8, 0x49, 0xca, 0x3a, 0x8f, 0xa0, 0x18, 0x3c, 0x9c, 0x84, 0xb7, 0x81, 0x8e, 0xaf, 0x6a, 0xd7, 0x63, 0x22, 0x42, + 0x31, 0xb8, 0xaa, 0x72, 0xa7, 0x3d, 0x46, 0x1b, 0x2a, 0x83, 0xb7, 0x73, 0xe5, 0xd1, 0x44, 0x7d, 0x3a, 0x84, 0xa5, 0xc3, 0xd0, 0xb9, 0x00, 0x3e, 0xe2, 0x79, 0xf8, 0x90, 0x8a, 0xc0, 0x07, 0x3d, + 0x7f, 0xdf, 0xe0, 0x52, 0x0b, 0x16, 0x40, 0xfe, 0xff, 0xac, 0x46, 0xe7, 0x5b, 0x95, 0xe6, 0xd6, 0xaa, 0x22, 0x5f, 0x18, 0x3e, 0x9f, 0x8a, 0xd1, 0xf9, 0x8b, 0x23, 0xb0, 0x56, 0x33, 0x42, 0x10, + 0x38, 0x41, 0xd1, 0x5b, 0xf1, 0x87, 0x7f, 0x94, 0xb7, 0xc8, 0x9c, 0x6c, 0xe9, 0x27, 0x43, 0xcf, 0xff, 0x4b, 0xaf, 0xe6, 0xb4, 0x35, 0x5a, 0xf3, 0xaa, 0xb8, 0xb6, 0x5a, 0xb5, 0x7c, 0xcb, 0xb7, + 0xae, 0x8b, 0x67, 0xff, 0x5d, 0xcd, 0x96, 0xd7, 0xa2, 0x4a, 0xe8, 0x2a, 0xc6, 0x4b, 0x76, 0xb8, 0x76, 0x15, 0x56, 0x44, 0x52, 0x4f, 0x06, 0x7b, 0x1d, 0x79, 0x75, 0x5f, 0x70, 0x4b, 0x07, 0x04, + 0x3a, 0x6e, 0x43, 0xc5, 0x65, 0x56, 0xba, 0x1d, 0x26, 0x7b, 0xad, 0x19, 0x95, 0x47, 0x0c, 0x1b, 0xf7, 0xda, 0xc6, 0x5c, 0x29, 0xc7, 0x3d, 0x88, 0x63, 0xfb, 0x93, 0x16, 0x66, 0x7b, 0x01, 0x35, + 0x5d, 0x44, 0xc9, 0xb3, 0x22, 0xe3, 0x68, 0xa4, 0xe8, 0xb7, 0x36, 0xd5, 0xb3, 0xe9, 0x3f, 0x7a, 0xd0, 0x08, 0x18, 0xe5, 0x72, 0x9b, 0x6a, 0xf5, 0xb8, 0x88, 0x36, 0xd1, 0x34, 0x33, 0xeb, 0xd0, + 0xb0, 0xbc, 0xa5, 0xab, 0xd3, 0x6a, 0x58, 0x1e, 0x02, 0x28, 0xbb, 0xe2, 0x6b, 0x69, 0xba, 0x4a, 0x2b, 0x77, 0xeb, 0x7a, 0x6e, 0xe7, 0xc6, 0x2b, 0x00, 0x36, 0xa1, 0x3c, 0x1b, 0x47, 0x5a, 0xeb, + 0xf2, 0x63, 0x7b, 0xd6, 0x5f, 0xba, 0xdf, 0x32, 0x72, 0x2d, 0x27, 0x7d, 0x2c, 0x28, 0x06, 0x3c, 0x80, 0xa9, 0xee, 0xf8, 0xb8, 0x09, 0x1b, 0xe0, 0x4f, 0x25, 0x46, 0x5b, 0x56, 0x93, 0xa9, 0xb0, + 0x99, 0x51, 0xf6, 0x39, 0xf7, 0x35, 0xe3, 0x81, 0x30, 0x84, 0x6a, 0x1e, 0xe9, 0x3e, 0x86, 0xd5, 0x37, 0x0a, 0x4f, 0x16, 0x58, 0x4e, 0x46, 0xac, 0x21, 0x7c, 0x06, 0x65, 0x06, 0x7d, 0xf5, 0xce, + 0xe5, 0xbf, 0xbd, 0x5e, 0xa3, 0xa1, 0x5e, 0x20, 0x13, 0xfe, 0x67, 0x06, 0x17, 0x78, 0x2b, 0x8b, 0x8c, 0xe1, 0x8c, 0x05, 0xf4, 0xd9, 0x5e, 0xf1, 0x3f, 0xff, 0xfb, 0x1b, 0xa9, 0x27, 0x77, 0x91, + 0x2e, 0x0b, 0xd2, 0x87, 0x97, 0x0b, 0xba, 0x52, 0xb9, 0xef, 0x16, 0x55, 0x62, 0xfa, 0xc3, 0x11, 0x3f, 0x34, 0x89, 0x71, 0x3c, 0x9d, 0x96, 0xb4, 0x3d, 0x16, 0x8d, 0x55, 0x53, 0x7c, 0xa0, 0xb8, + 0x8c, 0x4a, 0xd6, 0x7d, 0x4d, 0x1a, 0x86, 0xc5, 0x2a, 0x9d, 0xee, 0xcd, 0xcc, 0x1c, 0xab, 0x7b, 0x34, 0xe3, 0x76, 0x4f, 0x64, 0x08, 0x09, 0x08, 0xbe, 0x4c, 0xb8, 0x38, 0x76, 0x0a, 0x00, 0xa3, + 0xf4, 0x32, 0x56, 0xe6, 0x0a, 0x33, 0x3d, 0x9e, 0x4e, 0xdb, 0x43, 0x1f, 0x20, 0x18, 0x51, 0xe5, 0x63, 0x04, 0x30, 0x46, 0x88, 0x54, 0xe9, 0xfb, 0x36, 0x84, 0x88, 0x5b, 0x57, 0x38, 0xc6, 0x74, + 0xe5, 0x6b, 0x34, 0x10, 0x07, 0xb2, 0xc4, 0x69, 0x36, 0x02, 0xd1, 0x18, 0xe4, 0x4e, 0xf5, 0xfe, 0xbb, 0xc3, 0xa8, 0x01, 0x8d, 0xd5, 0x1d, 0xe6, 0x6f, 0x56, 0x85, 0x8d, 0x33, 0xe2, 0x59, 0x0c, + 0x2d, 0x1e, 0x7e, 0x2e, 0x14, 0x6d, 0xc5, 0x7f, 0x38, 0x28, 0x83, 0xb3, 0x8d, 0x46, 0xe0, 0xd1, 0x3b, 0x73, 0xd2, 0x31, 0xc2, 0x14, 0xe9, 0x8e, 0xd5, 0x6d, 0xe5, 0xa6, 0x50, 0xba, 0xef, 0x04, + 0x18, 0x70, 0xc7, 0xbc, 0x53, 0xe8, 0x10, 0xaf, 0x6b, 0x0b, 0x52, 0x3c, 0x9a, 0xdb, 0xbb, 0x3e, 0xee, 0x5f, 0xd0, 0x2a, 0xe1, 0x2d, 0x44, 0xd0, 0xb1, 0x30, 0x99, 0x0a, 0x11, 0x50, 0x56, 0xd5, + 0xeb, 0x26, 0x23, 0xde, 0x09, 0xb3, 0xe3, 0x8d, 0x4b, 0x73, 0xb5, 0xe3, 0x55, 0x70, 0x23, 0x0d, 0x1f, 0x57, 0xdd, 0x1d, 0xe0, 0x27, 0x5e, 0x47, 0xa3, 0x00, 0x28, 0x5c, 0x1f, 0x86, 0x58, 0xa7, + 0xe8, 0x12, 0x15, 0x4c, 0xcf, 0x9c, 0x70, 0xf3, 0x7b, 0x61, 0xa4, 0xfc, 0xb7, 0xe0, 0xf6, 0x45, 0xfa, 0x0d, 0xad, 0xab, 0xee, 0x62, 0x2d, 0x6d, 0xe4, 0x83, 0x00, 0x97, 0x9a, 0x1c, 0x07, 0xc0, + 0x3e, 0xfc, 0xf7, 0x3f, 0x98, 0x3e, 0x15, 0x7a, 0x6a, 0x7f, 0x6b, 0x1c, 0xde, 0x6b, 0xf9, 0x68, 0x55, 0x7c, 0x9e, 0x15, 0x58, 0x82, 0x0f, 0x3f, 0x55, 0x7c, 0x94, 0x2d, 0x0a, 0x74, 0xeb, 0x2d, + 0x92, 0x7d, 0xc2, 0x6a, 0x17, 0x8b, 0xad, 0xbb, 0xf9, 0x77, 0x7d, 0xe6, 0x92, 0x2f, 0xe2, 0x61, 0x0a, 0x27, 0x32, 0x4f, 0xe2, 0xaf, 0xaa, 0x81, 0x58, 0x14, 0x99, 0x64, 0xf2, 0xf7, 0x71, 0x0f, + 0x46, 0xef, 0xfd, 0xd9, 0xce, 0x87, 0xf2, 0x8b, 0xb5, 0xca, 0xc2, 0x97, 0xdf, 0x96, 0x22, 0x86, 0xb1, 0x57, 0xcf, 0x4a, 0x81, 0x0c, 0x9d, 0x82, 0x8f, 0xdc, 0x3f, 0xc0, 0x40, 0xf2, 0x7c, 0x4a, + 0x12, 0x3e, 0xf9, 0xb2, 0x86, 0xa5, 0xb1, 0x71, 0x9a, 0x76, 0x82, 0x76, 0x82, 0xa9, 0xb3, 0x42, 0x34, 0xbc, 0x23, 0x3a, 0x92, 0xb8, 0x6f, 0x69, 0xf3, 0xe6, 0xbc, 0x52, 0xd6, 0x03, 0xe7, 0x0a, + 0x8e, 0x04, 0x9c, 0x02, 0xe3, 0xae, 0x9d, 0x11, 0xa9, 0x60, 0x34, 0x21, 0xf2, 0x44, 0xb7, 0x26, 0x82, 0x61, 0x82, 0x5f, 0xdb, 0x37, 0x3b, 0x35, 0x4f, 0xfc, 0x18, 0xe6, 0xee, 0xa9, 0xee, 0x85, + 0x8d, 0x21, 0xed, 0x0f, 0x22, 0x58, 0x84, 0xf9, 0x0a, 0x34, 0x29, 0x16, 0xc6, 0x7b, 0x1e, 0x27, 0x1b, 0x36, 0x4c, 0x98, 0x08, 0x24, 0x36, 0xef, 0x9c, 0xb2, 0x92, 0xe7, 0x63, 0x23, 0xbb, 0x16, + 0x13, 0xe2, 0xab, 0x2d, 0x32, 0xfa, 0x78, 0xdd, 0x7b, 0x87, 0x8d, 0xc7, 0xe2, 0xc5, 0x16, 0x26, 0x93, 0xe0, 0xfa, 0x99, 0x17, 0xde, 0x1f, 0x0b, 0x42, 0xec, 0xb1, 0x08, 0xb8, 0x53, 0xd9, 0x9a, + 0x18, 0x2d, 0xcf, 0xd2, 0xaa, 0x8d, 0xa7, 0xa8, 0xe3, 0xe3, 0x95, 0x26, 0xa5, 0x40, 0x5d, 0x15, 0xf2, 0xf0, 0xed, 0xe6, 0x83, 0xb1, 0x36, 0xc5, 0xae, 0xbb, 0xa0, 0x73, 0x25, 0xc4, 0xd9, 0x38, + 0xd0, 0x13, 0xa1, 0x78, 0xa3, 0x8d, 0x2d, 0xaf, 0x13, 0x6d, 0x9c, 0x80, 0x8e, 0xc7, 0xc5, 0xd7, 0x76, 0x6b, 0x02, 0x66, 0x63, 0x0f, 0xbb, 0x9c, 0x30, 0xc0, 0x86, 0xaa, 0xc9, 0x77, 0x88, 0xb3, + 0x0c, 0x29, 0xf7, 0xea, 0x5e, 0x08, 0x32, 0x1a, 0x03, 0x0a, 0xe4, 0xdb, 0x35, 0x75, 0xb1, 0xc8, 0xba, 0x23, 0xab, 0xfb, 0x1f, 0xc5, 0xd3, 0xa4, 0xbb, 0x03, 0x7c, 0xeb, 0xa2, 0x36, 0xc8, 0x30, + 0x20, 0x99, 0xfb, 0x68, 0x5a, 0x9f, 0x8d, 0x32, 0x7b, 0x6a, 0xf5, 0x1b, 0xfc, 0x2c, 0xc6, 0x79, 0x2f, 0x6c, 0x23, 0xb7, 0x2f, 0x7e, 0x3d, 0x98, 0x6c, 0x80, 0xbb, 0x87, 0x6c, 0xe8, 0xa4, 0xb9, + 0xe5, 0x03, 0xf4, 0x57, 0x33, 0xfb, 0xc1, 0x02, 0xfc, 0x0a, 0xff, 0x8e, 0x13, 0x40, 0x22, 0xc2, 0x3e, 0xc7, 0xb5, 0x17, 0xf9, 0xf7, 0x6c, 0x63, 0x0b, 0x76, 0xac, 0xb2, 0x04, 0x3e, 0x3d, 0x71, + 0xb6, 0x11, 0x29, 0x0b, 0x28, 0x3a, 0x1c, 0x58, 0xde, 0xec, 0xc8, 0xab, 0x3e, 0xca, 0xca, 0x84, 0xe2, 0x3f, 0xd2, 0xd9, 0x28, 0xdf, 0x6e, 0xa0, 0x87, 0x81, 0xaa, 0x71, 0x8a, 0xbf, 0x68, 0x4f, + 0xec, 0x85, 0xe2, 0x13, 0x2b, 0x6a, 0xa5, 0x49, 0xdc, 0xc4, 0xbd, 0xbf, 0xe7, 0x52, 0x88, 0x0d, 0x45, 0x97, 0x10, 0x7d, 0xfd, 0x3d, 0x8e, 0x7e, 0x50, 0xf6, 0xba, 0x86, 0x73, 0x9b, 0xa4, 0x10, + 0xe9, 0xa4, 0x1e, 0xe3, 0x34, 0x2a, 0xd8, 0x42, 0xaf, 0x86, 0x1e, 0x73, 0x43, 0xa4, 0x72, 0xc5, 0xc9, 0x89, 0x3b, 0xfb, 0x2b, 0x12, 0x21, 0x47, 0x31, 0x5b, 0x66, 0xa7, 0xa4, 0x39, 0x7d, 0xa2, + 0x8d, 0xd3, 0xa6, 0x37, 0xf0, 0x9d, 0x4d, 0xad, 0x4f, 0xf1, 0x5f, 0xc5, 0x9d, 0x49, 0xe3, 0x2c, 0xfb, 0xe9, 0xb2, 0x1f, 0x6d, 0xdd, 0xda, 0xb2, 0x56, 0xa3, 0x05, 0x8a, 0x0c, 0x43, 0xbc, 0x00, + 0xc6, 0x8a, 0xe3, 0x0c, 0xc1, 0xbe, 0xe3, 0x4f, 0xba, 0xf7, 0x55, 0xa8, 0xe2, 0x5b, 0xa4, 0x5c, 0x25, 0x82, 0x52, 0x61, 0x4d, 0xa5, 0x8f, 0x60, 0x9e, 0x93, 0xd9, 0x9c, 0xb4, 0xb2, 0x38, 0x6b, + 0x5c, 0x0a, 0x2b, 0x25, 0xd4, 0x18, 0x4e, 0xb4, 0x3c, 0xc5, 0xbd, 0x71, 0xc2, 0x88, 0xf8, 0x02, 0xb4, 0x5f, 0x23, 0x41, 0x0f, 0xc3, 0x4a, 0x96, 0x89, 0xba, 0x34, 0x42, 0x36, 0x53, 0x81, 0xc0, + 0x55, 0x2c, 0xe8, 0xaf, 0x5d, 0x2f, 0x35, 0x01, 0x96, 0xd1, 0xbf, 0x91, 0x97, 0xa4, 0xbf, 0x43, 0x72, 0xa1, 0xe2, 0xb5, 0xf1, 0xf6, 0x0f, 0xe5, 0xbf, 0x74, 0xa4, 0xc0, 0x64, 0x12, 0xa5, 0x52, + 0x96, 0xd2, 0x72, 0x4d, 0xb4, 0xcc, 0x19, 0xd8, 0xbc, 0x1f, 0x03, 0xa7, 0xf7, 0x77, 0xd0, 0xa6, 0xab, 0x35, 0x4f, 0x56, 0x91, 0x97, 0xe4, 0x00, 0x0b, 0xe0, 0x37, 0xad, 0x99, 0x9b, 0x3a, 0xb4, + 0x1c, 0x05, 0xba, 0x6f, 0xe0, 0x04, 0x65, 0x1e, 0xca, 0xfd, 0xb2, 0xfc, 0x89, 0x38, 0xa2, 0x4c, 0xfd, 0x83, 0x98, 0x73, 0x97, 0xa8, 0xa9, 0x52, 0x18, 0x5f, 0x27, 0x28, 0x42, 0x67, 0xd4, 0x32, + 0x68, 0x26, 0x58, 0x48, 0x17, 0xa8, 0xbd, 0x4f, 0x92, 0x36, 0xa1, 0xc7, 0x21, 0x44, 0x0d, 0xd6, 0xae, 0xa6, 0x87, 0x83, 0x47, 0x91, 0x1f, 0xa4, 0xf7, 0x42, 0x26, 0x76, 0x58, 0x0b, 0xaa, 0x96, + 0x8d, 0xc0, 0xcb, 0x25, 0xfd, 0x4e, 0xe2, 0x6d, 0x1c, 0x74, 0xe0, 0xee, 0x43, 0x13, 0x98, 0x2f, 0x8d, 0x68, 0x52, 0xa0, 0xe9, 0x0f, 0xab, 0x1d, 0x15, 0x00, 0xaf, 0x45, 0x31, 0xcb, 0x80, 0xd6, + 0x82, 0x59, 0x99, 0x5e, 0x9f, 0x5e, 0xa5, 0x7f, 0x71, 0xf8, 0xde, 0xce, 0x22, 0xef, 0xd8, 0x50, 0x7f, 0x7c, 0x80, 0x11, 0xb3, 0xdc, 0x22, 0xc2, 0x07, 0x98, 0x77, 0x00, 0x64, 0xd2, 0x0b, 0x3d, + 0xb7, 0xce, 0xee, 0x4f, 0x0b, 0xa8, 0x85, 0xcc, 0x18, 0x19, 0x50, 0x0d, 0x87, 0xc0, 0x83, 0x5a, 0x7d, 0xf9, 0x42, 0x7b, 0xc5, 0x7f, 0x4f, 0x61, 0x9e, 0x1e, 0xb0, 0x31, 0xba, 0xd2, 0xd6, 0x3e, + 0xfb, 0x73, 0xb0, 0xe4, 0x1f, 0x64, 0x3e, 0x43, 0xff, 0x2b, 0xc8, 0xeb, 0x74, 0x54, 0xee, 0x62, 0x47, 0x56, 0xe0, 0xca, 0x7d, 0xe8, 0xa3, 0xd3, 0xd8, 0x7a, 0x66, 0x5a, 0x69, 0x57, 0x9f, 0x40, + 0xc2, 0x74, 0x7a, 0x23, 0xd8, 0xce, 0x1e, 0xcf, 0x3f, 0x43, 0x88, 0xc6, 0xcd, 0x56, 0xbf, 0xb6, 0x0c, 0xa4, 0xfc, 0x6a, 0x4e, 0x3b, 0xa5, 0x91, 0x99, 0xca, 0xbc, 0xb6, 0xb9, 0x25, 0x28, 0x76, + 0xd8, 0xcd, 0xde, 0xd6, 0x06, 0xd0, 0xa6, 0x64, 0x35, 0x4d, 0x24, 0x00, 0xb5, 0xd7, 0x01, 0x5b, 0xcd, 0x9e, 0x7a, 0xb7, 0x81, 0x65, 0xca, 0x38, 0x4d, 0xe0, 0x89, 0xdd, 0x7c, 0x67, 0xd2, 0x87, + 0x76, 0x5f, 0xa5, 0x43, 0xdf, 0x37, 0x72, 0x0e, 0xdb, 0xc6, 0x4a, 0x99, 0x0e, 0x6b, 0x16, 0x0c, 0x69, 0x21, 0x8c, 0x73, 0x57, 0x79, 0x77, 0xed, 0x4f, 0x4e, 0x77, 0xf6, 0x77, 0x43, 0x61, 0x69, + 0x01, 0x5f, 0xba, 0x47, 0x14, 0x2a, 0x50, 0x1c, 0x07, 0x31, 0x93, 0x8c, 0x73, 0x81, 0x65, 0x2d, 0xb9, 0x73, 0x22, 0x7c, 0x9c, 0x58, 0x2a, 0x54, 0x65, 0x94, 0xfd, 0x8c, 0x44, 0x64, 0x3d, 0xd6, + 0xed, 0x7d, 0x92, 0xa4, 0x19, 0x54, 0x41, 0x90, 0xb5, 0x95, 0xb8, 0xda, 0xfd, 0x3b, 0x7b, 0xbe, 0x84, 0x1a, 0x60, 0x34, 0x54, 0xc1, 0xad, 0x75, 0x4c, 0x3e, 0x98, 0x92, 0x96, 0xe3, 0x27, 0x32, + 0xc3, 0xc2, 0x88, 0x84, 0x59, 0x26, 0xa6, 0x79, 0xb8, 0xa4, 0xd1, 0xa5, 0x57, 0xc2, 0x16, 0x4f, 0xcf, 0x64, 0x02, 0xdd, 0xae, 0x2d, 0x69, 0xc3, 0x1f, 0x33, 0xd9, 0x96, 0x52, 0xfb, 0x5f, 0xd3, + 0x94, 0xda, 0x6a, 0xce, 0xc5, 0xef, 0x84, 0x19, 0x35, 0x64, 0x94, 0x9f, 0x30, 0x7d, 0x23, 0x6a, 0xe1, 0x64, 0x7a, 0x3f, 0x1a, 0x34, 0xf5, 0xec, 0xb5, 0x70, 0xf8, 0x5b, 0xb9, 0xea, 0xa7, 0x1b, + 0x2b, 0x77, 0x37, 0x34, 0xb2, 0xd7, 0x47, 0x65, 0xde, 0x6d, 0xd7, 0x29, 0xcc, 0x3d, 0x7f, 0x84, 0x7f, 0x50, 0x4e, 0x06, 0xe4, 0x8f, 0xb9, 0x64, 0x0c, 0x28, 0xc2, 0xdd, 0x8f, 0xd6, 0xd7, 0x56, + 0x1f, 0xb6, 0xea, 0xe2, 0x40, 0x35, 0x84, 0x93, 0x14, 0xfa, 0xd2, 0xd3, 0x6c, 0xa1, 0x25, 0x6f, 0x73, 0x73, 0x63, 0xb3, 0xe7, 0x42, 0x49, 0xa9, 0x9f, 0xac, 0x9b, 0xf6, 0x17, 0x65, 0xcd, 0xb3, + 0xee, 0x6b, 0x2c, 0xa0, 0xd0, 0x35, 0x61, 0x5c, 0x61, 0x1b, 0x31, 0xb7, 0x4b, 0x11, 0x64, 0x97, 0xe0, 0x6b, 0x3c, 0xf7, 0x94, 0x62, 0xca, 0x80, 0x6c, 0x05, 0x71, 0x06, 0x04, 0xf0, 0xce, 0x4a, + 0xce, 0x36, 0x7f, 0xb3, 0xb3, 0x5a, 0x72, 0x3e, 0x61, 0xa1, 0x05, 0xb7, 0x28, 0x7a, 0xf0, 0x7d, 0xd8, 0x5c, 0x94, 0x5e, 0x87, 0x42, 0xaf, 0xa9, 0x9d, 0x83, 0x0c, 0xfa, 0x08, 0x40, 0x5f, 0xc4, + 0x8e, 0x41, 0x42, 0x7b, 0xcb, 0xff, 0xb5, 0xbf, 0xe0, 0x59, 0x18, 0x87, 0xe3, 0x23, 0x5b, 0x24, 0xe9, 0x0f, 0x5a, 0x1f, 0xc8, 0xa1, 0xce, 0x63, 0x63, 0x35, 0xad, 0x57, 0x4c, 0xc3, 0x58, 0xbf, + 0xd7, 0x88, 0xb1, 0x10, 0x26, 0x08, 0xfa, 0x65, 0x7b, 0x57, 0xe7, 0xe7, 0x77, 0xbf, 0x8b, 0xae, 0xad, 0xef, 0x62, 0x3f, 0x77, 0x6a, 0x67, 0x6a, 0x1d, 0x19, 0x49, 0xce, 0xa8, 0xab, 0x19, 0xc8, + 0x71, 0xeb, 0xc4, 0xe7, 0xa6, 0x69, 0x52, 0xdd, 0x28, 0x63, 0x17, 0x32, 0x50, 0x88, 0x1b, 0x36, 0x9f, 0x28, 0x24, 0xcf, 0x05, 0x65, 0x50, 0xb6, 0xd7, 0x7c, 0x4b, 0x63, 0x7d, 0xa3, 0x19, 0x8a, + 0x2a, 0xca, 0xf9, 0x51, 0xd5, 0xbd, 0x37, 0xe9, 0x1b, 0x47, 0xdb, 0x72, 0xfa, 0x4f, 0xb1, 0xf6, 0x2f, 0x89, 0xee, 0x59, 0xc3, 0x2e, 0xf2, 0xa1, 0x3a, 0xbb, 0xeb, 0x0a, 0x94, 0x0a, 0x09, 0x17, + 0x22, 0x5d, 0x9f, 0x1e, 0xab, 0x3c, 0x3f, 0x1e, 0xad, 0xea, 0xdb, 0xed, 0xb6, 0x3b, 0x73, 0x5e, 0xa7, 0xe4, 0x4c, 0xef, 0x87, 0xf0, 0xce, 0x7e, 0xef, 0x7a, 0x9e, 0x6c, 0x95, 0xcb, 0x1a, 0xe9, + 0xc6, 0xb9, 0xd9, 0x99, 0xdf, 0xcb, 0x34, 0x29, 0x86, 0xaf, 0x9e, 0xb1, 0x5d, 0x01, 0x14, 0x54, 0xde, 0x51, 0x35, 0xea, 0xd3, 0x6a, 0x28, 0x0a, 0xcb, 0x6c, 0x36, 0xec, 0x1d, 0xa1, 0xd3, 0xdf, + 0x03, 0xf5, 0x5b, 0x67, 0xf5, 0xd8, 0x96, 0xec, 0x4b, 0x77, 0xe0, 0x97, 0x78, 0xd3, 0xff, 0x92, 0xd9, 0x01, 0x9d, 0x6d, 0x44, 0xed, 0xd6, 0xdf, 0x06, 0x16, 0x70, 0x21, 0x5b, 0x6b, 0xc1, 0xff, + 0x74, 0x95, 0x1d, 0xf7, 0x4d, 0x76, 0x90, 0x95, 0x3d, 0xb2, 0xbe, 0x7e, 0xa5, 0x73, 0xbb, 0x1b, 0x87, 0x38, 0x08, 0x78, 0x95, 0x58, 0x46, 0x76, 0x49, 0xc0, 0xdc, 0xae, 0x16, 0x65, 0x5c, 0xc9, + 0xb7, 0x7c, 0xdd, 0xdf, 0x1c, 0x4c, 0x7c, 0x20, 0x09, 0xd1, 0x41, 0x64, 0x50, 0x60, 0xd3, 0x38, 0x84, 0x93, 0xac, 0xa4, 0x51, 0x22, 0xe1, 0xdc, 0xe1, 0x37, 0xe1, 0x5c, 0xca, 0x49, 0x9d, 0x05, + 0x1e, 0x09, 0x04, 0x78, 0xeb, 0xb4, 0x4d, 0xba, 0x24, 0x1e, 0xe6, 0x89, 0x87, 0x9d, 0xa2, 0xbc, 0x25, 0x96, 0xf6, 0xac, 0xef, 0xfd, 0x78, 0x55, 0x51, 0x76, 0xed, 0xa1, 0xc3, 0x3d, 0x42, 0x4d, + 0xba, 0x23, 0x2f, 0x61, 0x45, 0xd1, 0x89, 0xea, 0xbf, 0xcd, 0x7f, 0xba, 0x05, 0x03, 0xfa, 0xb4, 0x62, 0xb9, 0x1e, 0x0d, 0x40, 0xc8, 0x09, 0x45, 0xfd, 0x96, 0x46, 0x9c, 0xae, 0xe9, 0xba, 0xca, + 0x4f, 0x5a, 0x9d, 0x3e, 0xe9, 0x8f, 0xd7, 0x54, 0x38, 0x5e, 0x14, 0x6d, 0x1b, 0x4e, 0x65, 0x50, 0x2c, 0x53, 0x73, 0x3b, 0x57, 0x4a, 0xf9, 0xe2, 0x25, 0xd5, 0x7e, 0xee, 0xa7, 0x79, 0x2c, 0xb5, + 0xa7, 0x12, 0x66, 0xf8, 0x84, 0x84, 0x88, 0xa4, 0xc9, 0x09, 0x7d, 0xd2, 0x23, 0x68, 0x22, 0xa0, 0x84, 0xc9, 0x70, 0x27, 0x9f, 0xe6, 0x74, 0xc9, 0xad, 0x3f, 0xc9, 0x5f, 0xfc, 0xf4, 0x4c, 0xb1, + 0xdd, 0x7c, 0x82, 0x49, 0x0a, 0xfe, 0xe6, 0x04, 0xb6, 0x8a, 0x06, 0x97, 0xe9, 0xc1, 0x74, 0xbd, 0x7b, 0x92, 0xd3, 0x68, 0xa6, 0x17, 0x10, 0xfb, 0x83, 0xb9, 0x42, 0x29, 0x10, 0xe5, 0x72, 0x6c, + 0xf9, 0x24, 0x76, 0xd3, 0xcd, 0xa9, 0xe5, 0xa5, 0x74, 0x35, 0x7d, 0x13, 0xaf, 0x8a, 0xe2, 0x2f, 0x42, 0x44, 0xc2, 0x38, 0xd1, 0xa3, 0xc9, 0x27, 0x5d, 0x91, 0xcc, 0x06, 0xea, 0x94, 0xbc, 0x85, + 0x08, 0x91, 0xd0, 0xae, 0x96, 0xd5, 0x5d, 0x18, 0xb1, 0x57, 0x63, 0x10, 0x1b, 0x6f, 0xbd, 0xfd, 0x35, 0xe9, 0x97, 0x05, 0x9b, 0x0e, 0xf3, 0x42, 0xe7, 0x48, 0x14, 0xa6, 0xa2, 0xa8, 0xc0, 0x95, + 0xa9, 0x94, 0x3a, 0x47, 0x39, 0xf0, 0x8f, 0xbd, 0x1e, 0xe2, 0xf4, 0x82, 0x66, 0x80, 0x2a, 0x57, 0x51, 0x3e, 0x30, 0xd2, 0x2d, 0x51, 0x89, 0x68, 0x21, 0xc7, 0xe3, 0x89, 0x31, 0xec, 0x6c, 0x74, + 0x29, 0xcb, 0x12, 0x6a, 0x69, 0x73, 0xda, 0x7d, 0x69, 0x1d, 0x12, 0x85, 0x36, 0xe3, 0x32, 0x74, 0x13, 0x33, 0x93, 0x1c, 0xce, 0x6f, 0x20, 0x9a, 0x48, 0x31, 0xa9, 0x6d, 0x57, 0x26, 0x2a, 0x1f, + 0xa3, 0x95, 0x85, 0x53, 0x8c, 0x75, 0xb1, 0x1d, 0xa6, 0x23, 0x3e, 0x4f, 0x18, 0xbc, 0x4f, 0x23, 0x01, 0x4e, 0x2f, 0xb1, 0x6a, 0x9b, 0xde, 0xec, 0xc7, 0x10, 0xd1, 0xca, 0xde, 0xe1, 0x79, 0x85, + 0x55, 0xaa, 0x74, 0x84, 0xc3, 0x97, 0x48, 0x78, 0x0a, 0xaf, 0xca, 0xa3, 0xfb, 0x4d, 0x50, 0x15, 0x03, 0x20, 0x2d, 0xdb, 0x5e, 0xb7, 0x36, 0xc8, 0xe9, 0x1b, 0x18, 0xb4, 0x1f, 0x15, 0x02, 0x24, + 0x52, 0x6a, 0x2c, 0x9c, 0xd6, 0x23, 0xdc, 0xac, 0x75, 0x1d, 0xb1, 0x47, 0x8d, 0x15, 0x47, 0xc3, 0x38, 0xd0, 0x61, 0x72, 0x40, 0x1b, 0x1e, 0xbc, 0x04, 0x47, 0xfc, 0x0e, 0xa0, 0x7d, 0x15, 0xe7, + 0x35, 0xc6, 0x0d, 0x84, 0x66, 0x9d, 0x50, 0x38, 0xe0, 0xe2, 0xc0, 0x6f, 0x1b, 0xe0, 0xa0, 0x89, 0x69, 0x2b, 0xa6, 0x9d, 0x33, 0xf1, 0x79, 0xfc, 0x18, 0x92, 0xef, 0xff, 0xc0, 0xed, 0x6e, 0x98, + 0xa6, 0xb1, 0xb4, 0x47, 0xac, 0xef, 0xca, 0x2d, 0x0d, 0xd1, 0x26, 0xdc, 0x06, 0x7c, 0x0f, 0xb9, 0xee, 0x4d, 0x6e, 0x69, 0x94, 0xca, 0x8c, 0x7e, 0x58, 0x67, 0x33, 0x3e, 0x6c, 0x97, 0x36, 0x03, + 0x6a, 0x20, 0xdd, 0xad, 0xec, 0x95, 0x34, 0x07, 0x6f, 0x1a, 0x7e, 0xf9, 0x34, 0xe9, 0xc9, 0xb9, 0x80, 0xcf, 0x9d, 0xfb, 0xe3, 0x35, 0x87, 0xa3, 0xd6, 0xee, 0xad, 0x90, 0x7a, 0x43, 0x03, 0x54, + 0xd9, 0xa9, 0xa4, 0xfb, 0xdc, 0xfc, 0x16, 0x2f, 0x1d, 0xed, 0x19, 0xfa, 0x32, 0x8e, 0xed, 0x4f, 0x93, 0xd7, 0xca, 0x8b, 0xef, 0x29, 0xe4, 0xd7, 0x01, 0xe2, 0xf3, 0x38, 0xc2, 0xc2, 0x3d, 0x60, + 0x82, 0x88, 0xb6, 0x00, 0x7e, 0x89, 0xa9, 0xca, 0x14, 0x09, 0x78, 0xd9, 0x52, 0xa4, 0xbc, 0x67, 0xfa, 0xbe, 0xf7, 0xad, 0x3e, 0x5c, 0x39, 0x92, 0xd2, 0xcd, 0xb0, 0x96, 0xef, 0xba, 0x91, 0xd1, + 0x68, 0x07, 0x6b, 0xc2, 0x9c, 0xdf, 0xaf, 0x8c, 0xd6, 0x41, 0x55, 0x60, 0x52, 0xed, 0x8d, 0x5b, 0xa0, 0xbb, 0x10, 0xbf, 0x56, 0xc7, 0xc9, 0xea, 0x37, 0x24, 0x77, 0x63, 0x65, 0x21, 0xf4, 0x61, + 0x5c, 0xe8, 0x50, 0x0a, 0xbc, 0xf4, 0x67, 0x5e, 0xe9, 0x1f, 0x50, 0x09, 0x70, 0x43, 0xd6, 0x75, 0x74, 0x0a, 0x0d, 0xed, 0xcd, 0xf0, 0xcb, 0x60, 0xa4, 0x5d, 0xa6, 0x8c, 0xf8, 0x90, 0x41, 0x9f, + 0x92, 0xf1, 0xfc, 0xda, 0x8c, 0x6c, 0x1c, 0x84, 0xff, 0xcc, 0x2b, 0xc2, 0x43, 0xa4, 0xb4, 0xb5, 0xb3, 0x3c, 0xa5, 0xa6, 0x55, 0x5a, 0x92, 0x28, 0xa5, 0x9f, 0xd1, 0x58, 0x4f, 0xab, 0x29, 0x10, + 0x61, 0xfb, 0xf1, 0x17, 0xfd, 0x43, 0xba, 0x2f, 0x6e, 0xa4, 0x12, 0x6f, 0xf0, 0x89, 0x3e, 0x2a, 0x6b, 0xf6, 0xb4, 0x00, 0x89, 0x7b, 0x56, 0x30, 0xc1, 0x79, 0x70, 0xce, 0x58, 0xd6, 0xee, 0x6f, + 0x50, 0x27, 0x87, 0x6a, 0x67, 0xa8, 0x63, 0xe6, 0x62, 0x15, 0x7a, 0x39, 0xfb, 0x18, 0xe3, 0xe4, 0x97, 0x95, 0x9e, 0x0b, 0x03, 0x5f, 0xf1, 0x81, 0x6d, 0x3f, 0x20, 0x0b, 0x2d, 0xee, 0xb6, 0x4c, + 0x3d, 0xa1, 0x76, 0xd2, 0xa3, 0x5c, 0x19, 0x5d, 0xc2, 0x29, 0x12, 0x3e, 0x63, 0xb6, 0x84, 0xb3, 0xed, 0x3c, 0x9d, 0x33, 0x31, 0x58, 0xfa, 0x37, 0x65, 0xd3, 0x8a, 0xe8, 0xc2, 0x1e, 0x8d, 0x1b, + 0xa9, 0x13, 0x03, 0x86, 0x18, 0x3c, 0x47, 0x34, 0x00, 0x64, 0xf5, 0x1c, 0xd9, 0xfb, 0x49, 0x9f, 0x6d, 0xc5, 0xb1, 0x43, 0x4c, 0x15, 0x69, 0xa2, 0x4a, 0xd7, 0x39, 0x01, 0x02, 0x4a, 0xf3, 0x34, + 0x00, 0xe1, 0xc7, 0x23, 0x66, 0xdc, 0x88, 0xaf, 0x14, 0x25, 0xab, 0xb4, 0x03, 0x2f, 0x3f, 0x8c, 0x28, 0xb4, 0xd3, 0x0d, 0x95, 0xa7, 0x88, 0x75, 0xeb, 0x12, 0x95, 0x7b, 0xca, 0x1f, 0x13, 0x54, + 0xae, 0x9e, 0x7a, 0x71, 0xba, 0xbf, 0x9b, 0xa9, 0x41, 0xf5, 0xd2, 0xd3, 0x57, 0xa9, 0x5d, 0x1a, 0xa6, 0x3b, 0x99, 0xca, 0x86, 0xfb, 0xac, 0x15, 0x74, 0xc4, 0xc8, 0x9a, 0x2c, 0x6e, 0xca, 0x30, + 0xfd, 0x74, 0x9b, 0x78, 0xbd, 0xeb, 0xc1, 0xc5, 0x1f, 0xa0, 0xd2, 0xf5, 0x3b, 0x27, 0x54, 0xbf, 0xa0, 0x7f, 0x1e, 0x2d, 0x2d, 0x98, 0xad, 0x19, 0x68, 0xf9, 0xca, 0x47, 0xe0, 0x19, 0xa6, 0xdd, + 0x48, 0x08, 0xf2, 0x13, 0x7a, 0xfe, 0x61, 0x06, 0x64, 0x7f, 0xe7, 0x44, 0xeb, 0x3b, 0x4b, 0x58, 0x2e, 0x91, 0x84, 0xa0, 0x00, 0xf7, 0xcc, 0xab, 0x43, 0x57, 0x1d, 0xb6, 0xb8, 0xe4, 0x75, 0xae, + 0xfc, 0x42, 0x23, 0xca, 0xd0, 0x53, 0xf8, 0xef, 0xb4, 0x99, 0x87, 0xa3, 0x39, 0x35, 0x75, 0x12, 0x4c, 0xb9, 0xcb, 0x23, 0x28, 0x80, 0x28, 0x40, 0x5e, 0xf9, 0xf6, 0xea, 0x30, 0x6f, 0x96, 0x37, + 0xcc, 0xad, 0x23, 0xfb, 0x14, 0x70, 0xb2, 0x4d, 0xf7, 0x4b, 0x01, 0x30, 0xcc, 0x79, 0x2a, 0x2c, 0x27, 0x73, 0x0b, 0xf8, 0x46, 0xcf, 0x26, 0x1a, 0x11, 0x19, 0x29, 0xfa, 0x08, 0x74, 0xba, 0xea, + 0xf1, 0x15, 0x89, 0xd1, 0x22, 0x97, 0x8a, 0x45, 0x64, 0xee, 0xb4, 0xcd, 0x30, 0x74, 0x4f, 0x1f, 0x66, 0x0d, 0xaa, 0xb2, 0x99, 0xf5, 0xb3, 0x11, 0xc7, 0x7f, 0xf1, 0x61, 0x92, 0x62, 0x66, 0x93, + 0x2f, 0x27, 0x21, 0x1d, 0x08, 0x06, 0x1c, 0xd2, 0x6d, 0x09, 0x59, 0xc7, 0xfc, 0xc4, 0x27, 0xe0, 0x43, 0x6c, 0x24, 0x3f, 0xae, 0x73, 0xad, 0x9e, 0x69, 0x3d, 0x3b, 0x05, 0x3d, 0x8e, 0x39, 0x23, + 0x01, 0x2d, 0x9e, 0x05, 0xb0, 0x83, 0x79, 0xa7, 0x6d, 0x96, 0x70, 0x27, 0x28, 0x2b, 0xba, 0xe3, 0x6b, 0x0c, 0x58, 0x77, 0x43, 0x2b, 0x42, 0xbc, 0xdf, 0x2c, 0x3f, 0x4e, 0xa1, 0x5d, 0xf2, 0x35, + 0x45, 0x7f, 0x41, 0x03, 0xc2, 0x0f, 0xb5, 0x10, 0x48, 0xb1, 0x6a, 0x3f, 0x63, 0x68, 0x9d, 0x3d, 0xe2, 0x74, 0x64, 0xfd, 0xfa, 0x24, 0xc3, 0x3e, 0xc6, 0xfd, 0x2d, 0xde, 0xeb, 0x62, 0xdd, 0xa0, + 0x70, 0x2f, 0x9d, 0xbb, 0xe3, 0x57, 0x6c, 0xec, 0x7b, 0xda, 0x52, 0xb4, 0xeb, 0xe5, 0x9e, 0x3b, 0x81, 0x30, 0x21, 0x20, 0x6d, 0x6b, 0x29, 0x74, 0x11, 0xa9, 0x15, 0xa0, 0xb7, 0x44, 0x7c, 0xc7, + 0x41, 0x76, 0x75, 0x0c, 0xd1, 0x31, 0x60, 0xd5, 0xa1, 0x42, 0x6e, 0x45, 0xcc, 0x5d, 0xe6, 0x9a, 0x28, 0x64, 0x39, 0x3e, 0x12, 0xa2, 0x40, 0xcf, 0x51, 0xfa, 0xe9, 0x2d, 0x94, 0x65, 0x17, 0x88, + 0x2f, 0xa8, 0xfd, 0x36, 0xd4, 0xf6, 0xfc, 0xd1, 0x0b, 0x8e, 0x7d, 0x84, 0x13, 0x14, 0x6d, 0xff, 0xbd, 0xe2, 0xa9, 0x84, 0xd4, 0x85, 0x72, 0xc8, 0xf2, 0x44, 0x91, 0x41, 0xe9, 0x1f, 0x91, 0x0f, + 0x8f, 0xf1, 0x36, 0xfc, 0x2d, 0xa4, 0x0f, 0x6a, 0xc5, 0xd5, 0xc1, 0x04, 0x9f, 0xfd, 0xbd, 0x9b, 0x13, 0x69, 0x62, 0xea, 0xfa, 0x7c, 0x74, 0x2b, 0xee, 0x73, 0xf5, 0x4b, 0x82, 0x6a, 0x36, 0x1a, + 0x5c, 0xc6, 0x6e, 0x3f, 0xbc, 0xa0, 0x0f, 0xca, 0xbc, 0x70, 0xf4, 0x11, 0xed, 0x4b, 0xc9, 0x12, 0xbc, 0xff, 0x55, 0xe7, 0xc2, 0x5d, 0x0d, 0x88, 0x88, 0xa7, 0x60, 0x3a, 0xa4, 0x38, 0x8e, 0xca, + 0x54, 0xa9, 0x61, 0x23, 0xa2, 0x9b, 0xd4, 0xcc, 0x38, 0xdb, 0xf9, 0xfa, 0x37, 0x8c, 0xf4, 0x42, 0xcf, 0xd1, 0xf3, 0xbd, 0x38, 0x26, 0xc0, 0xee, 0xf6, 0xbd, 0xaf, 0xd5, 0xec, 0x74, 0x86, 0x6d, + 0x7e, 0x05, 0xbe, 0xaa, 0x11, 0x62, 0x32, 0x30, 0xd4, 0x41, 0x4f, 0xf3, 0x58, 0x42, 0x99, 0x85, 0xba, 0xc7, 0xf8, 0x6f, 0x53, 0xf2, 0x2c, 0x03, 0xe3, 0xe5, 0x7e, 0x0e, 0xae, 0x92, 0x97, 0x00, + 0x1a, 0x4b, 0x7b, 0x6b, 0x27, 0x5a, 0xea, 0xf6, 0x94, 0xdb, 0xab, 0x0c, 0x45, 0x61, 0x23, 0xa6, 0x33, 0x5c, 0xce, 0x22, 0xd2, 0xbc, 0x9e, 0x99, 0xf8, 0xa1, 0xb9, 0x52, 0x0d, 0x81, 0x7b, 0xb4, + 0x79, 0x5c, 0xaf, 0xf2, 0x93, 0xef, 0x30, 0x52, 0x2c, 0xed, 0x81, 0x38, 0x2e, 0x7a, 0xf6, 0x4e, 0x1a, 0x24, 0xad, 0x1e, 0x9d, 0x5e, 0x5a, 0x78, 0x2f, 0xed, 0x70, 0xb6, 0xba, 0x4a, 0x08, 0x24, + 0x93, 0xb8, 0x6f, 0x8a, 0xa7, 0xda, 0xce, 0x85, 0xe7, 0x55, 0xe7, 0x1b, 0xba, 0x48, 0x3e, 0x2c, 0x3c, 0x56, 0x6c, 0x32, 0x89, 0xab, 0xf8, 0x09, 0x2c, 0x5e, 0x65, 0x3f, 0x93, 0xab, 0xeb, 0xd6, + 0x35, 0xd4, 0xf0, 0x34, 0xa6, 0xf5, 0xa9, 0xce, 0xd8, 0x10, 0xac, 0x0c, 0xad, 0xac, 0x0c, 0x4f, 0x4d, 0xfe, 0x6c, 0x91, 0xdd, 0x01, 0xec, 0x9a, 0x4c, 0xe1, 0xaf, 0xf7, 0x28, 0x46, 0x9b, 0x21, + 0xb6, 0xa4, 0x02, 0x2e, 0x87, 0x5a, 0xc6, 0x83, 0x4a, 0xca, 0xff, 0x9c, 0x76, 0x62, 0xbf, 0x02, 0x27, 0x4e, 0x3f, 0xc5, 0x0c, 0xfa, 0xc0, 0x6a, 0x2b, 0x9c, 0x69, 0x94, 0x02, 0x98, 0x23, 0x53, + 0x31, 0xcd, 0xe9, 0x10, 0xbb, 0x9b, 0x4a, 0xa1, 0xc3, 0xf2, 0x2c, 0xd5, 0x11, 0xad, 0x44, 0xba, 0x80, 0xcb, 0xd4, 0x49, 0xb5, 0x4a, 0xa0, 0x4b, 0xb1, 0x99, 0x66, 0xe5, 0xf8, 0xc4, 0x5a, 0xd6, + 0x3f, 0x67, 0xd1, 0x7c, 0x24, 0x7e, 0x4b, 0xd2, 0xd9, 0xb5, 0x67, 0xcc, 0x3e, 0x17, 0x9c, 0x01, 0x3b, 0x41, 0x89, 0x99, 0x40, 0x25, 0x71, 0x12, 0x14, 0xb0, 0xc9, 0xc0, 0x24, 0x44, 0xf9, 0x3b, + 0x7f, 0x5b, 0x44, 0x31, 0xdf, 0xae, 0xc8, 0xef, 0xcb, 0xf7, 0x5d, 0x9a, 0x8a, 0x39, 0x9d, 0x9a, 0x15, 0x44, 0xfc, 0xf6, 0xc1, 0x0b, 0xf7, 0x30, 0xf0, 0xae, 0x66, 0xe2, 0x1f, 0x0e, 0xed, 0x3f, + 0xf3, 0x95, 0x8e, 0x84, 0x13, 0xcc, 0x8e, 0x38, 0x8a, 0x73, 0xd2, 0x43, 0xcb, 0x60, 0xfc, 0xe4, 0x55, 0xc0, 0x55, 0xd2, 0x93, 0x13, 0x6f, 0xff, 0x08, 0x94, 0xfa, 0x7c, 0x85, 0xbe, 0xd8, 0x8a, + 0x41, 0xb8, 0x32, 0x47, 0xe8, 0xd1, 0xbe, 0x23, 0x0e, 0x01, 0xbd, 0xe8, 0x80, 0xc7, 0x2b, 0x16, 0xfb, 0x2d, 0xbf, 0xf1, 0xac, 0x41, 0x31, 0x4d, 0x1e, 0x88, 0xf2, 0x23, 0xd3, 0xfc, 0xa6, 0xb0, + 0x63, 0xa6, 0x61, 0x4f, 0x5e, 0xd0, 0xea, 0x61, 0x29, 0x90, 0x17, 0xc5, 0x50, 0xe7, 0xa5, 0x9f, 0xca, 0xcc, 0xe5, 0x3e, 0x06, 0x21, 0x6e, 0xd2, 0x8e, 0x6a, 0xd1, 0x62, 0x43, 0x57, 0x33, 0xdb, + 0xa6, 0xea, 0x2a, 0x56, 0x29, 0xe2, 0x0e, 0x67, 0xcb, 0x0f, 0x3f, 0x5c, 0xf8, 0x36, 0xfd, 0xd1, 0x77, 0x5f, 0x7b, 0xc5, 0xd4, 0xcc, 0x6b, 0x95, 0x29, 0xb2, 0x6d, 0x7a, 0x94, 0x74, 0x7f, 0xd5, + 0xf1, 0xc8, 0x58, 0x06, 0xca, 0x5e, 0xbf, 0x97, 0xaf, 0xd7, 0x27, 0xe9, 0x94, 0x57, 0x74, 0xde, 0xe3, 0x28, 0xce, 0xcd, 0x86, 0xac, 0x9b, 0x11, 0x7d, 0x68, 0x95, 0x9d, 0x68, 0x17, 0xd9, 0x7b, + 0xdd, 0xa0, 0x01, 0x4c, 0xeb, 0xb0, 0x57, 0x35, 0xe2, 0xa1, 0xb8, 0xd7, 0x2d, 0xc7, 0xb6, 0x0c, 0x08, 0x6c, 0x57, 0x97, 0xce, 0x99, 0xeb, 0xa6, 0x7c, 0xf2, 0xd0, 0xb4, 0x40, 0x54, 0x7d, 0x87, + 0xef, 0x70, 0x23, 0x87, 0x64, 0xd5, 0x41, 0x2e, 0xad, 0x5c, 0xc1, 0xbe, 0x47, 0xfa, 0x93, 0x9c, 0x8a, 0x6c, 0x28, 0xce, 0xd5, 0xff, 0xdc, 0x2c, 0xa6, 0x40, 0x76, 0xf8, 0x9a, 0x24, 0xdd, 0xc6, + 0x6b, 0x3b, 0x82, 0xe8, 0x87, 0xbb, 0xd0, 0x8d, 0xa6, 0xc0, 0x95, 0x1c, 0x8b, 0x9d, 0xba, 0x4b, 0xb2, 0xe0, 0x01, 0xdd, 0xb5, 0xb3, 0x65, 0x5c, 0x2f, 0xc5, 0xd6, 0x3f, 0x34, 0x4e, 0x50, 0xa1, + 0x54, 0x71, 0x87, 0x22, 0xdf, 0xe7, 0x2c, 0x2e, 0x1c, 0x1e, 0x0f, 0xde, 0x88, 0x98, 0x12, 0x98, 0xfe, 0xb6, 0x35, 0xff, 0x10, 0x73, 0xee, 0x96, 0x9c, 0x04, 0xf8, 0x28, 0xff, 0xa7, 0x2f, 0xe2, + 0x16, 0x33, 0x3c, 0x9e, 0x7e, 0x54, 0x07, 0xb4, 0x22, 0xb2, 0x2f, 0xc4, 0x33, 0xa1, 0xe8, 0x78, 0x2f, 0xfb, 0x87, 0xb6, 0x20, 0x1f, 0xf1, 0x2e, 0x14, 0xb9, 0xf4, 0xe0, 0x73, 0x42, 0x68, 0xb4, + 0x12, 0xf1, 0xeb, 0x69, 0xee, 0x97, 0x20, 0x82, 0x3e, 0x76, 0x7d, 0xa1, 0x2c, 0x46, 0x08, 0x49, 0xba, 0x22, 0x78, 0xed, 0x17, 0xea, 0x71, 0xf6, 0xf0, 0xb9, 0x05, 0xd4, 0x84, 0x2a, 0x19, 0x93, + 0x6f, 0xf3, 0x98, 0xf4, 0xa6, 0xd4, 0x54, 0x81, 0x15, 0x48, 0xd1, 0xed, 0xb8, 0x83, 0x6f, 0x17, 0x6a, 0x44, 0xd0, 0x41, 0xdd, 0xaa, 0x1f, 0xa9, 0x50, 0x6c, 0x26, 0x88, 0xd6, 0x93, 0x63, 0xdc, + 0x17, 0x37, 0xae, 0xe3, 0xba, 0x93, 0x52, 0x5a, 0xf2, 0x29, 0x7f, 0x42, 0xb4, 0xad, 0x77, 0x72, 0xec, 0xa9, 0x53, 0x8d, 0x95, 0x8f, 0x88, 0xce, 0xbc, 0xf0, 0xa2, 0xf5, 0x07, 0x25, 0x9a, 0x81, + 0xd3, 0x67, 0xdc, 0x3b, 0xb4, 0x87, 0x1c, 0x90, 0xdb, 0xfa, 0xea, 0x2a, 0xa1, 0xe8, 0xcb, 0xf3, 0xc5, 0xbe, 0x32, 0xca, 0x69, 0x80, 0x33, 0x51, 0xd2, 0x0d, 0x34, 0x2b, 0x51, 0xbd, 0x60, 0x2d, + 0x25, 0x6b, 0xbd, 0x75, 0xb7, 0x0c, 0x24, 0xd5, 0xbb, 0x79, 0x01, 0xbf, 0xc4, 0x01, 0x9b, 0x36, 0x3c, 0xf2, 0x6c, 0x51, 0xfe, 0x87, 0x88, 0xe1, 0x60, 0x05, 0x53, 0xdb, 0x73, 0x9c, 0x48, 0xf1, + 0xc3, 0xc8, 0xee, 0x63, 0x6b, 0x32, 0x74, 0x19, 0x2b, 0xc9, 0xad, 0x95, 0x65, 0x66, 0x2e, 0x9d, 0x03, 0xb8, 0x05, 0xa1, 0xac, 0xa1, 0x93, 0xe1, 0x7e, 0x46, 0x76, 0x55, 0x2a, 0x54, 0x13, 0xdf, + 0xe0, 0xd1, 0x68, 0x36, 0x50, 0x5f, 0xb0, 0xfb, 0xd8, 0x27, 0x68, 0x6e, 0x92, 0x1c, 0xb2, 0xaa, 0xf1, 0xe4, 0xe4, 0x2b, 0x4a, 0x3a, 0x6b, 0xbe, 0x6c, 0x4b, 0x1f, 0x7c, 0x0d, 0xc3, 0xb8, 0xfe, + 0xd7, 0x85, 0x44, 0x15, 0xfc, 0x8a, 0x0b, 0xe7, 0x23, 0xa1, 0x2f, 0x02, 0xff, 0xe0, 0xbb, 0x0a, 0x7b, 0x23, 0x58, 0x4e, 0x8d, 0x4c, 0x8a, 0x4f, 0x58, 0xd3, 0x0f, 0x5a, 0xbb, 0x91, 0x29, 0xf0, + 0x73, 0xd2, 0x37, 0x2e, 0x06, 0x30, 0x9c, 0xca, 0x06, 0x98, 0x7e, 0x5c, 0x9d, 0xbf, 0xae, 0x05, 0xdc, 0xd7, 0xcf, 0x0a, 0xef, 0xc8, 0x8a, 0x6e, 0xce, 0x65, 0x54, 0xcb, 0x2f, 0x2b, 0x7a, 0xcd, + 0x38, 0x46, 0x0a, 0x17, 0x84, 0xb9, 0x75, 0xac, 0xd3, 0x5f, 0xda, 0x2e, 0xe1, 0xa1, 0x3d, 0xdc, 0x7f, 0xda, 0x9d, 0x0b, 0x11, 0xd5, 0x8f, 0x4c, 0x4d, 0xfd, 0x90, 0x62, 0xbd, 0xe2, 0x92, 0x81, + 0xd5, 0x10, 0x6d, 0x6b, 0xbe, 0x01, 0xc0, 0x18, 0x60, 0x98, 0x91, 0x2e, 0xc5, 0xe3, 0x38, 0x1a, 0x28, 0xed, 0xa8, 0x0d, 0x96, 0x35, 0x13, 0x06, 0x21, 0xd2, 0x1d, 0xf5, 0x64, 0xd5, 0x1d, 0x75, + 0x68, 0xa9, 0x88, 0x31, 0x17, 0x27, 0xfc, 0x48, 0x19, 0x31, 0xe9, 0x0b, 0x38, 0xf2, 0xb7, 0x0d, 0xc8, 0xff, 0x42, 0x34, 0x5a, 0x2a, 0x22, 0xde, 0xda, 0x57, 0x4a, 0x4d, 0x0d, 0xb1, 0xb7, 0x02, + 0xb7, 0x66, 0x69, 0x60, 0xef, 0x40, 0x05, 0x89, 0x7d, 0x20, 0x7f, 0xa6, 0xf6, 0x1b, 0x6f, 0xc8, 0x43, 0xea, 0xd0, 0x90, 0xe7, 0x4a, 0xea, 0x9a, 0x63, 0xd5, 0x95, 0x2d, 0xab, 0xf8, 0x1b, 0xb7, + 0xb2, 0xf0, 0x48, 0xf2, 0xfb, 0x76, 0xb8, 0x37, 0x23, 0x97, 0xb2, 0x9e, 0xa6, 0x36, 0x27, 0x53, 0xc4, 0xfd, 0xbb, 0xc4, 0x08, 0x8e, 0x99, 0xac, 0x12, 0x67, 0x17, 0x5e, 0xda, 0x54, 0x66, 0xb8, + 0xb3, 0x25, 0xbe, 0x6a, 0x16, 0x66, 0xea, 0xa2, 0x85, 0x96, 0x22, 0x5f, 0x5c, 0xeb, 0xbd, 0x5a, 0x47, 0xff, 0x92, 0xc0, 0x3d, 0x9d, 0x14, 0xd2, 0x8a, 0xdd, 0x9b, 0x67, 0x00, 0x3d, 0x9d, 0x7c, + 0x88, 0xae, 0xba, 0xb2, 0xb2, 0xec, 0x1c, 0xac, 0x55, 0xaf, 0x84, 0x5f, 0x36, 0x80, 0x91, 0x3d, 0x12, 0x94, 0x6b, 0xd3, 0x56, 0xcc, 0x4d, 0xdf, 0xa3, 0xc0, 0x2b, 0x88, 0x8c, 0xdc, 0x24, 0xc2, + 0xb6, 0xff, 0x6e, 0x17, 0xf8, 0x2d, 0x4f, 0x0a, 0x9f, 0xd9, 0xb4, 0x0c, 0xc2, 0x56, 0x1e, 0x48, 0xb4, 0xb8, 0x75, 0x65, 0x4a, 0x7f, 0xcc, 0x10, 0xd6, 0x89, 0xda, 0x3a, 0x1d, 0x61, 0xae, 0xb0, + 0x51, 0xa0, 0x43, 0x7d, 0xaf, 0xf0, 0x33, 0x06, 0x76, 0x99, 0x4d, 0xce, 0x24, 0x04, 0x6e, 0x97, 0x29, 0x49, 0x2a, 0xb7, 0xfe, 0x15, 0x86, 0xa2, 0x0b, 0x2f, 0xf1, 0x57, 0x65, 0x15, 0xbf, 0xcf, + 0x0f, 0x88, 0x8e, 0xba, 0x83, 0xbc, 0xa4, 0x4f, 0x40, 0x20, 0x5b, 0x65, 0x29, 0xe7, 0xc7, 0x40, 0x7a, 0x55, 0xfc, 0x5a, 0x80, 0xf7, 0x42, 0xb8, 0xe2, 0x2a, 0x75, 0xa1, 0x47, 0x20, 0x60, 0xbd, + 0x56, 0x17, 0x36, 0x30, 0xa3, 0x9f, 0xd7, 0x6f, 0x9f, 0xa6, 0xfb, 0xc5, 0x94, 0x98, 0xdd, 0x1b, 0x60, 0xd3, 0x24, 0xe3, 0x0d, 0xcd, 0xcd, 0x5d, 0xbb, 0x44, 0xa3, 0x69, 0x27, 0x9a, 0x9e, 0xdd, + 0x36, 0xaa, 0xd9, 0x55, 0x48, 0xd3, 0xc8, 0xef, 0xee, 0xdc, 0x19, 0xda, 0x6d, 0xec, 0x71, 0x04, 0x5d, 0x36, 0x97, 0xf8, 0xeb, 0x9b, 0xd3, 0xcb, 0xa4, 0xa8, 0x81, 0x47, 0xa8, 0xa4, 0x74, 0xfd, + 0xfd, 0xfd, 0xf5, 0x6b, 0xd6, 0x85, 0x12, 0x82, 0x00, 0x86, 0x84, 0xe5, 0x99, 0x3a, 0x28, 0x0b, 0x96, 0x26, 0xc3, 0x69, 0xbb, 0x44, 0xc1, 0x4a, 0x20, 0xb2, 0x7d, 0xef, 0xeb, 0x3b, 0x44, 0x79, + 0x16, 0xf4, 0xcb, 0x2c, 0x13, 0x41, 0x4f, 0xb3, 0xc7, 0x4a, 0xae, 0xab, 0xbb, 0x3f, 0x7f, 0x79, 0x94, 0xaa, 0x15, 0xbc, 0x4e, 0x90, 0xf0, 0xc3, 0xe1, 0xee, 0x01, 0x7c, 0xf4, 0x95, 0xc8, 0x7d, + 0x16, 0x5d, 0xf4, 0x71, 0x88, 0x43, 0x52, 0x36, 0x22, 0xe2, 0xc0, 0x08, 0x56, 0x8b, 0x5b, 0xbd, 0x14, 0x27, 0x21, 0xf5, 0x17, 0x83, 0x86, 0x14, 0x50, 0xb2, 0xa4, 0xd3, 0x28, 0x17, 0xb6, 0x58, + 0x35, 0xfa, 0x02, 0x8c, 0x78, 0xe9, 0xf9, 0x2f, 0x9e, 0x3a, 0x1c, 0x98, 0x10, 0xda, 0xa8, 0xb8, 0x32, 0xc1, 0x8e, 0xe7, 0x5a, 0x2b, 0x94, 0x7c, 0x8b, 0xa6, 0x6b, 0x77, 0x28, 0x5d, 0x49, 0x1c, + 0xe3, 0x54, 0x0a, 0x0f, 0x32, 0x73, 0x41, 0x6b, 0x49, 0x0c, 0x82, 0x87, 0x51, 0xa5, 0xaf, 0x70, 0x8c, 0xa9, 0x84, 0x12, 0x52, 0xe5, 0x24, 0x47, 0xc6, 0x5f, 0x61, 0xd4, 0x72, 0xe2, 0x78, 0xad, + 0xf4, 0xcd, 0xac, 0xe9, 0x80, 0x66, 0x2a, 0x02, 0x05, 0xb7, 0x1f, 0x2e, 0x3e, 0xe3, 0xbd, 0x90, 0xf3, 0x1f, 0xf2, 0xbe, 0x53, 0xf0, 0x77, 0x26, 0xb1, 0x7c, 0x63, 0x2a, 0x58, 0xc5, 0x0b, 0x3b, + 0x8e, 0x93, 0x6f, 0x6a, 0x17, 0xbe, 0x76, 0xee, 0x70, 0x77, 0x1a, 0xc6, 0xda, 0x80, 0xea, 0xae, 0x5c, 0x65, 0x23, 0x7e, 0x9f, 0x69, 0xd7, 0x41, 0xec, 0x5a, 0xb4, 0x9c, 0x49, 0x95, 0xec, 0xd8, + 0x8b, 0xdc, 0x68, 0xde, 0xde, 0xda, 0xbf, 0x42, 0xc8, 0xb8, 0x6b, 0xae, 0xa2, 0x9b, 0x99, 0x4f, 0xc7, 0xed, 0x82, 0xe1, 0x3c, 0xc8, 0xce, 0x45, 0x50, 0xc3, 0x4a, 0x9e, 0x3f, 0xd7, 0x83, 0xbf, + 0x8e, 0xdd, 0xc4, 0x2f, 0xd9, 0x92, 0x58, 0x81, 0x42, 0x09, 0x93, 0x78, 0xc4, 0x04, 0x4a, 0xd2, 0x05, 0x35, 0x60, 0xc0, 0x62, 0xe6, 0x57, 0x7d, 0x90, 0x59, 0x01, 0x3f, 0xf6, 0x8f, 0x97, 0x6d, + 0x9a, 0x21, 0x24, 0x1f, 0x8d, 0xed, 0xb4, 0x21, 0x0e, 0x43, 0xa6, 0xce, 0xaa, 0x55, 0x5a, 0x2a, 0x7f, 0x14, 0xe5, 0xa1, 0xb0, 0x7c, 0xb9, 0x34, 0x43, 0xca, 0x45, 0x71, 0x40, 0x8a, 0xf3, 0x7c, + 0x87, 0x43, 0x44, 0x52, 0x7d, 0xdb, 0x58, 0x66, 0x13, 0x54, 0xfd, 0xd8, 0x79, 0x17, 0xd5, 0x21, 0x0b, 0xc7, 0x88, 0x37, 0xd5, 0xdf, 0xe6, 0xd0, 0x88, 0xf2, 0xb7, 0xc5, 0xd0, 0x44, 0x3e, 0xa0, + 0x53, 0x85, 0xd1, 0x0b, 0x21, 0xf4, 0x1e, 0x2f, 0xd8, 0x22, 0x4a, 0x97, 0x58, 0x7a, 0xe8, 0x8f, 0xa7, 0xb1, 0xbc, 0xb1, 0xb1, 0x05, 0x7b, 0xe6, 0x3b, 0xf4, 0x60, 0xa0, 0x02, 0xa4, 0xa8, 0x67, + 0xca, 0xb6, 0x4e, 0x0c, 0x7f, 0x49, 0xeb, 0x09, 0x2d, 0x03, 0x1d, 0x58, 0xcb, 0x7d, 0x35, 0x8e, 0x1c, 0x45, 0xf5, 0xe3, 0x14, 0x1f, 0x88, 0x6d, 0x97, 0xa9, 0x9c, 0x4c, 0xc9, 0x60, 0x7a, 0xf9, + 0xb6, 0xb3, 0x28, 0xf2, 0x19, 0xf9, 0x8d, 0x40, 0xa6, 0xad, 0x14, 0x8c, 0x8f, 0xe2, 0x44, 0xd3, 0x74, 0x77, 0x54, 0x4e, 0x0b, 0x44, 0xd2, 0xcf, 0xe6, 0x93, 0xce, 0x38, 0x38, 0x06, 0x42, 0x9b, + 0x7f, 0xfc, 0xc3, 0xa8, 0x4d, 0x53, 0x85, 0xdd, 0x33, 0xfb, 0xb9, 0x92, 0x40, 0xa3, 0x93, 0x98, 0xfb, 0x30, 0x0b, 0xa0, 0xf7, 0x08, 0x96, 0xdf, 0x85, 0x08, 0x55, 0x33, 0x5b, 0x05, 0xd2, 0x6b, + 0x88, 0x18, 0x85, 0xed, 0x80, 0xfd, 0xef, 0x47, 0x45, 0x87, 0x7c, 0xb8, 0x0c, 0xc5, 0x3f, 0xdc, 0xaf, 0x72, 0x32, 0x55, 0x5e, 0xd8, 0xf0, 0x37, 0xd9, 0x5e, 0x79, 0xb9, 0xd6, 0x5a, 0x78, 0x45, + 0x83, 0x5a, 0xa2, 0x1d, 0xf6, 0x32, 0xf0, 0xf6, 0x1d, 0x50, 0x2b, 0x4c, 0x20, 0x1b, 0xcf, 0x19, 0x48, 0xf8, 0x4d, 0xd1, 0x38, 0x48, 0x3a, 0x78, 0x49, 0x87, 0x42, 0xf1, 0xd9, 0xaf, 0xa8, 0x9e, + 0xd2, 0x1d, 0xa1, 0x6d, 0x82, 0x21, 0xd7, 0x2c, 0x36, 0x32, 0x93, 0x66, 0x22, 0x88, 0x41, 0x9c, 0xd1, 0x70, 0x16, 0x03, 0x1d, 0xb5, 0x5e, 0xe2, 0x89, 0x10, 0xe6, 0xab, 0x34, 0x08, 0x54, 0x5f, + 0x8d, 0xe5, 0x69, 0xce, 0xc2, 0x66, 0x7a, 0x7e, 0xe9, 0x44, 0xd7, 0x18, 0x66, 0xc8, 0xd4, 0xb3, 0xa9, 0xbd, 0x4e, 0x97, 0xeb, 0x83, 0x75, 0xed, 0x8a, 0x17, 0x61, 0xb5, 0xd4, 0x96, 0x21, 0x53, + 0xb8, 0xf9, 0x75, 0xe4, 0x06, 0xb5, 0x6d, 0x36, 0x16, 0xa2, 0x4b, 0x55, 0xce, 0xbb, 0x11, 0xb9, 0xae, 0x1f, 0x8b, 0x32, 0xeb, 0x5b, 0xd3, 0x64, 0xc2, 0xa1, 0x92, 0x93, 0x72, 0xda, 0x6e, 0x0f, + 0x91, 0x8b, 0xe4, 0x51, 0x9e, 0xba, 0xd9, 0x63, 0x1f, 0x8d, 0x48, 0x9b, 0x72, 0xda, 0xde, 0x55, 0x32, 0x58, 0xda, 0xe9, 0xf7, 0x0c, 0xf6, 0x36, 0x3c, 0xe4, 0xc5, 0x6f, 0x7f, 0xac, 0xf6, 0x37, + 0x8b, 0xe3, 0xe2, 0x5c, 0x72, 0x98, 0x19, 0x97, 0xe6, 0xed, 0x5c, 0xbc, 0xdd, 0x7b, 0x9d, 0x4a, 0xe3, 0x10, 0xa8, 0x65, 0x51, 0x24, 0xe8, 0xd8, 0x34, 0xe8, 0x31, 0x96, 0x47, 0x3a, 0xa7, 0x03, + 0xd9, 0x69, 0x0e, 0xe3, 0x6c, 0x5e, 0x63, 0x32, 0x95, 0x52, 0x50, 0x4c, 0x3c, 0x53, 0x51, 0x54, 0x7f, 0x82, 0x0a, 0x5a, 0xdc, 0x9a, 0xd7, 0xdd, 0x89, 0xc0, 0x58, 0xa8, 0x44, 0x8e, 0x5d, 0x7a, + 0xad, 0x14, 0x7a, 0xc7, 0x53, 0xc8, 0x2e, 0xf4, 0x43, 0xda, 0x01, 0x51, 0x83, 0x6e, 0xb4, 0xfa, 0xff, 0x24, 0x68, 0x82, 0x7a, 0x53, 0xc7, 0xe5, 0x31, 0x48, 0x8b, 0x70, 0xda, 0xaf, 0x35, 0xbd, + 0xa0, 0xf0, 0x63, 0xfc, 0x47, 0x45, 0xbc, 0xa5, 0xb0, 0x88, 0xd1, 0xd1, 0x40, 0x11, 0xd8, 0x4a, 0x73, 0x54, 0xc4, 0x7d, 0xe0, 0xc2, 0x40, 0xca, 0x52, 0x20, 0x05, 0x1a, 0xe9, 0x09, 0xa2, 0x87, + 0xf9, 0x1b, 0x8b, 0xcf, 0x3a, 0x43, 0x04, 0x92, 0xe9, 0x27, 0xac, 0xd7, 0x61, 0xc9, 0x8a, 0xff, 0x6a, 0x25, 0x7e, 0x65, 0x82, 0x15, 0x65, 0xb5, 0xe7, 0xf6, 0x6c, 0xa7, 0x31, 0x88, 0x61, 0xe1, + 0x6f, 0x25, 0x91, 0x9e, 0xb0, 0xc2, 0x9e, 0x50, 0xb6, 0x37, 0x5f, 0xa8, 0x31, 0x6a, 0x5f, 0x14, 0x5e, 0xed, 0xff, 0xab, 0x31, 0x10, 0x87, 0x8a, 0x21, 0xf4, 0xff, 0xa5, 0x30, 0x54, 0x10, 0x36, + 0x2e, 0x3c, 0x5b, 0x70, 0xa9, 0x00, 0xfc, 0x62, 0xed, 0x7a, 0xa1, 0xd1, 0x59, 0x95, 0x72, 0xe8, 0x46, 0xb5, 0x5b, 0xc1, 0x1d, 0xf7, 0x80, 0x22, 0x5b, 0x04, 0x1b, 0x1a, 0x30, 0x97, 0x45, 0x5f, + 0x53, 0xae, 0x8f, 0x07, 0xd0, 0xbc, 0xd9, 0xae, 0x31, 0x0c, 0xfa, 0x07, 0x13, 0xfa, 0x23, 0xa3, 0x8d, 0xc4, 0x66, 0x7e, 0x74, 0xc9, 0xda, 0xd0, 0x55, 0x7e, 0xa0, 0xdc, 0x5f, 0x3e, 0xdd, 0x75, + 0x3f, 0x3e, 0x2b, 0xca, 0x8d, 0x12, 0x38, 0xea, 0x7f, 0x6d, 0xf4, 0x98, 0x30, 0x52, 0xcb, 0x7a, 0x64, 0xec, 0x3c, 0x19, 0x3d, 0x0d, 0xd4, 0x9b, 0x9d, 0xc9, 0x2b, 0xdc, 0x4d, 0x3c, 0xfe, 0x6c, + 0xad, 0x32, 0xc1, 0x7c, 0x91, 0xd7, 0xd9, 0x31, 0x57, 0x25, 0x2e, 0x0e, 0xfe, 0x95, 0xdc, 0x8d, 0x5a, 0x8b, 0xfa, 0xb7, 0xb4, 0x36, 0x1a, 0x9a, 0x87, 0x5d, 0xbf, 0x1f, 0x5f, 0xcb, 0xdd, 0x13, + 0xf6, 0xf2, 0x13, 0xe5, 0x19, 0x58, 0x35, 0x6c, 0xc3, 0x47, 0xe1, 0xa4, 0x58, 0x55, 0xab, 0xcf, 0xf5, 0xfa, 0x9d, 0x02, 0xbc, 0x71, 0x76, 0x91, 0xef, 0xad, 0xa3, 0x68, 0xff, 0xc3, 0xab, 0x68, + 0x4f, 0x40, 0x49, 0x8d, 0x30, 0x2f, 0x15, 0x57, 0xf4, 0x5d, 0xf3, 0x8a, 0x90, 0xaf, 0xef, 0x0e, 0x6b, 0xf6, 0xe7, 0xa9, 0x5a, 0xaf, 0xe7, 0x96, 0xff, 0xa9, 0x1b, 0x78, 0x23, 0x70, 0x81, 0xfa, + 0xf1, 0x0b, 0xcb, 0xe1, 0x6f, 0x1b, 0x22, 0x1e, 0x73, 0x0d, 0xe5, 0xf8, 0xad, 0xd1, 0x66, 0x8a, 0xe0, 0x88, 0xff, 0xd9, 0x07, 0x64, 0xaa, 0x83, 0xb5, 0x42, 0x30, 0x72, 0x4c, 0xdc, 0x3b, 0x50, + 0xe5, 0xaf, 0xb6, 0x4b, 0x81, 0x89, 0x0d, 0xf3, 0x6f, 0x58, 0x1b, 0x49, 0x36, 0x1e, 0xc2, 0x06, 0xf5, 0x71, 0xe8, 0x28, 0x83, 0xb9, 0xd1, 0x35, 0x84, 0xcd, 0x3e, 0xd6, 0x81, 0xf5, 0xdc, 0x6b, + 0x82, 0x6f, 0xa0, 0x72, 0x6b, 0xae, 0x5d, 0xaf, 0x78, 0x3f, 0x56, 0x7f, 0xdb, 0x7e, 0x1a, 0x2b, 0xd4, 0x2c, 0x12, 0x16, 0x34, 0xf8, 0x9b, 0xad, 0x31, 0x53, 0x6e, 0xef, 0x44, 0x05, 0xf0, 0x21, + 0x45, 0x1e, 0xe3, 0x7d, 0x4d, 0xb8, 0x02, 0x0c, 0xc4, 0x72, 0x86, 0x8a, 0xb0, 0xe9, 0x2b, 0xa1, 0xa9, 0x3f, 0xfb, 0x20, 0xd1, 0x36, 0xa6, 0xe4, 0x93, 0x49, 0xfc, 0x78, 0x60, 0x7a, 0x55, 0xc8, + 0xe3, 0x77, 0x3e, 0x3b, 0x44, 0x7b, 0xc5, 0xac, 0x18, 0x6c, 0x99, 0x59, 0xe5, 0xd3, 0xf6, 0xf4, 0x93, 0x6e, 0x99, 0xdb, 0xd6, 0xbd, 0x05, 0xe0, 0xad, 0x63, 0x89, 0x37, 0x02, 0xdd, 0x3b, 0x18, + 0x7a, 0x3b, 0x39, 0x3c, 0x4a, 0xf3, 0x55, 0xbe, 0x20, 0x54, 0x4d, 0x55, 0xc9, 0xc6, 0x6d, 0x94, 0x0e, 0x6b, 0x0b, 0x72, 0xe4, 0x36, 0xd5, 0xe6, 0x67, 0x6e, 0xcb, 0xaa, 0xbb, 0x96, 0x19, 0xe5, + 0x05, 0x4a, 0xc7, 0x3c, 0xb7, 0x27, 0xec, 0xa3, 0xfe, 0x4a, 0xe3, 0x06, 0xd3, 0xfb, 0x80, 0x78, 0xc3, 0xef, 0x9a, 0x0f, 0x4d, 0x48, 0xe7, 0x64, 0x0c, 0x31, 0xd1, 0x9e, 0xbb, 0x07, 0x4e, 0xcf, + 0xee, 0x6b, 0x69, 0x67, 0x9a, 0x21, 0x3a, 0xa6, 0x61, 0x22, 0x12, 0xb0, 0x64, 0xe3, 0x89, 0x72, 0x2e, 0x15, 0xd9, 0x54, 0xe9, 0xcf, 0xce, 0xa4, 0xc0, 0x11, 0xfe, 0x76, 0xb3, 0xcc, 0x7e, 0x4c, + 0x17, 0xe5, 0xce, 0xe0, 0xf9, 0x76, 0x3e, 0x7c, 0x8d, 0x09, 0x9b, 0xb6, 0x50, 0xd0, 0x25, 0x24, 0xe0, 0x93, 0x68, 0xaa, 0x2c, 0x0e, 0xfa, 0x60, 0xfa, 0x73, 0x5b, 0x96, 0x88, 0xd1, 0x2b, 0x72, + 0x25, 0x0b, 0xcd, 0xb7, 0x22, 0x73, 0xf1, 0x29, 0xfa, 0x49, 0x6c, 0xdc, 0x07, 0x24, 0x1d, 0x5d, 0x81, 0xb4, 0xe9, 0x6f, 0x73, 0xde, 0xc2, 0xd3, 0x49, 0xae, 0x12, 0xa1, 0xcd, 0x2a, 0xea, 0x36, + 0xa9, 0xaa, 0x40, 0xdd, 0xfb, 0x0e, 0x8e, 0x33, 0x16, 0xc7, 0x10, 0xfa, 0x52, 0x11, 0x2d, 0xb4, 0x19, 0x48, 0xf2, 0xcd, 0x97, 0xff, 0xaf, 0xf2, 0xc8, 0x36, 0x94, 0xba, 0x81, 0x15, 0x24, 0x6e, + 0x6a, 0x72, 0xab, 0x06, 0xb7, 0x75, 0xd0, 0x8c, 0x50, 0x80, 0x68, 0x15, 0xde, 0x81, 0x06, 0xbb, 0xc5, 0xd3, 0xfa, 0x5b, 0x6b, 0x26, 0x08, 0x13, 0x30, 0x2a, 0x8d, 0x1d, 0x38, 0xde, 0xc1, 0xf6, + 0xdc, 0xf2, 0x50, 0xd6, 0xa3, 0xc7, 0x18, 0x2c, 0x07, 0x2a, 0x7f, 0xe7, 0xdd, 0x84, 0xcc, 0x42, 0xb3, 0xfb, 0x8b, 0x32, 0x24, 0x6a, 0x68, 0x8a, 0xd2, 0x48, 0x55, 0x25, 0x66, 0x52, 0xda, 0x1e, + 0xdc, 0xea, 0x1b, 0x5f, 0xdc, 0x91, 0xc0, 0x06, 0x60, 0x46, 0xf4, 0xaa, 0xc6, 0x3d, 0x10, 0xbd, 0x57, 0x7c, 0x47, 0x8c, 0xa4, 0xa2, 0x55, 0xc2, 0x85, 0x53, 0xdc, 0x9e, 0x9b, 0x85, 0xaf, 0x9d, + 0x44, 0x97, 0x24, 0x42, 0x06, 0xd7, 0x9f, 0xe2, 0x51, 0xa1, 0x1d, 0x4e, 0x82, 0xdb, 0xec, 0xd8, 0x19, 0xbf, 0xb5, 0x3d, 0xf7, 0xf2, 0x6e, 0xdc, 0xec, 0x17, 0x5c, 0x2a, 0x13, 0xca, 0x04, 0xb0, + 0xfe, 0xb4, 0x2c, 0x3c, 0x4b, 0x24, 0xf1, 0x8a, 0x62, 0x42, 0x60, 0xbe, 0x19, 0x1a, 0x70, 0xc5, 0xa6, 0x0d, 0x31, 0x1b, 0xa5, 0x9b, 0x1c, 0xb3, 0xd5, 0xe9, 0x7b, 0x4c, 0xf0, 0x2f, 0x73, 0xc6, + 0xea, 0x41, 0x79, 0x7a, 0x7b, 0xf8, 0x7b, 0x59, 0x92, 0x04, 0x65, 0x78, 0x08, 0xdd, 0x75, 0x1b, 0xe3, 0x9e, 0x34, 0xfd, 0x7d, 0xee, 0x17, 0x0d, 0x85, 0x09, 0xf7, 0x93, 0x0b, 0x86, 0xb2, 0xd9, + 0xcb, 0x1e, 0x4c, 0x0e, 0xe1, 0x63, 0xe4, 0xc9, 0x8f, 0xeb, 0x1a, 0x86, 0x8c, 0x37, 0xff, 0x3a, 0x5d, 0x94, 0xcd, 0x87, 0x2b, 0xc4, 0x16, 0xb7, 0x18, 0x26, 0x82, 0xbd, 0x80, 0x34, 0x5c, 0x4c, + 0x17, 0x12, 0x2a, 0x8e, 0xbc, 0xb4, 0xbd, 0xf4, 0xfd, 0x1a, 0xb0, 0x9c, 0xee, 0x61, 0x0f, 0xfb, 0x8a, 0x65, 0x71, 0x64, 0x4c, 0x06, 0x4e, 0x14, 0xf9, 0x25, 0xbd, 0x9a, 0x35, 0x83, 0xe8, 0x7b, + 0x25, 0xbe, 0xe6, 0x6e, 0xed, 0x08, 0xc9, 0x39, 0xb9, 0x79, 0x68, 0xc9, 0xaa, 0x00, 0x63, 0x16, 0x84, 0x8e, 0x13, 0x31, 0xbb, 0xc4, 0xa4, 0xff, 0xa7, 0x1b, 0xa4, 0x7f, 0xbc, 0x94, 0xb8, 0xa7, + 0x87, 0x4f, 0x76, 0xed, 0x18, 0xf7, 0x50, 0x3b, 0x3e, 0x66, 0x61, 0xfe, 0x16, 0xd6, 0x0f, 0x96, 0xb7, 0x40, 0x33, 0x97, 0xd4, 0xf0, 0xfe, 0x4f, 0xa2, 0x81, 0x55, 0xa7, 0x81, 0xa9, 0x91, 0xb5, + 0xa8, 0xab, 0xd5, 0xcb, 0x20, 0xdb, 0xda, 0x57, 0xc1, 0xd6, 0x6a, 0x19, 0x07, 0x4c, 0x75, 0x8f, 0xf9, 0x19, 0xab, 0x1f, 0x1c, 0x51, 0xbc, 0x00, 0xde, 0xf5, 0xeb, 0x00, 0x86, 0xf0, 0xb7, 0x30, + 0x4d, 0xac, 0xe3, 0xc4, 0x40, 0x18, 0x52, 0x33, 0x58, 0xff, 0x48, 0x6c, 0x9c, 0xad, 0xfe, 0x94, 0x12, 0xac, 0x37, 0x26, 0x2c, 0x36, 0x07, 0x41, 0xb7, 0x07, 0xeb, 0xe3, 0x10, 0x6f, 0x46, 0xa1, + 0xd6, 0xe0, 0x43, 0xb1, 0xaa, 0xdd, 0xca, 0xd1, 0x78, 0x64, 0xa7, 0xa5, 0x23, 0x3e, 0x44, 0x87, 0x51, 0x8a, 0x90, 0xc7, 0x78, 0xa3, 0x44, 0x64, 0x12, 0x7f, 0x92, 0xfe, 0xbd, 0x73, 0x35, 0xa3, + 0x4e, 0x1d, 0xe7, 0x42, 0xa9, 0xf9, 0xc7, 0xe1, 0xa4, 0x1c, 0x5f, 0x88, 0x3d, 0x64, 0x8f, 0x43, 0x5b, 0xb1, 0xeb, 0xbf, 0x0b, 0xe7, 0xd9, 0xe5, 0xb2, 0x5e, 0x16, 0x8a, 0x83, 0xff, 0x2b, 0x1a, + 0xe2, 0x40, 0xd8, 0xb6, 0x36, 0x32, 0x68, 0x14, 0xc9, 0x0d, 0xd4, 0xf2, 0x13, 0x67, 0x64, 0x65, 0x77, 0x51, 0x88, 0xa5, 0x1e, 0x97, 0x35, 0x60, 0x21, 0x5c, 0xdb, 0x27, 0xeb, 0xd8, 0x63, 0x28, + 0x2e, 0x25, 0x1d, 0x9e, 0x66, 0x09, 0xa4, 0xa8, 0x35, 0xdc, 0x2a, 0xd2, 0xc0, 0x91, 0x2b, 0x98, 0xf6, 0x3c, 0x8f, 0x70, 0xe2, 0x69, 0xfe, 0x5b, 0xd3, 0x47, 0x45, 0x5a, 0x84, 0x85, 0xb0, 0x1a, + 0x63, 0x79, 0x91, 0x77, 0xc4, 0xae, 0x0b, 0xcc, 0x4e, 0xf2, 0x36, 0xd0, 0xe6, 0xae, 0xe3, 0xa1, 0x12, 0x35, 0xe7, 0x3d, 0x45, 0x58, 0x09, 0x77, 0x44, 0xcb, 0x3b, 0x8f, 0xf8, 0x67, 0x71, 0xe9, + 0x93, 0x66, 0xcc, 0xbf, 0x6d, 0xb8, 0x4a, 0x8c, 0xc5, 0xb9, 0x94, 0xcc, 0x8a, 0x0e, 0x54, 0x83, 0xd6, 0x60, 0x08, 0x8b, 0x5b, 0x4c, 0xad, 0xac, 0xd4, 0x73, 0x5e, 0xc5, 0x84, 0x61, 0x11, 0x26, + 0x0e, 0xf2, 0x60, 0x13, 0x1a, 0x24, 0x56, 0xc5, 0xba, 0x7f, 0x9e, 0xb2, 0x05, 0xac, 0xb9, 0xa2, 0xa9, 0xf5, 0x76, 0xe4, 0x67, 0x1d, 0xc9, 0x88, 0xd4, 0xc4, 0x84, 0x67, 0x4c, 0xce, 0xda, 0xd9, + 0x57, 0xc2, 0xb4, 0xc3, 0x6b, 0xfd, 0xd2, 0x1e, 0x32, 0x80, 0x15, 0xb4, 0x7d, 0x1c, 0x52, 0x3d, 0x80, 0xfb, 0x5a, 0xa1, 0x35, 0x1f, 0x4f, 0x31, 0x4c, 0x90, 0xed, 0x80, 0x43, 0xa9, 0x84, 0x7c, + 0xdb, 0xd7, 0x39, 0xc0, 0xd2, 0x46, 0x7c, 0x6d, 0xfb, 0x59, 0x22, 0x0e, 0x05, 0x7c, 0x06, 0x8d, 0x68, 0xca, 0x4a, 0x26, 0x3c, 0x29, 0x0e, 0xd9, 0x2d, 0xc6, 0x77, 0x2f, 0xb8, 0x7a, 0xe5, 0x5c, + 0x86, 0x24, 0x8d, 0x75, 0xd7, 0x7a, 0xa9, 0x1e, 0x81, 0xbe, 0x5e, 0x04, 0x3e, 0xdb, 0x44, 0xb6, 0xaf, 0x45, 0x9c, 0xe0, 0xca, 0xdf, 0x46, 0x14, 0x06, 0x30, 0xaa, 0xfc, 0xab, 0x83, 0x79, 0x8f, + 0x30, 0xc3, 0xf7, 0x97, 0x2f, 0x9f, 0x60, 0x82, 0x41, 0xb6, 0x7f, 0x4f, 0x15, 0x24, 0x14, 0x3c, 0xa2, 0x87, 0x30, 0xbd, 0x38, 0xa8, 0x89, 0x6d, 0xac, 0xc4, 0xe3, 0x44, 0x61, 0x80, 0xb3, 0x2a, + 0xe6, 0x7b, 0xb6, 0x31, 0x15, 0x5a, 0x83, 0xdb, 0x41, 0x95, 0x77, 0x4d, 0x95, 0x8e, 0x37, 0xca, 0xdd, 0xe1, 0x81, 0xe9, 0x97, 0x7e, 0x27, 0xba, 0x9e, 0xe1, 0x9b, 0xaf, 0xed, 0x55, 0x4f, 0xc3, + 0xbb, 0x10, 0xf3, 0x97, 0x76, 0xce, 0x19, 0x06, 0x08, 0x99, 0x0a, 0xde, 0xea, 0xf8, 0xbf, 0x7a, 0xee, 0xa3, 0xb1, 0x3a, 0x0f, 0xbf, 0xf7, 0xdd, 0x50, 0x6a, 0xcf, 0x95, 0xea, 0x31, 0x88, 0x30, + 0x79, 0x73, 0xaf, 0xf2, 0x94, 0xf7, 0x40, 0x3c, 0x3e, 0xea, 0x12, 0xe9, 0x84, 0x70, 0x71, 0x8c, 0x88, 0xfe, 0xbe, 0x15, 0xe1, 0xa0, 0xfd, 0x5d, 0x1c, 0xaf, 0xf1, 0x5b, 0x7f, 0x6b, 0xf2, 0xb9, + 0xe3, 0x41, 0x59, 0x0b, 0x48, 0xf9, 0x99, 0xe0, 0xd1, 0xfb, 0x82, 0x29, 0x72, 0x9f, 0x1d, 0x4e, 0x15, 0xed, 0x3f, 0x73, 0x63, 0x31, 0x28, 0xa2, 0xdb, 0x35, 0x12, 0xa7, 0xd9, 0xb8, 0x89, 0x19, + 0x38, 0x76, 0xd6, 0xa3, 0x8e, 0xab, 0x95, 0x1d, 0xe1, 0x6c, 0x71, 0xe6, 0xdc, 0x7a, 0x95, 0x98, 0xfb, 0x5a, 0xc0, 0x1a, 0xb5, 0x46, 0xc0, 0x49, 0x4b, 0x4a, 0x7a, 0xc8, 0x7d, 0x46, 0xe5, 0x62, + 0xa5, 0x41, 0xc3, 0x27, 0x74, 0x37, 0xc2, 0x2d, 0xcb, 0x4e, 0x27, 0x00, 0x8d, 0x83, 0xd0, 0x67, 0xf1, 0x01, 0xf4, 0x6b, 0xb3, 0x0c, 0x80, 0x67, 0xd4, 0x34, 0xe3, 0x5f, 0xc3, 0xc0, 0x4a, 0xe2, + 0x37, 0xd3, 0xd5, 0xac, 0x24, 0x7f, 0x29, 0xab, 0x29, 0xd9, 0x15, 0x62, 0xa7, 0xc5, 0xf8, 0xbd, 0xee, 0xd0, 0x0f, 0x47, 0xb4, 0xda, 0xaa, 0x33, 0xd4, 0xc5, 0x0a, 0xc7, 0x00, 0x1c, 0x76, 0x19, + 0x6d, 0x27, 0x52, 0x34, 0x52, 0xb2, 0x89, 0x9a, 0x43, 0x38, 0x36, 0x04, 0x80, 0x95, 0xc5, 0x26, 0x0f, 0xca, 0x83, 0xbd, 0x74, 0xa2, 0x9a, 0xfe, 0x72, 0xda, 0x33, 0xcb, 0x9c, 0x63, 0xe5, 0xdc, + 0x3f, 0x4c, 0xe6, 0x35, 0x5d, 0xff, 0x75, 0xa7, 0xcf, 0xb2, 0x44, 0x68, 0x4f, 0x53, 0x4b, 0xd9, 0x4c, 0x68, 0xa3, 0x62, 0x25, 0x43, 0xd1, 0x80, 0x0f, 0x8d, 0x64, 0xd3, 0xeb, 0x2a, 0xd9, 0xca, + 0x5c, 0xe1, 0x9f, 0x43, 0x60, 0x95, 0xaf, 0x0c, 0x0a, 0x15, 0xc0, 0xf7, 0x2f, 0xeb, 0x48, 0xe3, 0x5c, 0x0f, 0xb9, 0xc0, 0xa1, 0xfb, 0x0a, 0x72, 0x65, 0xcc, 0xbf, 0xe6, 0x3d, 0x5c, 0x71, 0x8c, + 0xf2, 0xed, 0x85, 0xae, 0x0f, 0x02, 0x50, 0x19, 0x74, 0x15, 0xc7, 0x88, 0x2e, 0xd7, 0x44, 0x8f, 0x99, 0x98, 0x38, 0x6b, 0xf9, 0xca, 0xbe, 0x9c, 0x87, 0x31, 0x92, 0x2f, 0xda, 0x9d, 0x94, 0xb2, + 0x95, 0xa7, 0xd3, 0x7e, 0x58, 0xff, 0x99, 0x4a, 0x81, 0xf1, 0x20, 0x75, 0xfb, 0x20, 0x17, 0xd6, 0x04, 0xbb, 0x31, 0xee, 0x71, 0xf9, 0x4b, 0x83, 0xe6, 0xc5, 0x1d, 0xc5, 0x66, 0xf4, 0x74, 0xbb, + 0x45, 0x58, 0xa7, 0x90, 0x0f, 0xd9, 0x43, 0xba, 0xb6, 0x20, 0x5e, 0xeb, 0xda, 0x8e, 0x56, 0xd2, 0x4e, 0x91, 0x84, 0xfc, 0x3b, 0xa4, 0x8f, 0xe5, 0x85, 0x60, 0x7d, 0xfb, 0x87, 0x20, 0x7b, 0x78, + 0xaa, 0x7d, 0xf4, 0xdf, 0x83, 0xbd, 0x79, 0x50, 0x6e, 0x19, 0x84, 0x23, 0x00, 0xef, 0x5f, 0xa3, 0xf2, 0xaf, 0x4c, 0x38, 0x59, 0x86, 0xf8, 0x0e, 0x7e, 0x00, 0xfd, 0xaf, 0xd1, 0x5b, 0x7a, 0x19, + 0xe2, 0x86, 0x17, 0x50, 0xf2, 0x66, 0x13, 0x52, 0x27, 0xa4, 0x9f, 0xba, 0x61, 0x65, 0xc1, 0x24, 0x7c, 0xd7, 0xc9, 0x0d, 0x11, 0x1d, 0xd2, 0xf9, 0x19, 0xc1, 0x08, 0x60, 0xd8, 0xa2, 0xd8, 0x7a, + 0xb9, 0x61, 0x46, 0x16, 0x8b, 0x22, 0x02, 0x74, 0x09, 0xa3, 0x52, 0xd0, 0xde, 0xb0, 0x15, 0x99, 0x2e, 0x45, 0xdb, 0xb3, 0x29, 0x12, 0xc1, 0x65, 0x9e, 0x07, 0x4c, 0x3d, 0x7e, 0xb7, 0x73, 0x8c, + 0xd9, 0xac, 0x59, 0x2f, 0x9b, 0x14, 0x38, 0x04, 0x30, 0xe6, 0xad, 0x1e, 0x91, 0xc9, 0x97, 0x61, 0x5a, 0xeb, 0x16, 0x96, 0xc0, 0x63, 0x5c, 0x7f, 0xd7, 0x8e, 0xf2, 0xff, 0xeb, 0x2c, 0x4e, 0xca, + 0xab, 0x5a, 0x88, 0xa0, 0x99, 0x78, 0xdc, 0xa0, 0x30, 0x35, 0x20, 0xd6, 0x69, 0xf9, 0x92, 0xf9, 0xa3, 0x47, 0xa2, 0x3c, 0x81, 0xfa, 0x1f, 0x9e, 0xf4, 0xff, 0x0d, 0x46, 0x8e, 0x84, 0x9c, 0xba, + 0x1e, 0xc6, 0xc1, 0x95, 0x3d, 0xb7, 0xdd, 0x3f, 0x57, 0x0e, 0x07, 0x59, 0xb7, 0x3e, 0xab, 0x08, 0xbc, 0xc9, 0xe9, 0xc7, 0xea, 0x3b, 0x93, 0x8a, 0x2e, 0x9b, 0xf6, 0xc6, 0xee, 0x95, 0xd5, 0x44, + 0xa7, 0x96, 0x46, 0xac, 0x6c, 0xe4, 0xd2, 0x63, 0x80, 0x22, 0x50, 0x30, 0x52, 0xb2, 0x4a, 0xdf, 0xfe, 0x77, 0x6b, 0x79, 0x57, 0xc9, 0xf1, 0x4b, 0xce, 0xea, 0x5b, 0x16, 0xcd, 0x8b, 0x6e, 0x6f, + 0xe3, 0xd9, 0x88, 0xc7, 0xab, 0xc0, 0xa3, 0x9a, 0xaf, 0x53, 0xa5, 0xfc, 0xfe, 0x3e, 0x7a, 0x74, 0xbb, 0x52, 0x38, 0xa1, 0xa3, 0x20, 0x59, 0x49, 0x9c, 0x44, 0x1d, 0x1e, 0x27, 0xdc, 0x1e, 0x3c, + 0xf7, 0x6f, 0x8b, 0xce, 0x77, 0x1b, 0x1f, 0x84, 0x18, 0x57, 0x5b, 0x56, 0xd5, 0xc3, 0xf6, 0x1a, 0x81, 0xec, 0xf2, 0x24, 0xfd, 0xdb, 0x26, 0x0d, 0x78, 0x4d, 0xf9, 0xe2, 0xc2, 0x61, 0x5c, 0x58, + 0x2c, 0x6e, 0xcc, 0xb3, 0x78, 0xc6, 0x27, 0x20, 0x5b, 0xab, 0x56, 0xf1, 0x66, 0x79, 0x96, 0x67, 0xe0, 0xfc, 0x15, 0x95, 0x40, 0x0d, 0xaa, 0xf3, 0x51, 0x4a, 0x57, 0x38, 0x51, 0x63, 0x41, 0x88, + 0xd1, 0xf9, 0x7b, 0x69, 0x8a, 0xe4, 0x52, 0xb9, 0x2f, 0xdd, 0x9e, 0x4d, 0x0f, 0xdb, 0x25, 0xb6, 0x52, 0x9d, 0xdf, 0xba, 0x6a, 0xb9, 0xea, 0xef, 0x16, 0x64, 0x10, 0x23, 0xc6, 0xbf, 0x58, 0x6e, + 0xe0, 0xf4, 0x79, 0x49, 0x4c, 0x8f, 0xf3, 0xd3, 0xaf, 0xd5, 0xd1, 0x9b, 0xd1, 0x0f, 0xeb, 0x8b, 0x3e, 0x03, 0x34, 0x24, 0x69, 0x39, 0xab, 0x46, 0x6a, 0x2b, 0xd1, 0x07, 0x0d, 0x0e, 0x10, 0x57, + 0x14, 0x9b, 0x8c, 0xe5, 0x32, 0xff, 0xd1, 0x44, 0x95, 0xc2, 0x2c, 0xe3, 0xf7, 0x7c, 0xee, 0xda, 0x77, 0xa3, 0x77, 0x0f, 0xb7, 0x74, 0xcc, 0xbc, 0x73, 0xa3, 0xb4, 0xaa, 0x24, 0xde, 0x87, 0xe9, + 0x9c, 0x8b, 0x12, 0xb1, 0xe7, 0x4a, 0x68, 0x4f, 0x40, 0x7f, 0x40, 0xa5, 0xaa, 0x7f, 0x69, 0x26, 0xad, 0xf1, 0xa7, 0x10, 0x66, 0xb7, 0x06, 0x86, 0xac, 0xef, 0x4f, 0x35, 0x13, 0x45, 0xbc, 0xad, + 0xda, 0xb6, 0x1b, 0xf3, 0xb6, 0x92, 0x63, 0xa9, 0xaa, 0x35, 0x96, 0x1e, 0x6a, 0xcb, 0x99, 0xf8, 0xfe, 0xdd, 0x2e, 0x6d, 0xee, 0x72, 0x01, 0xe7, 0x58, 0xb4, 0xc5, 0xc1, 0x4a, 0x9b, 0x92, 0x81, + 0xba, 0x3b, 0x3a, 0x56, 0x6d, 0x27, 0x43, 0x23, 0x91, 0xb0 +}; diff --git a/fpsdk_common/test/video_test.cpp b/fpsdk_common/test/video_test.cpp new file mode 100644 index 00000000..3b0dc3bb --- /dev/null +++ b/fpsdk_common/test/video_test.cpp @@ -0,0 +1,109 @@ +/** + * \verbatim + * ___ ___ + * \ \ / / + * \ \/ / Copyright (c) Fixposition AG (www.fixposition.com) and contributors + * / /\ \ License: see the LICENSE file + * /__/ \__\ + * \endverbatim + * + * @file + * @brief Fixposition SDK: tests for fpsdk::common::video + */ + +/* LIBC/STL */ + +/* EXTERNAL */ +#include + +/* PACKAGE */ +#include +#include +#include + +namespace { +/* ****************************************************************************************************************** */ +#if FPSDK_USE_FFMPEG + +# include "data/hevc_i_frame.cpp" + +using namespace fpsdk::common::video; +using namespace fpsdk::common::path; + +TEST(VideoTest, DecodeFrame_RGB24) +{ + auto dec = CreateVideoFrameDecoder({ "test", VideoCodec::H265, PixelFmt::RGB24, 1.0, HwAccel::SW }); + EXPECT_NE(dec, nullptr); + EXPECT_TRUE(dec->IsOkay()); + + auto img = dec->DecodeFrame(HEVC_I_FRAME_BIN); + EXPECT_TRUE(img); + EXPECT_TRUE(dec->IsOkay()); + + EXPECT_EQ(img->fmt_, PixelFmt::RGB24); + EXPECT_EQ(img->width_, 1024); + EXPECT_EQ(img->height_, 768); + EXPECT_EQ(img->data_.size(), 1024 * 768 * 3); + + // FileSpew("test-rgb24.bin", img->data_); + // $ convert -depth 8 -size 1024x768+0 rgb:test-rgb24.bin test-rgb24.png +} + +// --------------------------------------------------------------------------------------------------------------------- + +TEST(VideoTest, DecodeFrame_Y8) +{ + auto dec = CreateVideoFrameDecoder({ "test", VideoCodec::H265, PixelFmt::Y8, 1.0, HwAccel::SW }); + EXPECT_NE(dec, nullptr); + EXPECT_TRUE(dec->IsOkay()); + + auto img = dec->DecodeFrame(HEVC_I_FRAME_BIN); + EXPECT_TRUE(img); + EXPECT_TRUE(dec->IsOkay()); + + EXPECT_EQ(img->fmt_, PixelFmt::Y8); + EXPECT_EQ(img->width_, 1024); + EXPECT_EQ(img->height_, 768); + EXPECT_EQ(img->data_.size(), 1024 * 768); + + // FileSpew("test-y8.bin", img->data_); + // $ convert -depth 8 -size 1024x768+0 gray:test-y8.bin test-y8.png +} + +// --------------------------------------------------------------------------------------------------------------------- + +TEST(VideoTest, DecodeFrame_RGB24_05) +{ + auto dec = CreateVideoFrameDecoder({ "test", VideoCodec::H265, PixelFmt::RGB24, 0.5, HwAccel::SW }); + EXPECT_NE(dec, nullptr); + EXPECT_TRUE(dec->IsOkay()); + + auto img = dec->DecodeFrame(HEVC_I_FRAME_BIN); + EXPECT_TRUE(img); + EXPECT_TRUE(dec->IsOkay()); + + EXPECT_EQ(img->fmt_, PixelFmt::RGB24); + EXPECT_EQ(img->width_, 1024 / 2); + EXPECT_EQ(img->height_, 768 / 2); + EXPECT_EQ(img->data_.size(), (1024 / 2) * (768 / 2) * 3); + + // FileSpew("test-rgb24-05.bin", img->data_); + // $ convert -depth 8 -size 512x384+0 rgb:test-rgb24-05.bin test-rgb24-05.png +} + +#endif // FPSDK_USE_FFMPEG +/* ****************************************************************************************************************** */ +} // namespace + +int main(int argc, char** argv) +{ + testing::InitGoogleTest(&argc, argv); + auto level = fpsdk::common::logging::LoggingLevel::WARNING; + for (int ix = 0; ix < argc; ix++) { + if ((argv[ix][0] == '-') && argv[ix][1] == 'v') { + level++; + } + } + fpsdk::common::logging::LoggingSetParams(level); + return RUN_ALL_TESTS(); +} diff --git a/fpsdk_doc/Doxyfile b/fpsdk_doc/Doxyfile index e7fdd44b..bd8556e3 100644 --- a/fpsdk_doc/Doxyfile +++ b/fpsdk_doc/Doxyfile @@ -2465,7 +2465,8 @@ INCLUDE_FILE_PATTERNS = PREDEFINED = _DOXYGEN_ \ NDEBUG \ - FPSDK_USE_PROJ=1 + FPSDK_USE_PROJ=1 \ + FPSDK_USE_FFMPEG=1 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The diff --git a/fpsdk_doc/fpsdk-overview.drawio.svg b/fpsdk_doc/fpsdk-overview.drawio.svg index dab45c23..1622878a 100644 --- a/fpsdk_doc/fpsdk-overview.drawio.svg +++ b/fpsdk_doc/fpsdk-overview.drawio.svg @@ -1,4 +1,4 @@ - + @@ -234,7 +234,7 @@ - + @@ -287,7 +287,7 @@ - +
@@ -306,7 +306,7 @@ - + @@ -470,14 +470,14 @@ - - + + -
+

@@ -489,7 +489,7 @@

- + PROJ @@ -608,10 +608,10 @@ - + - +
@@ -686,13 +686,13 @@ - + -
+
utils @@ -700,51 +700,25 @@
- + utils - - - - - - - - - - - -
-
-
- time -
-
-
-
- - time - -
-
-
- - - + + - + -
+
path @@ -752,25 +726,25 @@
- + path - - + + - + -
+
math @@ -778,15 +752,15 @@
- + math - - + + @@ -816,13 +790,13 @@ - + -
+
fpl @@ -830,25 +804,25 @@
- + fpl - - + + - + -
+
logging @@ -856,25 +830,25 @@
- + logging - - + + - + -
+
string @@ -882,15 +856,15 @@
- + string - - + + @@ -920,13 +894,13 @@ - + -
+
trafo @@ -934,84 +908,51 @@
- + trafo - - + + - - - t - - - - - + -
+
- types + video
- - types - - - - - - - - - - - - - - - - -
-
-
- yaml -
-
-
-
- - yaml + + video
- - + + - + -
+
utils @@ -1019,15 +960,15 @@
- + utils - - + + @@ -1085,13 +1026,13 @@ - + -
+
parser @@ -1099,24 +1040,24 @@
- + parser - - + + - + -
+
optional @@ -1124,7 +1065,7 @@
- + optional @@ -1176,39 +1117,13 @@ - - - - - - -
-
-
- msgs -
-
-
-
- - msgs - -
-
-
- - - - - - - + -
+
thread @@ -1216,15 +1131,15 @@
- + thread - - + + @@ -1314,14 +1229,14 @@ - - + + -
+

@@ -1333,21 +1248,21 @@

- + OpenSSL - - + + -
+

@@ -1359,7 +1274,7 @@

- + nloh...-json3 @@ -1367,7 +1282,7 @@ - + @@ -1392,7 +1307,7 @@ - + @@ -1438,7 +1353,7 @@ - + @@ -1515,7 +1430,7 @@ - + @@ -1538,6 +1453,239 @@ + + + + + + + + +
+
+
+ yaml +
+
+
+
+ + yaml + +
+
+
+ + + + + + + + + + + + +
+
+
+

+ + FFmpeg (LGPL) + +

+
+
+
+
+ + FFmpeg (LGPL) + +
+
+
+ + + + + + + +
+
+
+ optional +
+
+
+
+ + optional + +
+
+
+ + + + + + + + + + + + +
+
+
+ cam +
+
+
+
+ + cam + +
+
+
+ + + + + + + + + + + + +
+
+
+ can +
+
+
+
+ + can + +
+
+
+ + + + + + + + + + + + +
+
+
+ gnss +
+
+
+
+ + gnss + +
+
+
+ + + + + + + + + + + + +
+
+
+ ros1 +
+
+
+
+ + ros1 + +
+
+
+ + + + + + + + + + + + +
+
+
+ time +
+
+
+
+ + time + +
+
+
+ + + + + + + + + + + + +
+
+
+ types +
+
+
+
+ + types + +
+
+
+ + + +
diff --git a/fpsdk_doc/fpsdk_build.hpp b/fpsdk_doc/fpsdk_build.hpp index dac4e444..a6a1f745 100644 --- a/fpsdk_doc/fpsdk_build.hpp +++ b/fpsdk_doc/fpsdk_build.hpp @@ -46,19 +46,22 @@ namespace fpsdk { - nlohmann-json3 (≥ 3.7.3) - Various Linux tools, including Bash, CMake, make, xxd, sed, awk, ... - PROJ (*) (≥ 9.4.x) + - FFmpeg (libavcodec, libavutil, ...) (**) (= 7.1.x) - ROS1 (*) (Noetic), or - ROS2 (*) (Humble or Jazzy) (*) Optional dependencies. Without these some functionality in the libraries and apps is unavailable (compiled out). + (**) Optional. The FFmpeg libraries must be configured with --disable-gpl and --disable-nonfree in order to comply + with the Fixposition SDK license. See *Configure* below for related build configuration options. + For development additionally: - clang-format (≥ 19, tested with 19) - Doxygen (≥ 1.11.0, tested with 1.14.0) - GTest (≥ 1.13.0) - Note that the list above may be incomplete or wrong. See @ref FPSDK_BUILD_CIVERSIONS for the versions used in the CI - builds. + See @ref FPSDK_BUILD_CIVERSIONS for the versions used in the CI builds. @@ -147,12 +150,13 @@ namespace fpsdk { - Build type: `-DCMAKE_BUILD_TYPE=Debug` or `-DCMAKE_BUILD_TYPE=Release` (default) - Force ROS1 package path: `-DROS_PACKAGE_PATH=/path/to/ros` (default: auto-detect) - - Explicitly enable or disable testing: `-DBUILD_TESTING=OFF` or `-DBUILD_TESTING=ON`. + - Explicitly enable or disable testing: `-DFPSDK_BUILD_TESTING=ON` or `-DFPSDK_BUILD_TESTING=OFF`. The default is to automatically enable testing if a suitable GTest library is found. - Note that the instead of the standard BUILD_TESTING variable the variable `FPSDK_BUILD_TESTING=...` - can be used. - Explicitly enable or disable use of the PROJ library: `-DFPSDK_USE_PROJ=ON` or `-DFPSDK_USE_PROJ=OFF`. - The default is to automatically use the PROJ library if a suitable version is found + The default is to automatically use the PROJ library if a suitable version is found. + - Explicitly enable or disable use of the FFmpeg libraries: `-DFPSDK_USE_FFMPEG=ON` or `-DFPSDK_USE_FFMPEG=OFF`. + The default is to automatically use the FFmpeg libraries if suitable versions are found. + - Add `-DCMAKE_PREFIX_PATH=...` to hint at non-standard installation paths, such as `/path/to/ffmpeg-lgpl`. 4. Build diff --git a/fpsdk_ros1/CMakeLists.txt b/fpsdk_ros1/CMakeLists.txt index c8d0ed4f..774b993a 100644 --- a/fpsdk_ros1/CMakeLists.txt +++ b/fpsdk_ros1/CMakeLists.txt @@ -3,7 +3,7 @@ message(STATUS "fpsdk: ----- ${CMAKE_CURRENT_SOURCE_DIR} -----") cmake_minimum_required(VERSION 3.16) include(../fpsdk_common/cmake/setup.cmake) -include(../fpsdk_common/cmake/ros.cmake) +include(../fpsdk_common/cmake/utils.cmake) project(fpsdk_ros1 LANGUAGES CXX @@ -86,6 +86,7 @@ set(PROJECT_RUNTIME_DIR ${CMAKE_INSTALL_FULL_BINDIR}) set(PROJECT_LIBRARY_DIR ${CMAKE_INSTALL_FULL_LIBDIR}) set(PROJECT_INCLUDE_DIR ${CMAKE_INSTALL_FULL_INCLUDEDIR}/${PROJECT_NAME}) set(PROJECT_DATA_DIR ${CMAKE_INSTALL_FULL_DATAROOTDIR}/${PROJECT_NAME}) +set(PROJECT_DOC_DIR ${CMAKE_INSTALL_FULL_DOCDIR}) # Headers install(DIRECTORY include/${PROJECT_NAME}/ @@ -144,6 +145,14 @@ install( DESTINATION lib/pkgconfig ) +# Documentation +install( + FILES + ${PROJECT_SOURCE_DIR}/README.md + ${PROJECT_SOURCE_DIR}/LICENSE + DESTINATION ${PROJECT_DOC_DIR} +) + # TESTS ================================================================================================================ diff --git a/fpsdk_ros1/doc/doc.hpp b/fpsdk_ros1/doc/doc.hpp index 33caa797..9c4e02eb 100644 --- a/fpsdk_ros1/doc/doc.hpp +++ b/fpsdk_ros1/doc/doc.hpp @@ -32,7 +32,6 @@ namespace ros1 { @section FPSDK_ROS1_MODULES Modules - @subpage FPSDK_ROS1_BAGWRITER - - @subpage FPSDK_ROS1_MSGS - @subpage FPSDK_ROS1_UTILS @section FPSDK_ROS1_LICENSE License diff --git a/fpsdk_ros2/CMakeLists.txt b/fpsdk_ros2/CMakeLists.txt index 07e99dff..fa214831 100644 --- a/fpsdk_ros2/CMakeLists.txt +++ b/fpsdk_ros2/CMakeLists.txt @@ -89,6 +89,7 @@ set(PROJECT_RUNTIME_DIR ${CMAKE_INSTALL_FULL_BINDIR}) set(PROJECT_LIBRARY_DIR ${CMAKE_INSTALL_FULL_LIBDIR}) set(PROJECT_INCLUDE_DIR ${CMAKE_INSTALL_FULL_INCLUDEDIR}/${PROJECT_NAME}) set(PROJECT_DATA_DIR ${CMAKE_INSTALL_FULL_DATAROOTDIR}/${PROJECT_NAME}) +set(PROJECT_DOC_DIR ${CMAKE_INSTALL_FULL_DOCDIR}) # Headers install(DIRECTORY include/${PROJECT_NAME}/ @@ -147,6 +148,14 @@ install( DESTINATION lib/pkgconfig ) +# Documentation +install( + FILES + ${PROJECT_SOURCE_DIR}/README.md + ${PROJECT_SOURCE_DIR}/LICENSE + DESTINATION ${PROJECT_DOC_DIR} +) + # TESTS ================================================================================================================