diff --git a/src/ApplicationManager.cpp b/src/ApplicationManager.cpp index 628769a..ab81342 100644 --- a/src/ApplicationManager.cpp +++ b/src/ApplicationManager.cpp @@ -364,6 +364,7 @@ void ApplicationManager::handleSpoolDetected(const AppMessage& msg) { else if (strcmp(s.tag_format, "TigerTag") == 0) spool.tagType = 2; else if (strcmp(s.tag_format, "OpenTag3D") == 0) spool.tagType = 3; else if (strcmp(s.tag_format, "BambuTag") == 0) spool.tagType = 4; + else if (strcmp(s.tag_format, "OpenSpool") == 0) spool.tagType = 6; else spool.tagType = 0; display_->showSpool(spool); } else if (display_) { diff --git a/src/ConfigHTML.h b/src/ConfigHTML.h index 984c4e7..838fae1 100644 --- a/src/ConfigHTML.h +++ b/src/ConfigHTML.h @@ -49,6 +49,7 @@ const char CONFIG_HTML[] PROGMEM = R"rawliteral( OpenPrintTag TigerTag OpenTag3D + OpenSpool NFC+ Update Troubleshooting diff --git a/src/DisplayI.h b/src/DisplayI.h index b8dbdbd..8616238 100644 --- a/src/DisplayI.h +++ b/src/DisplayI.h @@ -9,7 +9,7 @@ struct DisplaySpoolData { char colorHex[8]; // "RRGGBB" no leading # float remainingWeight; // grams float totalWeight; // grams - uint8_t tagType; // 0=unknown, 1=OpenPrintTag, 2=TigerTag, 3=OpenTag3D, 4=Bambu, 5=NFC+ + uint8_t tagType; // 0=unknown, 1=OpenPrintTag, 2=TigerTag, 3=OpenTag3D, 4=Bambu, 5=NFC+, 6=OpenSpool }; // Display interface — implemented by LCDManager and TFTManager. diff --git a/src/LandingHTML.h b/src/LandingHTML.h index 31d2eac..495471d 100644 --- a/src/LandingHTML.h +++ b/src/LandingHTML.h @@ -21,6 +21,7 @@ const char LANDING_HTML[] PROGMEM = R"rawliteral( OpenPrintTag TigerTag OpenTag3D + OpenSpool NFC+ Update Troubleshooting @@ -71,6 +72,12 @@ const char LANDING_HTML[] PROGMEM = R"rawliteral(
Write filament data to NTAG215/216 tags using the OpenTag3D format.
+ + OpenSpool +
OpenSpool Writer
+
Write filament data to NTAG215/216 tags using the OpenSpool format.
+
+
💳
NFC+ Registration
diff --git a/src/NFCManager.cpp b/src/NFCManager.cpp index 169fa1b..f9da351 100644 --- a/src/NFCManager.cpp +++ b/src/NFCManager.cpp @@ -1,6 +1,7 @@ #include "NFCManager.h" #include "ConversionUtils.h" #include "TigerTagParser.h" +#include "OpenSpoolParser.h" #ifndef NATIVE_TEST #include "ApplicationManager.h" #include "HardwareNFCConnection.h" @@ -112,6 +113,15 @@ bool NFCManager::getLastOpenTag3DData(opentag3d_t& out) { return valid; } +bool NFCManager::getLastOpenSpoolData(OpenSpoolData& out) { + if (tagMutex == nullptr) return false; + if (xSemaphoreTake(tagMutex, pdMS_TO_TICKS(50)) != pdTRUE) return false; + bool valid = lastOpenSpoolValid_; + if (valid) out = lastOpenSpool_; + xSemaphoreGive(tagMutex); + return valid; +} + void NFCManager::setGenericTagSpoolInfo(const GenericTagSpoolInfo& info) { if (tagMutex && xSemaphoreTake(tagMutex, pdMS_TO_TICKS(50)) == pdTRUE) { lastGenericTagSpoolInfo_ = info; @@ -310,10 +320,12 @@ void NFCManager::scanLoop() { Serial.printf("NFCManager: Bambu Lab tag — UID=%s (encrypted, no data access)\n", scan.uid_hex); sendGenericTagMessage(); } else if (scan.kind == TagKind::GenericUidTag) { - // ISO14443A tag — try TigerTag, then OpenTag3D, fall back to UID-only + // ISO14443A tag — try TigerTag, then OpenTag3D, then OpenSpool, fall back to UID-only bool isTigerTag = false; bool isOpenTag3D = false; + bool isOpenSpool = false; TigerTagData tigerData; + OpenSpoolData openSpoolData; opentag3d_t ot3dData; memset(&tigerData, 0, sizeof(tigerData)); memset(&ot3dData, 0, sizeof(ot3dData)); @@ -414,6 +426,51 @@ void NFCManager::scanLoop() { } } } + + // Check for OpenSpool (application/json + "protocol":"openspool") + if (!isOpenTag3D) { + const char* jsonMime = "application/json"; + size_t jsonMimeLen = strlen(jsonMime); + if (typeLen == jsonMimeLen && + memcmp(pageData + ndefStart + headerSize, jsonMime, jsonMimeLen) == 0) { + uint32_t osPayloadLen = 0; + if (sr) { + osPayloadLen = pageData[ndefStart + 2]; + } else { + osPayloadLen = ((uint32_t)pageData[ndefStart + 2] << 24) | + ((uint32_t)pageData[ndefStart + 3] << 16) | + ((uint32_t)pageData[ndefStart + 4] << 8) | + pageData[ndefStart + 5]; + } + uint16_t osOffset = ndefStart + headerSize + typeLen; + uint8_t osPayload[256] = {0}; + uint16_t osBytes = 0; + + if (osOffset + osPayloadLen <= bytesRead) { + osBytes = (uint16_t)osPayloadLen; + if (osBytes > sizeof(osPayload)) osBytes = sizeof(osPayload); + memcpy(osPayload, pageData + osOffset, osBytes); + } else { + uint8_t osStartPage = 4 + (osOffset / 4); + uint16_t osPagesNeeded = (uint16_t)((osPayloadLen + 3) / 4) + 1; + if (osPagesNeeded > 50) osPagesNeeded = 50; + uint8_t osExtData[256] = {0}; + uint16_t osExtRead = connection_->readISO14443Pages( + osStartPage, (uint8_t)osPagesNeeded, osExtData, sizeof(osExtData)); + uint16_t osOffInPage = osOffset % 4; + if (osExtRead > osOffInPage) { + osBytes = osExtRead - osOffInPage; + if (osBytes > osPayloadLen) osBytes = (uint16_t)osPayloadLen; + if (osBytes > sizeof(osPayload)) osBytes = sizeof(osPayload); + memcpy(osPayload, osExtData + osOffInPage, osBytes); + } + } + + if (osBytes > 0 && parseOpenSpool(osPayload, osBytes, openSpoolData)) { + isOpenSpool = true; + } + } + } } } break; @@ -449,14 +506,26 @@ void NFCManager::scanLoop() { lastOpenTag3D_ = ot3dData; lastOpenTag3DValid_ = true; lastTigerTagValid_ = false; + lastOpenSpoolValid_ = false; Serial.printf("NFCManager: OpenTag3D detected — %s %s %.2fmm %ug\n", ot3dData.manufacturer, ot3dData.base_material, opentag3d_diameter_mm(&ot3dData), ot3dData.target_weight_g); + } else if (isOpenSpool) { + currentSpool.kind = TagKind::OpenSpoolTag; + currentSpool.tag_data_valid = false; + lastOpenSpool_ = openSpoolData; + lastOpenSpoolValid_ = true; + lastTigerTagValid_ = false; + lastOpenTag3DValid_ = false; + Serial.printf("NFCManager: OpenSpool detected — %s %s #%s\n", + openSpoolData.brand, openSpoolData.material, + openSpoolData.color_hex); } else { currentSpool.kind = TagKind::GenericUidTag; currentSpool.tag_data_valid = false; lastTigerTagValid_ = false; lastOpenTag3DValid_ = false; + lastOpenSpoolValid_ = false; } xSemaphoreGive(tagMutex); } else { @@ -467,6 +536,8 @@ void NFCManager::scanLoop() { sendTigerTagMessage(tigerData); } else if (isOpenTag3D) { sendOpenTag3DMessage(ot3dData); + } else if (isOpenSpool) { + sendOpenSpoolMessage(currentSpool.spool_id, openSpoolData); } else { sendGenericTagMessage(); } @@ -1093,6 +1164,56 @@ void NFCManager::sendOpenTag3DMessage(const opentag3d_t& ot3d) { ApplicationManager::getInstance().sendMessage(msg); } +void NFCManager::sendOpenSpoolMessage(const char* uid, const OpenSpoolData& os) { + AppMessage msg; + msg.type = AppMessageType::SPOOL_DETECTED; + auto& s = msg.payload.spoolDetected; + memset(&s, 0, sizeof(s)); + + strncpy(s.spool_id, uid, sizeof(s.spool_id) - 1); + strncpy(s.manufacturer, os.brand, sizeof(s.manufacturer) - 1); + strncpy(s.material_name, os.material, sizeof(s.material_name) - 1); + + // Material type lookup + s.material_type = 0; + if (strcasecmp(os.material, "PLA") == 0) s.material_type = OPT_MATERIAL_TYPE_PLA; + else if (strcasecmp(os.material, "PETG") == 0) s.material_type = OPT_MATERIAL_TYPE_PETG; + else if (strcasecmp(os.material, "ABS") == 0) s.material_type = OPT_MATERIAL_TYPE_ABS; + else if (strcasecmp(os.material, "ASA") == 0) s.material_type = OPT_MATERIAL_TYPE_ASA; + else if (strcasecmp(os.material, "TPU") == 0) s.material_type = OPT_MATERIAL_TYPE_TPU; + else if (strcasecmp(os.material, "PA") == 0 || strcasecmp(os.material, "Nylon") == 0) s.material_type = OPT_MATERIAL_TYPE_PA6; + else if (strcasecmp(os.material, "PC") == 0) s.material_type = OPT_MATERIAL_TYPE_PC; + + // Parse color hex to RGB + if (strlen(os.color_hex) == 6) { + unsigned int r, g, b; + if (sscanf(os.color_hex, "%02x%02x%02x", &r, &g, &b) == 3) { + s.primary_color[0] = (uint8_t)r; + s.primary_color[1] = (uint8_t)g; + s.primary_color[2] = (uint8_t)b; + s.primary_color[3] = 255; + s.has_color = true; + } + } + + s.min_print_temp = os.min_temp; + s.max_print_temp = os.max_temp; + + strncpy(s.tag_format, "OpenSpool", sizeof(s.tag_format) - 1); + s.spoolman_id = -1; + + Serial.println("--- OpenSpool SpoolDetected payload ---"); + Serial.printf(" uid: %s\n", s.spool_id); + Serial.printf(" brand: %s\n", s.manufacturer); + Serial.printf(" material: %s\n", s.material_name); + Serial.printf(" color: #%s\n", os.color_hex); + Serial.printf(" nozzle: %d-%d°C\n", s.min_print_temp, s.max_print_temp); + Serial.printf(" version: %s\n", os.version); + Serial.println("---------------------------------------"); + + ApplicationManager::getInstance().sendMessage(msg); +} + TagScanResult NFCManager::classifyTag(const uint8_t* uid, uint8_t uid_length) { TagScanResult result; result.present = true; @@ -1651,6 +1772,91 @@ bool NFCManager::executeWrite(const NFCWriteRequest& request) { return ok; } + // Handle WRITE_OPENSPOOL — write NDEF-wrapped JSON payload to NTAG pages + if (request.type == NFCWriteType::WRITE_OPENSPOOL) { + if (xSemaphoreTake(tagMutex, pdMS_TO_TICKS(100)) != pdTRUE) { + Serial.println("NFCManager: WRITE_OPENSPOOL - could not acquire tagMutex"); + return false; + } + if (request.expected_spool_id[0] != '\0' && + strcmp(currentSpool.spool_id, request.expected_spool_id) != 0) { + xSemaphoreGive(tagMutex); + Serial.println("NFCManager: WRITE_OPENSPOOL rejected - UID mismatch"); + return false; + } + xSemaphoreGive(tagMutex); + + if (!rawWritePending_ || rawWriteBufferSize_ == 0) { + Serial.println("NFCManager: WRITE_OPENSPOOL - no raw data available"); + return false; + } + + const uint8_t* jsonPayload = rawWriteBuffer_; + uint16_t payloadLen = (uint16_t)rawWriteBufferSize_; + rawWritePending_ = false; + + const char* mime = "application/json"; + uint8_t mimeLen = (uint8_t)strlen(mime); + bool sr = (payloadLen <= 255); + uint8_t ndefHeaderSize = 2 + (sr ? 1 : 4); + uint16_t ndefRecordLen = ndefHeaderSize + mimeLen + payloadLen; + + bool longTlv = (ndefRecordLen > 254); + uint16_t tlvHeaderSize = 1 + (longTlv ? 3 : 1); + uint16_t totalSize = tlvHeaderSize + ndefRecordLen + 1; + + uint8_t ndefBuf[256]; + if (totalSize > sizeof(ndefBuf)) { + Serial.printf("NFCManager: WRITE_OPENSPOOL - NDEF too large (%u bytes)\n", totalSize); + return false; + } + + uint16_t idx = 0; + ndefBuf[idx++] = 0x03; + if (longTlv) { + ndefBuf[idx++] = 0xFF; + ndefBuf[idx++] = (uint8_t)(ndefRecordLen >> 8); + ndefBuf[idx++] = (uint8_t)(ndefRecordLen & 0xFF); + } else { + ndefBuf[idx++] = (uint8_t)ndefRecordLen; + } + + uint8_t ndefFlags = 0xC0 | 0x02; + if (sr) ndefFlags |= 0x10; + ndefBuf[idx++] = ndefFlags; + ndefBuf[idx++] = mimeLen; + if (sr) { + ndefBuf[idx++] = (uint8_t)payloadLen; + } else { + ndefBuf[idx++] = (uint8_t)((payloadLen >> 24) & 0xFF); + ndefBuf[idx++] = (uint8_t)((payloadLen >> 16) & 0xFF); + ndefBuf[idx++] = (uint8_t)((payloadLen >> 8) & 0xFF); + ndefBuf[idx++] = (uint8_t)(payloadLen & 0xFF); + } + + memcpy(ndefBuf + idx, mime, mimeLen); + idx += mimeLen; + memcpy(ndefBuf + idx, jsonPayload, payloadLen); + idx += payloadLen; + ndefBuf[idx++] = 0xFE; + + while (idx % 4 != 0) ndefBuf[idx++] = 0x00; + + uint8_t pagesNeeded = (uint8_t)(idx / 4); + Serial.printf("NFCManager: WRITE_OPENSPOOL - writing %u bytes (%u pages)\n", idx, pagesNeeded); + bool ok = connection_->writeISO14443Pages(4, pagesNeeded, ndefBuf, idx); + if (ok) { + Serial.printf("NFCManager: WRITE_OPENSPOOL succeeded (%u bytes, %u pages)\n", idx, pagesNeeded); + if (xSemaphoreTake(tagMutex, pdMS_TO_TICKS(100)) == pdTRUE) { + currentSpool.present = false; + xSemaphoreGive(tagMutex); + } + } else { + Serial.println("NFCManager: WRITE_OPENSPOOL failed"); + } + return ok; + } + // Handle WRITE_ATOMIC — build complete CBOR map from sidecar fields, write once if (request.type == NFCWriteType::WRITE_ATOMIC) { if (!atomicWriteFields_.pending) { diff --git a/src/NFCManager.h b/src/NFCManager.h index 4161080..b3ce35a 100644 --- a/src/NFCManager.h +++ b/src/NFCManager.h @@ -12,6 +12,7 @@ #include "NFCTypes.h" #include "NFCConnectionI.h" #include "TigerTagParser.h" +#include "OpenSpoolParser.h" #include "opentag3d_lib.h" // Sidecar for WRITE_ATOMIC: filled by HTTP handler, consumed by scan task. @@ -75,6 +76,7 @@ class NFCManager { bool getCurrentSpoolState(CurrentSpoolState& out); bool getLastTigerTagData(TigerTagData& out); bool getLastOpenTag3DData(opentag3d_t& out); + bool getLastOpenSpoolData(OpenSpoolData& out); // Returns reader identification string (e.g. "PN5180 v3.4", "PN532 v1.6") bool getNfcReaderInfo(char* buf, size_t len) const; void pauseScanTask(); @@ -123,6 +125,7 @@ class NFCManager { void sendGenericTagMessage(); void sendTigerTagMessage(const TigerTagData& tt); void sendOpenTag3DMessage(const opentag3d_t& ot3d); + void sendOpenSpoolMessage(const char* uid, const OpenSpoolData& data); void sendTagRemovedMessage(); void processWriteQueue(); bool executeWrite(const NFCWriteRequest& request); @@ -153,6 +156,8 @@ class NFCManager { // Last parsed OpenTag3D data (retained for /api/status) opentag3d_t lastOpenTag3D_; bool lastOpenTag3DValid_ = false; + OpenSpoolData lastOpenSpool_; + bool lastOpenSpoolValid_ = false; // Resolved Spoolman data for the current generic UID tag GenericTagSpoolInfo lastGenericTagSpoolInfo_ = {}; diff --git a/src/NFCTypes.h b/src/NFCTypes.h index 343f333..73e073c 100644 --- a/src/NFCTypes.h +++ b/src/NFCTypes.h @@ -18,6 +18,7 @@ enum class TagKind : uint8_t { OpenTag3D, // OpenTag3D format — ISO14443A TigerTag, // TigerTag format — ISO14443A (NTAG213/215/216) BambuTag, // Bambu Lab spool — MIFARE Classic (encrypted, UID-only) + OpenSpoolTag, // OpenSpool format — ISO14443A (NTAG215/216, NDEF JSON) BlankTag, Unsupported }; diff --git a/src/NFCWriteTypes.h b/src/NFCWriteTypes.h index e17cd35..57bd59b 100644 --- a/src/NFCWriteTypes.h +++ b/src/NFCWriteTypes.h @@ -26,6 +26,7 @@ enum class NFCWriteType : uint8_t { SET_MAX_BED_TEMP, // Set maximum bed temperature (°C) WRITE_TIGERTAG, // Write 40-byte TigerTag binary to NTAG pages 4-13 WRITE_OPENTAG3D, // Write NDEF-wrapped OpenTag3D payload to NTAG pages + WRITE_OPENSPOOL, // Write NDEF-wrapped OpenSpool JSON payload to NTAG pages WRITE_ATOMIC, // Atomic single-pass write: build complete CBOR map, write once }; diff --git a/src/OpenPrintTagWriterHTML.h b/src/OpenPrintTagWriterHTML.h index 01a8d92..e13ddf2 100644 --- a/src/OpenPrintTagWriterHTML.h +++ b/src/OpenPrintTagWriterHTML.h @@ -25,6 +25,7 @@ const char OPENPRINTTAG_WRITER_HTML[] PROGMEM = R"rawliteral(
OpenPrintTag TigerTag OpenTag3D + OpenSpool NFC+ Update Troubleshooting diff --git a/src/OpenSpoolLogo.h b/src/OpenSpoolLogo.h new file mode 100644 index 0000000..a62b230 --- /dev/null +++ b/src/OpenSpoolLogo.h @@ -0,0 +1,652 @@ +#pragma once +#include + +// OpenSpool logo PNG served at GET /img/openspool.png + +static const uint8_t OPENSPOOL_LOGO_PNG[] PROGMEM = { + 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0xff, 0xe1, 0x00, 0x74, + 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x04, 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x3e, 0x01, 0x1b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x46, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x02, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x64, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0xff, 0xed, 0x00, 0x38, 0x50, 0x68, + 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x20, 0x33, 0x2e, 0x30, 0x00, + 0x38, 0x42, 0x49, 0x4d, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x42, 0x49, 0x4d, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, 0xe9, 0x80, 0x09, 0x98, + 0xec, 0xf8, 0x42, 0x7e, 0xff, 0xe2, 0x0d, 0x28, 0x49, 0x43, 0x43, 0x5f, + 0x50, 0x52, 0x4f, 0x46, 0x49, 0x4c, 0x45, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x0d, 0x18, 0x61, 0x70, 0x70, 0x6c, 0x02, 0x10, 0x00, 0x00, 0x6d, 0x6e, + 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5a, 0x20, 0x07, 0xe8, + 0x00, 0x0a, 0x00, 0x10, 0x00, 0x08, 0x00, 0x14, 0x00, 0x1e, 0x61, 0x63, + 0x73, 0x70, 0x41, 0x50, 0x50, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, + 0x50, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xd6, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d, 0x61, 0x70, 0x70, 0x6c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x64, 0x65, + 0x73, 0x63, 0x00, 0x00, 0x01, 0x50, 0x00, 0x00, 0x00, 0x62, 0x64, 0x73, + 0x63, 0x6d, 0x00, 0x00, 0x01, 0xb4, 0x00, 0x00, 0x01, 0xf4, 0x63, 0x70, + 0x72, 0x74, 0x00, 0x00, 0x03, 0xa8, 0x00, 0x00, 0x00, 0x23, 0x77, 0x74, + 0x70, 0x74, 0x00, 0x00, 0x03, 0xcc, 0x00, 0x00, 0x00, 0x14, 0x72, 0x58, + 0x59, 0x5a, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x00, 0x00, 0x14, 0x67, 0x58, + 0x59, 0x5a, 0x00, 0x00, 0x03, 0xf4, 0x00, 0x00, 0x00, 0x14, 0x62, 0x58, + 0x59, 0x5a, 0x00, 0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x14, 0x72, 0x54, + 0x52, 0x43, 0x00, 0x00, 0x04, 0x1c, 0x00, 0x00, 0x08, 0x0c, 0x61, 0x61, + 0x72, 0x67, 0x00, 0x00, 0x0c, 0x28, 0x00, 0x00, 0x00, 0x20, 0x76, 0x63, + 0x67, 0x74, 0x00, 0x00, 0x0c, 0x48, 0x00, 0x00, 0x00, 0x30, 0x6e, 0x64, + 0x69, 0x6e, 0x00, 0x00, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x3e, 0x6d, 0x6d, + 0x6f, 0x64, 0x00, 0x00, 0x0c, 0xb8, 0x00, 0x00, 0x00, 0x28, 0x76, 0x63, + 0x67, 0x70, 0x00, 0x00, 0x0c, 0xe0, 0x00, 0x00, 0x00, 0x38, 0x62, 0x54, + 0x52, 0x43, 0x00, 0x00, 0x04, 0x1c, 0x00, 0x00, 0x08, 0x0c, 0x67, 0x54, + 0x52, 0x43, 0x00, 0x00, 0x04, 0x1c, 0x00, 0x00, 0x08, 0x0c, 0x61, 0x61, + 0x62, 0x67, 0x00, 0x00, 0x0c, 0x28, 0x00, 0x00, 0x00, 0x20, 0x61, 0x61, + 0x67, 0x67, 0x00, 0x00, 0x0c, 0x28, 0x00, 0x00, 0x00, 0x20, 0x64, 0x65, + 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x44, 0x69, + 0x73, 0x70, 0x6c, 0x61, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x6d, 0x6c, 0x75, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x27, 0x00, 0x00, 0x00, 0x0c, 0x68, 0x72, 0x48, 0x52, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x6b, 0x6f, 0x4b, 0x52, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x6e, 0x62, 0x4e, 0x4f, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x69, 0x64, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x68, 0x75, 0x48, 0x55, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x63, 0x73, 0x43, 0x5a, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x73, 0x6c, 0x53, 0x49, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x64, 0x61, 0x44, 0x4b, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x6e, 0x6c, 0x4e, 0x4c, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x66, 0x69, 0x46, 0x49, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x69, 0x74, 0x49, 0x54, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x65, 0x73, 0x45, 0x53, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x72, 0x6f, 0x52, 0x4f, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x66, 0x72, 0x43, 0x41, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x61, 0x72, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x75, 0x6b, 0x55, 0x41, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x68, 0x65, 0x49, 0x4c, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x7a, 0x68, 0x54, 0x57, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x76, 0x69, 0x56, 0x4e, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x73, 0x6b, 0x53, 0x4b, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x7a, 0x68, 0x43, 0x4e, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x72, 0x75, 0x52, 0x55, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x65, 0x6e, 0x47, 0x42, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x66, 0x72, 0x46, 0x52, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x6d, 0x73, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x68, 0x69, 0x49, 0x4e, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x74, 0x68, 0x54, 0x48, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x63, 0x61, 0x45, 0x53, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x65, 0x6e, 0x41, 0x55, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x65, 0x73, 0x58, 0x4c, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x64, 0x65, 0x44, 0x45, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x65, 0x6e, 0x55, 0x53, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x70, 0x74, 0x42, 0x52, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x70, 0x6c, 0x50, 0x4c, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x65, 0x6c, 0x47, 0x52, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x73, 0x76, 0x53, 0x45, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x74, 0x72, 0x54, 0x52, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x70, 0x74, 0x50, 0x54, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x6a, 0x61, 0x4a, 0x50, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x01, 0xe4, 0x00, 0x4c, 0x00, 0x43, 0x00, 0x34, + 0x00, 0x39, 0x00, 0x47, 0x00, 0x39, 0x00, 0x35, 0x00, 0x54, 0x74, 0x65, + 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, + 0x67, 0x68, 0x74, 0x20, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x49, 0x6e, + 0x63, 0x2e, 0x2c, 0x20, 0x32, 0x30, 0x32, 0x34, 0x00, 0x00, 0x58, 0x59, + 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf3, 0xd8, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x16, 0x08, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7b, 0xed, 0x00, 0x00, 0x34, 0x71, 0x00, 0x00, + 0x01, 0x49, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x55, 0x0c, 0x00, 0x00, 0xbc, 0x5f, 0x00, 0x00, 0x10, 0x64, 0x58, 0x59, + 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0xdd, 0x00, 0x00, + 0x0f, 0x30, 0x00, 0x00, 0xc1, 0x7f, 0x63, 0x75, 0x72, 0x76, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, + 0x00, 0x0f, 0x00, 0x14, 0x00, 0x19, 0x00, 0x1e, 0x00, 0x23, 0x00, 0x28, + 0x00, 0x2d, 0x00, 0x32, 0x00, 0x36, 0x00, 0x3b, 0x00, 0x40, 0x00, 0x45, + 0x00, 0x4a, 0x00, 0x4f, 0x00, 0x54, 0x00, 0x59, 0x00, 0x5e, 0x00, 0x63, + 0x00, 0x68, 0x00, 0x6d, 0x00, 0x72, 0x00, 0x77, 0x00, 0x7c, 0x00, 0x81, + 0x00, 0x86, 0x00, 0x8b, 0x00, 0x90, 0x00, 0x95, 0x00, 0x9a, 0x00, 0x9f, + 0x00, 0xa3, 0x00, 0xa8, 0x00, 0xad, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xbc, + 0x00, 0xc1, 0x00, 0xc6, 0x00, 0xcb, 0x00, 0xd0, 0x00, 0xd5, 0x00, 0xdb, + 0x00, 0xe0, 0x00, 0xe5, 0x00, 0xeb, 0x00, 0xf0, 0x00, 0xf6, 0x00, 0xfb, + 0x01, 0x01, 0x01, 0x07, 0x01, 0x0d, 0x01, 0x13, 0x01, 0x19, 0x01, 0x1f, + 0x01, 0x25, 0x01, 0x2b, 0x01, 0x32, 0x01, 0x38, 0x01, 0x3e, 0x01, 0x45, + 0x01, 0x4c, 0x01, 0x52, 0x01, 0x59, 0x01, 0x60, 0x01, 0x67, 0x01, 0x6e, + 0x01, 0x75, 0x01, 0x7c, 0x01, 0x83, 0x01, 0x8b, 0x01, 0x92, 0x01, 0x9a, + 0x01, 0xa1, 0x01, 0xa9, 0x01, 0xb1, 0x01, 0xb9, 0x01, 0xc1, 0x01, 0xc9, + 0x01, 0xd1, 0x01, 0xd9, 0x01, 0xe1, 0x01, 0xe9, 0x01, 0xf2, 0x01, 0xfa, + 0x02, 0x03, 0x02, 0x0c, 0x02, 0x14, 0x02, 0x1d, 0x02, 0x26, 0x02, 0x2f, + 0x02, 0x38, 0x02, 0x41, 0x02, 0x4b, 0x02, 0x54, 0x02, 0x5d, 0x02, 0x67, + 0x02, 0x71, 0x02, 0x7a, 0x02, 0x84, 0x02, 0x8e, 0x02, 0x98, 0x02, 0xa2, + 0x02, 0xac, 0x02, 0xb6, 0x02, 0xc1, 0x02, 0xcb, 0x02, 0xd5, 0x02, 0xe0, + 0x02, 0xeb, 0x02, 0xf5, 0x03, 0x00, 0x03, 0x0b, 0x03, 0x16, 0x03, 0x21, + 0x03, 0x2d, 0x03, 0x38, 0x03, 0x43, 0x03, 0x4f, 0x03, 0x5a, 0x03, 0x66, + 0x03, 0x72, 0x03, 0x7e, 0x03, 0x8a, 0x03, 0x96, 0x03, 0xa2, 0x03, 0xae, + 0x03, 0xba, 0x03, 0xc7, 0x03, 0xd3, 0x03, 0xe0, 0x03, 0xec, 0x03, 0xf9, + 0x04, 0x06, 0x04, 0x13, 0x04, 0x20, 0x04, 0x2d, 0x04, 0x3b, 0x04, 0x48, + 0x04, 0x55, 0x04, 0x63, 0x04, 0x71, 0x04, 0x7e, 0x04, 0x8c, 0x04, 0x9a, + 0x04, 0xa8, 0x04, 0xb6, 0x04, 0xc4, 0x04, 0xd3, 0x04, 0xe1, 0x04, 0xf0, + 0x04, 0xfe, 0x05, 0x0d, 0x05, 0x1c, 0x05, 0x2b, 0x05, 0x3a, 0x05, 0x49, + 0x05, 0x58, 0x05, 0x67, 0x05, 0x77, 0x05, 0x86, 0x05, 0x96, 0x05, 0xa6, + 0x05, 0xb5, 0x05, 0xc5, 0x05, 0xd5, 0x05, 0xe5, 0x05, 0xf6, 0x06, 0x06, + 0x06, 0x16, 0x06, 0x27, 0x06, 0x37, 0x06, 0x48, 0x06, 0x59, 0x06, 0x6a, + 0x06, 0x7b, 0x06, 0x8c, 0x06, 0x9d, 0x06, 0xaf, 0x06, 0xc0, 0x06, 0xd1, + 0x06, 0xe3, 0x06, 0xf5, 0x07, 0x07, 0x07, 0x19, 0x07, 0x2b, 0x07, 0x3d, + 0x07, 0x4f, 0x07, 0x61, 0x07, 0x74, 0x07, 0x86, 0x07, 0x99, 0x07, 0xac, + 0x07, 0xbf, 0x07, 0xd2, 0x07, 0xe5, 0x07, 0xf8, 0x08, 0x0b, 0x08, 0x1f, + 0x08, 0x32, 0x08, 0x46, 0x08, 0x5a, 0x08, 0x6e, 0x08, 0x82, 0x08, 0x96, + 0x08, 0xaa, 0x08, 0xbe, 0x08, 0xd2, 0x08, 0xe7, 0x08, 0xfb, 0x09, 0x10, + 0x09, 0x25, 0x09, 0x3a, 0x09, 0x4f, 0x09, 0x64, 0x09, 0x79, 0x09, 0x8f, + 0x09, 0xa4, 0x09, 0xba, 0x09, 0xcf, 0x09, 0xe5, 0x09, 0xfb, 0x0a, 0x11, + 0x0a, 0x27, 0x0a, 0x3d, 0x0a, 0x54, 0x0a, 0x6a, 0x0a, 0x81, 0x0a, 0x98, + 0x0a, 0xae, 0x0a, 0xc5, 0x0a, 0xdc, 0x0a, 0xf3, 0x0b, 0x0b, 0x0b, 0x22, + 0x0b, 0x39, 0x0b, 0x51, 0x0b, 0x69, 0x0b, 0x80, 0x0b, 0x98, 0x0b, 0xb0, + 0x0b, 0xc8, 0x0b, 0xe1, 0x0b, 0xf9, 0x0c, 0x12, 0x0c, 0x2a, 0x0c, 0x43, + 0x0c, 0x5c, 0x0c, 0x75, 0x0c, 0x8e, 0x0c, 0xa7, 0x0c, 0xc0, 0x0c, 0xd9, + 0x0c, 0xf3, 0x0d, 0x0d, 0x0d, 0x26, 0x0d, 0x40, 0x0d, 0x5a, 0x0d, 0x74, + 0x0d, 0x8e, 0x0d, 0xa9, 0x0d, 0xc3, 0x0d, 0xde, 0x0d, 0xf8, 0x0e, 0x13, + 0x0e, 0x2e, 0x0e, 0x49, 0x0e, 0x64, 0x0e, 0x7f, 0x0e, 0x9b, 0x0e, 0xb6, + 0x0e, 0xd2, 0x0e, 0xee, 0x0f, 0x09, 0x0f, 0x25, 0x0f, 0x41, 0x0f, 0x5e, + 0x0f, 0x7a, 0x0f, 0x96, 0x0f, 0xb3, 0x0f, 0xcf, 0x0f, 0xec, 0x10, 0x09, + 0x10, 0x26, 0x10, 0x43, 0x10, 0x61, 0x10, 0x7e, 0x10, 0x9b, 0x10, 0xb9, + 0x10, 0xd7, 0x10, 0xf5, 0x11, 0x13, 0x11, 0x31, 0x11, 0x4f, 0x11, 0x6d, + 0x11, 0x8c, 0x11, 0xaa, 0x11, 0xc9, 0x11, 0xe8, 0x12, 0x07, 0x12, 0x26, + 0x12, 0x45, 0x12, 0x64, 0x12, 0x84, 0x12, 0xa3, 0x12, 0xc3, 0x12, 0xe3, + 0x13, 0x03, 0x13, 0x23, 0x13, 0x43, 0x13, 0x63, 0x13, 0x83, 0x13, 0xa4, + 0x13, 0xc5, 0x13, 0xe5, 0x14, 0x06, 0x14, 0x27, 0x14, 0x49, 0x14, 0x6a, + 0x14, 0x8b, 0x14, 0xad, 0x14, 0xce, 0x14, 0xf0, 0x15, 0x12, 0x15, 0x34, + 0x15, 0x56, 0x15, 0x78, 0x15, 0x9b, 0x15, 0xbd, 0x15, 0xe0, 0x16, 0x03, + 0x16, 0x26, 0x16, 0x49, 0x16, 0x6c, 0x16, 0x8f, 0x16, 0xb2, 0x16, 0xd6, + 0x16, 0xfa, 0x17, 0x1d, 0x17, 0x41, 0x17, 0x65, 0x17, 0x89, 0x17, 0xae, + 0x17, 0xd2, 0x17, 0xf7, 0x18, 0x1b, 0x18, 0x40, 0x18, 0x65, 0x18, 0x8a, + 0x18, 0xaf, 0x18, 0xd5, 0x18, 0xfa, 0x19, 0x20, 0x19, 0x45, 0x19, 0x6b, + 0x19, 0x91, 0x19, 0xb7, 0x19, 0xdd, 0x1a, 0x04, 0x1a, 0x2a, 0x1a, 0x51, + 0x1a, 0x77, 0x1a, 0x9e, 0x1a, 0xc5, 0x1a, 0xec, 0x1b, 0x14, 0x1b, 0x3b, + 0x1b, 0x63, 0x1b, 0x8a, 0x1b, 0xb2, 0x1b, 0xda, 0x1c, 0x02, 0x1c, 0x2a, + 0x1c, 0x52, 0x1c, 0x7b, 0x1c, 0xa3, 0x1c, 0xcc, 0x1c, 0xf5, 0x1d, 0x1e, + 0x1d, 0x47, 0x1d, 0x70, 0x1d, 0x99, 0x1d, 0xc3, 0x1d, 0xec, 0x1e, 0x16, + 0x1e, 0x40, 0x1e, 0x6a, 0x1e, 0x94, 0x1e, 0xbe, 0x1e, 0xe9, 0x1f, 0x13, + 0x1f, 0x3e, 0x1f, 0x69, 0x1f, 0x94, 0x1f, 0xbf, 0x1f, 0xea, 0x20, 0x15, + 0x20, 0x41, 0x20, 0x6c, 0x20, 0x98, 0x20, 0xc4, 0x20, 0xf0, 0x21, 0x1c, + 0x21, 0x48, 0x21, 0x75, 0x21, 0xa1, 0x21, 0xce, 0x21, 0xfb, 0x22, 0x27, + 0x22, 0x55, 0x22, 0x82, 0x22, 0xaf, 0x22, 0xdd, 0x23, 0x0a, 0x23, 0x38, + 0x23, 0x66, 0x23, 0x94, 0x23, 0xc2, 0x23, 0xf0, 0x24, 0x1f, 0x24, 0x4d, + 0x24, 0x7c, 0x24, 0xab, 0x24, 0xda, 0x25, 0x09, 0x25, 0x38, 0x25, 0x68, + 0x25, 0x97, 0x25, 0xc7, 0x25, 0xf7, 0x26, 0x27, 0x26, 0x57, 0x26, 0x87, + 0x26, 0xb7, 0x26, 0xe8, 0x27, 0x18, 0x27, 0x49, 0x27, 0x7a, 0x27, 0xab, + 0x27, 0xdc, 0x28, 0x0d, 0x28, 0x3f, 0x28, 0x71, 0x28, 0xa2, 0x28, 0xd4, + 0x29, 0x06, 0x29, 0x38, 0x29, 0x6b, 0x29, 0x9d, 0x29, 0xd0, 0x2a, 0x02, + 0x2a, 0x35, 0x2a, 0x68, 0x2a, 0x9b, 0x2a, 0xcf, 0x2b, 0x02, 0x2b, 0x36, + 0x2b, 0x69, 0x2b, 0x9d, 0x2b, 0xd1, 0x2c, 0x05, 0x2c, 0x39, 0x2c, 0x6e, + 0x2c, 0xa2, 0x2c, 0xd7, 0x2d, 0x0c, 0x2d, 0x41, 0x2d, 0x76, 0x2d, 0xab, + 0x2d, 0xe1, 0x2e, 0x16, 0x2e, 0x4c, 0x2e, 0x82, 0x2e, 0xb7, 0x2e, 0xee, + 0x2f, 0x24, 0x2f, 0x5a, 0x2f, 0x91, 0x2f, 0xc7, 0x2f, 0xfe, 0x30, 0x35, + 0x30, 0x6c, 0x30, 0xa4, 0x30, 0xdb, 0x31, 0x12, 0x31, 0x4a, 0x31, 0x82, + 0x31, 0xba, 0x31, 0xf2, 0x32, 0x2a, 0x32, 0x63, 0x32, 0x9b, 0x32, 0xd4, + 0x33, 0x0d, 0x33, 0x46, 0x33, 0x7f, 0x33, 0xb8, 0x33, 0xf1, 0x34, 0x2b, + 0x34, 0x65, 0x34, 0x9e, 0x34, 0xd8, 0x35, 0x13, 0x35, 0x4d, 0x35, 0x87, + 0x35, 0xc2, 0x35, 0xfd, 0x36, 0x37, 0x36, 0x72, 0x36, 0xae, 0x36, 0xe9, + 0x37, 0x24, 0x37, 0x60, 0x37, 0x9c, 0x37, 0xd7, 0x38, 0x14, 0x38, 0x50, + 0x38, 0x8c, 0x38, 0xc8, 0x39, 0x05, 0x39, 0x42, 0x39, 0x7f, 0x39, 0xbc, + 0x39, 0xf9, 0x3a, 0x36, 0x3a, 0x74, 0x3a, 0xb2, 0x3a, 0xef, 0x3b, 0x2d, + 0x3b, 0x6b, 0x3b, 0xaa, 0x3b, 0xe8, 0x3c, 0x27, 0x3c, 0x65, 0x3c, 0xa4, + 0x3c, 0xe3, 0x3d, 0x22, 0x3d, 0x61, 0x3d, 0xa1, 0x3d, 0xe0, 0x3e, 0x20, + 0x3e, 0x60, 0x3e, 0xa0, 0x3e, 0xe0, 0x3f, 0x21, 0x3f, 0x61, 0x3f, 0xa2, + 0x3f, 0xe2, 0x40, 0x23, 0x40, 0x64, 0x40, 0xa6, 0x40, 0xe7, 0x41, 0x29, + 0x41, 0x6a, 0x41, 0xac, 0x41, 0xee, 0x42, 0x30, 0x42, 0x72, 0x42, 0xb5, + 0x42, 0xf7, 0x43, 0x3a, 0x43, 0x7d, 0x43, 0xc0, 0x44, 0x03, 0x44, 0x47, + 0x44, 0x8a, 0x44, 0xce, 0x45, 0x12, 0x45, 0x55, 0x45, 0x9a, 0x45, 0xde, + 0x46, 0x22, 0x46, 0x67, 0x46, 0xab, 0x46, 0xf0, 0x47, 0x35, 0x47, 0x7b, + 0x47, 0xc0, 0x48, 0x05, 0x48, 0x4b, 0x48, 0x91, 0x48, 0xd7, 0x49, 0x1d, + 0x49, 0x63, 0x49, 0xa9, 0x49, 0xf0, 0x4a, 0x37, 0x4a, 0x7d, 0x4a, 0xc4, + 0x4b, 0x0c, 0x4b, 0x53, 0x4b, 0x9a, 0x4b, 0xe2, 0x4c, 0x2a, 0x4c, 0x72, + 0x4c, 0xba, 0x4d, 0x02, 0x4d, 0x4a, 0x4d, 0x93, 0x4d, 0xdc, 0x4e, 0x25, + 0x4e, 0x6e, 0x4e, 0xb7, 0x4f, 0x00, 0x4f, 0x49, 0x4f, 0x93, 0x4f, 0xdd, + 0x50, 0x27, 0x50, 0x71, 0x50, 0xbb, 0x51, 0x06, 0x51, 0x50, 0x51, 0x9b, + 0x51, 0xe6, 0x52, 0x31, 0x52, 0x7c, 0x52, 0xc7, 0x53, 0x13, 0x53, 0x5f, + 0x53, 0xaa, 0x53, 0xf6, 0x54, 0x42, 0x54, 0x8f, 0x54, 0xdb, 0x55, 0x28, + 0x55, 0x75, 0x55, 0xc2, 0x56, 0x0f, 0x56, 0x5c, 0x56, 0xa9, 0x56, 0xf7, + 0x57, 0x44, 0x57, 0x92, 0x57, 0xe0, 0x58, 0x2f, 0x58, 0x7d, 0x58, 0xcb, + 0x59, 0x1a, 0x59, 0x69, 0x59, 0xb8, 0x5a, 0x07, 0x5a, 0x56, 0x5a, 0xa6, + 0x5a, 0xf5, 0x5b, 0x45, 0x5b, 0x95, 0x5b, 0xe5, 0x5c, 0x35, 0x5c, 0x86, + 0x5c, 0xd6, 0x5d, 0x27, 0x5d, 0x78, 0x5d, 0xc9, 0x5e, 0x1a, 0x5e, 0x6c, + 0x5e, 0xbd, 0x5f, 0x0f, 0x5f, 0x61, 0x5f, 0xb3, 0x60, 0x05, 0x60, 0x57, + 0x60, 0xaa, 0x60, 0xfc, 0x61, 0x4f, 0x61, 0xa2, 0x61, 0xf5, 0x62, 0x49, + 0x62, 0x9c, 0x62, 0xf0, 0x63, 0x43, 0x63, 0x97, 0x63, 0xeb, 0x64, 0x40, + 0x64, 0x94, 0x64, 0xe9, 0x65, 0x3d, 0x65, 0x92, 0x65, 0xe7, 0x66, 0x3d, + 0x66, 0x92, 0x66, 0xe8, 0x67, 0x3d, 0x67, 0x93, 0x67, 0xe9, 0x68, 0x3f, + 0x68, 0x96, 0x68, 0xec, 0x69, 0x43, 0x69, 0x9a, 0x69, 0xf1, 0x6a, 0x48, + 0x6a, 0x9f, 0x6a, 0xf7, 0x6b, 0x4f, 0x6b, 0xa7, 0x6b, 0xff, 0x6c, 0x57, + 0x6c, 0xaf, 0x6d, 0x08, 0x6d, 0x60, 0x6d, 0xb9, 0x6e, 0x12, 0x6e, 0x6b, + 0x6e, 0xc4, 0x6f, 0x1e, 0x6f, 0x78, 0x6f, 0xd1, 0x70, 0x2b, 0x70, 0x86, + 0x70, 0xe0, 0x71, 0x3a, 0x71, 0x95, 0x71, 0xf0, 0x72, 0x4b, 0x72, 0xa6, + 0x73, 0x01, 0x73, 0x5d, 0x73, 0xb8, 0x74, 0x14, 0x74, 0x70, 0x74, 0xcc, + 0x75, 0x28, 0x75, 0x85, 0x75, 0xe1, 0x76, 0x3e, 0x76, 0x9b, 0x76, 0xf8, + 0x77, 0x56, 0x77, 0xb3, 0x78, 0x11, 0x78, 0x6e, 0x78, 0xcc, 0x79, 0x2a, + 0x79, 0x89, 0x79, 0xe7, 0x7a, 0x46, 0x7a, 0xa5, 0x7b, 0x04, 0x7b, 0x63, + 0x7b, 0xc2, 0x7c, 0x21, 0x7c, 0x81, 0x7c, 0xe1, 0x7d, 0x41, 0x7d, 0xa1, + 0x7e, 0x01, 0x7e, 0x62, 0x7e, 0xc2, 0x7f, 0x23, 0x7f, 0x84, 0x7f, 0xe5, + 0x80, 0x47, 0x80, 0xa8, 0x81, 0x0a, 0x81, 0x6b, 0x81, 0xcd, 0x82, 0x30, + 0x82, 0x92, 0x82, 0xf4, 0x83, 0x57, 0x83, 0xba, 0x84, 0x1d, 0x84, 0x80, + 0x84, 0xe3, 0x85, 0x47, 0x85, 0xab, 0x86, 0x0e, 0x86, 0x72, 0x86, 0xd7, + 0x87, 0x3b, 0x87, 0x9f, 0x88, 0x04, 0x88, 0x69, 0x88, 0xce, 0x89, 0x33, + 0x89, 0x99, 0x89, 0xfe, 0x8a, 0x64, 0x8a, 0xca, 0x8b, 0x30, 0x8b, 0x96, + 0x8b, 0xfc, 0x8c, 0x63, 0x8c, 0xca, 0x8d, 0x31, 0x8d, 0x98, 0x8d, 0xff, + 0x8e, 0x66, 0x8e, 0xce, 0x8f, 0x36, 0x8f, 0x9e, 0x90, 0x06, 0x90, 0x6e, + 0x90, 0xd6, 0x91, 0x3f, 0x91, 0xa8, 0x92, 0x11, 0x92, 0x7a, 0x92, 0xe3, + 0x93, 0x4d, 0x93, 0xb6, 0x94, 0x20, 0x94, 0x8a, 0x94, 0xf4, 0x95, 0x5f, + 0x95, 0xc9, 0x96, 0x34, 0x96, 0x9f, 0x97, 0x0a, 0x97, 0x75, 0x97, 0xe0, + 0x98, 0x4c, 0x98, 0xb8, 0x99, 0x24, 0x99, 0x90, 0x99, 0xfc, 0x9a, 0x68, + 0x9a, 0xd5, 0x9b, 0x42, 0x9b, 0xaf, 0x9c, 0x1c, 0x9c, 0x89, 0x9c, 0xf7, + 0x9d, 0x64, 0x9d, 0xd2, 0x9e, 0x40, 0x9e, 0xae, 0x9f, 0x1d, 0x9f, 0x8b, + 0x9f, 0xfa, 0xa0, 0x69, 0xa0, 0xd8, 0xa1, 0x47, 0xa1, 0xb6, 0xa2, 0x26, + 0xa2, 0x96, 0xa3, 0x06, 0xa3, 0x76, 0xa3, 0xe6, 0xa4, 0x56, 0xa4, 0xc7, + 0xa5, 0x38, 0xa5, 0xa9, 0xa6, 0x1a, 0xa6, 0x8b, 0xa6, 0xfd, 0xa7, 0x6e, + 0xa7, 0xe0, 0xa8, 0x52, 0xa8, 0xc4, 0xa9, 0x37, 0xa9, 0xa9, 0xaa, 0x1c, + 0xaa, 0x8f, 0xab, 0x02, 0xab, 0x75, 0xab, 0xe9, 0xac, 0x5c, 0xac, 0xd0, + 0xad, 0x44, 0xad, 0xb8, 0xae, 0x2d, 0xae, 0xa1, 0xaf, 0x16, 0xaf, 0x8b, + 0xb0, 0x00, 0xb0, 0x75, 0xb0, 0xea, 0xb1, 0x60, 0xb1, 0xd6, 0xb2, 0x4b, + 0xb2, 0xc2, 0xb3, 0x38, 0xb3, 0xae, 0xb4, 0x25, 0xb4, 0x9c, 0xb5, 0x13, + 0xb5, 0x8a, 0xb6, 0x01, 0xb6, 0x79, 0xb6, 0xf0, 0xb7, 0x68, 0xb7, 0xe0, + 0xb8, 0x59, 0xb8, 0xd1, 0xb9, 0x4a, 0xb9, 0xc2, 0xba, 0x3b, 0xba, 0xb5, + 0xbb, 0x2e, 0xbb, 0xa7, 0xbc, 0x21, 0xbc, 0x9b, 0xbd, 0x15, 0xbd, 0x8f, + 0xbe, 0x0a, 0xbe, 0x84, 0xbe, 0xff, 0xbf, 0x7a, 0xbf, 0xf5, 0xc0, 0x70, + 0xc0, 0xec, 0xc1, 0x67, 0xc1, 0xe3, 0xc2, 0x5f, 0xc2, 0xdb, 0xc3, 0x58, + 0xc3, 0xd4, 0xc4, 0x51, 0xc4, 0xce, 0xc5, 0x4b, 0xc5, 0xc8, 0xc6, 0x46, + 0xc6, 0xc3, 0xc7, 0x41, 0xc7, 0xbf, 0xc8, 0x3d, 0xc8, 0xbc, 0xc9, 0x3a, + 0xc9, 0xb9, 0xca, 0x38, 0xca, 0xb7, 0xcb, 0x36, 0xcb, 0xb6, 0xcc, 0x35, + 0xcc, 0xb5, 0xcd, 0x35, 0xcd, 0xb5, 0xce, 0x36, 0xce, 0xb6, 0xcf, 0x37, + 0xcf, 0xb8, 0xd0, 0x39, 0xd0, 0xba, 0xd1, 0x3c, 0xd1, 0xbe, 0xd2, 0x3f, + 0xd2, 0xc1, 0xd3, 0x44, 0xd3, 0xc6, 0xd4, 0x49, 0xd4, 0xcb, 0xd5, 0x4e, + 0xd5, 0xd1, 0xd6, 0x55, 0xd6, 0xd8, 0xd7, 0x5c, 0xd7, 0xe0, 0xd8, 0x64, + 0xd8, 0xe8, 0xd9, 0x6c, 0xd9, 0xf1, 0xda, 0x76, 0xda, 0xfb, 0xdb, 0x80, + 0xdc, 0x05, 0xdc, 0x8a, 0xdd, 0x10, 0xdd, 0x96, 0xde, 0x1c, 0xde, 0xa2, + 0xdf, 0x29, 0xdf, 0xaf, 0xe0, 0x36, 0xe0, 0xbd, 0xe1, 0x44, 0xe1, 0xcc, + 0xe2, 0x53, 0xe2, 0xdb, 0xe3, 0x63, 0xe3, 0xeb, 0xe4, 0x73, 0xe4, 0xfc, + 0xe5, 0x84, 0xe6, 0x0d, 0xe6, 0x96, 0xe7, 0x1f, 0xe7, 0xa9, 0xe8, 0x32, + 0xe8, 0xbc, 0xe9, 0x46, 0xe9, 0xd0, 0xea, 0x5b, 0xea, 0xe5, 0xeb, 0x70, + 0xeb, 0xfb, 0xec, 0x86, 0xed, 0x11, 0xed, 0x9c, 0xee, 0x28, 0xee, 0xb4, + 0xef, 0x40, 0xef, 0xcc, 0xf0, 0x58, 0xf0, 0xe5, 0xf1, 0x72, 0xf1, 0xff, + 0xf2, 0x8c, 0xf3, 0x19, 0xf3, 0xa7, 0xf4, 0x34, 0xf4, 0xc2, 0xf5, 0x50, + 0xf5, 0xde, 0xf6, 0x6d, 0xf6, 0xfb, 0xf7, 0x8a, 0xf8, 0x19, 0xf8, 0xa8, + 0xf9, 0x38, 0xf9, 0xc7, 0xfa, 0x57, 0xfa, 0xe7, 0xfb, 0x77, 0xfc, 0x07, + 0xfc, 0x98, 0xfd, 0x29, 0xfd, 0xba, 0xfe, 0x4b, 0xfe, 0xdc, 0xff, 0x6d, + 0xff, 0xff, 0x70, 0x61, 0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x02, 0x66, 0x66, 0x00, 0x00, 0xf2, 0xa7, 0x00, 0x00, + 0x0d, 0x59, 0x00, 0x00, 0x13, 0xd0, 0x00, 0x00, 0x0a, 0x5b, 0x76, 0x63, + 0x67, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x6e, 0x64, + 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, + 0xb1, 0xc0, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x46, 0x40, 0x00, 0x00, + 0xa8, 0xc0, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x0e, 0x80, 0x00, 0x00, + 0x50, 0x40, 0x00, 0x00, 0x54, 0x40, 0x00, 0x02, 0x33, 0x33, 0x00, 0x02, + 0x33, 0x33, 0x00, 0x02, 0x33, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x6d, 0x6d, 0x6f, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4c, 0x2d, 0x00, 0x00, 0x70, 0x53, 0x43, 0x58, 0x44, 0x37, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x63, 0x67, 0x70, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x66, 0x66, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x02, 0x66, 0x66, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, + 0x66, 0x66, 0x00, 0x00, 0x00, 0x02, 0x33, 0x33, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x33, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x33, 0x33, + 0x00, 0x00, 0xff, 0xc0, 0x00, 0x11, 0x08, 0x00, 0x60, 0x00, 0x64, 0x03, + 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x00, + 0x1f, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x10, 0x00, + 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, + 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, + 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, + 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, + 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, + 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, + 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, + 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, + 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, + 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, + 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, + 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc4, 0x00, + 0x1f, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x11, 0x00, + 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, + 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, + 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, + 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, + 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, + 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, + 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, + 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, + 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xdb, 0x00, + 0x43, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x06, 0x04, 0x04, 0x06, + 0x09, 0x06, 0x06, 0x06, 0x09, 0x0c, 0x09, 0x09, 0x09, 0x09, 0x0c, 0x0f, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x12, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x19, 0x19, 0x19, 0x19, 0x19, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0xff, 0xdb, 0x00, 0x43, 0x01, 0x04, + 0x05, 0x05, 0x07, 0x07, 0x07, 0x0c, 0x07, 0x07, 0x0c, 0x1d, 0x14, 0x10, + 0x14, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0xff, 0xdd, 0x00, 0x04, 0x00, 0x07, 0xff, 0xda, 0x00, + 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0xfb, + 0xfa, 0x8a, 0x28, 0xa0, 0x02, 0x8a, 0xa1, 0xa9, 0xea, 0x9a, 0x7e, 0x8d, + 0x61, 0x36, 0xa7, 0xaa, 0x4e, 0x96, 0xd6, 0xb6, 0xea, 0x5e, 0x49, 0x1c, + 0xe0, 0x00, 0x2b, 0xe6, 0x2b, 0xef, 0x88, 0x7e, 0x3e, 0xf8, 0xaf, 0x34, + 0xf6, 0x1f, 0x0c, 0xa3, 0x1a, 0x36, 0x85, 0x01, 0x29, 0x3e, 0xb3, 0x74, + 0x36, 0xee, 0x1d, 0xfc, 0xa0, 0x7f, 0xfd, 0x7f, 0x4a, 0x69, 0x5c, 0x4d, + 0x9f, 0x43, 0x6b, 0xde, 0x32, 0xf0, 0xbf, 0x86, 0x21, 0x69, 0xf5, 0xdd, + 0x4a, 0x0b, 0x45, 0x51, 0x92, 0x1d, 0xc0, 0x3f, 0x97, 0x5a, 0xf2, 0x0b, + 0xef, 0xda, 0x5b, 0xe1, 0xe5, 0xbc, 0xa6, 0x3b, 0x11, 0x79, 0xa8, 0x01, + 0x9f, 0x9a, 0xde, 0x07, 0x65, 0xcf, 0xd7, 0x15, 0xf3, 0xf6, 0xa3, 0xa8, + 0x7c, 0x13, 0xf0, 0x25, 0xdb, 0xb6, 0xaf, 0x25, 0xcf, 0x8e, 0x7c, 0x40, + 0x3f, 0xd6, 0xcb, 0x2b, 0x16, 0x88, 0x49, 0xdf, 0xbe, 0x3a, 0xfb, 0x9a, + 0xa2, 0xdf, 0x1f, 0x7c, 0x62, 0xdf, 0xbb, 0xf0, 0x87, 0x85, 0xec, 0xf4, + 0xfb, 0x51, 0xf7, 0x40, 0x87, 0x27, 0xf3, 0x00, 0x0a, 0xd1, 0x40, 0x70, + 0x8c, 0xe6, 0xed, 0x05, 0x7f, 0x45, 0x73, 0xde, 0xa2, 0xfd, 0xa8, 0xbc, + 0x21, 0xb8, 0x8b, 0xad, 0x2b, 0x53, 0x81, 0x47, 0xf1, 0x34, 0x04, 0xff, + 0x00, 0x2a, 0xf4, 0x2f, 0x0d, 0x7c, 0x6d, 0xf8, 0x73, 0xe2, 0x89, 0x52, + 0xda, 0xc7, 0x55, 0x48, 0x6e, 0x1f, 0xa4, 0x53, 0xe6, 0x26, 0xcf, 0xa6, + 0x1b, 0x15, 0xf2, 0x12, 0x7c, 0x78, 0xf8, 0xb2, 0x87, 0x75, 0xee, 0x89, + 0x69, 0x73, 0x1f, 0x75, 0x30, 0xf6, 0xfd, 0x6a, 0x29, 0xbe, 0x27, 0xfc, + 0x32, 0xf1, 0x49, 0x36, 0xdf, 0x10, 0xbc, 0x1f, 0xfd, 0x9b, 0x3b, 0xff, + 0x00, 0xcb, 0xdd, 0x88, 0xd8, 0xea, 0x7d, 0x78, 0xc7, 0xf2, 0x35, 0x4e, + 0x99, 0x75, 0x29, 0x55, 0xa7, 0xad, 0x48, 0x35, 0xea, 0x8f, 0xd1, 0x78, + 0xe4, 0x8e, 0x54, 0x12, 0x44, 0xc1, 0xd5, 0xb9, 0x04, 0x1c, 0x83, 0x4f, + 0xaf, 0x8a, 0x7c, 0x3b, 0x71, 0xe2, 0xcf, 0x07, 0xd8, 0x9d, 0x7f, 0xe1, + 0x5e, 0xb4, 0x3c, 0x5f, 0xe1, 0xc8, 0xc0, 0x79, 0xf4, 0xd9, 0x8e, 0x6e, + 0x61, 0x5e, 0xfb, 0x47, 0x5e, 0x3d, 0xbf, 0x2a, 0xfa, 0x43, 0xe1, 0xef, + 0xc4, 0xef, 0x0d, 0xfc, 0x46, 0xb0, 0x37, 0x1a, 0x44, 0x86, 0x2b, 0xb8, + 0x78, 0x9e, 0xd2, 0x5e, 0x25, 0x88, 0xfb, 0x8f, 0x4f, 0x7a, 0xc9, 0xc6, + 0xc6, 0x69, 0xdc, 0xf4, 0x6a, 0x28, 0xa2, 0xa4, 0x61, 0x45, 0x14, 0x50, + 0x07, 0xff, 0xd0, 0xfb, 0xfa, 0xa1, 0xb8, 0xb8, 0x86, 0xd6, 0x09, 0x2e, + 0x6e, 0x5c, 0x47, 0x14, 0x4a, 0x5d, 0xdd, 0x8e, 0x02, 0xa8, 0x19, 0x24, + 0x9a, 0x9a, 0xbe, 0x6c, 0xf8, 0xe5, 0xae, 0x6a, 0x7a, 0xf6, 0xa3, 0xa5, + 0x7c, 0x1f, 0xf0, 0xc4, 0x85, 0x2f, 0xb5, 0xd6, 0x0f, 0x7b, 0x22, 0xff, + 0x00, 0xcb, 0x2b, 0x41, 0xf7, 0x89, 0xf6, 0x38, 0x24, 0xfb, 0x0c, 0x77, + 0xa6, 0x90, 0x99, 0xe7, 0x1e, 0x27, 0xf1, 0x22, 0x7c, 0x59, 0xd4, 0xaf, + 0xf5, 0xdd, 0x76, 0xea, 0x4b, 0x0f, 0x86, 0xbe, 0x1a, 0x90, 0x79, 0x85, + 0x72, 0x0d, 0xec, 0xa0, 0x81, 0xb4, 0x63, 0x93, 0x93, 0xc7, 0xb0, 0xf7, + 0x34, 0xbf, 0x1e, 0xbc, 0x6d, 0x6d, 0x07, 0xc2, 0xff, 0x00, 0x0c, 0xc1, + 0xf0, 0xf2, 0x41, 0x6b, 0xa0, 0x6b, 0x12, 0x3c, 0x6d, 0x25, 0xb8, 0xda, + 0x16, 0x38, 0x80, 0xc4, 0x7c, 0x72, 0x09, 0x39, 0xcf, 0x73, 0x8a, 0x5f, + 0x14, 0x7c, 0x55, 0xf8, 0x5d, 0xe1, 0x6d, 0x29, 0xfe, 0x11, 0x5b, 0x69, + 0x52, 0x6a, 0xba, 0x35, 0x9a, 0x08, 0x2e, 0xa4, 0x8b, 0x81, 0xbc, 0x1c, + 0xb3, 0x67, 0xbb, 0x6e, 0xe4, 0x9f, 0x5a, 0xf4, 0xad, 0x07, 0x49, 0xf8, + 0x5b, 0xf1, 0x5b, 0xe1, 0x92, 0x78, 0x2f, 0xc3, 0xe4, 0x43, 0x63, 0x6e, + 0xb9, 0x84, 0x0f, 0xf5, 0xb0, 0x4b, 0x92, 0x77, 0xf3, 0x82, 0x4e, 0x49, + 0xcf, 0xae, 0x6b, 0x4b, 0x5b, 0x52, 0x6d, 0xdc, 0xf8, 0x56, 0xc6, 0x0b, + 0x78, 0x62, 0xf3, 0xf4, 0x7b, 0x68, 0xe3, 0x81, 0x3a, 0xdf, 0xde, 0xf4, + 0x63, 0xdc, 0xa2, 0x9a, 0xb4, 0x6e, 0xd6, 0xe8, 0xec, 0x37, 0xb7, 0xfa, + 0x9b, 0x0e, 0xbf, 0x65, 0x4d, 0x91, 0x8f, 0xa1, 0x02, 0x9f, 0xe3, 0x0f, + 0x06, 0xf8, 0x83, 0xe1, 0x86, 0xba, 0x74, 0xaf, 0x14, 0xda, 0x1b, 0xdb, + 0x74, 0x0c, 0x2c, 0x1d, 0x98, 0xf9, 0x0e, 0x37, 0x70, 0xdc, 0x7e, 0xaa, + 0x79, 0x14, 0xc3, 0x71, 0x2d, 0xc3, 0x18, 0x9e, 0xfe, 0x79, 0xdd, 0x78, + 0x30, 0x69, 0xd1, 0xed, 0x8d, 0x3d, 0xb7, 0x77, 0xae, 0xa8, 0xbb, 0xec, + 0x7d, 0xa6, 0x0b, 0x11, 0x4e, 0xa5, 0x3b, 0x47, 0x4f, 0x2d, 0xbf, 0x0b, + 0xa5, 0xf8, 0x49, 0xf7, 0x18, 0x2d, 0xd6, 0x3c, 0xb9, 0xb4, 0xd5, 0xa1, + 0xff, 0x00, 0x69, 0x5f, 0x76, 0x3f, 0x0a, 0x70, 0xbb, 0x92, 0xeb, 0x16, + 0xf0, 0xea, 0x09, 0x76, 0x7a, 0x7d, 0x9b, 0x50, 0x8f, 0x63, 0x9f, 0x60, + 0xfc, 0x73, 0x43, 0x23, 0xc0, 0x37, 0xb4, 0x5a, 0xad, 0xb6, 0x3f, 0x8c, + 0x36, 0xff, 0x00, 0xcc, 0x55, 0x1b, 0xcd, 0x47, 0xce, 0x44, 0xb6, 0x95, + 0xe3, 0xd6, 0xd2, 0xe0, 0xf9, 0x68, 0xa5, 0x0c, 0x77, 0x28, 0xfd, 0xba, + 0x0c, 0x9e, 0x68, 0x3a, 0x6a, 0x4e, 0x34, 0xa2, 0xdc, 0x9d, 0x97, 0xdd, + 0xf9, 0x59, 0xff, 0x00, 0xe4, 0xb2, 0x3a, 0x8f, 0x87, 0x7a, 0xde, 0xa9, + 0xe1, 0x8f, 0x89, 0x7a, 0x17, 0xf6, 0x04, 0x13, 0x58, 0xdd, 0x5d, 0xdd, + 0x47, 0x6f, 0x75, 0x68, 0x49, 0x68, 0xe4, 0x8a, 0x46, 0x00, 0xe3, 0xd4, + 0x63, 0x24, 0x7a, 0x62, 0xbe, 0x90, 0xf1, 0xc5, 0xa7, 0x86, 0x75, 0x2f, + 0x88, 0x57, 0xa9, 0xf0, 0xaa, 0xf7, 0xfb, 0x37, 0xc7, 0xba, 0x4a, 0x34, + 0xb3, 0x43, 0x1a, 0xed, 0x8a, 0xec, 0x20, 0x05, 0xe2, 0x23, 0xee, 0x97, + 0xc6, 0x33, 0xeb, 0x56, 0x7e, 0x05, 0xfc, 0x17, 0x97, 0xc2, 0x82, 0x3f, + 0x88, 0x1e, 0x3e, 0x6c, 0x6a, 0x51, 0xc7, 0xfe, 0x8d, 0x0c, 0xa7, 0x3f, + 0x67, 0x4c, 0x7d, 0xf6, 0x3c, 0xfc, 0xf8, 0xe8, 0x3b, 0x7d, 0x6b, 0x9e, + 0xd7, 0xbe, 0x31, 0x7c, 0x2b, 0xf0, 0xcf, 0x8f, 0xee, 0xbc, 0x51, 0xe1, + 0xcd, 0x15, 0xb5, 0x2d, 0x66, 0x5d, 0xd1, 0xcf, 0x75, 0x11, 0x21, 0x7b, + 0x06, 0x20, 0x1e, 0x37, 0x10, 0x39, 0x22, 0xb9, 0xdf, 0xbc, 0xf4, 0x3e, + 0x26, 0xac, 0x95, 0x4a, 0x8d, 0xc2, 0x3b, 0xf4, 0xff, 0x00, 0x86, 0xfd, + 0x0f, 0xa2, 0x7e, 0x15, 0x7c, 0x4a, 0xb4, 0xf8, 0x87, 0xa2, 0x96, 0x99, + 0x7e, 0xcb, 0xac, 0x58, 0x9f, 0x2a, 0xfa, 0xd1, 0xb8, 0x68, 0xe4, 0x1c, + 0x13, 0x83, 0xce, 0xd3, 0x8f, 0xe9, 0x5e, 0xa9, 0x5f, 0x10, 0x6a, 0xbe, + 0x25, 0xd3, 0x63, 0xbf, 0xb2, 0xfd, 0xa0, 0x7e, 0x1e, 0x67, 0xec, 0xbe, + 0x6a, 0xdb, 0x78, 0x86, 0xc8, 0x0c, 0x15, 0x46, 0x20, 0x17, 0x65, 0xf5, + 0xf7, 0xf5, 0xc1, 0xf5, 0xaf, 0xb4, 0xb4, 0xdd, 0x46, 0xd3, 0x56, 0xd3, + 0xed, 0xb5, 0x4b, 0x09, 0x04, 0xb6, 0xd7, 0x51, 0xac, 0xb1, 0xb8, 0xe8, + 0x55, 0x86, 0x41, 0xac, 0xa4, 0xac, 0x64, 0x99, 0x76, 0x8a, 0x28, 0xa9, + 0x19, 0xff, 0xd1, 0xfb, 0xf5, 0x98, 0x2a, 0x96, 0x63, 0x80, 0x06, 0x4d, + 0x7c, 0x23, 0x07, 0x8b, 0x5e, 0x0b, 0x6f, 0x88, 0xdf, 0x1b, 0x66, 0x3f, + 0xbe, 0x9e, 0x43, 0xa3, 0x69, 0x05, 0xbb, 0x13, 0x85, 0xca, 0xfd, 0x06, + 0xd3, 0xf8, 0x1a, 0xfb, 0x2b, 0xc6, 0x77, 0xef, 0xa6, 0x78, 0x47, 0x59, + 0xd4, 0x23, 0xe1, 0xed, 0xec, 0xa7, 0x75, 0xfa, 0x84, 0x38, 0xaf, 0xcf, + 0x0f, 0x1a, 0xc6, 0xda, 0x7f, 0xc0, 0x3f, 0x01, 0xe9, 0x51, 0xe7, 0x3a, + 0xbd, 0xf5, 0xc5, 0xf4, 0xa3, 0xfb, 0xc7, 0x2c, 0x17, 0xf4, 0x61, 0x5a, + 0x41, 0x12, 0xd3, 0x6d, 0x24, 0x79, 0xf6, 0x85, 0x6c, 0x96, 0x5a, 0x41, + 0xbc, 0xbb, 0x1b, 0xa4, 0x98, 0x34, 0xd2, 0xb3, 0x72, 0x48, 0xeb, 0xce, + 0x7d, 0x7f, 0xad, 0x3b, 0xc2, 0x5a, 0xef, 0x88, 0xbc, 0x0f, 0x77, 0x1f, + 0x8a, 0xf4, 0x76, 0x2b, 0x1b, 0xb1, 0x79, 0xa0, 0x07, 0x01, 0xa3, 0xcf, + 0x19, 0x1f, 0xc8, 0xf6, 0xa9, 0xf5, 0xef, 0xdd, 0x69, 0x2d, 0x6f, 0x1f, + 0x1e, 0x61, 0x48, 0x47, 0xd0, 0x90, 0x29, 0xba, 0xf8, 0x31, 0x69, 0x06, + 0xd6, 0x1e, 0x0c, 0xad, 0x1c, 0x0b, 0xff, 0x00, 0x02, 0x20, 0x7f, 0x2a, + 0xfa, 0x2a, 0xb4, 0x63, 0xca, 0xe2, 0xf6, 0x8a, 0xfc, 0x7f, 0xa4, 0x7e, + 0xf3, 0x9c, 0x64, 0x98, 0x69, 0xe1, 0xe5, 0x84, 0x9e, 0x90, 0xc3, 0x51, + 0xba, 0xb6, 0xee, 0x72, 0xbf, 0xff, 0x00, 0x23, 0xff, 0x00, 0x93, 0x1f, + 0x62, 0xeb, 0x7f, 0x12, 0xfe, 0x1b, 0xfc, 0x42, 0xf8, 0x51, 0x77, 0xab, + 0x78, 0xa6, 0xdd, 0xee, 0x2d, 0xed, 0x8c, 0x71, 0x4c, 0x91, 0xae, 0x66, + 0x86, 0x57, 0x20, 0x2b, 0x2f, 0xa6, 0x0f, 0x7e, 0x86, 0xbc, 0x7f, 0x43, + 0xf8, 0x09, 0xab, 0xdf, 0x69, 0x50, 0xea, 0xba, 0x17, 0x89, 0xa1, 0xb7, + 0xf0, 0xd5, 0xe8, 0xfb, 0x4c, 0x37, 0x12, 0x0d, 0x92, 0x15, 0x6e, 0x30, + 0xc3, 0xd4, 0x63, 0x15, 0x5b, 0xe0, 0x7e, 0x97, 0x0d, 0xb7, 0xc5, 0x69, + 0xfc, 0x24, 0x22, 0x17, 0x5a, 0x46, 0xa5, 0xa7, 0xca, 0x2e, 0xe0, 0x90, + 0x6e, 0x5f, 0xdd, 0x80, 0x54, 0x91, 0xfe, 0xf7, 0x4f, 0xad, 0x45, 0xf1, + 0x2b, 0xc5, 0x97, 0x3e, 0x29, 0xd7, 0xa7, 0xd1, 0x6c, 0x1b, 0xec, 0x7e, + 0x1c, 0xd1, 0x64, 0x6b, 0x4b, 0x4b, 0x58, 0x4e, 0xd4, 0x93, 0xca, 0x3b, + 0x59, 0xdb, 0x1d, 0x46, 0x41, 0x00, 0x7b, 0x57, 0x8f, 0x4e, 0x84, 0xe5, + 0x53, 0xd9, 0xc7, 0x73, 0xf2, 0xcc, 0xbb, 0x22, 0xc7, 0x57, 0xcc, 0x1e, + 0x02, 0x93, 0xb4, 0xd6, 0xee, 0xfa, 0x25, 0xea, 0xbe, 0x47, 0x4b, 0xff, + 0x00, 0x0a, 0x03, 0x51, 0x99, 0x71, 0xe1, 0x7f, 0x19, 0xc1, 0x7f, 0x76, + 0xbf, 0xf2, 0xc9, 0x9b, 0xaf, 0xea, 0x6b, 0x67, 0xe1, 0xe7, 0x83, 0xfc, + 0x2b, 0xf0, 0xf3, 0xe2, 0x75, 0xb6, 0x8d, 0xe2, 0xf2, 0xda, 0xa7, 0x8a, + 0x2f, 0x6d, 0xde, 0xf0, 0x48, 0xb1, 0xff, 0x00, 0xa3, 0x5a, 0xa8, 0x52, + 0x73, 0xcf, 0xf1, 0x10, 0xa7, 0x9e, 0xd5, 0xe0, 0x31, 0x47, 0x71, 0x65, + 0x32, 0x5e, 0x69, 0x57, 0x12, 0x59, 0x5d, 0x44, 0x72, 0x92, 0xc4, 0xc4, + 0x10, 0x47, 0xaf, 0xad, 0x7d, 0x0f, 0xad, 0x78, 0xea, 0xe7, 0xc4, 0xbf, + 0x02, 0x75, 0x5f, 0x18, 0x2d, 0xbc, 0x71, 0xf8, 0x96, 0xc8, 0x26, 0x93, + 0x7b, 0x72, 0xaa, 0x03, 0x85, 0x32, 0x20, 0x66, 0x53, 0xd4, 0x07, 0x56, + 0x07, 0xf1, 0xad, 0x31, 0x18, 0x69, 0xd2, 0xb2, 0x93, 0xba, 0x67, 0x6f, + 0x11, 0xf0, 0xee, 0x37, 0x2a, 0x8c, 0x5d, 0x69, 0xf3, 0xc1, 0xec, 0xf5, + 0xb5, 0xfd, 0x1e, 0xcc, 0xe4, 0xbe, 0x34, 0xfc, 0x69, 0xb8, 0xf1, 0x75, + 0xfb, 0xf8, 0x2b, 0xc2, 0x53, 0xf9, 0x76, 0x85, 0x8a, 0x4f, 0x3a, 0x9c, + 0x6e, 0xdb, 0x9c, 0xaa, 0x9f, 0xc3, 0x93, 0x5e, 0x0f, 0xa6, 0xd9, 0x26, + 0x93, 0xa9, 0x3d, 0x98, 0xe6, 0x2b, 0x88, 0xc3, 0xc6, 0x4f, 0x5d, 0xcb, + 0xc3, 0x0f, 0xeb, 0x54, 0x12, 0xd6, 0xde, 0xd3, 0x4b, 0xb0, 0xbe, 0x80, + 0x61, 0xe3, 0x9d, 0x1d, 0x98, 0xf5, 0x3b, 0xce, 0xd6, 0x15, 0xb7, 0xac, + 0xb2, 0xc0, 0x6c, 0xee, 0xba, 0x18, 0xae, 0x54, 0x67, 0xd9, 0xf2, 0xa6, + 0xbd, 0x2c, 0x3d, 0x05, 0x4e, 0x3c, 0xcf, 0x75, 0x67, 0xf2, 0x3e, 0xe3, + 0x87, 0x32, 0x7a, 0x58, 0x0a, 0x0f, 0x19, 0x52, 0xce, 0xac, 0x3d, 0x9c, + 0xef, 0xd3, 0x92, 0x6b, 0x55, 0xf7, 0x73, 0x7c, 0xd2, 0x3b, 0xaf, 0x85, + 0x37, 0xd0, 0xe9, 0x7f, 0x10, 0x26, 0xf0, 0x8d, 0xf8, 0xce, 0x8d, 0xe3, + 0x4b, 0x67, 0xb1, 0x95, 0x0f, 0xdd, 0x12, 0xb2, 0x90, 0x8c, 0x07, 0xae, + 0xee, 0x3f, 0x1a, 0xfa, 0x87, 0xf6, 0x71, 0xd5, 0x6f, 0x61, 0xd0, 0x75, + 0x6f, 0x00, 0xea, 0xc4, 0x9b, 0xcf, 0x0a, 0x5e, 0xbd, 0xaf, 0xcd, 0xd7, + 0xca, 0x66, 0x25, 0x3f, 0x0c, 0x83, 0x8f, 0x6c, 0x57, 0xc5, 0x37, 0xb7, + 0x32, 0x69, 0xba, 0xae, 0x89, 0xac, 0x43, 0xc4, 0x96, 0x3a, 0x84, 0x12, + 0x03, 0xf4, 0x70, 0x7f, 0xa5, 0x7d, 0x9b, 0xe1, 0x12, 0xba, 0x47, 0xed, + 0x17, 0xe2, 0xbb, 0x08, 0xb0, 0xb1, 0x6b, 0x1a, 0x6d, 0xbd, 0xf6, 0x3d, + 0x5c, 0x05, 0x07, 0x1f, 0xad, 0x79, 0x98, 0xea, 0x6a, 0x15, 0x64, 0x97, + 0xa9, 0xf0, 0x5c, 0x5f, 0x81, 0x8e, 0x0f, 0x38, 0xad, 0x4e, 0x9a, 0xb4, + 0x5d, 0xa4, 0xbf, 0xed, 0xe5, 0x77, 0xf8, 0xdc, 0xfa, 0x5e, 0x8a, 0x28, + 0xaf, 0x38, 0xf9, 0x33, 0xff, 0xd2, 0xfb, 0x1f, 0xe2, 0x5c, 0x26, 0x7f, + 0x87, 0xde, 0x22, 0x89, 0x4e, 0x0b, 0x69, 0xf7, 0x1f, 0xfa, 0x01, 0x35, + 0xf0, 0x3f, 0xc4, 0x97, 0x12, 0x7c, 0x21, 0xf8, 0x5b, 0x7e, 0x9c, 0xc5, + 0x12, 0x4d, 0x13, 0x63, 0xb3, 0x29, 0x07, 0x1f, 0xa1, 0xaf, 0xd2, 0x3d, + 0x42, 0xce, 0x3d, 0x42, 0xc2, 0xe6, 0xc2, 0x51, 0x94, 0xb9, 0x89, 0xe2, + 0x6f, 0xa3, 0x82, 0x0f, 0xf3, 0xaf, 0xce, 0x09, 0xf4, 0xcb, 0xad, 0x57, + 0xe0, 0xa6, 0xbb, 0xe1, 0x29, 0xd4, 0x9d, 0x47, 0xc0, 0x9a, 0xb1, 0xb8, + 0x09, 0xfc, 0x42, 0xdd, 0x89, 0x49, 0x38, 0xf4, 0x04, 0x93, 0x5a, 0xc0, + 0x5c, 0xce, 0x32, 0x52, 0x47, 0x9e, 0x78, 0x90, 0x7f, 0xa0, 0xa4, 0xa7, + 0xa4, 0x53, 0xc6, 0xe7, 0xe9, 0x9a, 0x5f, 0x11, 0x10, 0xb6, 0x50, 0xdc, + 0x9f, 0xbb, 0x0d, 0xcc, 0x32, 0x1f, 0xa0, 0x61, 0x4a, 0x84, 0x6b, 0x3a, + 0x0e, 0x07, 0xde, 0x9a, 0x2c, 0x7f, 0xc0, 0xd7, 0x8f, 0xe6, 0x2b, 0x3d, + 0x6e, 0x66, 0xd6, 0x74, 0xd8, 0x34, 0x5b, 0x38, 0x1e, 0xeb, 0x52, 0xbb, + 0xc5, 0xba, 0xc0, 0x83, 0x2c, 0x5c, 0x1c, 0x67, 0x1f, 0xad, 0x7d, 0x2d, + 0x69, 0x46, 0xd2, 0x6d, 0xe9, 0x24, 0x7f, 0x45, 0x66, 0xf8, 0xdc, 0x3c, + 0x63, 0x89, 0xab, 0x39, 0xda, 0x15, 0xe8, 0xa9, 0x45, 0xbe, 0xae, 0x37, + 0xd3, 0xd7, 0xde, 0x8e, 0x87, 0xd0, 0x3f, 0x04, 0x65, 0x31, 0xfc, 0x71, + 0x29, 0x10, 0xf3, 0x05, 0xc6, 0x9d, 0x70, 0x1c, 0x8e, 0x76, 0x8c, 0x87, + 0x04, 0xfa, 0x67, 0xa5, 0x79, 0xbe, 0xb3, 0xa4, 0xdd, 0x78, 0x6b, 0x5f, + 0xd4, 0x7c, 0x33, 0xa9, 0x0d, 0xb7, 0x56, 0x33, 0xbe, 0x33, 0xff, 0x00, + 0x2d, 0x23, 0x76, 0x2c, 0x92, 0x0f, 0x50, 0xc0, 0xfe, 0x79, 0xaf, 0xa2, + 0x74, 0x0f, 0x83, 0x1e, 0x26, 0xf0, 0x4f, 0xc3, 0xfb, 0xcd, 0x43, 0x45, + 0xd4, 0x62, 0xb5, 0xf1, 0x86, 0xa4, 0x22, 0x37, 0x17, 0x52, 0x9f, 0x96, + 0x28, 0x41, 0x05, 0xa2, 0x52, 0x7a, 0x60, 0x75, 0x3d, 0xf1, 0x54, 0xbc, + 0x47, 0xab, 0xfc, 0x1e, 0xf1, 0x67, 0xd8, 0x34, 0x9f, 0x17, 0xea, 0xc2, + 0x6d, 0x7f, 0x4d, 0xb7, 0x58, 0x24, 0xd4, 0xe0, 0xf9, 0x37, 0xb8, 0x1f, + 0x37, 0x23, 0x82, 0x33, 0xda, 0xbc, 0x8a, 0x58, 0xae, 0x4a, 0xce, 0xa2, + 0x5a, 0x33, 0xf3, 0x0c, 0xb3, 0x8a, 0xfe, 0xa9, 0x9b, 0x54, 0xc7, 0xb8, + 0x7b, 0xb5, 0x34, 0x6b, 0xad, 0xb4, 0xfc, 0x74, 0xb9, 0xf3, 0xab, 0x32, + 0xa8, 0x2c, 0xc7, 0x00, 0x72, 0x49, 0xaf, 0x59, 0xb6, 0xd3, 0xaf, 0x74, + 0xcf, 0xd9, 0xb7, 0xc5, 0x3a, 0xcc, 0xf1, 0x15, 0x4d, 0x72, 0xf6, 0x39, + 0xa2, 0x18, 0xe7, 0xc9, 0x57, 0x8a, 0x25, 0x73, 0xec, 0xdb, 0x33, 0xf4, + 0xad, 0x53, 0xe1, 0x6f, 0x82, 0x1a, 0x4a, 0x7d, 0xb7, 0x58, 0xf1, 0x13, + 0xea, 0x70, 0x20, 0xcf, 0x90, 0x18, 0x0d, 0xc7, 0xd0, 0xed, 0xe4, 0xfd, + 0x2b, 0xb7, 0xd2, 0x1e, 0x5f, 0x88, 0xbe, 0x28, 0x86, 0x3f, 0x0f, 0xea, + 0x96, 0xd3, 0xf8, 0x0e, 0xe2, 0xc3, 0xec, 0x77, 0x7a, 0x59, 0xc0, 0x68, + 0xf6, 0xa1, 0x50, 0x14, 0x76, 0x3b, 0xb0, 0x41, 0xad, 0x71, 0x78, 0xb5, + 0x55, 0x25, 0x15, 0xa2, 0x3b, 0xf8, 0xbf, 0x8b, 0x68, 0xe6, 0xb4, 0xa1, + 0x86, 0xc2, 0xc5, 0xa8, 0xa7, 0x77, 0x7d, 0xef, 0xb2, 0xfc, 0xd9, 0xf1, + 0x95, 0xc4, 0x49, 0x1e, 0x8b, 0x69, 0x6a, 0x8d, 0xbf, 0xce, 0x96, 0x20, + 0x08, 0xee, 0x4b, 0x6e, 0x35, 0x6f, 0xc4, 0x4a, 0x64, 0xb6, 0xb7, 0x89, + 0x7e, 0xf3, 0xdd, 0x44, 0x07, 0xe0, 0x73, 0x5d, 0x1f, 0xc4, 0x6f, 0x87, + 0x3a, 0xbf, 0xc2, 0xaf, 0x12, 0xc3, 0x0d, 0xf8, 0x92, 0xeb, 0x41, 0x32, + 0x33, 0xd9, 0xdc, 0x01, 0x90, 0x37, 0x70, 0x15, 0xcf, 0x66, 0x5f, 0xd6, + 0xb9, 0xa7, 0x95, 0x35, 0x0d, 0x6a, 0x08, 0xa3, 0x3b, 0xe2, 0xb2, 0x8c, + 0xcc, 0xc4, 0x74, 0xde, 0xfc, 0x28, 0xfc, 0x06, 0x4d, 0x77, 0x52, 0xa9, + 0x1a, 0xb4, 0xdd, 0xba, 0xd9, 0x1f, 0x5f, 0x94, 0x66, 0x18, 0x7c, 0xc3, + 0x05, 0x52, 0x14, 0x7d, 0xd9, 0x49, 0x53, 0xa4, 0xa3, 0x7d, 0x52, 0x4b, + 0x57, 0xe9, 0xac, 0x9a, 0xf2, 0x43, 0xb5, 0xf0, 0x64, 0xfb, 0x05, 0xb2, + 0x0c, 0xbc, 0xf7, 0xb0, 0xaa, 0xfd, 0x77, 0x7f, 0xf5, 0xeb, 0xed, 0x0d, + 0x2a, 0x23, 0x71, 0xfb, 0x4e, 0x5e, 0x34, 0x67, 0xfe, 0x3c, 0x34, 0x08, + 0x92, 0x4f, 0xab, 0x63, 0x1f, 0xce, 0xbe, 0x5a, 0xf0, 0x4e, 0x91, 0xff, + 0x00, 0x09, 0x57, 0xc5, 0x5f, 0x0f, 0x68, 0xf8, 0xdd, 0x6d, 0x61, 0x30, + 0xbe, 0xb9, 0x3d, 0x95, 0x21, 0xfd, 0xe1, 0xcf, 0xa0, 0xe0, 0x0f, 0xc6, + 0xbe, 0xa4, 0xf8, 0x21, 0x20, 0xf1, 0x57, 0x8f, 0xbc, 0x79, 0xf1, 0x0f, + 0x69, 0x30, 0x5c, 0xdd, 0x25, 0x95, 0xab, 0x9e, 0x86, 0x38, 0x41, 0x07, + 0x1f, 0x80, 0x5a, 0xf3, 0x31, 0xf3, 0x52, 0xab, 0x2b, 0x74, 0x3f, 0x3b, + 0xe3, 0x7c, 0x5c, 0x71, 0x19, 0xd5, 0x5e, 0x47, 0xa4, 0x6d, 0x1f, 0xb9, + 0x6b, 0xf8, 0xb6, 0x7d, 0x3f, 0x45, 0x14, 0x57, 0x98, 0x7c, 0x59, 0xff, + 0xd3, 0xfb, 0xfa, 0xbe, 0x45, 0xf8, 0x97, 0x67, 0x0f, 0xc3, 0x2f, 0x89, + 0xd6, 0xfe, 0x3a, 0x78, 0xb7, 0xf8, 0x77, 0xc5, 0x68, 0x74, 0xfd, 0x5e, + 0x3c, 0x7c, 0xa1, 0xd8, 0x63, 0x71, 0xff, 0x00, 0x78, 0x73, 0xf8, 0x1a, + 0xfa, 0xea, 0xb9, 0x5f, 0x1a, 0xf8, 0x47, 0x4b, 0xf1, 0xcf, 0x86, 0xaf, + 0x7c, 0x35, 0xab, 0x20, 0x68, 0x6e, 0xd0, 0x85, 0x6e, 0xe8, 0xe3, 0xee, + 0xba, 0xfb, 0xa9, 0xaa, 0x8b, 0xb3, 0x13, 0x57, 0x3e, 0x11, 0xd4, 0x3e, + 0x05, 0xf8, 0xe7, 0x4b, 0xd4, 0xa4, 0x4f, 0x04, 0x08, 0x75, 0x1d, 0x06, + 0xf5, 0xbc, 0xdb, 0x5b, 0x87, 0x70, 0xbe, 0x4a, 0x37, 0x67, 0xcf, 0xf7, + 0x7a, 0x67, 0xbd, 0x7d, 0x17, 0xf0, 0xbb, 0xe1, 0x2f, 0x87, 0xfe, 0x14, + 0xe9, 0xb2, 0xf8, 0xa7, 0xc4, 0x37, 0x11, 0x5e, 0x6b, 0x12, 0x29, 0x69, + 0x2e, 0x8f, 0xdc, 0x88, 0x1e, 0x4a, 0xc7, 0x9e, 0x07, 0xbb, 0x57, 0x8c, + 0xda, 0x5f, 0x78, 0x9b, 0x4e, 0xd1, 0xb5, 0x3f, 0x80, 0xbe, 0x2a, 0xd4, + 0x5f, 0x47, 0xd5, 0x1c, 0xa0, 0xd2, 0xf5, 0x13, 0x9f, 0x2e, 0x74, 0x56, + 0xc8, 0x42, 0xc3, 0x9c, 0x32, 0xf1, 0xfa, 0x75, 0xa7, 0x7c, 0x6d, 0xb3, + 0xf1, 0x2f, 0x81, 0xbe, 0x15, 0x78, 0x4b, 0xc1, 0xf7, 0x3a, 0x93, 0xde, + 0xad, 0xc4, 0xb2, 0xa5, 0xe5, 0xde, 0x4e, 0x1b, 0xa3, 0x22, 0x67, 0xae, + 0xd0, 0x09, 0xc6, 0x7a, 0x81, 0x5d, 0x12, 0x9c, 0xa4, 0x94, 0x5b, 0xd0, + 0xe9, 0xab, 0x8d, 0xc4, 0x56, 0xa5, 0x4e, 0x85, 0x49, 0xb7, 0x18, 0x7c, + 0x29, 0xbd, 0x15, 0xfb, 0x1c, 0x6f, 0xc5, 0xdf, 0x8d, 0x9a, 0xb7, 0x8e, + 0x75, 0x09, 0x74, 0x6d, 0x0e, 0x77, 0xb2, 0xd0, 0x62, 0x7f, 0x2a, 0x59, + 0xd0, 0x13, 0xbf, 0x27, 0x82, 0x48, 0xe4, 0x2f, 0x1f, 0x8d, 0x79, 0x15, + 0xb6, 0x8f, 0x1b, 0x59, 0xa2, 0x4d, 0x63, 0xf6, 0xc5, 0x5c, 0xe2, 0xea, + 0xce, 0x4c, 0x96, 0x07, 0xfb, 0xca, 0x7b, 0xd4, 0xb0, 0x40, 0x34, 0x88, + 0x91, 0x26, 0x63, 0x61, 0x23, 0x0d, 0xa6, 0x60, 0x3c, 0xdb, 0x5b, 0x85, + 0x1d, 0x0b, 0x63, 0x38, 0x35, 0x6e, 0xde, 0xc9, 0xdf, 0x33, 0xc7, 0x60, + 0x08, 0x3c, 0xf9, 0xba, 0x7c, 0xdb, 0x73, 0xef, 0xb3, 0x35, 0xa4, 0x62, + 0x91, 0xee, 0x61, 0x72, 0xf5, 0x05, 0xcb, 0x38, 0xde, 0x4f, 0x7d, 0x2f, + 0xf8, 0x59, 0xfe, 0x4b, 0xc9, 0xd8, 0xa1, 0x1e, 0x8d, 0xa3, 0xee, 0xc1, + 0xb4, 0xd4, 0x24, 0x3f, 0xdc, 0xd8, 0x07, 0xeb, 0x57, 0xac, 0xf5, 0x1d, + 0x77, 0xc1, 0x1a, 0x97, 0xfc, 0x24, 0xda, 0x29, 0x5d, 0x28, 0x29, 0x40, + 0xb6, 0xa6, 0x4d, 0xed, 0x2f, 0x38, 0x20, 0xaf, 0xbf, 0xe9, 0x52, 0xfd, + 0x9e, 0xe2, 0x5c, 0xaf, 0x95, 0xaa, 0xbe, 0x3f, 0x84, 0xb8, 0x51, 0xf9, + 0xe6, 0xa2, 0x7f, 0x2e, 0xcb, 0x0f, 0x2c, 0x56, 0xda, 0x73, 0x0e, 0x92, + 0x4c, 0xff, 0x00, 0x68, 0x9f, 0x3e, 0xca, 0x33, 0xcd, 0x57, 0xa9, 0xbd, + 0x4c, 0x0d, 0x27, 0x4d, 0xc5, 0xc2, 0xde, 0x76, 0xb7, 0xe3, 0x6f, 0xce, + 0x49, 0x1f, 0x75, 0xf8, 0x0f, 0xc7, 0x5e, 0x15, 0xf8, 0xd7, 0xe1, 0x89, + 0x74, 0x2f, 0x10, 0xdb, 0xa4, 0x92, 0x3a, 0xec, 0x9e, 0xde, 0x5f, 0xbc, + 0x1b, 0x1d, 0x8f, 0x5c, 0xff, 0x00, 0x75, 0x85, 0x7c, 0xf1, 0xe2, 0x1f, + 0x80, 0x1e, 0x38, 0xf0, 0xa6, 0xb3, 0x3d, 0xbf, 0x83, 0xd6, 0x2d, 0x67, + 0x4e, 0x9d, 0xb7, 0xa2, 0xb3, 0x84, 0x9a, 0x3f, 0x40, 0xf9, 0xeb, 0x8e, + 0x99, 0xaf, 0x2b, 0xf0, 0x45, 0xc6, 0xa5, 0xa4, 0x7c, 0x42, 0xd0, 0x25, + 0xd0, 0x7c, 0xf5, 0x3a, 0x8d, 0xd4, 0x71, 0x32, 0xc9, 0xc3, 0xcc, 0x19, + 0xc6, 0xf6, 0x2a, 0x3a, 0x2f, 0x39, 0x1f, 0x4a, 0xfa, 0xc3, 0x5c, 0xd3, + 0x2e, 0xbe, 0x16, 0xfc, 0x44, 0xd6, 0x3e, 0x28, 0x78, 0x8b, 0xc4, 0x66, + 0x5d, 0x32, 0x74, 0x93, 0xec, 0xfa, 0x7e, 0x49, 0x91, 0xde, 0x40, 0x36, + 0x47, 0xb7, 0x38, 0xc2, 0x91, 0xc7, 0x15, 0x94, 0x65, 0x2a, 0x73, 0xbc, + 0x1e, 0xa7, 0x85, 0x43, 0x13, 0x89, 0xcb, 0xf1, 0x3e, 0xd2, 0x8c, 0xf9, + 0x67, 0x1e, 0xab, 0xcf, 0xfe, 0x01, 0xe5, 0xbf, 0xf0, 0x8e, 0x6a, 0x1f, + 0x08, 0x7c, 0x29, 0x79, 0x6b, 0x72, 0xcb, 0x73, 0xe3, 0xaf, 0x1a, 0x11, + 0x65, 0x0c, 0x70, 0x9d, 0xc6, 0xde, 0x17, 0x38, 0x20, 0x1f, 0x53, 0x9e, + 0x4f, 0xd2, 0xbe, 0xc5, 0xf8, 0x65, 0xe0, 0xb8, 0x3c, 0x03, 0xe0, 0xbd, + 0x3b, 0xc3, 0x91, 0xe0, 0xcb, 0x0a, 0x6f, 0xb8, 0x71, 0xfc, 0x73, 0x3f, + 0x2e, 0x7f, 0x3e, 0x07, 0xb0, 0xaf, 0x1a, 0xf8, 0x4b, 0xe0, 0xcd, 0x6f, + 0xc5, 0x9e, 0x22, 0x93, 0xe2, 0xef, 0x8f, 0x62, 0x29, 0x3c, 0xa3, 0xfe, + 0x25, 0x56, 0x8f, 0xcf, 0x93, 0x11, 0xe9, 0x21, 0x07, 0xa3, 0x11, 0xd3, + 0xf3, 0xaf, 0xa8, 0xeb, 0x09, 0xca, 0xec, 0xe1, 0x72, 0x94, 0xe4, 0xe7, + 0x37, 0x76, 0xc2, 0x8a, 0x28, 0xac, 0xc0, 0xff, 0xd4, 0xfb, 0xfa, 0x8a, + 0x28, 0xa0, 0x0f, 0x39, 0xf8, 0x8f, 0xf0, 0xd3, 0x41, 0xf8, 0x8f, 0xa5, + 0x0b, 0x3d, 0x49, 0x4c, 0x37, 0x90, 0x7c, 0xd6, 0xd7, 0x71, 0xf1, 0x24, + 0x4f, 0xd8, 0x83, 0xe9, 0x9e, 0xa2, 0xbe, 0x64, 0xd5, 0xb5, 0x0d, 0x77, + 0xc2, 0x36, 0x12, 0x78, 0x0b, 0xe3, 0x5e, 0x9a, 0xda, 0xbf, 0x87, 0x65, + 0xc2, 0xc1, 0xa9, 0xc2, 0x0b, 0x15, 0xdb, 0xf7, 0x58, 0x9e, 0xaa, 0xc3, + 0xd7, 0xaf, 0xd6, 0xbe, 0xe1, 0xaa, 0xb7, 0x96, 0x56, 0x9a, 0x85, 0xbb, + 0xda, 0xdf, 0x42, 0x93, 0xc2, 0xe3, 0x0c, 0x8e, 0x03, 0x02, 0x3e, 0x86, + 0xa9, 0x4a, 0xc4, 0xb5, 0x73, 0xf3, 0xd2, 0x7f, 0x83, 0x9a, 0x8c, 0x96, + 0xdf, 0xda, 0x7f, 0x0a, 0x35, 0xd8, 0x75, 0xed, 0x36, 0x41, 0xbc, 0x59, + 0xdc, 0x32, 0xf9, 0x80, 0x1f, 0xe1, 0xe7, 0x82, 0x47, 0xb8, 0x06, 0xbc, + 0xb3, 0x53, 0xd0, 0xb5, 0xbd, 0x1a, 0x72, 0x9a, 0xff, 0x00, 0x85, 0x2e, + 0xec, 0xa5, 0xee, 0xf6, 0xe1, 0xd5, 0x4f, 0xd3, 0x6e, 0x56, 0xbe, 0xe1, + 0xd7, 0xbf, 0x67, 0x5f, 0x0d, 0x5c, 0x5d, 0x3e, 0xa5, 0xe1, 0x2b, 0xdb, + 0x9f, 0x0e, 0xde, 0x31, 0xdd, 0x9b, 0x57, 0x3e, 0x59, 0x3f, 0xee, 0x74, + 0xfc, 0xab, 0x09, 0x7c, 0x07, 0xf1, 0xf7, 0x47, 0x1e, 0x4e, 0x9d, 0xe2, + 0x7b, 0x3d, 0x46, 0x15, 0x1f, 0x2f, 0xda, 0xa3, 0xf9, 0xbe, 0x9d, 0x0d, + 0x6c, 0xaa, 0x1d, 0x74, 0xf1, 0x95, 0xe0, 0x94, 0x6f, 0x74, 0xba, 0x34, + 0x9a, 0xf9, 0x5f, 0x6f, 0x91, 0xf1, 0x4a, 0xc2, 0xb7, 0x6e, 0x23, 0x8b, + 0x4a, 0xd5, 0xe7, 0x63, 0xd1, 0x01, 0x73, 0xfc, 0x96, 0xbd, 0x0f, 0xc3, + 0x9f, 0x0b, 0xbe, 0x20, 0x6a, 0xe0, 0x3e, 0x93, 0xe1, 0xf8, 0xb4, 0x48, + 0x4f, 0xde, 0xbc, 0xd4, 0x0e, 0x19, 0x47, 0xaf, 0xcd, 0x93, 0xfa, 0x0a, + 0xfa, 0x45, 0x74, 0x2f, 0xda, 0x3e, 0xe8, 0x18, 0x5a, 0xef, 0x49, 0xb3, + 0xff, 0x00, 0xa6, 0x88, 0x99, 0x3f, 0xfa, 0x0d, 0x49, 0x17, 0xc0, 0xef, + 0x1a, 0xf8, 0x89, 0xd5, 0xfc, 0x7f, 0xe3, 0x0b, 0x8b, 0x88, 0x8f, 0xde, + 0xb7, 0xb4, 0xfd, 0xda, 0x9f, 0x51, 0x9f, 0x4f, 0xc2, 0x9b, 0xa8, 0x6b, + 0xfd, 0xa3, 0x5f, 0xec, 0xd9, 0x3e, 0xe9, 0x24, 0xfe, 0xfd, 0xcf, 0x28, + 0xd2, 0x21, 0xf0, 0x57, 0xc2, 0x9d, 0x4d, 0x6e, 0xec, 0x6e, 0xa4, 0xf1, + 0x97, 0x8d, 0x65, 0x05, 0x22, 0xf2, 0x86, 0xf4, 0x85, 0x9b, 0x83, 0xb0, + 0x0c, 0xe0, 0xf6, 0xc9, 0xc9, 0xc5, 0x7a, 0xe7, 0x83, 0xfe, 0x14, 0x6b, + 0xfe, 0x2b, 0xd6, 0xa1, 0xf1, 0xd7, 0xc5, 0x96, 0x12, 0xcf, 0x19, 0xdf, + 0x6b, 0xa6, 0x03, 0x98, 0xa2, 0xee, 0x19, 0xfa, 0xe5, 0xbd, 0xab, 0xd8, + 0xbc, 0x1f, 0xf0, 0xdb, 0xc1, 0xfe, 0x07, 0x87, 0x66, 0x81, 0x60, 0x91, + 0x4a, 0xdf, 0x7a, 0x66, 0xf9, 0xa5, 0x6f, 0xab, 0x1e, 0x6b, 0xbb, 0xac, + 0x9c, 0xfb, 0x1e, 0x7d, 0x9b, 0x7c, 0xd2, 0x77, 0x62, 0x2a, 0xaa, 0x28, + 0x55, 0x00, 0x00, 0x30, 0x00, 0xed, 0x4b, 0x45, 0x15, 0x99, 0x41, 0x45, + 0x14, 0x50, 0x07, 0xff, 0xd9, +}; +static const size_t OPENSPOOL_LOGO_PNG_SIZE = 7721; diff --git a/src/OpenSpoolParser.cpp b/src/OpenSpoolParser.cpp new file mode 100644 index 0000000..f7c39d8 --- /dev/null +++ b/src/OpenSpoolParser.cpp @@ -0,0 +1,31 @@ +#include "OpenSpoolParser.h" +#include +#include + +bool parseOpenSpool(const uint8_t* json, size_t len, OpenSpoolData& out) { + memset(&out, 0, sizeof(out)); + out.valid = false; + + if (!json || len == 0) return false; + + StaticJsonDocument<256> doc; + DeserializationError err = deserializeJson(doc, json, len); + if (err) return false; + + const char* protocol = doc["protocol"] | ""; + if (strcmp(protocol, "openspool") != 0) return false; + + strncpy(out.protocol, protocol, sizeof(out.protocol) - 1); + strncpy(out.version, doc["version"] | "1.0", sizeof(out.version) - 1); + strncpy(out.material, doc["type"] | "", sizeof(out.material) - 1); + strncpy(out.color_hex, doc["color_hex"] | "", sizeof(out.color_hex) - 1); + strncpy(out.brand, doc["brand"] | "", sizeof(out.brand) - 1); + + const char* minT = doc["min_temp"] | "0"; + const char* maxT = doc["max_temp"] | "0"; + out.min_temp = (int16_t)atoi(minT); + out.max_temp = (int16_t)atoi(maxT); + + out.valid = true; + return true; +} diff --git a/src/OpenSpoolParser.h b/src/OpenSpoolParser.h new file mode 100644 index 0000000..d6be626 --- /dev/null +++ b/src/OpenSpoolParser.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +struct OpenSpoolData { + char protocol[16]; + char version[8]; + char material[32]; + char color_hex[8]; // "FFAABB" (no #) + char brand[64]; + int16_t min_temp; + int16_t max_temp; + bool valid; +}; + +bool parseOpenSpool(const uint8_t* json, size_t len, OpenSpoolData& out); diff --git a/src/OpenSpoolWriterHTML.h b/src/OpenSpoolWriterHTML.h new file mode 100644 index 0000000..8a8b880 --- /dev/null +++ b/src/OpenSpoolWriterHTML.h @@ -0,0 +1,304 @@ +#pragma once + +// OpenSpool writer page served at GET /writer/openspool +// Writes NDEF-wrapped OpenSpool JSON payload to NTAG215/216 via POST /api/write-openspool. + +const char OPENSPOOL_WRITER_HTML[] PROGMEM = R"rawliteral( + + + + + + OpenSpool Writer — SpoolSense + + + +
+ + +
+
+
+

Create OpenSpool Tag

+

Write filament data to an NTAG215/216 tag in OpenSpool format.

+
+
+ +
+
+
+
+

Filament

+
+
+ + +
+
+ + +
+
+
+ +
+

Color

+
+
+ +
+ + +
+
+
+
+ +
+

Temperature

+
+
+ + +
+
+ + +
+
+
+ +
Keep the tag still — do not remove until writing is complete.
+ +
+ + +
+
+
+
+ + + + +
+ + + + + +)rawliteral"; diff --git a/src/OpenTag3DWriterHTML.h b/src/OpenTag3DWriterHTML.h index dc5a72b..4137eb2 100644 --- a/src/OpenTag3DWriterHTML.h +++ b/src/OpenTag3DWriterHTML.h @@ -21,6 +21,7 @@ const char OPENTAG3D_WRITER_HTML[] PROGMEM = R"rawliteral( OpenPrintTag TigerTag OpenTag3D + OpenSpool NFC+ Update Troubleshooting diff --git a/src/ReaderHTML.h b/src/ReaderHTML.h index 41b45a2..799138a 100644 --- a/src/ReaderHTML.h +++ b/src/ReaderHTML.h @@ -21,6 +21,7 @@ const char READER_HTML[] PROGMEM = R"rawliteral( OpenPrintTag TigerTag OpenTag3D + OpenSpool NFC+ Update Troubleshooting @@ -143,6 +144,20 @@ const char READER_HTML[] PROGMEM = R"rawliteral( return html; } + function renderOpenSpool(s) { + var t = s.openspool || {}; + var html = ''; + html += row('Format', 'OpenSpool v' + (t.version || '1.0')); + html += row('UID', s.uid || '—'); + if (t.brand) html += row('Brand', t.brand); + if (t.material) html += row('Material', t.material); + if (t.color_hex) html += row('Color', colorValue(t.color_hex)); + if (t.min_temp > 0 && t.max_temp > 0) html += row('Nozzle Temp', t.min_temp + ' \u2013 ' + t.max_temp + ' \u00B0C'); + else if (t.min_temp > 0) html += row('Nozzle Temp', t.min_temp + ' \u00B0C'); + if (s.spoolman_id > 0) html += row('Spoolman ID', '' + s.spoolman_id + ''); + return html; + } + function renderGenericUid(s) { var html = ''; html += row('Format', tagKindLabel(s.tag_kind)); @@ -282,6 +297,8 @@ const char READER_HTML[] PROGMEM = R"rawliteral( html = renderTigerTag(s); } else if (kind === 'OpenTag3D' && s.opentag3d) { html = renderOpenTag3D(s); + } else if (kind === 'OpenSpoolTag' && s.openspool) { + html = renderOpenSpool(s); } else if (kind === 'OpenPrintTag' || (s.tag_data_valid && !kind)) { html = renderOpenPrintTag(s); } else { diff --git a/src/TFTManager.cpp b/src/TFTManager.cpp index f9d29dc..7538ca6 100644 --- a/src/TFTManager.cpp +++ b/src/TFTManager.cpp @@ -558,6 +558,10 @@ void TFTManager::drawTagIcon(uint8_t tagType, int x, int y) { label = "NFC+"; color = 0x00BCD4; break; + case TAG_TYPE_OPENSPOOL: + label = "OS"; + color = 0xE91E63; + break; default: return; } diff --git a/src/TFTManager.h b/src/TFTManager.h index c133d9f..51dafdd 100644 --- a/src/TFTManager.h +++ b/src/TFTManager.h @@ -13,6 +13,7 @@ #define TAG_TYPE_OPENTAG3D 3 #define TAG_TYPE_BAMBU 4 #define TAG_TYPE_NFC_PLAIN 5 +#define TAG_TYPE_OPENSPOOL 6 // --------------------------------------------------------------------------- // Display states diff --git a/src/TigerTagWriterHTML.h b/src/TigerTagWriterHTML.h index 163f15f..b9401c3 100644 --- a/src/TigerTagWriterHTML.h +++ b/src/TigerTagWriterHTML.h @@ -21,6 +21,7 @@ const char TIGERTAG_WRITER_HTML[] PROGMEM = R"rawliteral( OpenPrintTag TigerTag OpenTag3D + OpenSpool NFC+ Update Troubleshooting diff --git a/src/TroubleshootingHTML.h b/src/TroubleshootingHTML.h index 3c33fb3..8a2d844 100644 --- a/src/TroubleshootingHTML.h +++ b/src/TroubleshootingHTML.h @@ -67,6 +67,7 @@ const char TROUBLESHOOTING_HTML[] PROGMEM = R"rawliteral( OpenPrintTag TigerTag OpenTag3D + OpenSpool NFC+ Update Troubleshooting diff --git a/src/UIDRegistrationHTML.h b/src/UIDRegistrationHTML.h index 5b3a01e..e03b9fb 100644 --- a/src/UIDRegistrationHTML.h +++ b/src/UIDRegistrationHTML.h @@ -24,6 +24,7 @@ const char UID_REGISTRATION_HTML[] PROGMEM = R"rawliteral( OpenPrintTag TigerTag OpenTag3D + OpenSpool NFC+ Update Troubleshooting diff --git a/src/UpdateHTML.h b/src/UpdateHTML.h index 42a8ba8..f13bfcc 100644 --- a/src/UpdateHTML.h +++ b/src/UpdateHTML.h @@ -85,6 +85,7 @@ const char UPDATE_HTML[] PROGMEM = R"rawliteral( OpenPrintTag TigerTag OpenTag3D + OpenSpool NFC+ Update Troubleshooting diff --git a/src/WebServerManager.cpp b/src/WebServerManager.cpp index 362fe78..86c7529 100644 --- a/src/WebServerManager.cpp +++ b/src/WebServerManager.cpp @@ -22,6 +22,8 @@ #include "OpenPrintTagLogo.h" #include "TigerTagLogo.h" #include "OpenTag3DLogo.h" +#include "OpenSpoolLogo.h" +#include "OpenSpoolWriterHTML.h" #include "UpdateHTML.h" #include "ConfigurationManager.h" #include "NFCManager.h" @@ -65,6 +67,7 @@ static const char* tagKindToString(TagKind kind) { case TagKind::TigerTag: return "TigerTag"; case TagKind::OpenTag3D: return "OpenTag3D"; case TagKind::BambuTag: return "BambuTag"; + case TagKind::OpenSpoolTag: return "OpenSpoolTag"; case TagKind::BlankTag: return "BlankTag"; default: return "Unsupported"; } @@ -95,6 +98,7 @@ bool WebServerManager::begin(bool apMode, uint16_t port) { _server.on("/writer/openprinttag", HTTP_GET, [this]() { handleOpenPrintTagWriter(); }); _server.on("/writer/tigertag", HTTP_GET, [this]() { handleTigerTagWriter(); }); _server.on("/writer/opentag3d", HTTP_GET, [this]() { handleOpenTag3DWriter(); }); + _server.on("/writer/openspool", HTTP_GET, [this]() { handleOpenSpoolWriter(); }); // Static assets _server.on("/css/shared.css", HTTP_GET, [this]() { handleSharedCSS(); }); @@ -102,6 +106,7 @@ bool WebServerManager::begin(bool apMode, uint16_t port) { _server.on("/img/openprinttag.png", HTTP_GET, [this]() { handleOpenPrintTagLogo(); }); _server.on("/img/tigertag.png", HTTP_GET, [this]() { handleTigerTagLogo(); }); _server.on("/img/opentag3d.png", HTTP_GET, [this]() { handleOpenTag3DLogo(); }); + _server.on("/img/openspool.png", HTTP_GET, [this]() { handleOpenSpoolLogo(); }); // Update page _server.on("/update", HTTP_GET, [this]() { handleUpdatePage(); }); @@ -124,6 +129,7 @@ bool WebServerManager::begin(bool apMode, uint16_t port) { _server.on("/api/format-tag", HTTP_POST, [this]() { handleApiFormatTag(); }); _server.on("/api/write-tigertag", HTTP_POST, [this]() { handleApiWriteTigerTag(); }); _server.on("/api/write-opentag3d", HTTP_POST, [this]() { handleApiWriteOpenTag3D(); }); + _server.on("/api/write-openspool", HTTP_POST, [this]() { handleApiWriteOpenSpool(); }); _server.on("/api/register-uid", HTTP_POST, [this]() { handleApiRegisterUid(); }); _server.on("/api/spoolman/spools", HTTP_GET, [this]() { handleApiSpoolmanSpools(); }); _server.on("/api/spoolman/link", HTTP_POST, [this]() { handleApiSpoolmanLink(); }); @@ -205,6 +211,11 @@ void WebServerManager::handleOpenTag3DWriter() { _server.send_P(200, "text/html", OPENTAG3D_WRITER_HTML); } +void WebServerManager::handleOpenSpoolWriter() { + _server.sendHeader("Access-Control-Allow-Origin", "*"); + _server.send_P(200, "text/html", OPENSPOOL_WRITER_HTML); +} + void WebServerManager::handleSharedCSS() { _server.sendHeader("Access-Control-Allow-Origin", "*"); _server.sendHeader("Cache-Control", "public, max-age=86400"); @@ -232,6 +243,11 @@ void WebServerManager::handleOpenTag3DLogo() { _server.send_P(200, "image/png", reinterpret_cast(OPENTAG3D_LOGO_PNG), OPENTAG3D_LOGO_PNG_LEN); } +void WebServerManager::handleOpenSpoolLogo() { + _server.sendHeader("Cache-Control", "public, max-age=86400"); + _server.send_P(200, "image/jpeg", reinterpret_cast(OPENSPOOL_LOGO_PNG), OPENSPOOL_LOGO_PNG_SIZE); +} + void WebServerManager::handleUpdatePage() { _server.sendHeader("Access-Control-Allow-Origin", "*"); _server.send_P(200, "text/html", UPDATE_HTML); @@ -1083,6 +1099,19 @@ void WebServerManager::handleApiStatus() { if (ot3d.dry_time_hours > 0) otObj["dry_time_hours"] = ot3d.dry_time_hours; } } + } else if (state.kind == TagKind::OpenSpoolTag) { + OpenSpoolData os; + if (NFCManager::getInstance().getLastOpenSpoolData(os) && os.valid) { + JsonObject osObj = doc.createNestedObject("openspool"); + osObj["brand"] = os.brand; + osObj["material"] = os.material; + char osColorHex[8]; + snprintf(osColorHex, sizeof(osColorHex), "#%s", os.color_hex); + osObj["color_hex"] = osColorHex; + osObj["version"] = os.version; + if (os.min_temp > 0) osObj["min_temp"] = os.min_temp; + if (os.max_temp > 0) osObj["max_temp"] = os.max_temp; + } } else if (state.kind == TagKind::GenericUidTag) { // Generic UID tag — include resolved Spoolman data if available GenericTagSpoolInfo spoolInfo; @@ -1517,6 +1546,51 @@ void WebServerManager::handleApiWriteOpenTag3D() { _server.send(200, "application/json", "{\"success\":true}"); } +// --------------------------------------------------------------------------- +// API: Write OpenSpool +// --------------------------------------------------------------------------- + +void WebServerManager::handleApiWriteOpenSpool() { + Serial.println("WebServerManager: POST /api/write-openspool received"); + _server.sendHeader("Access-Control-Allow-Origin", "*"); + + StaticJsonDocument<256> doc; + DeserializationError err = deserializeJson(doc, _server.arg("plain")); + if (err) { + sendError(400, "Invalid JSON"); + return; + } + + // Build the tag payload using ArduinoJson to properly escape all values + StaticJsonDocument<256> tagDoc; + tagDoc["protocol"] = doc["protocol"] | "openspool"; + tagDoc["version"] = doc["version"] | "1.0"; + tagDoc["type"] = doc["type"] | "PLA"; + tagDoc["color_hex"] = doc["color_hex"] | "FF0000"; + tagDoc["brand"] = doc["brand"] | ""; + tagDoc["min_temp"] = doc["min_temp"] | "210"; + tagDoc["max_temp"] = doc["max_temp"] | "230"; + + char jsonPayload[256]; + size_t jsonLen = serializeJson(tagDoc, jsonPayload, sizeof(jsonPayload)); + if (jsonLen == 0 || jsonLen >= sizeof(jsonPayload)) { + sendError(400, "Payload too large"); + return; + } + + NFCWriteRequest req; + memset(&req, 0, sizeof(req)); + req.request_id = NFCManager::getInstance().generateRequestId(); + req.type = NFCWriteType::WRITE_OPENSPOOL; + + if (!NFCManager::getInstance().enqueueRawWrite(req, (const uint8_t*)jsonPayload, (size_t)jsonLen)) { + sendError(503, "Write queue full or busy"); + return; + } + + _server.send(200, "application/json", "{\"success\":true}"); +} + // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- diff --git a/src/WebServerManager.h b/src/WebServerManager.h index 1d26857..0d4316e 100644 --- a/src/WebServerManager.h +++ b/src/WebServerManager.h @@ -35,11 +35,13 @@ class WebServerManager { void handleOpenPrintTagWriter(); void handleTigerTagWriter(); void handleOpenTag3DWriter(); + void handleOpenSpoolWriter(); void handleSharedCSS(); void handleSharedJS(); void handleOpenPrintTagLogo(); void handleTigerTagLogo(); void handleOpenTag3DLogo(); + void handleOpenSpoolLogo(); void handleUpdatePage(); void handleConfigPage(); @@ -49,6 +51,7 @@ class WebServerManager { void handleApiFormatTag(); void handleApiWriteTigerTag(); void handleApiWriteOpenTag3D(); + void handleApiWriteOpenSpool(); void handleApiVersion(); void handleApiUploadFirmwareComplete(); void handleApiUploadFirmwareChunk();