diff --git a/.gitignore b/.gitignore index 9847f8f..06ac787 100644 --- a/.gitignore +++ b/.gitignore @@ -62,3 +62,4 @@ dependencies.lock sdkconfig .cache/ sdkconfig.old +.worktrees/ diff --git a/README.md b/README.md index 9ede0c3..e6f1ad3 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,18 @@ Workspace firmware centré sur une seule cible: Freenove FNK0102H avec `ui_freen - `docs/FNK0102H_SOURCE_OF_TRUTH.md`: matrice board/config/pins/support. - `README_ESP32_ZACUS.md`: notes runtime et signatures série utiles. +## Deux firmwares + +- `ui_freenove_allinone/` — firmware **Arduino/PlatformIO** historique (UI/audio/caméra/réseau), chemin release stabilisé. +- `idf_zacus/` — firmware **master ESP-IDF 5.4** (migration en cours, branche `feat/idf-migration`) : voix NPC (esp-sr wakeword + bridge WS), **énigmes locales** P1 son / P3 QR (caméra OV3660 + micro), **écran LVGL** (vue scène, viewfinder, shell Workbench, intro cracktro), et une **surface REST de jeu** (`/game/step`, `/game/puzzle_state`, `/game/file`). Voir `idf_zacus/README.md`. +- `box3_voice/` — firmware annexe **ESP32-S3-BOX-3** : pipeline voix + **générateur de stimulus** (`/stim/qr`, `/stim/melody`) pour exercer les énigmes du master. Voir `box3_voice/README.md`. + ## Arborescence utile - `platformio.ini`: env PlatformIO canonique `freenove_esp32s3_full_with_ui`. -- `ui_freenove_allinone/`: firmware UI/audio/caméra/réseau. +- `ui_freenove_allinone/`: firmware Arduino UI/audio/caméra/réseau. +- `idf_zacus/`: firmware master ESP-IDF (énigmes locales, écran LVGL, REST de jeu). +- `box3_voice/`: firmware BOX-3 (voix + générateur de stimulus QR/mélodie). - `data/`: contenu LittleFS. - `lib/`: bibliothèques runtime. - `scripts/`: bootstrap et scripts repo. diff --git a/box3_voice/README.md b/box3_voice/README.md index 09c652c..6a49290 100644 --- a/box3_voice/README.md +++ b/box3_voice/README.md @@ -54,3 +54,67 @@ Use `idf.py menuconfig` > **Zacus BOX-3 Voice Configuration**: | hints engine <--+ ``` + +## Générateur de stimulus (QR + mélodie) + +En plus du pipeline vocal, la BOX-3 sert de **source de stimulus** pour le +master Freenove. Elle affiche un QR code (lu par la caméra du master) et joue +une mélodie (entendue par le micro du master). Cela permet de tester les +énigmes locales P1 (son) et P3 (QR) du master de bout en bout, sans QR imprimé +ni instrument de musique. + +Les routes sont enregistrées sur le serveur HTTP existant (`scenario_server`) +au démarrage, juste après l'init de l'écran et de l'UI (`stimulus_init()` puis +`stimulus_register_routes()` dans `app_main`). + +### `POST /stim/qr` + +```json +{"text": "zacus-qr-1"} +``` + +Affiche le texte en QR plein écran : un widget `lv_qrcode` de **160 px** sur une +page LVGL dédiée, chargée au premier plan. Le rétroéclairage est **atténué à +35 %** pour réduire le halo de l'écran émissif et préserver le contraste pour la +caméra du master. + +```bash +curl -X POST http://192.168.1.50/stim/qr \ + -H 'Content-Type: application/json' \ + -d '{"text":"zacus-qr-1"}' +# {"status":"ok","text":"zacus-qr-1"} +``` + +### `POST /stim/melody` + +```json +{"notes": [60, 62, 64, 65], "ms": 400} +``` + +Joue la séquence de notes **MIDI** au haut-parleur. `notes` est un tableau +d'entiers `0-127` (60 = do central), `ms` est la durée par note en +millisecondes, bornée à `1-4000` (défaut 400). La séquence est jouée sur une +**tâche worker** dédiée, donc la réponse HTTP revient immédiatement. Maximum +32 notes ; les notes hors plage sont ignorées. + +```bash +curl -X POST http://192.168.1.50/stim/melody \ + -H 'Content-Type: application/json' \ + -d '{"notes":[60,62,64,65,67],"ms":300}' +# {"status":"ok","notes":5} +``` + +### Note de terrain + +Le décodage par la caméra du master du **QR affiché sur l'écran LCD n'est pas +fiable** : le contraste émissif est insuffisant pour quirc, qui ne retrouve pas +les motifs de repérage (finder patterns) quelle que soit la résolution, même +écran atténué. **La mélodie est le chemin de stimulus le plus robuste.** Pour +P3, le **QR imprimé reste recommandé** ; le `/stim/qr` sert surtout au cadrage +et au débogage de la caméra. + +### Configuration Wi-Fi + +Le générateur est joignable sur l'IP de la BOX-3 en mode station. Configurez le +réseau via `idf.py menuconfig` > **Zacus BOX-3 Voice Configuration**, ou +directement par `CONFIG_ZACUS_WIFI_SSID` / `CONFIG_ZACUS_WIFI_PASSWORD`. diff --git a/box3_voice/main/CMakeLists.txt b/box3_voice/main/CMakeLists.txt index c931303..68e4582 100644 --- a/box3_voice/main/CMakeLists.txt +++ b/box3_voice/main/CMakeLists.txt @@ -1,5 +1,5 @@ idf_component_register( - SRCS "main.c" "voice_ws_client.c" "scenario_server.c" "plip_virtual.c" "plip_ui.c" + SRCS "main.c" "voice_ws_client.c" "scenario_server.c" "plip_virtual.c" "plip_ui.c" "stimulus.c" INCLUDE_DIRS "." PRIV_REQUIRES driver @@ -12,4 +12,6 @@ idf_component_register( nvs_flash spiffs scenario_mesh + espressif__esp-box-3 + lvgl__lvgl ) diff --git a/box3_voice/main/Kconfig.projbuild b/box3_voice/main/Kconfig.projbuild index 2963391..4bd33f3 100644 --- a/box3_voice/main/Kconfig.projbuild +++ b/box3_voice/main/Kconfig.projbuild @@ -12,6 +12,16 @@ menu "Zacus BOX-3 Voice Configuration" help WiFi password. Leave empty for open networks. + config ZACUS_WIFI_CHANNEL + int "WiFi channel hint (0 = auto)" + default 0 + range 0 13 + help + Bias the STA scan to start on this channel (1-13). Set this to the + master's WiFi channel so ESP-NOW peers land co-channel — required + for the scenario relay on multi-AP / mesh networks where the same + SSID is broadcast on several channels. 0 = auto (scan all). + config ZACUS_VOICE_BRIDGE_URL string "Voice Bridge WebSocket URL" default "ws://192.168.0.119:8200/voice/ws" diff --git a/box3_voice/main/main.c b/box3_voice/main/main.c index 70fea23..c66d713 100644 --- a/box3_voice/main/main.c +++ b/box3_voice/main/main.c @@ -28,6 +28,7 @@ #include "scenario_server.h" #include "plip_virtual.h" #include "plip_ui.h" +#include "stimulus.h" #include "scenario_mesh.h" /* BSP header — provided by espressif/esp-box component */ @@ -88,6 +89,7 @@ static esp_err_t wifi_init_sta(void) .ssid = CONFIG_ZACUS_WIFI_SSID, .password = CONFIG_ZACUS_WIFI_PASSWORD, .threshold.authmode = WIFI_AUTH_WPA2_PSK, + .channel = CONFIG_ZACUS_WIFI_CHANNEL, /* co-channel master for ESP-NOW relay (0 = auto) */ }, }; @@ -106,6 +108,29 @@ static esp_err_t wifi_init_sta(void) /* --------------- Test tone (440 Hz sine) --------------- */ +// Play a single tone on the speaker (blocking). Public so the stimulus +// generator can sequence a melody for the master's microphone. +void audio_play_tone(float frequency, int duration_ms) +{ + if (!s_spk_handle || duration_ms <= 0) return; + const int total_samples = AUDIO_SAMPLE_RATE * duration_ms / 1000; + const float amplitude = 16000.0f; + int16_t buffer[256]; + size_t bytes_written = 0; + int sample_idx = 0; + while (sample_idx < total_samples) { + int chunk = (total_samples - sample_idx < 256) + ? (total_samples - sample_idx) : 256; + for (int i = 0; i < chunk; i++) { + float t = (float)(sample_idx + i) / (float)AUDIO_SAMPLE_RATE; + buffer[i] = (int16_t)(amplitude * sinf(2.0f * M_PI * frequency * t)); + } + i2s_channel_write(s_spk_handle, buffer, chunk * sizeof(int16_t), + &bytes_written, portMAX_DELAY); + sample_idx += chunk; + } +} + static void audio_test_tone(void) { if (!s_spk_handle) { @@ -404,6 +429,14 @@ void app_main(void) /* REST/ESP-NOW phone still works headless if the UI fails. */ ESP_LOGW(TAG, "plip_ui_init failed — on-screen phone unavailable"); } + + /* Stimulus generator: BOX-3 shows QR / plays melody for the Freenove + * master's camera + mic (POST /stim/qr, POST /stim/melody). */ + if (stimulus_init() == ESP_OK) { + stimulus_register_routes(scenario_server_handle()); + } else { + ESP_LOGW(TAG, "stimulus_init failed — QR/melody generator off"); + } } /* Start the ESP-NOW receiver so the master can relay scenarios to us even diff --git a/box3_voice/main/scenario_server.c b/box3_voice/main/scenario_server.c index 3c5a3b3..f476f81 100644 --- a/box3_voice/main/scenario_server.c +++ b/box3_voice/main/scenario_server.c @@ -25,7 +25,7 @@ #define TAG "scenario_srv" #define MAX_SCENARIO_BYTES (64 * 1024) -#define SPIFFS_LABEL "storage" +#define SPIFFS_LABEL "spiffs" #define SPIFFS_BASE "/spiffs" #define SCENARIO_PATH SPIFFS_BASE "/scenario.json" #define SCENARIO_BAK SPIFFS_BASE "/scenario.bak" diff --git a/box3_voice/main/stimulus.c b/box3_voice/main/stimulus.c new file mode 100644 index 0000000..3aae89e --- /dev/null +++ b/box3_voice/main/stimulus.c @@ -0,0 +1,170 @@ +// stimulus.c — QR display + melody playback, driven over REST. +#include "stimulus.h" + +#include +#include + +#include "bsp/esp-box-3.h" +#include "lvgl.h" +#include "libs/qrcode/lv_qrcode.h" +#include "cJSON.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +static const char *TAG = "stimulus"; +#define LOCK_MS 1000 + +static lv_obj_t *s_qr_screen; // dedicated fullscreen QR screen +static lv_obj_t *s_qr; // lv_qrcode widget +static lv_obj_t *s_qr_caption; + +esp_err_t stimulus_init(void) { + if (!bsp_display_lock(LOCK_MS)) return ESP_FAIL; + + s_qr_screen = lv_obj_create(NULL); + lv_obj_set_style_bg_color(s_qr_screen, lv_color_white(), 0); + + // 160 px QR on the 320x240 LCD — leaves ~40 px white top/bottom and + // ~80 px sides as the quiet zone quirc needs to lock the finder patterns + // (220 px starved the vertical quiet zone and blocked detection). + s_qr = lv_qrcode_create(s_qr_screen); + lv_qrcode_set_size(s_qr, 160); + lv_qrcode_set_dark_color(s_qr, lv_color_black()); + lv_qrcode_set_light_color(s_qr, lv_color_white()); + lv_qrcode_update(s_qr, "zacus", 5); + lv_obj_align(s_qr, LV_ALIGN_CENTER, 0, -10); + + s_qr_caption = lv_label_create(s_qr_screen); + lv_obj_set_style_text_color(s_qr_caption, lv_color_black(), 0); + lv_label_set_text(s_qr_caption, ""); + lv_obj_align(s_qr_caption, LV_ALIGN_BOTTOM_MID, 0, -10); + + bsp_display_unlock(); + ESP_LOGI(TAG, "stimulus QR screen ready"); + return ESP_OK; +} + +static void show_qr(const char *text) { + if (!s_qr || !bsp_display_lock(LOCK_MS)) return; + lv_qrcode_update(s_qr, text, strlen(text)); + lv_label_set_text(s_qr_caption, text); + lv_screen_load(s_qr_screen); // bring the QR to the front + bsp_display_unlock(); + // Dim the backlight hard: an emissive LCD at full brightness blooms and + // crushes QR contrast for the master's camera. ~35% keeps the modules + // readable while killing the glare halo. + bsp_display_brightness_set(35); + ESP_LOGI(TAG, "QR shown (dimmed): %s", text); +} + +// MIDI note -> frequency (A4=69=440 Hz). +static float midi_to_hz(int note) { + return 440.0f * powf(2.0f, (float)(note - 69) / 12.0f); +} + +// ─── REST ──────────────────────────────────────────────────────────────────── + +static esp_err_t read_body(httpd_req_t *req, char *buf, size_t cap) { + if (req->content_len <= 0 || (size_t)req->content_len >= cap) return ESP_FAIL; + int total = 0; + while (total < (int)req->content_len) { + int got = httpd_req_recv(req, buf + total, req->content_len - total); + if (got <= 0) { + if (got == HTTPD_SOCK_ERR_TIMEOUT) continue; + return ESP_FAIL; + } + total += got; + } + buf[total] = '\0'; + return ESP_OK; +} + +static esp_err_t reply(httpd_req_t *req, const char *status, const char *json) { + httpd_resp_set_status(req, status); + httpd_resp_set_type(req, "application/json"); + return httpd_resp_sendstr(req, json); +} + +static esp_err_t handle_qr_post(httpd_req_t *req) { + char body[256]; + if (read_body(req, body, sizeof(body)) != ESP_OK) { + return reply(req, "400 Bad Request", "{\"error\":\"body\"}"); + } + cJSON *root = cJSON_Parse(body); + const cJSON *t = root ? cJSON_GetObjectItemCaseSensitive(root, "text") : NULL; + if (!cJSON_IsString(t) || !t->valuestring[0]) { + cJSON_Delete(root); + return reply(req, "400 Bad Request", "{\"error\":\"text required\"}"); + } + show_qr(t->valuestring); + char resp[280]; + snprintf(resp, sizeof(resp), "{\"status\":\"ok\",\"text\":\"%s\"}", + t->valuestring); + cJSON_Delete(root); + return reply(req, "200 OK", resp); +} + +// Melody played on a worker task so the HTTP response returns immediately. +typedef struct { float hz[32]; int count; int note_ms; } melody_job_t; + +static void melody_task(void *arg) { + melody_job_t *job = (melody_job_t *)arg; + for (int i = 0; i < job->count; i++) { + audio_play_tone(job->hz[i], job->note_ms); + vTaskDelay(pdMS_TO_TICKS(40)); // brief gap = note onset for the mic + } + free(job); + vTaskDelete(NULL); +} + +static esp_err_t handle_melody_post(httpd_req_t *req) { + char body[512]; + if (read_body(req, body, sizeof(body)) != ESP_OK) { + return reply(req, "400 Bad Request", "{\"error\":\"body\"}"); + } + cJSON *root = cJSON_Parse(body); + const cJSON *notes = root ? cJSON_GetObjectItemCaseSensitive(root, "notes") : NULL; + if (!cJSON_IsArray(notes) || cJSON_GetArraySize(notes) == 0) { + cJSON_Delete(root); + return reply(req, "400 Bad Request", "{\"error\":\"notes required\"}"); + } + const cJSON *ms = cJSON_GetObjectItemCaseSensitive(root, "ms"); + const int note_ms = cJSON_IsNumber(ms) ? ms->valueint : 400; + + melody_job_t *job = calloc(1, sizeof(*job)); + if (!job) { cJSON_Delete(root); return reply(req, "500 Internal Server Error", "{\"error\":\"oom\"}"); } + job->note_ms = (note_ms > 0 && note_ms <= 4000) ? note_ms : 400; + const cJSON *n; + cJSON_ArrayForEach(n, notes) { + if (job->count >= 32) break; + if (cJSON_IsNumber(n) && n->valueint >= 0 && n->valueint <= 127) { + job->hz[job->count++] = midi_to_hz(n->valueint); + } + } + cJSON_Delete(root); + if (job->count == 0) { free(job); return reply(req, "400 Bad Request", "{\"error\":\"no valid notes\"}"); } + + if (xTaskCreate(melody_task, "melody", 4096, job, 5, NULL) != pdPASS) { + free(job); + return reply(req, "503 Service Unavailable", "{\"error\":\"busy\"}"); + } + char resp[64]; + snprintf(resp, sizeof(resp), "{\"status\":\"ok\",\"notes\":%d}", job->count); + return reply(req, "200 OK", resp); +} + +esp_err_t stimulus_register_routes(httpd_handle_t server) { + static const httpd_uri_t uri_qr = { + .uri = "/stim/qr", .method = HTTP_POST, + .handler = handle_qr_post, .user_ctx = NULL, + }; + static const httpd_uri_t uri_melody = { + .uri = "/stim/melody", .method = HTTP_POST, + .handler = handle_melody_post, .user_ctx = NULL, + }; + esp_err_t e = httpd_register_uri_handler(server, &uri_qr); + if (e == ESP_OK) e = httpd_register_uri_handler(server, &uri_melody); + if (e == ESP_OK) ESP_LOGI(TAG, "routes up: POST /stim/qr, POST /stim/melody"); + return e; +} diff --git a/box3_voice/main/stimulus.h b/box3_voice/main/stimulus.h new file mode 100644 index 0000000..edd501f --- /dev/null +++ b/box3_voice/main/stimulus.h @@ -0,0 +1,28 @@ +// stimulus.h — BOX-3 as a stimulus generator for the Freenove master's +// camera (QR) and microphone (melody). Lets the master's local puzzles be +// exercised end-to-end without printed cards or an instrument. +// +// REST (registered on the existing scenario_server httpd): +// POST /stim/qr {"text":"zacus-qr-1"} -> show QR fullscreen +// POST /stim/melody {"notes":[60,62,64,65],"ms":400} -> play melody +#pragma once +#include "esp_err.h" +#include "esp_http_server.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Provided by main.c — play one tone (blocking) on the BOX-3 speaker. +void audio_play_tone(float frequency, int duration_ms); + +// Build the QR screen (under bsp_display_lock). Call once after the display +// and UI are up. +esp_err_t stimulus_init(void); + +// Register /stim/* routes on an existing server. +esp_err_t stimulus_register_routes(httpd_handle_t server); + +#ifdef __cplusplus +} +#endif diff --git a/box3_voice/sdkconfig.defaults b/box3_voice/sdkconfig.defaults index 9abbbc2..b096919 100644 --- a/box3_voice/sdkconfig.defaults +++ b/box3_voice/sdkconfig.defaults @@ -59,3 +59,6 @@ CONFIG_I2C_ENABLE_LEGACY_DRIVERS=y # PLIP phone UI fonts (plip_ui.c) CONFIG_LV_FONT_MONTSERRAT_24=y CONFIG_LV_FONT_MONTSERRAT_48=y + +# Stimulus generator (QR display for the master camera, melody for its mic) +CONFIG_LV_USE_QRCODE=y diff --git a/idf_zacus/README.md b/idf_zacus/README.md index fcf2cb6..2eb6e4e 100644 --- a/idf_zacus/README.md +++ b/idf_zacus/README.md @@ -1,79 +1,228 @@ -# `idf_zacus/` — Zacus master ESP-IDF scaffold +# `idf_zacus/` — firmware master Zacus (ESP-IDF) -This tree is the future home of the Zacus master firmware. It is the **P1 -first slice** of the voice pipeline migration described in -`docs/superpowers/specs/2026-05-03-voice-pipeline-esp-sr-design.md`. +Firmware **master** du jeu *Le Mystère du Professeur Zacus*, pour la carte +**Freenove FNK0102H — ESP32-S3 WROOM N16R8** (16 MB flash, 8 MB PSRAM octale). -The Arduino firmware in `../ui_freenove_allinone/` keeps running unchanged -during the transition; this scaffold lives side-by-side until feature parity -is reached. +Ce n'est plus une *slice* de migration : c'est le master complet. Le firmware +réunit aujourd'hui : -## Prerequisites +- **Voix NPC** — capture I2S + esp-sr (AFE + WakeNet9, mot-clé placeholder + `wn9_hiesp` / « Hi ESP »), pont WebSocket vers le voice-bridge MacStudio pour + la STT et le retour TTS, plus un *hook* REST PLIP (téléphone rétro). +- **Énigmes locales** caméra et micro résolues sur l'appareil (P1 son / + P3 QR), agrégées en un code de sortie. +- **Écran LVGL** (ST7796 320×480) : vue statut, vue scène, viewfinder caméra, + shell Workbench, navigation 5 directions. +- **Surface REST de jeu** (pilotage scénario, étapes, profil de groupe, + relais ESP-NOW) et **serveur OTA** double-banque. -- ESP-IDF v5.4 or v5.5 installed under `~/esp/esp-idf/`. -- Source the IDF environment in each shell: - ```bash - . $HOME/esp/esp-idf/export.sh - ``` +Le firmware Arduino historique (`../ui_freenove_allinone/`, PlatformIO) reste la +source de vérité matérielle (pin-maps, drivers de référence), mais l'IDF est +désormais le firmware master complet. -## Build +## Prérequis, build, flash + +ESP-IDF v5.4 installé sous `~/esp/esp-idf/`. + +> **QUIRK de cette machine** — l'environnement Python par défaut d'IDF échoue +> ici. Il faut forcer le venv py3.11 **avant** de sourcer `export.sh` : +> +> ```bash +> export IDF_PYTHON_ENV_PATH=$HOME/.espressif/python_env/idf5.4_py3.11_env +> source ~/esp/esp-idf/export.sh +> ``` + +Build : ```bash -cd idf_zacus idf.py set-target esp32s3 idf.py build ``` -## Flash & monitor +Flash + monitor (le port de cette machine est fixe) : ```bash -idf.py -p /dev/cu.usbmodem* flash monitor +idf.py -p /dev/cu.usbmodem5AB90753301 flash monitor ``` -Exit the monitor with `Ctrl-]`. +Sortie du monitor : `Ctrl-]`. -## What the first slice does +Au premier `idf.py build`, le component manager clone LovyanGFX (tag 1.2.21, +épinglé par SHA) depuis GitHub et récupère LVGL `~8.4.0`, esp-sr, esp-dsp, +mdns, esp_websocket_client et littlefs dans `managed_components/`. Les modèles +esp-sr sont flashés automatiquement (`srmodels.bin`) dans la partition `model`. -`main/main.c` boots the device, initializes NVS, mounts the LittleFS -`storage` partition on `/littlefs`, lists its contents, logs heap stats -(internal + PSRAM), and enters an idle heartbeat loop (no deep sleep — the -inherited `ota_server` listening loop will be wired in slice 2). +## Carte des composants -Inherited components: +Tous les composants vivent sous `components/` et sont des composants ESP-IDF +standard (chacun avec son `include/`). -- `components/ota_server/` — HTTP server on :80 with rate-limited OTA upload - and 30 s watchdog auto-rollback (`POST /ota`, `POST /ota/rollback`, - `GET /version`, `GET /status`, `GET /ota/status`). Not yet started by - `main.c`; that comes next. +| Composant | Rôle | +|---|---| +| `ota_server` | Serveur HTTP `:80` : OTA double-banque (`POST /ota`, `POST /ota/rollback`), watchdog d'auto-rollback, `GET /version` / `/status`. Détient l'instance `esp_http_server` partagée par les autres endpoints. | +| `game_endpoint` | Surface REST de jeu greffée sur le httpd d'`ota_server` : profil de groupe hints, pilotage scénario, étapes (`/game/step`), `puzzle_state`, relais ESP-NOW. Voir son `README.md`. | +| `voice_pipeline` | Capture I2S + esp-sr (AFE `AFE_TYPE_SR`, WakeNet9), machine d'états idle/listening/speaking/muted, pont WebSocket vers le voice-bridge (STT/TTS), lecture TTS sur DAC. Inclut `voice_dispatcher` (routage STT → npc_engine, fast-path mots-clés). | +| `voice_hook_endpoint` | Pont REST `POST /voice/hook` depuis le PLIP (téléphone Si3210) : off-hook arme la voix directement (bypass wake-word), on-hook ferme la session. Greffé sur le httpd d'`ota_server`. | +| `npc_engine` | Port C du moteur de décision NPC Arduino : table de cues, humeur, déclenchement de cues via `media_manager`. | +| `hints_client` | Client HTTP du moteur de hints (`POST /hints/ask`, `/hints/puzzle_start`, `/hints/attempt_failed`), profil de groupe (`TECH` / `NON_TECH` / `MIXED` / `BOTH`). | +| `media_manager` | Port C du MediaManager Arduino : catalogue, play/stop, enregistrement WAV. Décodage MP3 et capture micro stubés ici (le micro réel passe par `mic_broker`). | +| `local_puzzles` | Énigmes locales caméra/micro et leur câblage vers `puzzle_state` : `qr_puzzle`, `sound_puzzle`, `mic_broker`, validateurs `seq_validator` / `melody_validator`, pin-map `board_pins_mediakit.h`. | +| `puzzle_state` | Agrégation master des énigmes résolues et assemblage du **code** de sortie (jusqu'à 8 énigmes, fragments → chiffres décimaux). | +| `display_ui` | Écran LVGL 8.4 sur LovyanGFX : vues statut/scène, viewfinder caméra, effets, shell Workbench, browser de fichiers, intro cracktro ; boutons 5 directions (`buttons_input`). | -## Layout +## Séquence de boot (`main/main.c`) -``` -idf_zacus/ -├── CMakeLists.txt # project entry, points EXTRA_COMPONENT_DIRS at components/ -├── sdkconfig.defaults # ESP32-S3, octal PSRAM 80 MHz, custom partitions -├── partitions.csv # OTA layout + 2 MB LittleFS "storage" -├── main/ -│ ├── CMakeLists.txt -│ ├── idf_component.yml # joltwallet/littlefs ^1.14 -│ └── main.c # app_main + LittleFS mount + heartbeat -└── components/ - └── ota_server/ # inherited from 2026-04-03 IDF bootstrap +`app_main` exécute, dans l'ordre : + +1. **NVS** — init (efface + réinit si pages pleines / nouvelle version). +2. **Écran** — `display_ui_init()` (splash tôt, non fatal). Si OK : enregistre + `display_ui_camera_frame` comme callback de preview QR, puis + `buttons_input_init()` (5 directions). +3. **Wi-Fi** — lit les creds NVS (namespace `wifi`, clés `ssid`/`pwd`). Si + présents : **STA** (attente `GOT_IP`, 8 retries, timeout 20 s). Sinon, ou + échec : **AP** ouvert de secours `zacus-setup` (IP `192.168.4.1`). +4. **mDNS** — uniquement en STA : publie `zacus-master.local` et le service + `_zacus._tcp:80` (TXT `path=/voice/hook`). Sauté en AP-fallback. +5. **OTA server** — `ota_server_init()` (`:80`). Sur succès, greffe sur le même + httpd : `voice_hook_endpoint_init()` puis `game_endpoint_init()` (qui monte + aussi `scenario_mesh` / ESP-NOW), seed du registre de peers relais depuis + NVS (namespace `peers`), et `game_endpoint_set_puzzle_state(&s_pstate)`. +6. **LittleFS** — monte la partition `storage` sur `/littlefs` (reformat si + échec) et liste la racine. Puis, sous ce montage : + - `media_manager_init()` (catalogues sur LittleFS) + smoke-test de lecture ; + - `npc_engine_init()` (table de cues vide à ce stade) ; + - `hints_client_init()` vers le backend hints, puis chargement du + `group_profile` depuis NVS (namespace `zacus`, défaut `MIXED`) ; + - `voice_dispatcher_init()` ; + - `puzzle_state_init()` + `local_puzzles_init()` ; + - `mic_broker_init()` sur les pins micro Media Kit (3/14/46, 16 kHz) **avant** + `voice_pipeline_init()` (le pipeline no-op alors avec `ESP_ERR_INVALID_STATE`) ; + - `voice_pipeline_init()` : wake-word + auto-start capture activés, URL du + voice-bridge, lecture TTS activée, **pins forcés** aux valeurs Media Kit + (mic 3/14/46, HP 42/41/1) pour éviter la collision avec les pins caméra. +7. **`ota_server_mark_valid()`** — valide l'image (désamorce l'auto-rollback) + une fois les sous-systèmes montés. +8. **Boucle de statut** — réveil toutes les 500 ms : rafraîchit l'écran + (IP, état wake-word, étape/armé, code assemblé, métadonnées de scène), + traite les lancements d'apps du shell (`/littlefs/apps//step.txt` → + `game_endpoint_apply_step`). Toutes les 60 s : heartbeat + `media_manager_update` + + `npc_engine_update`. + +URLs hardcodées (déplacées en NVS dans un suivi) : hints +`http://192.168.0.150:8302`, voice-bridge WS `ws://100.116.92.12:8200/voice/ws`. + +## Énigmes locales (P1 son / P3 QR) + +`local_puzzles` arme deux types d'énigmes et reporte leur fragment dans +`puzzle_state` à la résolution : + +- **P1 — son/mélodie** (`local_puzzles_arm_sound`) : `mic_broker` (propriétaire + unique du micro I2S RX) passe en mode `MIC_P1_SOUND` et route les trames PCM16 + vers `sound_puzzle`, qui détecte les notes et les valide via + `melody_validator` (notes MIDI + tolérance en demi-tons). Le **même broker** + est partagé avec la voix (`MIC_NPC_LISTEN`) : un seul propriétaire des pins, + un consommateur actif à la fois. +- **P3 — QR séquentiel** (`local_puzzles_arm_qr`) : `qr_puzzle` initialise la + caméra (QVGA 320×240 niveaux de gris), décode avec **quirc**, et valide + l'ordre des codes via `seq_validator`. Les trames sont mirrorées au + viewfinder de l'écran via le callback de preview. Teardown caméra + asynchrone (réarmement à retenter après ~100 ms). + +### Pin-map Media Kit (`board_pins_mediakit.h`) + +Freenove FNK0102H Media Kit v1.2, **zéro collision** entre caméra, micro, +HP et écran : + +- **Caméra** (capteur réel : **OV3660**) — XCLK 15, SIOD/SIOC 4/5, VSYNC 6, + HREF 7, PCLK 13, D0–D7 = 11/9/8/10/12/18/17/16. +- **Micro I2S IN** — BCLK 3, WS 14, DIN 46. +- **HP I2S OUT** (MAX98357A) — BCLK 42, LRC 41, DOUT 1. +- **Écran ST7796 (SPI2/FSPI, 80 MHz)** — SCK 47, MOSI 21, DC 45, RST 20, BL 2. + +> Les défauts du composant `voice_pipeline` (mic 14/15/22, HP 11/12/13) +> **entrent en collision** avec les pins caméra ; `main.c` les écrase par les +> valeurs Media Kit ci-dessus. Ne pas réintroduire les défauts. + +### Validateurs testables sur hôte + +`seq_validator` et `melody_validator` sont de la logique pure, sans I/O, +compilables et testables sur la machine de dev : + +```bash +make -C idf_zacus/components/local_puzzles/test/host test ``` -## Coexistence with Arduino +## Écran (`display_ui`) -`../ui_freenove_allinone/` (Arduino, PlatformIO) remains the production -firmware until the IDF port reaches feature parity. The two trees do **not** -share build artifacts. To work on the Arduino tree: -`cd ui_freenove_allinone && pio run`. To work on the IDF tree, source the -ESP-IDF env first. +Pile **LVGL 8.4 + LovyanGFX** (driver ST7796, `setAddrWindow` / `writePixels` +RGB565). Tâche de rendu dédiée (LovyanGFX n'est pas thread-safe inter-tâches) ; +`display_ui_set_status` copie un snapshot sous mutex. -## Roadmap (next P1 slices) +Vues et fonctions : -1. Boot `ota_server_init()` from `app_main` after a small Wi-Fi STA bring-up. -2. Port the NPC engine and media manager skeleton. -3. Scaffold the voice pipeline (I2S RX task, no esp-sr yet). -4. Bring up esp-sr AFE + wakenet ("hi_esp" placeholder) — start of P3. +- **Vue statut** — IP, wake-word, étape/armé, compte d'énigmes résolues, code. +- **Vue scène** — titre/sous-titre/code de l'étape, avec effets + (`pulse` / `glitch` / `gyro` / `none`) issus de l'IR de scène. +- **Viewfinder caméra** — `lv_canvas` en PSRAM, alimenté par les trames QR + (grayscale → RGB565, ~5 fps) quand la vue scène est active et le QR armé. +- **Shell Workbench** — port d'`ui_amiga_shell` : tuiles statiques (Statut, + Scene, Auto, Lumiere, Fichiers) + tuiles dynamiques d'apps lues dans + `/littlefs/apps//` (`icon.png` optionnel, `step.txt` = l'étape à armer). +- **Browser de fichiers** — drawer LittleFS navigable. +- **Intro cracktro** — port fidèle de `ui_manager_intro` (starfield parallaxe + 3 couches, copper bars). -See the design spec for the full plan. +Navigation **5 directions** (`buttons_input`, échelle analogique sur GPIO19, +ADC2) : 1=SELECT 2=DOWN 3=MENU 4=LEFT/RIGHT 5=UP. Shell fermé : SELECT / +LEFT-RIGHT bascule scène↔statut, MENU ouvre le shell, UP/DOWN règlent la +luminosité. Shell ouvert : navigation grille, SELECT lance la tuile. + +## Surface REST de jeu + +Tous les endpoints de jeu partagent l'instance `esp_http_server` d'`ota_server` +(port 80, pas de second socket). Détail des routes (`/game/group_profile`, +`/game/step`, `/game/puzzle_state`, `/game/scenario`, `/game/scenario/relay`…) +et de la persistance NVS dans **`components/game_endpoint/README.md`**. + +## Tests hôte + +Trois suites de logique pure tournent sur la machine de dev (Unity), sans +matériel ni IDF flashé : + +| Suite | Cas | Commande | +|---|---|---| +| `local_puzzles` (validateurs séquence + mélodie) | 6 | `make -C idf_zacus/components/local_puzzles/test/host test` | +| `puzzle_state` (agrégation + assemblage du code) | 2 | `make -C idf_zacus/components/puzzle_state/test/host test` | +| `game_endpoint` (binding puzzle/scène) | 10 | `make -C idf_zacus/components/game_endpoint/test/host test` | + +## Layout flash (`partitions.csv`) + +Cible 16 MB (N16R8) : + +| Partition | Type | Taille | +|---|---|---| +| `nvs` | data/nvs | 24 KB | +| `otadata` | data/ota | 8 KB | +| `phy_init` | data/phy | 4 KB | +| `factory` / `ota_0` / `ota_1` | app | **3 MB chacune** | +| `model` | data/spiffs | 1 MB (modèles esp-sr) | +| `storage` | data/littlefs | 5 MB (assets, scénario, apps) | + +La partition app est passée de 2 MB à **3 MB** (2026-06-10) pour loger +LVGL + LovyanGFX + polices + effets/preview. NVS/otadata/phy gardent leurs +offsets (les creds Wi-Fi survivent au reflash) ; `ota_0`/`ota_1`/`model`/ +`storage` se décalent, donc **le contenu LittleFS est perdu au reflash** +(reformatage auto ; repousser le scénario via `POST /game/scenario`). + +## Limites connues / terrain + +- **Décodage QR sur écran LCD émissif** : lire un QR **affiché à l'écran** via + la caméra **ne fonctionne pas de façon fiable** — quirc ne retrouve pas les + motifs (contraste insuffisant sur un panneau émissif). Le **QR imprimé** reste + la méthode recommandée pour P3. +- **Note repérée dans le code** : le commentaire d'en-tête de `qr_puzzle.h` + mentionne encore « OV2640 » ; le capteur réel de la carte est **OV3660** + (commentaire à corriger, sans impact fonctionnel sur le chemin grayscale/quirc). +- **Provisioning** : creds Wi-Fi, URL hints et URL voice-bridge passent encore + partiellement par des constantes / NVS pré-flashé ; le passage tout-NVS au + runtime est un suivi. diff --git a/idf_zacus/components/display_ui/CMakeLists.txt b/idf_zacus/components/display_ui/CMakeLists.txt new file mode 100644 index 0000000..e2bd212 --- /dev/null +++ b/idf_zacus/components/display_ui/CMakeLists.txt @@ -0,0 +1,12 @@ +idf_component_register( + SRCS "display_ui.cpp" + "intro_fx3d.cpp" + "buttons_input.c" + "fonts/lv_font_orbitron_40.c" + "fonts/lv_font_ibmplexmono_18.c" + INCLUDE_DIRS "include" + PRIV_REQUIRES driver local_puzzles esp_timer esp_adc +) + +# LovyanGFX requires C++17. +target_compile_options(${COMPONENT_LIB} PRIVATE -std=gnu++17) diff --git a/idf_zacus/components/display_ui/README.md b/idf_zacus/components/display_ui/README.md new file mode 100644 index 0000000..688166a --- /dev/null +++ b/idf_zacus/components/display_ui/README.md @@ -0,0 +1,47 @@ +# display_ui + +Écran ST7796 (320×480, paysage) du master Freenove, piloté via **LovyanGFX** + +**LVGL 8.4**. Port fidèle de l'UI Arduino d'origine (`ui_freenove_allinone`). + +Tous les appels LVGL et LovyanGFX s'exécutent exclusivement sur la +`display_task` ; `display_ui_set_status` copie l'état sous mutex et lève un flag +dirty que la tâche replie dans les labels. + +## Pile + +- **HAL panneau** — `Panel_ST7796` sur `Bus_SPI` (SPI2_HOST/FSPI, 80 MHz + écriture / 20 MHz lecture). Configuration **identique** à l'UI Arduino + d'origine (`FreenoveLgfxDevice` dans `ui_freenove_allinone`) : pins + SCK=47/MOSI=21/DC=45/RST=20/BL=2, rotation 1, inversion on, ordre RGB. +- **Rétroéclairage PWM** — LEDC sur `LEDC_TIMER_1` / `LEDC_CHANNEL_1`. Le + `LEDC_TIMER_0` / `LEDC_CHANNEL_0` est réservé au **XCLK de la caméra** + (`qr_puzzle`). Duty exposé 0-100 % via `display_ui_set_brightness`. +- **Vue statut** + **vue scène** — la vue scène affiche le step courant et le + code assemblé, avec un effet (**pulse** = respiration d'opacité, **glitch** = + jitter + scintillement du titre, **gyro** = anneau-gyrophare rotatif) et un + **viewfinder caméra** : `lv_canvas` PSRAM nourri par les trames grayscale du + scan QR, converties en **RGB565** (~5 fps), visible seulement quand l'énigme + QR est armée. +- **Boutons 5 directions** — échelle résistive sur ADC (GPIO19 → ADC2_CH8), + seuils en mV repris de l'original (`{0, 447, 730, 1008, 1307, 1659}`, plancher + « relâché » 2800 mV, décodage par milieu entre seuils adjacents). +- **Shell Workbench** — tuiles builtin **Statut / Scene / Auto / Lumiere / + Fichiers**, plus des **apps dynamiques** chargées depuis + `/littlefs/apps//` (`icon.png` optionnel + `step.txt` = action de l'app). +- **Browser de fichiers** — drawer parcourant `/littlefs`. +- **Intro cracktro** — port fidèle de la phase A d'origine : starfield parallaxe + 3 couches en **Q8.8**, copper bars, scrolltext en onde sinus, logo avec ombre + portée. + +## API publique (`display_ui.h`) + +- `display_ui_init()` — init panneau + splash, spawn la tâche de refresh. +- `display_ui_set_status(s)` — copie un snapshot d'état et demande un redraw + async (thread-safe ; NULL = no-op). +- `display_ui_set_brightness(pct)` — duty 0-100 % (défaut 100). +- `display_ui_camera_frame(gray, w, h)` — pousse une trame caméra pour le + viewfinder (câblé comme preview callback de `qr_puzzle`) ; non-QVGA ignorée. +- `display_ui_handle_key(key)` — traite une touche 5 directions (1=SELECT + 2=DOWN 3=MENU 4=LEFT/RIGHT 5=UP). +- `display_ui_take_pending_launch(id_out, cap)` — récupère (one-shot) une + demande de lancement d'app du shell dynamique. diff --git a/idf_zacus/components/display_ui/buttons_input.c b/idf_zacus/components/display_ui/buttons_input.c new file mode 100644 index 0000000..4fa172a --- /dev/null +++ b/idf_zacus/components/display_ui/buttons_input.c @@ -0,0 +1,133 @@ +// buttons_input.c — 5-way analog ladder scan (IDF port of ButtonManager). +// +// Faithful to ui_freenove_allinone/src/drivers/input/button_manager.cpp: +// - ladder thresholds (mV): {0, 447, 730, 1008, 1307, 1659} for keys 1..5 +// - "no button" floor: 2800 mV +// - decode: midpoint splits between adjacent thresholds +// - debounce: 30 ms of stable raw key before an event fires +// - scan period: 20 ms (~50 Hz) +// Differences vs the original: long-press detection is not ported yet (no +// consumer needs it); events go straight to display_ui_handle_key(). + +#include "buttons_input.h" +#include "display_ui.h" +#include "board_pins_mediakit.h" + +#include "esp_adc/adc_oneshot.h" +#include "esp_adc/adc_cali.h" +#include "esp_adc/adc_cali_scheme.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +static const char *TAG = "buttons"; + +// GPIO19 → ADC2_CHANNEL_8 on the ESP32-S3. +#define BTN_ADC_UNIT ADC_UNIT_2 +#define BTN_ADC_CHANNEL ADC_CHANNEL_8 + +#define BTN_DEBOUNCE_MS 30 +#define BTN_SCAN_MS 20 + +static const int kThresholdsMv[6] = {0, 447, 730, 1008, 1307, 1659}; +static const int kNoButtonMv = 2800; + +static adc_oneshot_unit_handle_t s_adc; +static adc_cali_handle_t s_cali; +static bool s_cali_ok; + +static uint8_t decode_key(int mv) { + if (mv >= (kThresholdsMv[5] + kNoButtonMv) / 2) return 0; // released + if (mv < (kThresholdsMv[1] + kThresholdsMv[2]) / 2) return 1; + if (mv < (kThresholdsMv[2] + kThresholdsMv[3]) / 2) return 2; + if (mv < (kThresholdsMv[3] + kThresholdsMv[4]) / 2) return 3; + if (mv < (kThresholdsMv[4] + kThresholdsMv[5]) / 2) return 4; + return 5; +} + +static void scan_task(void *arg) { + (void) arg; + uint8_t raw_key = 0; + TickType_t raw_changed = xTaskGetTickCount(); + uint8_t reported_key = 0; + + for (;;) { + vTaskDelay(pdMS_TO_TICKS(BTN_SCAN_MS)); + + int raw = 0; + // ADC2 + Wi-Fi arbitration can time out — skip the sample. + if (adc_oneshot_read(s_adc, BTN_ADC_CHANNEL, &raw) != ESP_OK) continue; + + int mv; + if (s_cali_ok) { + if (adc_cali_raw_to_voltage(s_cali, raw, &mv) != ESP_OK) continue; + } else { + // crude conversion without calibration: 12-bit, ~3100 mV span + mv = (raw * 3100) / 4095; + } + + const uint8_t key = decode_key(mv); + const TickType_t now = xTaskGetTickCount(); + + if (key != raw_key) { + raw_key = key; + raw_changed = now; + continue; + } + if ((now - raw_changed) < pdMS_TO_TICKS(BTN_DEBOUNCE_MS)) continue; + + // Stable. Fire on press transitions only (release → key). + if (key != reported_key) { + reported_key = key; + if (key != 0) { + display_ui_handle_key(key); + } + } + } +} + +esp_err_t buttons_input_init(void) { + adc_oneshot_unit_init_cfg_t ucfg = { + .unit_id = BTN_ADC_UNIT, + .ulp_mode = ADC_ULP_MODE_DISABLE, + }; + esp_err_t err = adc_oneshot_new_unit(&ucfg, &s_adc); + if (err != ESP_OK) { + ESP_LOGE(TAG, "adc_oneshot_new_unit: %s", esp_err_to_name(err)); + return err; + } + + adc_oneshot_chan_cfg_t ccfg = { + .atten = ADC_ATTEN_DB_12, // full-range, like the original 11dB + .bitwidth = ADC_BITWIDTH_12, + }; + err = adc_oneshot_config_channel(s_adc, BTN_ADC_CHANNEL, &ccfg); + if (err != ESP_OK) { + ESP_LOGE(TAG, "adc channel cfg: %s", esp_err_to_name(err)); + adc_oneshot_del_unit(s_adc); + s_adc = NULL; + return err; + } + + adc_cali_curve_fitting_config_t cal = { + .unit_id = BTN_ADC_UNIT, + .chan = BTN_ADC_CHANNEL, + .atten = ADC_ATTEN_DB_12, + .bitwidth = ADC_BITWIDTH_12, + }; + s_cali_ok = (adc_cali_create_scheme_curve_fitting(&cal, &s_cali) == ESP_OK); + if (!s_cali_ok) { + ESP_LOGW(TAG, "no ADC calibration — using crude raw→mV conversion"); + } + + if (xTaskCreate(scan_task, "btn_scan", 3072, NULL, 4, NULL) != pdPASS) { + ESP_LOGE(TAG, "scan task create failed"); + adc_oneshot_del_unit(s_adc); + s_adc = NULL; + return ESP_ERR_NO_MEM; + } + + ESP_LOGI(TAG, "5-way ladder up (GPIO19/ADC2_CH8, debounce %d ms)", + BTN_DEBOUNCE_MS); + return ESP_OK; +} diff --git a/idf_zacus/components/display_ui/display_ui.cpp b/idf_zacus/components/display_ui/display_ui.cpp new file mode 100644 index 0000000..94b52bf --- /dev/null +++ b/idf_zacus/components/display_ui/display_ui.cpp @@ -0,0 +1,1299 @@ +// display_ui.cpp — ST7796 + LVGL status screen, Phase 2a. +// +// Panel / bus configuration faithfully replicates the original Freenove +// Arduino firmware (ui_freenove_allinone/src/drivers/display/display_hal_lgfx.cpp +// FreenoveLgfxDevice, lines ~132-208): +// +// - Panel: Panel_ST7796 +// - Bus: Bus_SPI, SPI2_HOST (FSPI; FREENOVE_LCD_USE_HSPI == 0) +// - Freq: 80 MHz write / 20 MHz read +// - spi_mode 0, spi_3wire false, use_lock false, dma_channel 0 (CPU mode) +// - Pins: SCK=47, MOSI=21, MISO=-1, DC=45, CS=-1, RST=20, BL=2 +// - Panel: memory/panel 320×480, offset_x/y 0, offset_rotation 0, +// dummy_read_pixel 8, dummy_read_bits 1, readable false (no MISO), +// invert true (TFT_INVERSION_ON), rgb_order true (not BGR), +// dlen_16bit false, bus_shared true +// - Rotation: 1 (landscape) +// +// LVGL (Phase 2a): +// LVGL 8.4 (same line as the original: platformio.ini:35 lvgl/lvgl@8.4.0), +// configured via the managed component's Kconfig (sdkconfig: depth 16, +// swap 0, mem 160 KB, Montserrat 24/28 — mirroring the original lv_conf.h). +// Flush callback is the canonical LovyanGFX↔LVGL8 glue (startWrite / +// setAddrWindow / writePixels rgb565 / endWrite), matching the effective +// behaviour of the original ui_manager_display.cpp glue (its DMA path is +// disabled at runtime in the original HAL, so the synchronous path is the +// faithful one). Draw buffer: width × 40 lines, single, INTERNAL ram — +// exactly like the original (buffer_cfg_.draw_in_psram = false, lines = 40). +// Tick: esp_timer periodic 2 ms → lv_tick_inc(2) (the original calls +// lv_tick_inc(elapsed) from its UI task; LV_TICK_CUSTOM is off both sides). +// +// BACKLIGHT (Phase 2a): +// LEDC PWM on LEDC_TIMER_1 / LEDC_CHANNEL_1 (low-speed), ~44.1 kHz like the +// original Light_PWM. LEDC_TIMER_0 / LEDC_CHANNEL_0 stay reserved for the +// camera XCLK (qr_puzzle). display_ui_set_brightness() exposes duty 0-100%. +// +// Thread model: +// ALL LVGL and LovyanGFX calls run exclusively on the display_task. +// display_ui_set_status() copies the status under a mutex and sets a dirty +// flag; the task folds it into LVGL labels within ~50 ms. lv_tick_inc is +// called from the esp_timer task (atomic counter increment — the standard +// espressif pattern). + +#include "display_ui.h" +#include "board_pins_mediakit.h" + +#include +#include +#include +#include +#include + +#include "driver/gpio.h" +#include "driver/ledc.h" +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "esp_random.h" +#include "esp_timer.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/task.h" + +#define LGFX_USE_V1 +#include +#include "lvgl.h" + +// Original UI fonts (fonts/ — copied from ui_freenove_allinone/src/ui/fonts/). +LV_FONT_DECLARE(lv_font_orbitron_40); +LV_FONT_DECLARE(lv_font_ibmplexmono_18); + +static const char *TAG = "display_ui"; + +// ─── LovyanGFX device (faithful port of FreenoveLgfxDevice) ────────────────── + +class ZacusDisplay final : public lgfx::LGFX_Device { + public: + ZacusDisplay() { + // ── SPI bus ────────────────────────────────────────────────────────── + { + auto cfg = _bus_instance.config(); + cfg.spi_host = SPI2_HOST; // FSPI (FREENOVE_LCD_USE_HSPI == 0) + cfg.spi_mode = 0; + cfg.freq_write = 80000000; // 80 MHz + cfg.freq_read = 20000000; // 20 MHz + cfg.spi_3wire = false; + cfg.use_lock = false; + // CPU mode — DMA out-link init can be unavailable on this + // board/runtime mix (same note as original firmware, line ~145). + cfg.dma_channel = 0; + cfg.pin_sclk = TFT_PIN_SCK; + cfg.pin_mosi = TFT_PIN_MOSI; + cfg.pin_miso = TFT_PIN_MISO; // -1 (not wired) + cfg.pin_dc = TFT_PIN_DC; + _bus_instance.config(cfg); + _panel_instance.setBus(&_bus_instance); + } + + // ── Panel ───────────────────────────────────────────────────────────── + { + auto cfg = _panel_instance.config(); + cfg.pin_cs = -1; // CS not wired + cfg.pin_rst = TFT_PIN_RST; + cfg.pin_busy = -1; + cfg.memory_width = 320; + cfg.memory_height = 480; + cfg.panel_width = 320; + cfg.panel_height = 480; + cfg.offset_x = 0; + cfg.offset_y = 0; + cfg.offset_rotation = 0; + cfg.dummy_read_pixel = 8; + cfg.dummy_read_bits = 1; + cfg.readable = false; // MISO == -1 + cfg.invert = true; // TFT_INVERSION_ON in original + cfg.rgb_order = true; // not BGR + cfg.dlen_16bit = false; + cfg.bus_shared = true; + _panel_instance.config(cfg); + } + + // Backlight handled externally (LEDC PWM, see file header). + + setPanel(&_panel_instance); + } + + private: + lgfx::Bus_SPI _bus_instance; + lgfx::Panel_ST7796 _panel_instance; +}; + +static ZacusDisplay s_lcd; + +// ─── Status state ───────────────────────────────────────────────────────────── + +static display_status_t s_status; +static SemaphoreHandle_t s_mutex = NULL; +static volatile bool s_dirty = false; +static bool s_running = false; + +// ─── Palette (Amiga Workbench-ish) ──────────────────────────────────────────── +static const uint32_t COL_BG = 0x0055AA; // Workbench blue +static const uint32_t COL_TITLEBAR = 0x0000AA; // darker blue +static const uint32_t COL_TITLE_TXT = 0xFFFFFF; // white +static const uint32_t COL_LABEL = 0xAAAAAA; // mid grey +static const uint32_t COL_VALUE = 0xFFFFFF; // white +static const uint32_t COL_CODE = 0xFF8800; // Workbench orange + +// ─── LVGL plumbing ──────────────────────────────────────────────────────────── + +#define DUI_HOR_RES 480 // rotation 1 (landscape) +#define DUI_VER_RES 320 +#define DUI_BUF_LINES 40 // like the original (buffer_cfg_.lines) + +static lv_disp_draw_buf_t s_draw_buf; +static lv_color_t *s_buf1; +static lv_disp_drv_t s_disp_drv; +static esp_timer_handle_t s_tick_timer; + +// Canonical LovyanGFX↔LVGL8 glue (synchronous — matches the original's +// effective path; its DMA branch is disabled at runtime). +static void lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, + lv_color_t *color_p) { + const uint32_t w = (uint32_t) (area->x2 - area->x1 + 1); + const uint32_t h = (uint32_t) (area->y2 - area->y1 + 1); + s_lcd.startWrite(); + s_lcd.setAddrWindow(area->x1, area->y1, w, h); + s_lcd.writePixels((lgfx::rgb565_t *) &color_p->full, w * h); + s_lcd.endWrite(); + lv_disp_flush_ready(drv); +} + +static void lvgl_tick_cb(void *arg) { + (void) arg; + lv_tick_inc(2); +} + +// ─── LVGL status screen objects (display task only) ─────────────────────────── + +typedef struct { + lv_obj_t *value; // value label of a row +} dui_row_t; + +static lv_obj_t *s_scr_status; // status screen (no active step) +static lv_obj_t *s_lbl_title; +static dui_row_t s_row_ip, s_row_wake, s_row_step, s_row_armed, s_row_solved; +static lv_obj_t *s_lbl_code; + +// Scene view (active step) — structure mirrors the original ui_manager scene +// frame: title (Orbitron), subtitle (IBM Plex Mono), symbol slot (the +// assembled code, pulse effect = SceneEffect::kPulse default). +static lv_obj_t *s_scr_scene; +static lv_obj_t *s_scene_title; // step id, Orbitron 40, top-center +static lv_obj_t *s_scene_subtitle; // armed puzzle, IBM Plex 18, bottom +static lv_obj_t *s_scene_code; // assembled code, Orbitron 40, center + +// Camera viewfinder (QR aiming aid). The qr_puzzle scan task pushes +// grayscale QVGA frames via display_ui_camera_frame(); the display task +// converts to RGB565 into the canvas buffer at ~5 fps. +#define CAM_W 320 +#define CAM_H 240 +static lv_obj_t *s_scene_cam; // lv_canvas, PSRAM-backed +static lv_color_t *s_cam_canvas_buf; // CAM_W*CAM_H rgb565 (PSRAM) +static uint8_t *s_cam_gray; // incoming copy (PSRAM) +static SemaphoreHandle_t s_cam_mutex; +static volatile bool s_cam_fresh = false; +static bool s_cam_visible = false; + +// ─── Amiga Workbench shell (port of ui_amiga_shell, same grid metrics) ─────── +#define SHELL_GRID_COLS 4 // = AmigaUIShell::GRID_COLS +#define SHELL_ICON_SIZE 64 // = ICON_SIZE +#define SHELL_ICON_SPACING 16 // = ICON_SPACING +#define SHELL_GRID_X 16 // = GRID_START_X +#define SHELL_GRID_Y 48 // GRID_START_Y(32) + our taller title bar +#define SHELL_BUILTINS 5 +#define SHELL_MAX_TILES 12 +static lv_obj_t *s_scr_shell; +static lv_obj_t *s_shell_tiles[SHELL_MAX_TILES]; +// Written by the buttons task, applied by the display task. +static volatile uint8_t s_shell_sel = 0; +static volatile bool s_shell_open = false; +// Accent colors from the original shell palette (ui_amiga_shell.cpp:280-287). +static const struct { const char *name; uint32_t color; } +kShellBuiltins[SHELL_BUILTINS] = { + {"Statut", 0x00FFFF}, // cyan + {"Scene", 0xFF8800}, // orange + {"Auto", 0x00FF88}, // green + {"Lumiere", 0xFFFF00}, // yellow + {"Fichiers", 0x88FF00}, // lime — opens the file browser +}; +// Dynamic apps from /littlefs/apps// (icon.png optional, step.txt = the +// step to arm on launch — handled by main via the pending-launch poll). +typedef struct { char id[32]; } shell_dyn_app_t; +static shell_dyn_app_t s_shell_dyn[SHELL_MAX_TILES - SHELL_BUILTINS]; +static uint8_t s_shell_count = SHELL_BUILTINS; // builtins + dynamic +static bool s_shell_loaded = false; // lazy (littlefs mounts late) + +// Pending app launch (buttons task → main poll). +static char s_pending_launch[32]; +static volatile bool s_pending_launch_set = false; + +// ─── File browser (original shell's drawer, master edition) ───────────────── +#define BROWSER_MAX_ENTRIES 24 +static lv_obj_t *s_scr_browser; +static lv_obj_t *s_browser_list; // plain container, rebuilt on reload +static lv_obj_t *s_browser_title; +static char s_browser_path[96] = "/littlefs"; +static char s_browser_items[BROWSER_MAX_ENTRIES][40]; +static bool s_browser_isdir[BROWSER_MAX_ENTRIES]; +static uint8_t s_browser_count = 0; +static volatile uint8_t s_browser_sel = 0; +static volatile bool s_browser_open = false; +static volatile bool s_browser_reload = false; + +// ─── Intro — faithful port of the original cracktro ────────────────────────── +// Phase A from ui_manager_intro.cpp: 3-layer parallax starfield in Q8.8 +// fixed point (layer split 50/30/20%, speeds {54,116,198} px/s, sizes 1-3 px, +// opacities 30/60/100%, 1-in-8 twinkle), logo + drop shadow (offset 1,23), +// bottom sine-wave scrolltext, copper bars. Phases B/C/D are the original 3D +// FX (rotozoom, dot sphere, ray corridor) ported in intro_fx3d.cpp, rendered +// into a full-screen LVGL canvas behind the logo + scroller. If the canvas +// PSRAM allocation fails, the intro gracefully stays on phase A throughout. +#include "intro_fx3d.h" +#define INTRO_STARS 48 +#define INTRO_BARS 4 +#define INTRO_PHASE_A_MS 7000 +#define INTRO_FX_MS 3000 +#define INTRO_DURATION_MS (INTRO_PHASE_A_MS + 3 * INTRO_FX_MS) +typedef struct { int32_t x_q8, y_q8; uint8_t layer; int16_t speed; } intro_star_t; +static lv_obj_t *s_scr_intro; +static lv_obj_t *s_intro_logo, *s_intro_logo_shadow; +static lv_obj_t *s_intro_scroll; +static lv_obj_t *s_star_objs[INTRO_STARS]; +static intro_star_t s_stars[INTRO_STARS]; +static lv_obj_t *s_intro_bars[INTRO_BARS]; +static int32_t s_scroll_x_q8; // scrolltext x in Q8.8 +static uint32_t s_intro_t_ms; // elapsed intro time +static volatile bool s_intro_done = false; +static lv_obj_t *s_fx_canvas; // phases B/C/D render target +static lv_color_t *s_fx_canvas_buf; // 480x320 RGB565, PSRAM +static bool s_fx_tried, s_fx_ok; // lazy one-shot setup at phase B +static fx3d_mode_t s_fx_order[3]; // 3 distinct FX drawn per boot +// Original kIntro defaults: scroll 90 px/s, sine amp 14 px, period 120 px. +#define INTRO_SCROLL_PXS 90 +#define INTRO_SINE_AMP 14 +#define INTRO_SINE_PERIOD 120.0f + +static lv_obj_t *make_row(lv_obj_t *parent, int y, const char *label_txt, + dui_row_t *out) { + lv_obj_t *lbl = lv_label_create(parent); + lv_obj_set_style_text_color(lbl, lv_color_hex(COL_LABEL), 0); + lv_obj_set_style_text_font(lbl, &lv_font_montserrat_14, 0); + lv_label_set_text(lbl, label_txt); + lv_obj_set_pos(lbl, 8, y); + + lv_obj_t *val = lv_label_create(parent); + lv_obj_set_style_text_color(val, lv_color_hex(COL_VALUE), 0); + lv_obj_set_style_text_font(val, &lv_font_montserrat_14, 0); + lv_label_set_text(val, "..."); + lv_obj_set_pos(val, 160, y); + lv_label_set_long_mode(val, LV_LABEL_LONG_DOT); + lv_obj_set_width(val, DUI_HOR_RES - 168); + + out->value = val; + return val; +} + +// ─── Scene effects (ports of the original ui_manager SceneEffect set) ──────── + +// Pulse — SceneEffect::kPulse equivalent (opacity breathe on the symbol/code). +static void pulse_anim_cb(void *obj, int32_t v) { + lv_obj_set_style_opa((lv_obj_t *) obj, (lv_opa_t) v, 0); +} + +// Glitch — jitter + flicker on the title (text-artifact evocation of the +// original glitch overlay; the full pixel-shred shader is a later phase). +static lv_timer_t *s_glitch_timer; +static void glitch_timer_cb(lv_timer_t *t) { + (void) t; + const int dx = (int) (esp_random() % 7) - 3; + lv_obj_align(s_scene_title, LV_ALIGN_TOP_MID, dx, 24); + lv_obj_set_style_opa(s_scene_title, + (esp_random() % 8) ? LV_OPA_COVER : LV_OPA_60, 0); +} + +// Gyrophare — rotating beacon ring around the scene. +static lv_obj_t *s_gyro_arc; +static void gyro_anim_cb(void *obj, int32_t v) { + lv_arc_set_rotation((lv_obj_t *) obj, (uint16_t) v); +} + +static uint8_t s_fx_applied = 0xFF; // force first apply + +static void start_pulse(void) { + lv_anim_t a; + lv_anim_init(&a); + lv_anim_set_var(&a, s_scene_code); + lv_anim_set_exec_cb(&a, pulse_anim_cb); + lv_anim_set_values(&a, LV_OPA_COVER, LV_OPA_50); + lv_anim_set_time(&a, 600); + lv_anim_set_playback_time(&a, 600); + lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE); + lv_anim_start(&a); +} + +static void apply_effect(uint8_t fx) { + if (fx == s_fx_applied) return; + s_fx_applied = fx; + + // Stop everything, restore neutral state. + lv_anim_del(s_scene_code, pulse_anim_cb); + lv_obj_set_style_opa(s_scene_code, LV_OPA_COVER, 0); + if (s_glitch_timer) lv_timer_pause(s_glitch_timer); + lv_obj_align(s_scene_title, LV_ALIGN_TOP_MID, 0, 24); + lv_obj_set_style_opa(s_scene_title, LV_OPA_COVER, 0); + if (s_gyro_arc) { + lv_anim_del(s_gyro_arc, gyro_anim_cb); + lv_obj_add_flag(s_gyro_arc, LV_OBJ_FLAG_HIDDEN); + } + + switch (fx) { + case 0: // pulse (default) + start_pulse(); + break; + case 1: // glitch + if (s_glitch_timer) lv_timer_resume(s_glitch_timer); + break; + case 2: // gyro + if (s_gyro_arc) { + lv_obj_clear_flag(s_gyro_arc, LV_OBJ_FLAG_HIDDEN); + lv_anim_t a; + lv_anim_init(&a); + lv_anim_set_var(&a, s_gyro_arc); + lv_anim_set_exec_cb(&a, gyro_anim_cb); + lv_anim_set_values(&a, 0, 360); + lv_anim_set_time(&a, 1200); + lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE); + lv_anim_start(&a); + } + break; + default: // none + break; + } +} + +static void build_scene_screen(void) { + s_scr_scene = lv_obj_create(NULL); + lv_obj_set_style_bg_color(s_scr_scene, lv_color_hex(0x000000), 0); + lv_obj_set_style_bg_opa(s_scr_scene, LV_OPA_COVER, 0); + + // Viewfinder canvas (hidden unless the QR puzzle is armed). + s_cam_canvas_buf = (lv_color_t *) heap_caps_malloc( + (size_t) CAM_W * CAM_H * sizeof(lv_color_t), MALLOC_CAP_SPIRAM); + s_cam_gray = (uint8_t *) heap_caps_malloc( + (size_t) CAM_W * CAM_H, MALLOC_CAP_SPIRAM); + if (s_cam_canvas_buf && s_cam_gray) { + s_scene_cam = lv_canvas_create(s_scr_scene); + lv_canvas_set_buffer(s_scene_cam, s_cam_canvas_buf, + CAM_W, CAM_H, LV_IMG_CF_TRUE_COLOR); + lv_canvas_fill_bg(s_scene_cam, lv_color_hex(0x101010), LV_OPA_COVER); + lv_obj_align(s_scene_cam, LV_ALIGN_CENTER, 0, 8); + lv_obj_add_flag(s_scene_cam, LV_OBJ_FLAG_HIDDEN); + } else { + ESP_LOGW(TAG, "viewfinder buffers alloc failed — preview disabled"); + } + + s_scene_title = lv_label_create(s_scr_scene); + lv_obj_set_style_text_color(s_scene_title, lv_color_hex(COL_VALUE), 0); + lv_obj_set_style_text_font(s_scene_title, &lv_font_orbitron_40, 0); + lv_label_set_text(s_scene_title, ""); + lv_label_set_long_mode(s_scene_title, LV_LABEL_LONG_SCROLL_CIRCULAR); + lv_obj_set_width(s_scene_title, DUI_HOR_RES - 24); + lv_obj_set_style_text_align(s_scene_title, LV_TEXT_ALIGN_CENTER, 0); + lv_obj_align(s_scene_title, LV_ALIGN_TOP_MID, 0, 24); + + s_scene_code = lv_label_create(s_scr_scene); + lv_obj_set_style_text_color(s_scene_code, lv_color_hex(COL_CODE), 0); + lv_obj_set_style_text_font(s_scene_code, &lv_font_orbitron_40, 0); + lv_label_set_text(s_scene_code, ""); + lv_obj_align(s_scene_code, LV_ALIGN_CENTER, 0, 16); + + s_scene_subtitle = lv_label_create(s_scr_scene); + lv_obj_set_style_text_color(s_scene_subtitle, lv_color_hex(COL_LABEL), 0); + lv_obj_set_style_text_font(s_scene_subtitle, &lv_font_ibmplexmono_18, 0); + lv_label_set_text(s_scene_subtitle, ""); + lv_obj_align(s_scene_subtitle, LV_ALIGN_BOTTOM_LEFT, 8, -10); + + // Effect plumbing: glitch timer (paused until selected) + gyro ring + // (hidden until selected). Pulse starts via apply_effect on first status. + s_glitch_timer = lv_timer_create(glitch_timer_cb, 120, NULL); + lv_timer_pause(s_glitch_timer); + + s_gyro_arc = lv_arc_create(s_scr_scene); + lv_obj_set_size(s_gyro_arc, 300, 300); + lv_obj_center(s_gyro_arc); + lv_arc_set_bg_angles(s_gyro_arc, 0, 360); + lv_arc_set_angles(s_gyro_arc, 0, 60); // 60° beacon segment + lv_obj_remove_style(s_gyro_arc, NULL, LV_PART_KNOB); + lv_obj_clear_flag(s_gyro_arc, LV_OBJ_FLAG_CLICKABLE); + lv_obj_set_style_arc_opa(s_gyro_arc, LV_OPA_TRANSP, LV_PART_MAIN); + lv_obj_set_style_arc_color(s_gyro_arc, lv_color_hex(0xFF3300), + LV_PART_INDICATOR); + lv_obj_set_style_arc_width(s_gyro_arc, 8, LV_PART_INDICATOR); + lv_obj_add_flag(s_gyro_arc, LV_OBJ_FLAG_HIDDEN); +} + +static void build_shell_screen(void) { + s_scr_shell = lv_obj_create(NULL); + lv_obj_set_style_bg_color(s_scr_shell, lv_color_hex(COL_BG), 0); + lv_obj_set_style_bg_opa(s_scr_shell, LV_OPA_COVER, 0); + + lv_obj_t *bar = lv_obj_create(s_scr_shell); + lv_obj_remove_style_all(bar); + lv_obj_set_size(bar, DUI_HOR_RES, 36); + lv_obj_set_style_bg_color(bar, lv_color_hex(COL_TITLEBAR), 0); + lv_obj_set_style_bg_opa(bar, LV_OPA_COVER, 0); + lv_obj_t *t = lv_label_create(bar); + lv_obj_set_style_text_color(t, lv_color_hex(COL_TITLE_TXT), 0); + lv_obj_set_style_text_font(t, &lv_font_montserrat_24, 0); + lv_label_set_text(t, "Zacus Workbench"); + lv_obj_align(t, LV_ALIGN_LEFT_MID, 8, 0); + // Tiles are created lazily by shell_populate() — littlefs mounts after + // display_ui_init, so dynamic apps can't be scanned here. +} + +static lv_obj_t *shell_make_tile(int index, const char *name, uint32_t color, + const char *icon_path) { + const int col = index % SHELL_GRID_COLS; + const int row = index / SHELL_GRID_COLS; + lv_obj_t *tile = lv_obj_create(s_scr_shell); + lv_obj_remove_style_all(tile); + lv_obj_set_size(tile, SHELL_ICON_SIZE, SHELL_ICON_SIZE); + lv_obj_set_pos(tile, + SHELL_GRID_X + col * (SHELL_ICON_SIZE + SHELL_ICON_SPACING), + SHELL_GRID_Y + row * (SHELL_ICON_SIZE + SHELL_ICON_SPACING)); + lv_obj_set_style_bg_color(tile, lv_color_hex(color), 0); + lv_obj_set_style_bg_opa(tile, LV_OPA_COVER, 0); + lv_obj_set_style_radius(tile, 4, 0); // like the original + lv_obj_set_style_border_color(tile, lv_color_white(), 0); + lv_obj_set_style_border_width(tile, 1, 0); + + if (icon_path) { + // PNG icon via LVGL FS drive 'L' (→ /littlefs), like the original's + // "L:" + icon.png path. + lv_obj_t *img = lv_img_create(tile); + lv_img_set_src(img, icon_path); + lv_obj_align(img, LV_ALIGN_TOP_MID, 0, 2); + } + + lv_obj_t *lbl = lv_label_create(tile); + lv_obj_set_style_text_color(lbl, lv_color_hex(0x000000), 0); + lv_obj_set_style_text_font(lbl, &lv_font_montserrat_14, 0); + lv_label_set_text(lbl, name); + lv_obj_align(lbl, LV_ALIGN_BOTTOM_MID, 0, -4); + return tile; +} + +// Lazy tile creation: builtins + /littlefs/apps// (display task only). +static void shell_populate(void) { + if (s_shell_loaded) return; + s_shell_loaded = true; + + for (int i = 0; i < SHELL_BUILTINS; i++) { + s_shell_tiles[i] = shell_make_tile(i, kShellBuiltins[i].name, + kShellBuiltins[i].color, NULL); + } + s_shell_count = SHELL_BUILTINS; + + DIR *dir = opendir("/littlefs/apps"); + if (!dir) return; // no apps dir — builtins only + struct dirent *ent; + while ((ent = readdir(dir)) != NULL && + s_shell_count < SHELL_MAX_TILES) { + if (ent->d_type != DT_DIR || ent->d_name[0] == '.') continue; + shell_dyn_app_t *app = &s_shell_dyn[s_shell_count - SHELL_BUILTINS]; + strncpy(app->id, ent->d_name, sizeof(app->id) - 1); + app->id[sizeof(app->id) - 1] = '\0'; + + // Optional PNG icon (LVGL path with the L: drive). + char fs_path[96], lv_path[96]; + snprintf(fs_path, sizeof(fs_path), "/littlefs/apps/%s/icon.png", app->id); + struct stat st; + const bool has_icon = (stat(fs_path, &st) == 0); + snprintf(lv_path, sizeof(lv_path), "L:apps/%s/icon.png", app->id); + + s_shell_tiles[s_shell_count] = shell_make_tile( + s_shell_count, app->id, 0x335577, + has_icon ? lv_path : NULL); + s_shell_count++; + } + closedir(dir); + ESP_LOGI(TAG, "shell: %d tiles (%d apps from /littlefs/apps)", + s_shell_count, s_shell_count - SHELL_BUILTINS); +} + +// Highlight the selected tile (display task only). +static void apply_shell_selection(void) { + for (int i = 0; i < s_shell_count; i++) { + if (s_shell_tiles[i]) + lv_obj_set_style_border_width(s_shell_tiles[i], + (i == s_shell_sel) ? 4 : 1, 0); + } +} + +// ─── File browser (display task only for LVGL parts) ──────────────────────── + +static void build_browser_screen(void) { + s_scr_browser = lv_obj_create(NULL); + lv_obj_set_style_bg_color(s_scr_browser, lv_color_hex(0x000000), 0); + lv_obj_set_style_bg_opa(s_scr_browser, LV_OPA_COVER, 0); + + lv_obj_t *bar = lv_obj_create(s_scr_browser); + lv_obj_remove_style_all(bar); + lv_obj_set_size(bar, DUI_HOR_RES, 36); + lv_obj_set_style_bg_color(bar, lv_color_hex(COL_TITLEBAR), 0); + lv_obj_set_style_bg_opa(bar, LV_OPA_COVER, 0); + s_browser_title = lv_label_create(bar); + lv_obj_set_style_text_color(s_browser_title, lv_color_hex(COL_TITLE_TXT), 0); + lv_obj_set_style_text_font(s_browser_title, &lv_font_ibmplexmono_18, 0); + lv_label_set_text(s_browser_title, s_browser_path); + lv_obj_align(s_browser_title, LV_ALIGN_LEFT_MID, 8, 0); + + s_browser_list = lv_obj_create(s_scr_browser); + lv_obj_remove_style_all(s_browser_list); + lv_obj_set_size(s_browser_list, DUI_HOR_RES, DUI_VER_RES - 36); + lv_obj_set_pos(s_browser_list, 0, 36); +} + +static void browser_reload(void) { + s_browser_count = 0; + DIR *dir = opendir(s_browser_path); + if (dir) { + struct dirent *ent; + while ((ent = readdir(dir)) != NULL && + s_browser_count < BROWSER_MAX_ENTRIES) { + strncpy(s_browser_items[s_browser_count], ent->d_name, + sizeof(s_browser_items[0]) - 1); + s_browser_items[s_browser_count][sizeof(s_browser_items[0]) - 1] = '\0'; + s_browser_isdir[s_browser_count] = (ent->d_type == DT_DIR); + s_browser_count++; + } + closedir(dir); + } + + lv_obj_clean(s_browser_list); + lv_label_set_text(s_browser_title, s_browser_path); + for (int i = 0; i < s_browser_count; i++) { + lv_obj_t *row = lv_label_create(s_browser_list); + lv_obj_set_style_text_font(row, &lv_font_ibmplexmono_18, 0); + lv_obj_set_pos(row, 12, 6 + i * 22); + char line[56]; + snprintf(line, sizeof(line), "%s%s", + s_browser_isdir[i] ? "[D] " : " ", s_browser_items[i]); + lv_label_set_text(row, line); + lv_obj_set_style_text_color(row, + (i == s_browser_sel) ? lv_color_hex(COL_CODE) : lv_color_white(), 0); + } + if (s_browser_count == 0) { + lv_obj_t *row = lv_label_create(s_browser_list); + lv_obj_set_style_text_font(row, &lv_font_ibmplexmono_18, 0); + lv_obj_set_pos(row, 12, 6); + lv_obj_set_style_text_color(row, lv_color_hex(COL_LABEL), 0); + lv_label_set_text(row, "(vide)"); + } +} + +static void build_intro_screen(void) { + s_scr_intro = lv_obj_create(NULL); + lv_obj_set_style_bg_color(s_scr_intro, lv_color_hex(0x000000), 0); + lv_obj_set_style_bg_opa(s_scr_intro, LV_OPA_COVER, 0); + + // Starfield — original layer split: 50% layer0, 30% layer1, 20% layer2. + static const int16_t kSpeeds[3] = {54, 116, 198}; // speeds_fast + const int l0_end = (INTRO_STARS * 50) / 100; + const int l1_end = (INTRO_STARS * 80) / 100; + for (int i = 0; i < INTRO_STARS; i++) { + const uint8_t layer = (i < l0_end) ? 0 : (i < l1_end) ? 1 : 2; + intro_star_t *st = &s_stars[i]; + st->layer = layer; + st->speed = kSpeeds[layer]; + st->x_q8 = (int32_t) (esp_random() % DUI_HOR_RES) << 8; + st->y_q8 = (int32_t) (esp_random() % DUI_VER_RES) << 8; + + lv_obj_t *star = lv_obj_create(s_scr_intro); + lv_obj_remove_style_all(star); + lv_obj_set_size(star, 1 + layer, 1 + layer); + lv_obj_set_style_radius(star, LV_RADIUS_CIRCLE, 0); + lv_obj_set_style_bg_color(star, + (layer == 2) ? lv_color_hex(0x00FFFF) : lv_color_white(), 0); + lv_obj_set_style_bg_opa(star, + (layer == 0) ? LV_OPA_30 : (layer == 1) ? LV_OPA_60 : LV_OPA_COVER, 0); + lv_obj_set_pos(star, st->x_q8 >> 8, st->y_q8 >> 8); + s_star_objs[i] = star; + } + + // Copper bars (4 horizontal gradient bars bobbing on sines). + static const uint32_t kBarCols[INTRO_BARS] = + {0xFF0088, 0x0088FF, 0x00FF88, 0xFFFF00}; + for (int i = 0; i < INTRO_BARS; i++) { + lv_obj_t *bar = lv_obj_create(s_scr_intro); + lv_obj_remove_style_all(bar); + lv_obj_set_size(bar, DUI_HOR_RES, 6); + lv_obj_set_style_bg_color(bar, lv_color_hex(kBarCols[i]), 0); + lv_obj_set_style_bg_opa(bar, LV_OPA_70, 0); + lv_obj_set_pos(bar, 0, 60 + i * 14); + s_intro_bars[i] = bar; + } + + // Logo + drop shadow (original offsets: shadow at TOP_MID +1,+23; + // logo at TOP_MID 0,22 — shadow drawn first, behind). + s_intro_logo_shadow = lv_label_create(s_scr_intro); + lv_obj_set_style_text_color(s_intro_logo_shadow, lv_color_hex(0x222222), 0); + lv_obj_set_style_text_font(s_intro_logo_shadow, &lv_font_orbitron_40, 0); + lv_label_set_text(s_intro_logo_shadow, "Professeur ZACUS"); + lv_obj_align(s_intro_logo_shadow, LV_ALIGN_TOP_MID, 1, 23); + + s_intro_logo = lv_label_create(s_scr_intro); + lv_obj_set_style_text_color(s_intro_logo, lv_color_hex(COL_CODE), 0); + lv_obj_set_style_text_font(s_intro_logo, &lv_font_orbitron_40, 0); + lv_label_set_text(s_intro_logo, "Professeur ZACUS"); + lv_obj_align(s_intro_logo, LV_ALIGN_TOP_MID, 0, 22); + + // Bottom sine scroller. + s_intro_scroll = lv_label_create(s_scr_intro); + lv_obj_set_style_text_color(s_intro_scroll, lv_color_hex(0x00FFFF), 0); + lv_obj_set_style_text_font(s_intro_scroll, &lv_font_ibmplexmono_18, 0); + lv_label_set_text(s_intro_scroll, + "*** ZACUS MASTER CRACKTRO *** LE MYSTERE CONTINUE ... " + "GREETINGS TO THE GAME MASTER ... "); + s_scroll_x_q8 = (int32_t) DUI_HOR_RES << 8; + lv_obj_set_pos(s_intro_scroll, DUI_HOR_RES, DUI_VER_RES - 60); +} + +// Phase B entry: allocate the FX module + a full-screen canvas behind the +// logo/scroller, hide the phase-A starfield + bars. One-shot; on failure the +// intro simply keeps the starfield (s_fx_ok stays false). Display task only. +static bool intro_fx_setup(void) { + if (!fx3d_init()) return false; + // Fisher-Yates over the 6-FX pool, keep the first 3: each boot shows a + // different random selection/order without lengthening the intro. + fx3d_mode_t pool[FX3D_MODE_COUNT]; + for (int i = 0; i < FX3D_MODE_COUNT; i++) pool[i] = (fx3d_mode_t) i; + for (int i = FX3D_MODE_COUNT - 1; i > 0; i--) { + const int j = (int) (esp_random() % (uint32_t) (i + 1)); + const fx3d_mode_t tmp = pool[i]; pool[i] = pool[j]; pool[j] = tmp; + } + for (int i = 0; i < 3; i++) s_fx_order[i] = pool[i]; + ESP_LOGI(TAG, "intro fx: order=%d,%d,%d (0=roto 1=sphere 2=corridor " + "3=star3d 4=voxel 5=cube)", + (int) s_fx_order[0], (int) s_fx_order[1], (int) s_fx_order[2]); + s_fx_canvas_buf = (lv_color_t *) heap_caps_malloc( + (size_t) DUI_HOR_RES * DUI_VER_RES * sizeof(lv_color_t), + MALLOC_CAP_SPIRAM); + if (!s_fx_canvas_buf) return false; + s_fx_canvas = lv_canvas_create(s_scr_intro); + lv_canvas_set_buffer(s_fx_canvas, s_fx_canvas_buf, + DUI_HOR_RES, DUI_VER_RES, LV_IMG_CF_TRUE_COLOR); + lv_obj_set_pos(s_fx_canvas, 0, 0); + lv_obj_move_background(s_fx_canvas); + for (int i = 0; i < INTRO_STARS; i++) + lv_obj_add_flag(s_star_objs[i], LV_OBJ_FLAG_HIDDEN); + for (int i = 0; i < INTRO_BARS; i++) + lv_obj_add_flag(s_intro_bars[i], LV_OBJ_FLAG_HIDDEN); + return true; +} + +// Per-tick intro animation (display task only; dt in ms). Faithful Q8.8 math. +static void update_intro(uint32_t dt_ms) { + s_intro_t_ms += dt_ms; + + // Phase sequencing: A starfield, then B/C/D = rotozoom, dot sphere, + // ray corridor (original fx_mode order) on the FX canvas. + if (s_intro_t_ms >= INTRO_PHASE_A_MS && !s_fx_tried) { + s_fx_tried = true; + s_fx_ok = intro_fx_setup(); + } + if (s_fx_ok && s_intro_t_ms >= INTRO_PHASE_A_MS) { + int fx = (int) ((s_intro_t_ms - INTRO_PHASE_A_MS) / INTRO_FX_MS); + if (fx > 2) fx = 2; + // Render every other tick (20 ms): the SPI flush of a full-screen + // canvas dominates anyway, no point racing it. + static bool skip; + skip = !skip; + if (!skip) { + fx3d_render(s_fx_order[fx], s_intro_t_ms, + (uint16_t *) s_fx_canvas_buf, DUI_HOR_RES, DUI_VER_RES); + lv_obj_invalidate(s_fx_canvas); + } + goto scroller; // stars/bars hidden — skip their updates + } + + // Starfield: fall + wrap + twinkle (original update loop). + for (int i = 0; i < INTRO_STARS; i++) { + intro_star_t *st = &s_stars[i]; + st->y_q8 += (int32_t) (((uint32_t) st->speed * dt_ms * 256u) / 1000u); + if (st->y_q8 > ((int32_t) (DUI_VER_RES + 4) << 8)) { + st->y_q8 = -(int32_t) ((esp_random() % 36u) << 8); + st->x_q8 = (int32_t) (esp_random() % DUI_HOR_RES) << 8; + } + if ((esp_random() & 0x7u) == 0u) { + lv_obj_set_style_bg_opa(s_star_objs[i], + (lv_opa_t) (96u + (esp_random() % 160u)), 0); + } + lv_obj_set_pos(s_star_objs[i], st->x_q8 >> 8, st->y_q8 >> 8); + } + + // Copper bars: vertical sine bob, phase-shifted per bar. + for (int i = 0; i < INTRO_BARS; i++) { + const float ph = (float) s_intro_t_ms * 0.004f + (float) i * 0.9f; + lv_obj_set_y(s_intro_bars[i], 60 + i * 14 + (int) (sinf(ph) * 18.0f)); + } + +scroller: + // Sine scroller: leftward at INTRO_SCROLL_PXS, y rides a sine of the + // x position (original sine_amp/sine_period defaults). + s_scroll_x_q8 -= (int32_t) ((INTRO_SCROLL_PXS * dt_ms * 256u) / 1000u); + const int sx = s_scroll_x_q8 >> 8; + const int w = lv_obj_get_width(s_intro_scroll); + if (sx < -w) s_scroll_x_q8 = (int32_t) DUI_HOR_RES << 8; + const int sy = DUI_VER_RES - 60 + + (int) (sinf((float) sx / INTRO_SINE_PERIOD * 6.2832f) * + INTRO_SINE_AMP); + lv_obj_set_pos(s_intro_scroll, sx, sy); +} + +static void build_status_screen(void) { + lv_obj_t *scr = s_scr_status; + lv_obj_set_style_bg_color(scr, lv_color_hex(COL_BG), 0); + lv_obj_set_style_bg_opa(scr, LV_OPA_COVER, 0); + + // Title bar + lv_obj_t *bar = lv_obj_create(scr); + lv_obj_remove_style_all(bar); + lv_obj_set_size(bar, DUI_HOR_RES, 36); + lv_obj_set_pos(bar, 0, 0); + lv_obj_set_style_bg_color(bar, lv_color_hex(COL_TITLEBAR), 0); + lv_obj_set_style_bg_opa(bar, LV_OPA_COVER, 0); + + s_lbl_title = lv_label_create(bar); + lv_obj_set_style_text_color(s_lbl_title, lv_color_hex(COL_TITLE_TXT), 0); + lv_obj_set_style_text_font(s_lbl_title, &lv_font_montserrat_24, 0); + lv_label_set_text(s_lbl_title, "ZACUS MASTER"); + lv_obj_align(s_lbl_title, LV_ALIGN_LEFT_MID, 8, 0); + + // Rows + int y = 52; + const int LINE_H = 30; + make_row(scr, y, "IP", &s_row_ip); y += LINE_H; + make_row(scr, y, "Wake", &s_row_wake); y += LINE_H; + make_row(scr, y, "Etape", &s_row_step); y += LINE_H; + make_row(scr, y, "Enigme", &s_row_armed); y += LINE_H; + make_row(scr, y, "Resolues", &s_row_solved); y += LINE_H; + + // Assembled code — big, orange + s_lbl_code = lv_label_create(scr); + lv_obj_set_style_text_color(s_lbl_code, lv_color_hex(COL_CODE), 0); + lv_obj_set_style_text_font(s_lbl_code, &lv_font_montserrat_28, 0); + lv_label_set_text(s_lbl_code, "Code: ---"); + lv_obj_set_pos(s_lbl_code, 8, y + 12); +} + +static void apply_status(const display_status_t *s) { + // Status screen rows + lv_label_set_text(s_row_ip.value, s->ip[0] ? s->ip : "(aucune)"); + lv_label_set_text(s_row_wake.value, s->wake_active ? "oui" : "non"); + lv_label_set_text(s_row_step.value, s->step_id[0] ? s->step_id : "(aucune)"); + lv_label_set_text(s_row_armed.value, s->armed[0] ? s->armed : "none"); + + char tmp[48]; + snprintf(tmp, sizeof(tmp), "%u", (unsigned) s->solved_count); + lv_label_set_text(s_row_solved.value, tmp); + + char code[32]; + snprintf(code, sizeof(code), "Code: %s", s->code[0] ? s->code : "---"); + lv_label_set_text(s_lbl_code, code); + + // Scene screen — scene metadata wins, with sensible fallbacks. + lv_label_set_text(s_scene_title, + s->scene_title[0] ? s->scene_title : s->step_id); + if (s->scene_subtitle[0]) { + lv_label_set_text(s_scene_subtitle, s->scene_subtitle); + } else { + if (s->armed[0] && strcmp(s->armed, "none") != 0) { + snprintf(tmp, sizeof(tmp), "enigme: %s", s->armed); + } else { + snprintf(tmp, sizeof(tmp), "resolues: %u", + (unsigned) s->solved_count); + } + lv_label_set_text(s_scene_subtitle, tmp); + } + // Center slot: scenario symbol when provided, else the assembled code. + lv_label_set_text(s_scene_code, + s->scene_symbol[0] ? s->scene_symbol + : (s->code[0] ? s->code : "")); + apply_effect(s->scene_effect); + + // Viewfinder: visible only while the QR puzzle is armed. When visible, + // the centered code label yields its slot to the canvas (code moves into + // the subtitle line). + s_cam_visible = (s_scene_cam != NULL) && (strcmp(s->armed, "qr") == 0); + if (s_scene_cam) { + if (s_cam_visible) { + lv_obj_clear_flag(s_scene_cam, LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(s_scene_code, LV_OBJ_FLAG_HIDDEN); + if (s->code[0]) { + snprintf(tmp, sizeof(tmp), "enigme: qr code: %s", s->code); + lv_label_set_text(s_scene_subtitle, tmp); + } + } else { + lv_obj_add_flag(s_scene_cam, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(s_scene_code, LV_OBJ_FLAG_HIDDEN); + } + } + + // Active step → scene view; idle → status view. The 5-way buttons can + // override (1=force status, 2=force scene; 0=auto). Fade 240 ms, the + // original ui_manager default transition (SceneTransition::kFade, 240). + extern volatile uint8_t g_dui_view_override; + lv_obj_t *want; + if (!s_intro_done) { + want = s_scr_intro; // boot intro plays out first + } else if (s_browser_open) { + want = s_scr_browser; // file browser on top of everything + } else if (s_shell_open) { + want = s_scr_shell; // Workbench shell on top + } else switch (g_dui_view_override) { + case 1: want = s_scr_status; break; + case 2: want = s_scr_scene; break; + default: want = s->step_id[0] ? s_scr_scene : s_scr_status; break; + } + if (lv_scr_act() != want) { + lv_scr_load_anim(want, LV_SCR_LOAD_ANIM_FADE_ON, 240, 0, false); + } + extern volatile bool g_dui_on_scene; + g_dui_on_scene = (want == s_scr_scene); +} + +// View override shared with display_ui_handle_key (byte writes are atomic). +volatile uint8_t g_dui_view_override = 0; +// Mirror of the active view, written ONLY by the display task — lets +// display_ui_handle_key decide without calling LVGL from the buttons task. +volatile bool g_dui_on_scene = false; + +// Convert the freshest grayscale frame into the canvas (display task only). +static void refresh_viewfinder(void) { + if (!s_cam_visible || !s_cam_fresh || !s_cam_mutex) return; + + if (xSemaphoreTake(s_cam_mutex, pdMS_TO_TICKS(10)) != pdTRUE) return; + // Convert in place from the shared gray buffer into the canvas buffer. + const uint8_t *g = s_cam_gray; + uint16_t *dst = (uint16_t *) s_cam_canvas_buf; + for (size_t i = 0; i < (size_t) CAM_W * CAM_H; i++) { + const uint8_t v = g[i]; + dst[i] = (uint16_t) (((v >> 3) << 11) | ((v >> 2) << 5) | (v >> 3)); + } + s_cam_fresh = false; + xSemaphoreGive(s_cam_mutex); + + lv_obj_invalidate(s_scene_cam); +} + +// ─── Raw splash (pre-LVGL, visible during the rest of boot) ─────────────────── + +static void render_splash(void) { + const int W = s_lcd.width(); + const int H = s_lcd.height(); + + s_lcd.fillScreen(COL_BG); + s_lcd.fillRect(0, 0, W, 36, COL_TITLEBAR); + s_lcd.setTextColor(COL_TITLE_TXT); + s_lcd.setFont(&fonts::Font4); + s_lcd.setCursor(8, 8); + s_lcd.print("ZACUS MASTER"); + + s_lcd.setFont(&fonts::Font2); + s_lcd.setTextColor(COL_VALUE); + s_lcd.setCursor(8, 50); + s_lcd.print("Demarrage..."); + + s_lcd.setTextColor(COL_LABEL); + s_lcd.setCursor(8, H - 20); + s_lcd.print("FNK0102H ESP-IDF 5.4 Phase 2a"); +} + +// ─── Backlight (LEDC PWM — free pair; camera XCLK owns TIMER_0/CHANNEL_0) ───── + +#define DUI_BL_TIMER LEDC_TIMER_1 +#define DUI_BL_CHANNEL LEDC_CHANNEL_1 +#define DUI_BL_FREQ_HZ 44100 // like the original Light_PWM +#define DUI_BL_RES LEDC_TIMER_8_BIT + +static esp_err_t backlight_init(void) { + ledc_timer_config_t tcfg = {}; + tcfg.speed_mode = LEDC_LOW_SPEED_MODE; + tcfg.timer_num = DUI_BL_TIMER; + tcfg.duty_resolution = DUI_BL_RES; + tcfg.freq_hz = DUI_BL_FREQ_HZ; + tcfg.clk_cfg = LEDC_AUTO_CLK; + esp_err_t err = ledc_timer_config(&tcfg); + if (err != ESP_OK) return err; + + ledc_channel_config_t ccfg = {}; + ccfg.gpio_num = TFT_PIN_BL; + ccfg.speed_mode = LEDC_LOW_SPEED_MODE; + ccfg.channel = DUI_BL_CHANNEL; + ccfg.timer_sel = DUI_BL_TIMER; + ccfg.duty = 255; // full on by default + ccfg.hpoint = 0; + return ledc_channel_config(&ccfg); +} + +extern "C" void display_ui_set_brightness(uint8_t pct) { + if (pct > 100) pct = 100; + const uint32_t duty = (255u * pct) / 100u; + ledc_set_duty(LEDC_LOW_SPEED_MODE, DUI_BL_CHANNEL, duty); + ledc_update_duty(LEDC_LOW_SPEED_MODE, DUI_BL_CHANNEL); +} + +// ─── Display task ───────────────────────────────────────────────────────────── + +static void display_task(void *arg) { + (void) arg; + + // LVGL lives entirely on this task. + s_scr_status = lv_scr_act(); + build_status_screen(); + build_scene_screen(); + build_shell_screen(); + build_browser_screen(); + build_intro_screen(); + + // Boot intro: shown immediately, hands over after INTRO_DURATION_MS + // (any 5-way key skips it — see display_ui_handle_key). + lv_scr_load(s_scr_intro); + TickType_t intro_until = xTaskGetTickCount() + + pdMS_TO_TICKS(INTRO_DURATION_MS); + + display_status_t local_status; + memset(&local_status, 0, sizeof(local_status)); + + TickType_t last_cam = 0; + uint8_t shell_sel_applied = 0xFF; + bool shell_open_applied = false; + + for (;;) { + // Intro: animate while active, hand over at the deadline. dt is the + // REAL elapsed time: the loop period is vTaskDelay(10) + LVGL render + // (up to ~30 ms on a full-canvas frame), so a fixed dt=10 would make + // the intro clock lag wall time and starve the FX phases. + if (!s_intro_done) { + static TickType_t intro_prev_ticks; + const TickType_t now_ticks = xTaskGetTickCount(); + if (intro_prev_ticks == 0) intro_prev_ticks = now_ticks; + uint32_t dt = (uint32_t) pdTICKS_TO_MS(now_ticks - intro_prev_ticks); + intro_prev_ticks = now_ticks; + if (dt > 100) dt = 100; + update_intro(dt); + if (now_ticks >= intro_until) { + s_intro_done = true; + s_dirty = true; // re-evaluate the wanted screen + } + } + + // Shell state (selection highlight / open-close) from the buttons task. + if (s_shell_sel != shell_sel_applied || + s_shell_open != shell_open_applied) { + shell_sel_applied = s_shell_sel; + shell_open_applied = s_shell_open; + if (s_shell_open) shell_populate(); // lazy: littlefs mounts late + apply_shell_selection(); + s_dirty = true; + } + + // Browser reload requested (path change / open / selection move). + if (s_browser_reload) { + s_browser_reload = false; + browser_reload(); + s_dirty = true; + } + + if (s_dirty && + xSemaphoreTake(s_mutex, pdMS_TO_TICKS(20)) == pdTRUE) { + memcpy(&local_status, &s_status, sizeof(local_status)); + s_dirty = false; + xSemaphoreGive(s_mutex); + apply_status(&local_status); + } + + // Viewfinder refresh, throttled to ~5 fps (full-frame gray→rgb565 + // conversion is ~77k pixels — keep the UI task responsive). + const TickType_t now = xTaskGetTickCount(); + if ((now - last_cam) >= pdMS_TO_TICKS(200)) { + refresh_viewfinder(); + last_cam = now; + } + + lv_timer_handler(); + vTaskDelay(pdMS_TO_TICKS(10)); + } +} + +// ─── Public API ────────────────────────────────────────────────────────────── + +extern "C" esp_err_t display_ui_init(void) { + // Backlight PWM up first (panel garbage stays dark otherwise). + esp_err_t err = backlight_init(); + if (err != ESP_OK) { + ESP_LOGW(TAG, "backlight LEDC init: %s — falling back to plain GPIO", + esp_err_to_name(err)); + gpio_config_t bl_cfg = {}; + bl_cfg.pin_bit_mask = (1ULL << TFT_PIN_BL); + bl_cfg.mode = GPIO_MODE_OUTPUT; + gpio_config(&bl_cfg); + gpio_set_level((gpio_num_t) TFT_PIN_BL, 1); + } + + // Initialise panel + if (!s_lcd.init()) { + ESP_LOGE(TAG, "LovyanGFX panel init failed"); + return ESP_FAIL; + } + s_lcd.setRotation(1); // landscape, matches FREENOVE_LCD_ROTATION 1 + + ESP_LOGI(TAG, "ST7796 panel up (%dx%d)", + (int) s_lcd.width(), (int) s_lcd.height()); + + render_splash(); + + // ── LVGL ────────────────────────────────────────────────────────────── + lv_init(); + + // Draw buffer: width × 40 lines, single. DEVIATION from the original + // (draw_in_psram = false): on this firmware the camera DMA needs a 30 KB + // contiguous INTERNAL block (cam_hal), and a 38.4 KB internal draw buffer + // starved it (largest free block dropped to 28 KB → esp_camera_init + // ESP_FAIL). PSRAM costs some blit speed in CPU mode but keeps internal + // RAM for camera/voice DMA. The original Arduino UI never ran esp-sr + + // WS streaming + camera concurrently, so it could afford internal. + const size_t buf_px = (size_t) DUI_HOR_RES * DUI_BUF_LINES; + s_buf1 = (lv_color_t *) heap_caps_malloc(buf_px * sizeof(lv_color_t), + MALLOC_CAP_SPIRAM); + if (!s_buf1) { + ESP_LOGE(TAG, "LVGL draw buffer alloc failed (%u B)", + (unsigned) (buf_px * sizeof(lv_color_t))); + return ESP_ERR_NO_MEM; + } + lv_disp_draw_buf_init(&s_draw_buf, s_buf1, NULL, buf_px); + + lv_disp_drv_init(&s_disp_drv); + s_disp_drv.hor_res = DUI_HOR_RES; + s_disp_drv.ver_res = DUI_VER_RES; + s_disp_drv.flush_cb = lvgl_flush_cb; + s_disp_drv.draw_buf = &s_draw_buf; + if (!lv_disp_drv_register(&s_disp_drv)) { + ESP_LOGE(TAG, "lv_disp_drv_register failed"); + return ESP_FAIL; + } + + // Tick: 2 ms periodic (LV_TICK_CUSTOM off, like the original). + const esp_timer_create_args_t targs = { + .callback = lvgl_tick_cb, + .arg = NULL, + .dispatch_method = ESP_TIMER_TASK, + .name = "lv_tick", + .skip_unhandled_events = true, + }; + err = esp_timer_create(&targs, &s_tick_timer); + if (err == ESP_OK) err = esp_timer_start_periodic(s_tick_timer, 2000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "LVGL tick timer: %s", esp_err_to_name(err)); + return err; + } + + ESP_LOGI(TAG, "LVGL %d.%d up (buf %u lines, PSRAM)", + lv_version_major(), lv_version_minor(), (unsigned) DUI_BUF_LINES); + + // State + s_mutex = xSemaphoreCreateMutex(); + s_cam_mutex = xSemaphoreCreateMutex(); + if (!s_mutex || !s_cam_mutex) { + ESP_LOGE(TAG, "failed to create mutex"); + return ESP_ERR_NO_MEM; + } + memset(&s_status, 0, sizeof(s_status)); + s_dirty = false; + s_running = true; + + // Spawn refresh task (ALL LVGL + panel access lives here). + BaseType_t rc = xTaskCreate(display_task, "display_ui", 8192, NULL, 3, NULL); + if (rc != pdPASS) { + ESP_LOGE(TAG, "xTaskCreate failed"); + return ESP_ERR_NO_MEM; + } + + ESP_LOGI(TAG, "display_ui_init OK — LVGL status screen active"); + return ESP_OK; +} + +extern "C" void display_ui_set_status(const display_status_t *s) { + if (!s || !s_running || !s_mutex) { + return; + } + if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(50)) == pdTRUE) { + memcpy(&s_status, s, sizeof(s_status)); + s_dirty = true; + xSemaphoreGive(s_mutex); + } +} + +extern "C" void display_ui_handle_key(uint8_t key) { + static uint8_t s_brightness = 100; + + // Any key skips the boot intro. + if (!s_intro_done) { + s_intro_done = true; + s_dirty = true; + return; + } + + // ── Browser open: list navigation (SELECT enters dirs, MENU goes up + // then closes at the littlefs root). + if (s_browser_open) { + switch (key) { + case 2: // DOWN + if (s_browser_sel + 1 < s_browser_count) s_browser_sel++; + s_browser_reload = true; + break; + case 5: // UP + if (s_browser_sel > 0) s_browser_sel--; + s_browser_reload = true; + break; + case 1: { // SELECT — enter directory + const uint8_t sel = s_browser_sel; + if (sel < s_browser_count && s_browser_isdir[sel]) { + const size_t len = strlen(s_browser_path); + snprintf(s_browser_path + len, sizeof(s_browser_path) - len, + "/%s", s_browser_items[sel]); + s_browser_sel = 0; + s_browser_reload = true; + } + break; + } + case 3: { // MENU — up one level, close at root + char *slash = strrchr(s_browser_path, '/'); + if (slash && slash != s_browser_path && + strcmp(s_browser_path, "/littlefs") != 0) { + *slash = '\0'; + s_browser_sel = 0; + s_browser_reload = true; + } else { + s_browser_open = false; + s_shell_open = true; // back to the shell + } + break; + } + default: + break; + } + s_dirty = true; + return; + } + + // ── Shell open: navigate the Workbench grid (original key semantics: + // 1=SELECT launch, 2=DOWN row, 3=MENU close, 4=LEFT/RIGHT, 5=UP row). + if (s_shell_open) { + switch (key) { + case 1: { // SELECT — launch tile + const uint8_t sel = s_shell_sel; + s_shell_open = false; + switch (sel) { + case 0: g_dui_view_override = 1; break; // Statut + case 1: g_dui_view_override = 2; break; // Scene + case 2: g_dui_view_override = 0; break; // Auto + case 3: // Lumiere — cycle + s_brightness = (s_brightness <= 20) ? 100 + : (uint8_t)(s_brightness - 40); + display_ui_set_brightness(s_brightness); + s_shell_open = true; // stay in the shell for feedback + break; + case 4: // Fichiers — browser + strncpy(s_browser_path, "/littlefs", sizeof(s_browser_path)); + s_browser_sel = 0; + s_browser_open = true; + s_browser_reload = true; + break; + default: // dynamic app + if (sel < s_shell_count && !s_pending_launch_set) { + strncpy(s_pending_launch, + s_shell_dyn[sel - SHELL_BUILTINS].id, + sizeof(s_pending_launch) - 1); + s_pending_launch[sizeof(s_pending_launch) - 1] = '\0'; + s_pending_launch_set = true; + } + break; + } + break; + } + case 2: // DOWN — next row + if (s_shell_sel + SHELL_GRID_COLS < s_shell_count) + s_shell_sel = (uint8_t)(s_shell_sel + SHELL_GRID_COLS); + break; + case 5: // UP — previous row + if (s_shell_sel >= SHELL_GRID_COLS) + s_shell_sel = (uint8_t)(s_shell_sel - SHELL_GRID_COLS); + break; + case 4: // LEFT/RIGHT — next column (wraps) + s_shell_sel = (uint8_t)((s_shell_sel + 1) % s_shell_count); + break; + case 3: // MENU — close shell + s_shell_open = false; + break; + default: + break; + } + s_dirty = true; + return; + } + + // ── Shell closed: view shortcuts + brightness. + switch (key) { + case 1: // SELECT — toggle scene↔status manually + case 4: { // LEFT/RIGHT — same toggle + // g_dui_on_scene is the display task's mirror — no LVGL call here. + g_dui_view_override = g_dui_on_scene ? 1 : 2; + s_dirty = true; // wake the display task decision + break; + } + case 3: // MENU — open the Workbench shell + s_shell_open = true; + s_dirty = true; + break; + case 5: // UP — brightness + + s_brightness = (s_brightness >= 80) ? 100 : s_brightness + 20; + display_ui_set_brightness(s_brightness); + break; + case 2: // DOWN — brightness − + s_brightness = (s_brightness <= 20) ? 10 : s_brightness - 20; + display_ui_set_brightness(s_brightness); + break; + default: + break; + } +} + +extern "C" bool display_ui_take_pending_launch(char *id_out, size_t cap) { + if (!s_pending_launch_set || !id_out || cap == 0) return false; + strncpy(id_out, s_pending_launch, cap - 1); + id_out[cap - 1] = '\0'; + s_pending_launch_set = false; + return true; +} + +extern "C" void display_ui_camera_frame(const uint8_t *gray, int width, int height) { + // Runs in the qr_puzzle scan task — copy fast, never block scanning. + if (!gray || width != CAM_W || height != CAM_H) return; + if (!s_running || !s_cam_mutex || !s_cam_gray || !s_cam_visible) return; + if (xSemaphoreTake(s_cam_mutex, 0) != pdTRUE) return; // skip frame if busy + memcpy(s_cam_gray, gray, (size_t) CAM_W * CAM_H); + s_cam_fresh = true; + xSemaphoreGive(s_cam_mutex); +} diff --git a/idf_zacus/components/display_ui/fonts/README.md b/idf_zacus/components/display_ui/fonts/README.md new file mode 100644 index 0000000..49d851d --- /dev/null +++ b/idf_zacus/components/display_ui/fonts/README.md @@ -0,0 +1,3 @@ +# Fonts — copied from ui_freenove_allinone/src/ui/fonts/ (original Arduino UI). +# Generated LVGL 8 bitmap fonts (see each file header for lv_font_conv options). +# Orbitron 40: scene titles/symbol. IBM Plex Mono 18: scene body text. diff --git a/idf_zacus/components/display_ui/fonts/lv_font_ibmplexmono_18.c b/idf_zacus/components/display_ui/fonts/lv_font_ibmplexmono_18.c new file mode 100644 index 0000000..fdaf5bc --- /dev/null +++ b/idf_zacus/components/display_ui/fonts/lv_font_ibmplexmono_18.c @@ -0,0 +1,3199 @@ +/******************************************************************************* + * Size: 18 px + * Bpp: 4 + * Opts: --font IBMPlexMono-Regular.ttf --size 18 --bpp 4 --format lvgl --output lv_font_ibmplexmono_18.c --lv-include lvgl.h --range 0x20-0x7E --range 0xA0-0x00FF --range 0x0100-0x017F --range 0x2010-0x2030 --symbols ’”“«»…•–—°œŒàâäçéèêëîïôöùûüÿÀÂÄÇÉÈÊËÎÏÔÖÙÛÜŸ + ******************************************************************************/ + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl.h" +#endif + +#ifndef LV_FONT_IBMPLEXMONO_18 +#define LV_FONT_IBMPLEXMONO_18 1 +#endif + +#if LV_FONT_IBMPLEXMONO_18 + +/*----------------- + * BITMAPS + *----------------*/ + +/*Store the image of the glyphs*/ +static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = { + /* U+0020 " " */ + + /* U+0021 "!" */ + 0x6f, 0x30, 0xf, 0xc2, 0x60, 0x6c, 0x2, 0x30, + 0x11, 0x80, 0x14, 0x1, 0x0, 0xb2, 0x76, 0xd0, + 0x81, 0x80, + + /* U+0022 "\"" */ + 0xe8, 0xb, 0xb0, 0x7, 0xff, 0x23, 0x68, 0x26, + 0x40, + + /* U+0023 "#" */ + 0x0, 0xdc, 0xc1, 0x36, 0x1, 0xc2, 0x22, 0x5, + 0x30, 0xe, 0x20, 0x50, 0x2e, 0x0, 0xe5, 0x11, + 0xca, 0x0, 0x1f, 0xf4, 0xa8, 0x28, 0x67, 0xc0, + 0xe6, 0x8, 0x80, 0x43, 0x98, 0x90, 0x33, 0xbc, + 0x3d, 0x4c, 0xc2, 0x0, 0x11, 0x28, 0x29, 0x0, + 0x57, 0xfc, 0x4, 0x6, 0xdf, 0xa1, 0x38, 0xe2, + 0x30, 0x4e, 0x70, 0x11, 0x8a, 0x82, 0x83, 0x99, + 0x80, 0x25, 0x30, 0x31, 0x10, 0x7, 0x17, 0x7, + 0x28, 0x6, + + /* U+0024 "$" */ + 0x0, 0xe7, 0x10, 0xf, 0xd0, 0x60, 0x1e, 0x37, + 0xb, 0x40, 0xd, 0x39, 0x20, 0xf7, 0x82, 0xa, + 0xd1, 0xc1, 0x96, 0x50, 0x18, 0x2e, 0x1, 0x26, + 0x48, 0x7, 0xe2, 0x0, 0x60, 0xb8, 0x7, 0xca, + 0xa8, 0xe0, 0xa3, 0x0, 0xd5, 0x44, 0x7, 0xcb, + 0x0, 0xcb, 0x41, 0xb2, 0x90, 0x1, 0xf3, 0x2a, + 0x80, 0x3f, 0x19, 0x82, 0xe8, 0x3, 0x91, 0x43, + 0x17, 0x5c, 0x2b, 0x4a, 0x1, 0xec, 0x60, 0x1d, + 0xb0, 0xc0, 0x9, 0x9e, 0x1b, 0x66, 0x1, 0xff, + 0x0, + + /* U+0025 "%" */ + 0x7, 0xee, 0x40, 0x4, 0x3e, 0x69, 0x39, 0x85, + 0x60, 0x5, 0xa9, 0xeb, 0x91, 0x17, 0x0, 0x96, + 0x0, 0x3f, 0x4b, 0x10, 0x62, 0x8, 0x26, 0x22, + 0x20, 0x0, 0xa9, 0xde, 0xcc, 0x7c, 0x10, 0xa, + 0x7f, 0xa8, 0x2f, 0xb0, 0xc0, 0x21, 0xe3, 0x75, + 0xea, 0xd0, 0xa, 0x10, 0xee, 0xc6, 0xc6, 0x80, + 0x2d, 0xe0, 0x44, 0x0, 0x9, 0x4, 0x9a, 0x1, + 0x8, 0x0, 0x4c, 0x1a, 0x40, 0x9, 0xe0, 0x46, + 0x8e, 0x6a, 0x0, 0x80, 0xce, 0xa1, + + /* U+0026 "&" */ + 0x0, 0x15, 0x77, 0x20, 0x3, 0xde, 0xd7, 0x2f, + 0x20, 0x19, 0xb, 0x95, 0xee, 0xc0, 0x18, 0x40, + 0xc0, 0x8, 0x40, 0x19, 0x1, 0x80, 0x3f, 0xb, + 0xc1, 0x0, 0x7c, 0x4a, 0x1e, 0x20, 0xff, 0xe1, + 0xc, 0x7e, 0xa, 0xf, 0x3c, 0x16, 0x1b, 0x1f, + 0x59, 0x14, 0x30, 0xe0, 0x10, 0x2a, 0x67, 0xc0, + 0x7, 0x1, 0x80, 0x11, 0x44, 0x40, 0x6, 0x1d, + 0x76, 0xa0, 0x7a, 0x20, 0x6, 0x2c, 0x40, 0xea, + 0xf, 0xc0, + + /* U+0027 "'" */ + 0x4f, 0x10, 0xf, 0xfe, 0x2, 0xd8, 0x80, + + /* U+0028 "(" */ + 0x0, 0x8b, 0x98, 0x0, 0x39, 0xc, 0x0, 0x85, + 0x60, 0x1, 0x35, 0x0, 0x54, 0x82, 0x1, 0x37, + 0x0, 0x44, 0x2e, 0x1, 0x30, 0x10, 0x7, 0xff, + 0xd, 0x80, 0x80, 0x22, 0x17, 0x0, 0xcd, 0xe0, + 0x1a, 0xd4, 0x40, 0x23, 0x5b, 0x0, 0xd6, 0x8c, + 0x1, 0xe, 0xc3, 0x0, + + /* U+0029 ")" */ + 0x9, 0xc1, 0x0, 0xd2, 0xba, 0x1, 0xd2, 0x2c, + 0x1, 0xd3, 0x0, 0x1c, 0x80, 0xa0, 0x1c, 0xde, + 0x1, 0xda, 0x80, 0x1c, 0x22, 0x0, 0xe7, 0x1, + 0x0, 0xce, 0x2, 0x1, 0x84, 0x40, 0x1d, 0xaa, + 0x1, 0xcd, 0xc0, 0x19, 0x5, 0x40, 0x34, 0xc8, + 0x3, 0x48, 0xb0, 0x5, 0x29, 0xa0, 0x10, + + /* U+002A "*" */ + 0x0, 0xc9, 0xe2, 0x1, 0xff, 0xcf, 0xea, 0x50, + 0x1, 0xb5, 0xe0, 0x2, 0x6, 0xac, 0x36, 0x46, + 0x40, 0xd, 0x7c, 0x20, 0x9f, 0x4a, 0x1, 0x9c, + 0x5d, 0xc, 0x3, 0x8a, 0x62, 0xb, 0xc0, 0x1d, + 0x6, 0xe1, 0x66, 0xe0, 0x1a, 0xb4, 0x0, 0x38, + 0xe0, 0x0, + + /* U+002B "+" */ + 0x0, 0xc6, 0x1, 0xf2, 0x60, 0x80, 0x7f, 0xf3, + 0x4, 0x3, 0xbb, 0xfd, 0x41, 0xdf, 0xeb, 0xdc, + 0xc4, 0x5, 0xe6, 0x24, 0xcf, 0x2, 0x99, 0x88, + 0x3, 0xff, 0x8c, + + /* U+002C "," */ + 0x1, 0x43, 0x9, 0xb9, 0x3, 0xe, 0x5, 0x24, + 0x1, 0xa0, 0x30, 0x60, + + /* U+002D "-" */ + 0x19, 0x9e, 0x2, 0x66, 0xe0, + + /* U+002E "." */ + 0x59, 0x3b, 0x68, 0x40, 0xc0, + + /* U+002F "/" */ + 0x0, 0xf2, 0x78, 0x7, 0xdc, 0xa0, 0x1e, 0x15, + 0xe0, 0xf, 0x38, 0xa0, 0x7, 0xa9, 0x80, 0x3c, + 0x47, 0x40, 0x1e, 0xa3, 0x20, 0xf, 0x2d, 0x0, + 0x78, 0xc9, 0xc0, 0x3d, 0x4a, 0x20, 0x1e, 0x6f, + 0x0, 0xf2, 0x8a, 0x80, 0x7b, 0x9c, 0x3, 0xc2, + 0x94, 0x1, 0xe6, 0x23, 0x0, 0xf5, 0x50, 0x3, + 0xc6, 0x4c, 0x1, 0xe0, + + /* U+0030 "0" */ + 0x0, 0x47, 0x7e, 0xb8, 0x5, 0x8e, 0xd2, 0xf1, + 0x0, 0x53, 0xb8, 0x6a, 0x77, 0x17, 0x92, 0x0, + 0x52, 0xa, 0xee, 0x0, 0xc2, 0x2d, 0x23, 0x8, + 0xf5, 0x2, 0x60, 0x8, 0x80, 0xc0, 0x3d, 0x58, + 0xc0, 0x11, 0x18, 0x0, 0xc0, 0x4, 0xce, 0xe0, + 0xc, 0x22, 0xdf, 0x24, 0x0, 0xa4, 0x15, 0x47, + 0x70, 0xd4, 0xee, 0x20, 0xc7, 0x69, 0x78, 0x80, + 0x0, + + /* U+0031 "1" */ + 0x0, 0x93, 0xfd, 0x40, 0x1c, 0x96, 0xa2, 0x1, + 0xcb, 0x6d, 0x40, 0x1e, 0xe6, 0x90, 0xf, 0xae, + 0x40, 0x3f, 0xff, 0x4, 0xcd, 0x3, 0x34, 0xa0, + 0xb3, 0x30, 0x9c, 0xca, 0x80, + + /* U+0032 "2" */ + 0x1, 0xae, 0xfd, 0x50, 0x0, 0xfa, 0x34, 0x35, + 0x30, 0x40, 0x7c, 0x3d, 0xa4, 0x85, 0xd0, 0x80, + 0x58, 0x4, 0x44, 0x0, 0xde, 0x4, 0x1, 0xe5, + 0x40, 0xf, 0x41, 0x40, 0x7, 0x3b, 0xa4, 0xc0, + 0x34, 0x42, 0x58, 0x3, 0x53, 0xdb, 0x0, 0x6b, + 0x4c, 0x40, 0xd, 0x48, 0xca, 0xcd, 0x90, 0x41, + 0xe6, 0x7b, 0x80, + + /* U+0033 "3" */ + 0x0, 0x15, 0x77, 0xeb, 0x80, 0x47, 0xaf, 0x32, + 0x78, 0x90, 0x1, 0x3e, 0xbb, 0x52, 0xb0, 0x0, + 0x60, 0x3, 0x18, 0x80, 0x70, 0x93, 0x32, 0x0, + 0x34, 0x76, 0xc5, 0xb0, 0x6, 0xab, 0xa4, 0xb5, + 0x0, 0xc4, 0x8b, 0x4d, 0x60, 0x1f, 0xb0, 0x4c, + 0x3, 0xf9, 0xc3, 0x20, 0x3, 0x70, 0x28, 0x5b, + 0xdb, 0xbb, 0x12, 0x44, 0x1e, 0x9e, 0x20, 0x16, + 0xc0, + + /* U+0034 "4" */ + 0x0, 0xe4, 0xff, 0x20, 0x7, 0xd2, 0x80, 0x1f, + 0x9c, 0xe0, 0x3, 0xe2, 0x8a, 0x0, 0xfd, 0x28, + 0x80, 0xf, 0x95, 0x28, 0x3, 0xf5, 0xc0, 0x80, + 0x7d, 0x26, 0xe0, 0x1f, 0x13, 0x48, 0x8c, 0xe0, + 0x1, 0x4, 0xb, 0xee, 0xac, 0x2f, 0x85, 0xee, + 0xfa, 0x42, 0x2c, 0x44, 0x89, 0xca, 0x6, 0x80, + 0x1f, 0xfc, 0x0, + + /* U+0035 "5" */ + 0xd, 0xff, 0xf0, 0x0, 0xda, 0x67, 0x80, 0x3, + 0x8c, 0xdc, 0x1, 0x8, 0x7, 0x84, 0x18, 0x3, + 0xc6, 0xd, 0x9f, 0xeb, 0x20, 0x10, 0xfa, 0xb7, + 0x4f, 0x3, 0xcb, 0x64, 0x8a, 0x26, 0x3, 0x0, + 0xca, 0x1e, 0x1, 0xf8, 0x53, 0x4, 0x2, 0x60, + 0xc6, 0x2f, 0x86, 0x99, 0x22, 0xb, 0x11, 0xa5, + 0x26, 0xc0, + + /* U+0036 "6" */ + 0x0, 0x8b, 0x74, 0x40, 0x18, 0xb0, 0x70, 0x80, + 0x37, 0x1e, 0x8, 0x6, 0x83, 0xf0, 0xf, 0x3b, + 0x88, 0x3, 0x98, 0x33, 0x1f, 0xea, 0x10, 0xc1, + 0x8a, 0xb8, 0x4c, 0x6, 0x1d, 0x64, 0x7d, 0x26, + 0x14, 0x10, 0x8, 0xc7, 0xd9, 0xc0, 0x38, 0x43, + 0x4c, 0x80, 0x24, 0xd, 0x63, 0xc8, 0x69, 0xb5, + 0x40, 0xc7, 0x69, 0x48, 0xa0, + + /* U+0037 "7" */ + 0xdf, 0xff, 0xa4, 0x12, 0xab, 0x48, 0x70, 0x1a, + 0xae, 0x10, 0x67, 0xe0, 0xd, 0x4a, 0x34, 0xc0, + 0x12, 0x17, 0x0, 0x7b, 0xc9, 0x0, 0x38, 0x92, + 0xc0, 0x3d, 0x60, 0xe0, 0x1e, 0x66, 0x8, 0x7, + 0x30, 0x50, 0x7, 0xac, 0xcc, 0x1, 0xc8, 0x52, + 0x1, 0xee, 0x15, 0x0, 0xc0, + + /* U+0038 "8" */ + 0x0, 0x1d, 0xf7, 0xec, 0x90, 0x0, 0xf1, 0xa6, + 0x95, 0xf0, 0x42, 0x83, 0x1d, 0x62, 0x85, 0xc0, + 0x40, 0x40, 0x26, 0x3, 0x9, 0x19, 0x21, 0x39, + 0x35, 0x1, 0xf5, 0xde, 0xc1, 0xc0, 0x0, 0xe8, + 0x55, 0xcb, 0xd0, 0x2, 0xc7, 0x15, 0x1b, 0x15, + 0xc1, 0x50, 0x3, 0x9, 0xe0, 0x7, 0xe1, 0x70, + 0x47, 0x10, 0x8, 0x88, 0x81, 0x3, 0x8e, 0xd1, + 0x83, 0x20, 0x78, 0xf3, 0x26, 0x7f, 0x10, + + /* U+0039 "9" */ + 0x0, 0x4f, 0x7e, 0xb8, 0x5, 0xae, 0xd2, 0x91, + 0x20, 0xe5, 0x90, 0xd3, 0x4c, 0x56, 0x64, 0x1, + 0x28, 0x28, 0xb8, 0x7, 0xf, 0x3a, 0x80, 0x62, + 0x10, 0x91, 0xb3, 0x14, 0x90, 0xe3, 0xa5, 0xce, + 0x9c, 0x4, 0x5, 0xec, 0xdd, 0x8c, 0xc0, 0x11, + 0x92, 0x2c, 0x0, 0x71, 0x52, 0xa0, 0x6, 0x2f, + 0x2a, 0x0, 0xc9, 0x8d, 0x80, 0x10, + + /* U+003A ":" */ + 0xaf, 0x74, 0xc, 0xb6, 0x85, 0x93, 0x0, 0xfe, + 0x59, 0x3b, 0x68, 0x40, 0xc0, + + /* U+003B ";" */ + 0xa, 0xf7, 0x4, 0xc, 0xb, 0x68, 0x5, 0x93, + 0x0, 0xff, 0xe0, 0xbf, 0xe0, 0x68, 0x48, 0x38, + 0xa8, 0x13, 0x81, 0x85, 0x81, 0x61, 0x0, + + /* U+003C "<" */ + 0x0, 0xff, 0xe1, 0x96, 0x20, 0x7, 0x46, 0x82, + 0x80, 0x4b, 0xcd, 0x58, 0x20, 0x57, 0x4d, 0xca, + 0x0, 0x5d, 0x7d, 0x80, 0xc, 0x41, 0x24, 0x1, + 0xce, 0xd5, 0x64, 0x1, 0xd3, 0x8f, 0xb0, 0x1, + 0xc7, 0x92, 0xdc, 0xa0, 0x1c, 0xde, 0x94, 0x80, + 0x1c, 0x35, 0x8c, + + /* U+003D "=" */ + 0x0, 0xfe, 0xef, 0xff, 0xaf, 0x73, 0x3e, 0x93, + 0x3f, 0xc4, 0x1, 0xfd, 0xdf, 0xff, 0x5e, 0xe6, + 0x7d, 0x20, + + /* U+003E ">" */ + 0x0, 0xfe, 0x7a, 0x10, 0xf, 0x22, 0x39, 0xc0, + 0x38, 0xf9, 0xe3, 0x4c, 0x3, 0xa3, 0x5b, 0x24, + 0x40, 0x31, 0x5d, 0x3f, 0x90, 0x7, 0x2c, 0x8b, + 0x0, 0x63, 0xd8, 0x82, 0x0, 0x6, 0xb1, 0xf9, + 0xc0, 0xf, 0xcf, 0x74, 0x20, 0x7, 0x87, 0xd4, + 0x0, 0xc7, 0xb0, 0x1, 0xe0, + + /* U+003F "?" */ + 0x0, 0x46, 0xfe, 0xb8, 0x5, 0x8e, 0xb2, 0xf1, + 0x0, 0x63, 0xc8, 0x6a, 0x67, 0x16, 0xb3, 0x0, + 0xbc, 0xc, 0x10, 0x3, 0x78, 0x10, 0x6, 0x28, + 0x69, 0x0, 0xd7, 0xab, 0x4c, 0x1, 0xc9, 0xaa, + 0x1, 0xff, 0xc2, 0xbb, 0x0, 0x7c, 0xf0, 0x1, + 0xe3, 0x87, 0x60, 0xe, 0x21, 0x12, 0x80, 0x40, + + /* U+0040 "@" */ + 0x0, 0x47, 0x7e, 0xc0, 0x5, 0x4d, 0x17, 0x65, + 0xa0, 0x25, 0xd6, 0x46, 0xf5, 0x25, 0x33, 0xe, + 0x7a, 0x8b, 0xea, 0x85, 0x6, 0x8, 0x89, 0x84, + 0x12, 0x4d, 0x88, 0x7, 0xc4, 0x18, 0x3, 0x18, + 0x7, 0xe3, 0x1, 0x7, 0x0, 0xc3, 0xe0, 0x9e, + 0x6, 0x40, 0xe2, 0x14, 0x1d, 0x66, 0x26, 0xa0, + 0x37, 0xd1, 0x8d, 0x66, 0x60, 0xf, 0x1a, 0xea, + 0x0, 0x7a, 0x9e, 0xff, 0xe2, 0x0, 0xa7, 0xf7, + 0x30, 0x40, + + /* U+0041 "A" */ + 0x0, 0xd7, 0xf0, 0x1, 0xf9, 0x5, 0x40, 0x3e, + 0x50, 0x82, 0x20, 0x7, 0xbc, 0xd8, 0x14, 0x3, + 0xc8, 0x80, 0x4c, 0x0, 0xe3, 0x1b, 0xc, 0x40, + 0xe, 0xb1, 0x30, 0x50, 0x50, 0xc, 0xaa, 0x11, + 0x18, 0xf8, 0x4, 0x24, 0x9d, 0xd0, 0xa0, 0x4, + 0xe1, 0x57, 0x73, 0x89, 0x80, 0x34, 0x95, 0x12, + 0x90, 0xb0, 0x2, 0x28, 0x6, 0x13, 0x50, 0x40, + 0xc0, 0xe, 0xb2, 0x10, + + /* U+0042 "B" */ + 0x6f, 0xfd, 0xd2, 0x20, 0x14, 0xcc, 0xcc, 0xa0, + 0x9, 0xd9, 0x91, 0x4, 0x20, 0xf, 0x18, 0x30, + 0x7, 0x84, 0x18, 0x0, 0x62, 0x23, 0x85, 0x20, + 0x6, 0xf7, 0x30, 0x6c, 0x2, 0xab, 0xb5, 0x3f, + 0x90, 0x1, 0x12, 0x5b, 0xb, 0x0, 0xf8, 0xd4, + 0x3, 0xe2, 0x50, 0x3, 0x34, 0xf8, 0x16, 0x0, + 0x89, 0x94, 0x6, 0x10, + + /* U+0043 "C" */ + 0x0, 0x2e, 0x7f, 0x48, 0x80, 0x1a, 0x9e, 0x59, + 0xf0, 0x6, 0x56, 0xda, 0x30, 0x9d, 0xc1, 0x0, + 0x11, 0x63, 0xf8, 0x10, 0x6, 0x30, 0x32, 0x0, + 0xf8, 0x40, 0x3f, 0x8, 0x7, 0xe3, 0x20, 0xf, + 0xbc, 0x8, 0x3, 0x18, 0x38, 0x40, 0x4, 0x58, + 0xe3, 0xb, 0x6d, 0x18, 0x4e, 0xf, 0x4f, 0x2c, + 0x98, 0x0, + + /* U+0044 "D" */ + 0x4f, 0xfd, 0xac, 0x1, 0x9e, 0x64, 0xd3, 0x0, + 0x15, 0xb3, 0x2d, 0x9c, 0x40, 0x3d, 0x60, 0xc0, + 0x1e, 0x21, 0xd0, 0xf, 0x8c, 0x40, 0x3f, 0x38, + 0x7, 0xe7, 0x0, 0xf8, 0xc4, 0x3, 0xc4, 0x3a, + 0x1, 0xeb, 0x6, 0x0, 0x5b, 0x32, 0xd9, 0xc4, + 0x0, 0xf3, 0x26, 0x98, 0x0, + + /* U+0045 "E" */ + 0x6f, 0xff, 0x94, 0x1, 0x13, 0x3c, 0xe0, 0x5, + 0x66, 0xe2, 0x0, 0xff, 0xe4, 0x8, 0xf8, 0x2, + 0xce, 0xee, 0x20, 0x5, 0x5d, 0xf1, 0x0, 0x1d, + 0x13, 0x80, 0x3f, 0xf9, 0x2a, 0xcd, 0xc4, 0x0, + 0x89, 0x9e, 0x70, + + /* U+0046 "F" */ + 0x6f, 0xff, 0xa0, 0x1, 0x13, 0x3d, 0x60, 0x5, + 0x66, 0xe3, 0x0, 0xff, 0xe4, 0x8, 0xf8, 0x2, + 0xce, 0xee, 0x0, 0xaa, 0xef, 0x80, 0x27, 0x44, + 0xe0, 0xf, 0xfe, 0xc8, + + /* U+0047 "G" */ + 0x0, 0x47, 0x7e, 0xc0, 0x5, 0x6e, 0xe9, 0x47, + 0xb0, 0x44, 0x54, 0xb4, 0xda, 0x17, 0xa, 0x80, + 0x49, 0xe4, 0xea, 0x1, 0xf1, 0x10, 0x3, 0xfe, + 0xaf, 0xf9, 0x80, 0x34, 0x66, 0x0, 0x4, 0x40, + 0x1, 0x19, 0x80, 0xe, 0xa0, 0x18, 0x80, 0x18, + 0x2a, 0x1, 0x50, 0x1, 0x8a, 0xa1, 0xa8, 0x80, + 0x2c, 0x57, 0x80, 0xb0, 0x0, + + /* U+0048 "H" */ + 0x9f, 0x0, 0xc5, 0xea, 0x1, 0xff, 0xda, 0x11, + 0xe0, 0xd, 0xdd, 0xb0, 0x3, 0x5d, 0xe9, 0x0, + 0xc8, 0x9c, 0x1, 0xff, 0xda, + + /* U+0049 "I" */ + 0x7f, 0xff, 0x91, 0x6e, 0xce, 0x13, 0x76, 0x52, + 0x44, 0x68, 0x22, 0x42, 0x1, 0xff, 0xfc, 0x24, + 0x46, 0x82, 0x24, 0x2b, 0x76, 0x70, 0x9b, 0xb2, + 0x80, + + /* U+004A "J" */ + 0x6, 0xff, 0xea, 0x4, 0x99, 0xc2, 0x0, 0x26, + 0x6c, 0x1, 0xff, 0xe7, 0x20, 0xf, 0x46, 0x88, + 0x0, 0x87, 0x64, 0x71, 0xdd, 0x82, 0xc3, 0x8d, + 0x12, 0x98, 0x20, + + /* U+004B "K" */ + 0x7f, 0x0, 0xc3, 0xba, 0x0, 0xfa, 0x8f, 0xc0, + 0x3d, 0x9, 0x44, 0x1, 0xca, 0xf0, 0x80, 0x1c, + 0x74, 0xae, 0x1, 0xc3, 0xc1, 0x60, 0x1e, 0xa3, + 0x6, 0x0, 0xf2, 0x63, 0x41, 0x0, 0x75, 0x14, + 0x4, 0x80, 0x72, 0x80, 0xca, 0x20, 0x3, 0xe6, + 0x29, 0x0, 0xfd, 0x25, 0x0, 0x1f, 0x22, 0xb9, + 0x0, + + /* U+004C "L" */ + 0xda, 0x0, 0xff, 0xff, 0x80, 0x7f, 0xf0, 0x19, + 0xbc, 0x60, 0x73, 0x3e, + + /* U+004D "M" */ + 0xcf, 0x50, 0x9, 0xfe, 0x40, 0x1c, 0x1, 0x50, + 0x4, 0x48, 0x40, 0x84, 0x20, 0x4, 0x3a, 0xe, + 0x63, 0x0, 0xa9, 0x85, 0x68, 0x3, 0x30, 0xd1, + 0x98, 0x3, 0x99, 0x28, 0x3, 0xd4, 0xc, 0x1, + 0xe3, 0x90, 0xf, 0xcc, 0x1, 0xff, 0xd3, + + /* U+004E "N" */ + 0x9f, 0xc0, 0xd, 0xea, 0x0, 0x32, 0x0, 0xf1, + 0xd, 0x0, 0x7d, 0x2c, 0x1, 0xf3, 0xa, 0x80, + 0x78, 0x53, 0x80, 0x3e, 0xe4, 0x20, 0xf, 0x28, + 0xd0, 0x7, 0xcc, 0xc0, 0xf, 0xa8, 0x50, 0x3, + 0xc4, 0x7e, 0x1, 0xf4, 0xa0, 0x7, 0xca, 0x1, + 0x0, + + /* U+004F "O" */ + 0x0, 0x47, 0x7e, 0xb8, 0x5, 0x8e, 0xd2, 0xf1, + 0x0, 0x53, 0xa8, 0x6a, 0x77, 0x17, 0x8a, 0x80, + 0x52, 0xa, 0xea, 0x1, 0x84, 0x5a, 0x44, 0x0, + 0xe3, 0x60, 0xf, 0xfe, 0x31, 0x10, 0x3, 0x8d, + 0x9d, 0x40, 0x30, 0x8b, 0x7c, 0x54, 0x2, 0x90, + 0x55, 0x1d, 0x43, 0x53, 0xb8, 0x83, 0x1d, 0xa5, + 0xe2, 0x0, + + /* U+0050 "P" */ + 0x6f, 0xfe, 0xc4, 0x0, 0xa2, 0x66, 0xb, 0x30, + 0x2, 0xb3, 0x58, 0x14, 0x1, 0xf1, 0x10, 0x3, + 0xe3, 0x10, 0x0, 0x8c, 0x50, 0x3e, 0x0, 0xce, + 0xe6, 0xbb, 0x94, 0x1, 0x57, 0x6c, 0xe8, 0x0, + 0x9d, 0x10, 0x62, 0x1, 0xff, 0xda, + + /* U+0051 "Q" */ + 0x0, 0x47, 0x7e, 0xb0, 0x5, 0x6e, 0xe9, 0x79, + 0x80, 0x44, 0x54, 0xb5, 0x3b, 0x87, 0x85, 0x40, + 0x29, 0x7, 0x72, 0x80, 0x61, 0x17, 0x98, 0x80, + 0x71, 0x90, 0x98, 0x7, 0x9c, 0x40, 0x3f, 0x19, + 0x80, 0x38, 0xd9, 0xa0, 0xe, 0x12, 0xc3, 0x10, + 0x9, 0x42, 0xd8, 0x74, 0x40, 0xec, 0xcc, 0x3a, + 0x5d, 0xf8, 0x7c, 0x0, 0x3e, 0x80, 0xad, 0x20, + 0xc, 0x80, 0xe0, 0x1e, 0x33, 0x6f, 0xe8, 0x7, + 0x66, 0x5c, 0x0, + + /* U+0052 "R" */ + 0x6f, 0xfe, 0xc4, 0x0, 0xa2, 0x66, 0xb, 0x30, + 0x2, 0xb3, 0x58, 0x14, 0x1, 0xf1, 0x10, 0x3, + 0xe3, 0x10, 0x0, 0x8c, 0x50, 0x3e, 0x0, 0xce, + 0xe6, 0xbc, 0x28, 0x2, 0xac, 0xc7, 0xdc, 0x2, + 0x74, 0xf1, 0x60, 0xf, 0x1a, 0xc0, 0x7, 0xd0, + 0x2e, 0x1, 0xe2, 0x68, 0x10, 0xf, 0x40, 0x40, + + /* U+0053 "S" */ + 0x0, 0x15, 0x77, 0xf5, 0x10, 0x0, 0xf5, 0xe6, + 0x4c, 0x9a, 0x61, 0x21, 0x8e, 0xd1, 0xea, 0x20, + 0x26, 0x20, 0x10, 0xd1, 0x1, 0x18, 0x80, 0x7d, + 0x63, 0xf0, 0x82, 0x1, 0x8b, 0xdd, 0x6f, 0xb5, + 0x0, 0x21, 0x9d, 0xfb, 0x6b, 0x40, 0xf, 0x25, + 0x8f, 0x0, 0x7e, 0x31, 0xc, 0x90, 0xc, 0x44, + 0x30, 0xb6, 0xd7, 0x68, 0xc1, 0x90, 0x7b, 0x69, + 0x92, 0xbf, 0x88, + + /* U+0054 "T" */ + 0x8f, 0xff, 0xe5, 0xb9, 0x99, 0x82, 0x26, 0x67, + 0x36, 0x6b, 0x1, 0x59, 0xa2, 0x0, 0xff, 0xff, + 0x80, 0x7f, 0xf5, 0xc0, + + /* U+0055 "U" */ + 0x9f, 0x0, 0xc5, 0xea, 0x1, 0xff, 0xea, 0x10, + 0xf, 0xfe, 0x10, 0x80, 0x3c, 0x3, 0x9c, 0x18, + 0xc1, 0x40, 0x2c, 0x2, 0x42, 0xa8, 0x69, 0x75, + 0x10, 0xc5, 0x69, 0x76, 0xa0, + + /* U+0056 "V" */ + 0x3f, 0x60, 0xe, 0x8f, 0x3, 0x1c, 0x0, 0xe4, + 0x40, 0x1, 0x10, 0x1, 0x84, 0xf4, 0x1, 0xe2, + 0x60, 0x12, 0x2, 0x0, 0x10, 0x10, 0x2, 0xf2, + 0x20, 0x0, 0x4e, 0xc0, 0x24, 0x40, 0x6, 0xb3, + 0x0, 0x10, 0xe8, 0x6, 0x40, 0x40, 0x50, 0x40, + 0xc, 0x63, 0xa1, 0x88, 0x1, 0xe4, 0x40, 0x26, + 0x80, 0x7b, 0xc9, 0x81, 0x0, 0x3c, 0x81, 0x4, + 0x40, 0xf, 0x9, 0x8a, 0x0, 0x60, + + /* U+0057 "W" */ + 0x5f, 0x0, 0xf1, 0x79, 0x8, 0x8, 0x7, 0x30, + 0x13, 0x1, 0x80, 0x70, 0x80, 0xc, 0x4, 0x23, + 0xd4, 0xc, 0x40, 0x41, 0xc0, 0xc3, 0x0, 0x4c, + 0x2, 0x20, 0x55, 0x83, 0xc4, 0x0, 0x23, 0xb8, + 0xc0, 0x58, 0x0, 0x5e, 0x84, 0x80, 0x46, 0x60, + 0x3, 0x8e, 0xb8, 0x13, 0x88, 0x80, 0x2, 0x66, + 0xd0, 0x7d, 0x6f, 0x0, 0x18, 0xa9, 0x86, 0xa1, + 0x88, 0x3, 0x90, 0x54, 0xc, 0xc2, 0x40, 0x1, + 0x1, 0x10, 0x28, 0x1, 0xc0, + + /* U+0058 "X" */ + 0x1e, 0xb0, 0xe, 0xbd, 0x1, 0x84, 0x40, 0x4, + 0xa9, 0x20, 0x6, 0x39, 0x0, 0xad, 0x10, 0x1, + 0x71, 0x38, 0x40, 0xd0, 0x6, 0x26, 0x83, 0x78, + 0x10, 0xe, 0x91, 0xc2, 0x70, 0xf, 0x9c, 0x14, + 0x3, 0xe2, 0x62, 0x71, 0x0, 0xf4, 0x96, 0xd, + 0x80, 0x72, 0xa4, 0x8c, 0x29, 0x80, 0x43, 0x6a, + 0x80, 0xc7, 0xc0, 0x14, 0xd, 0x80, 0x5e, 0x4e, + 0x6, 0xd2, 0x20, 0x11, 0xac, 0x8, + + /* U+0059 "Y" */ + 0x6f, 0x40, 0xe, 0x7f, 0x36, 0x29, 0x0, 0xc3, + 0x2a, 0x61, 0x4, 0xa0, 0x14, 0x8c, 0x0, 0x14, + 0xe0, 0x0, 0x2e, 0xc4, 0x1, 0x79, 0x30, 0x40, + 0x40, 0x6, 0x35, 0x82, 0x68, 0x10, 0xe, 0x81, + 0xc1, 0x70, 0xf, 0x13, 0xd, 0x80, 0x7e, 0xc0, + 0x70, 0xf, 0xff, 0x20, + + /* U+005A "Z" */ + 0xd, 0xff, 0xfa, 0x82, 0x67, 0xce, 0x4, 0x8, + 0xcd, 0xc8, 0x50, 0x1, 0xe8, 0x3e, 0x0, 0xf1, + 0xbc, 0x18, 0x7, 0xb8, 0xdc, 0x3, 0xd2, 0x5a, + 0x1, 0xe4, 0x6a, 0x10, 0xe, 0x1a, 0x55, 0x0, + 0x7a, 0x82, 0xc0, 0x3c, 0xc9, 0x42, 0x1, 0xc3, + 0x24, 0x46, 0x6e, 0x53, 0x2, 0x99, 0xf5, 0x0, + + /* U+005B "[" */ + 0xff, 0xe1, 0x9, 0xcc, 0x84, 0x8, 0xce, 0x0, + 0xff, 0xff, 0x80, 0x75, 0xff, 0xc3, 0xbb, 0xc2, + + /* U+005C "\\" */ + 0x3f, 0x10, 0xf, 0x19, 0x30, 0x7, 0xd5, 0x40, + 0xf, 0x98, 0x8c, 0x3, 0xc2, 0x94, 0x1, 0xf7, + 0x38, 0x7, 0xca, 0x2a, 0x1, 0xf3, 0x78, 0x7, + 0xd4, 0xa2, 0x1, 0xe3, 0x27, 0x0, 0xf9, 0x68, + 0x3, 0xea, 0x32, 0x0, 0xf1, 0x1d, 0x0, 0x7d, + 0x4c, 0x1, 0xf3, 0x8a, 0x0, 0x78, 0x57, 0x80, + 0x3e, 0xe5, 0x0, + + /* U+005D "]" */ + 0x4f, 0xfd, 0x89, 0x99, 0x30, 0x0, 0xce, 0xe0, + 0xf, 0xff, 0xf8, 0x6, 0x4f, 0xfa, 0x1, 0x37, + 0x77, 0x0, + + /* U+005E "^" */ + 0x0, 0xd5, 0xee, 0x1, 0xe3, 0x54, 0x90, 0xf, + 0x42, 0x58, 0x40, 0x6, 0x34, 0x90, 0x97, 0x10, + 0xb, 0xd4, 0x81, 0xc6, 0x0, 0x8, 0x70, 0x1, + 0x43, 0x8, 0x4b, 0x10, 0x4, 0xc5, 0x0, 0xb0, + 0x1, 0xd3, 0xe0, + + /* U+005F "_" */ + 0x11, 0xfe, 0xfe, 0xef, 0xaa, 0x97, 0x7f, 0x40, + + /* U+0060 "`" */ + 0x28, 0x0, 0x33, 0xa0, 0x22, 0xf8, 0x2, 0xc0, + 0x0, + + /* U+0061 "a" */ + 0x4, 0xbe, 0xe5, 0x98, 0x0, 0xec, 0x6e, 0xcf, + 0x84, 0x7, 0x54, 0x55, 0x60, 0x28, 0x1, 0x40, + 0x31, 0x70, 0x1, 0xb3, 0xbf, 0xc4, 0x0, 0x69, + 0x4d, 0xd8, 0x80, 0x1a, 0x70, 0x64, 0x40, 0xf, + 0xe2, 0x0, 0x69, 0xa8, 0x9d, 0x91, 0x11, 0xa2, + 0x1d, 0xcf, 0x95, 0xa0, + + /* U+0062 "b" */ + 0x4f, 0x20, 0xf, 0xfe, 0xc4, 0xf7, 0xd0, 0x80, + 0x52, 0x30, 0xeb, 0x80, 0x16, 0xc3, 0x45, 0x12, + 0x80, 0x10, 0x2, 0x51, 0xc0, 0xf, 0x8c, 0x80, + 0x3f, 0xf8, 0x86, 0x40, 0x4, 0x0, 0x94, 0x70, + 0x1, 0xb0, 0xd3, 0x44, 0xa0, 0x9, 0x18, 0x65, + 0xc0, + + /* U+0063 "c" */ + 0x0, 0x2e, 0xff, 0x48, 0x4, 0xf4, 0x95, 0x2f, + 0x60, 0x30, 0xf2, 0xaf, 0xa4, 0x8, 0x14, 0x1, + 0x1c, 0x81, 0x1, 0x80, 0x7f, 0xf0, 0xc8, 0xc, + 0x3, 0xc8, 0x14, 0x1, 0xd, 0x88, 0xa1, 0xe9, + 0x5f, 0xdc, 0x41, 0xe9, 0xea, 0x5f, 0x40, + + /* U+0064 "d" */ + 0x0, 0xf2, 0xf8, 0x80, 0x7f, 0xf4, 0xf, 0x3f, + 0x98, 0x2, 0x1c, 0x28, 0x86, 0x70, 0x2, 0x43, + 0x1d, 0xa8, 0x80, 0x8, 0x82, 0x0, 0xb0, 0x0, + 0x42, 0x1, 0xff, 0xc4, 0x21, 0x0, 0xf9, 0x10, + 0x40, 0x16, 0x0, 0x24, 0x32, 0x1a, 0x88, 0x0, + 0x38, 0x4f, 0x19, 0xc0, 0x0, + + /* U+0065 "e" */ + 0x0, 0x3e, 0xfe, 0xb0, 0x5, 0x51, 0x57, 0x53, + 0x20, 0x45, 0xc7, 0x47, 0xa6, 0x3d, 0x33, 0x0, + 0x4a, 0x8, 0xa7, 0xff, 0xc1, 0xc0, 0x79, 0x9e, + 0x86, 0x13, 0x3f, 0xf, 0x12, 0x0, 0x45, 0x82, + 0x8b, 0x70, 0xaf, 0x80, 0x21, 0x50, 0xb5, 0x2d, + 0x80, + + /* U+0066 "f" */ + 0x0, 0xd5, 0xff, 0x60, 0x4, 0x8b, 0x57, 0x6f, + 0x0, 0x84, 0x11, 0x23, 0x0, 0x8c, 0x3, 0xaf, + 0xfd, 0x21, 0xdf, 0xec, 0x8b, 0xb3, 0x85, 0x5d, + 0xbc, 0xd1, 0x1c, 0x8, 0x91, 0x80, 0x7f, 0xf8, + 0x49, 0x11, 0xc0, 0x89, 0xd, 0x5d, 0x9c, 0x2a, + 0xec, 0xe0, + + /* U+0067 "g" */ + 0x0, 0xfc, 0x20, 0x1f, 0x4f, 0xf0, 0x4, 0xfd, + 0xfa, 0x97, 0x60, 0x4, 0xca, 0xf2, 0x88, 0x50, + 0x5, 0xa1, 0x4d, 0xd2, 0x0, 0x4, 0x4, 0x1, + 0x19, 0x0, 0x4, 0x4, 0x2, 0x22, 0x0, 0x8, + 0xe0, 0x81, 0x16, 0xc0, 0x2d, 0x1d, 0xfb, 0x86, + 0x0, 0xa1, 0x37, 0x5e, 0xe0, 0x19, 0x85, 0x10, + 0xca, 0x40, 0x13, 0xb4, 0xcd, 0x5a, 0xc0, 0xcb, + 0xdf, 0xf7, 0x34, 0x85, 0x38, 0x80, 0x43, 0x80, + 0x4, 0x57, 0x33, 0x23, 0x5a, 0x4, 0x3d, 0x66, + 0x2e, 0xa6, 0xc0, + + /* U+0068 "h" */ + 0x3f, 0x30, 0xf, 0xfe, 0xc4, 0xf7, 0x20, 0x3, + 0x40, 0xc3, 0xba, 0x0, 0x2d, 0x86, 0x96, 0x70, + 0x9, 0x40, 0x2c, 0x1, 0x0, 0xf0, 0x81, 0x80, + 0x7f, 0xf8, 0x40, + + /* U+0069 "i" */ + 0x0, 0xdd, 0xa0, 0x1e, 0x11, 0x10, 0x7, 0x87, + 0x74, 0x1, 0xf1, 0x10, 0x2, 0x1f, 0xfd, 0x60, + 0x10, 0xdd, 0xc4, 0x1, 0xc8, 0x90, 0x80, 0x7f, + 0xf9, 0x91, 0x21, 0x74, 0x41, 0x8d, 0xdc, 0x47, + 0x76, 0xe0, + + /* U+006A "j" */ + 0x0, 0xed, 0xe0, 0xf, 0x10, 0x88, 0x3, 0xbb, + 0x44, 0x3, 0x84, 0x81, 0x7f, 0xfb, 0x1, 0x2e, + 0xf1, 0x80, 0x5, 0x13, 0x30, 0x7, 0xff, 0xfd, + 0x13, 0x33, 0x0, 0xae, 0xf0, 0xc0, 0x0, + + /* U+006B "k" */ + 0x1f, 0x50, 0xf, 0xfe, 0xfb, 0x7b, 0x0, 0x79, + 0xa6, 0x4c, 0x1, 0xcd, 0x32, 0x60, 0xe, 0x69, + 0x86, 0x0, 0xee, 0x90, 0xf, 0xc9, 0x6d, 0x4, + 0x1, 0xd2, 0x92, 0x5e, 0x1, 0xc6, 0x0, 0xe2, + 0x80, 0xf, 0x8d, 0xdc, 0x80, 0x1f, 0x41, 0xd0, + 0x80, + + /* U+006C "l" */ + 0x9f, 0xfc, 0x40, 0x15, 0xdc, 0xe0, 0x1c, 0x48, + 0x8d, 0x0, 0xff, 0xff, 0x80, 0x7f, 0xc4, 0x88, + 0xd0, 0x74, 0x40, 0xdd, 0xce, 0x15, 0x76, 0x40, + + /* U+006D "m" */ + 0xf, 0x7d, 0xf6, 0x67, 0x69, 0x0, 0x37, 0x1a, + 0x47, 0xc2, 0x0, 0x1e, 0x38, 0xc, 0x2a, 0xc0, + 0x1, 0x1, 0x0, 0xc2, 0x1, 0xff, 0xf0, + + /* U+006E "n" */ + 0x3f, 0x4a, 0xfe, 0x80, 0xd, 0xfe, 0xd8, 0x78, + 0x0, 0xb9, 0x49, 0x9d, 0xc0, 0x13, 0x0, 0x5a, + 0x2, 0x1, 0xe1, 0x3, 0x0, 0xff, 0xf0, 0x80, + + /* U+006F "o" */ + 0x0, 0x47, 0x7e, 0xb0, 0x5, 0x6d, 0x34, 0xb3, + 0x0, 0xa9, 0xae, 0xb1, 0x2e, 0x5e, 0x46, 0x1, + 0x30, 0x2b, 0x30, 0x3, 0x87, 0x80, 0x3f, 0x99, + 0x80, 0x1c, 0x3d, 0xc2, 0x30, 0x9, 0x81, 0x51, + 0x1a, 0xeb, 0x12, 0xe4, 0x16, 0xd3, 0x4b, 0x30, + 0x0, + + /* U+0070 "p" */ + 0x4f, 0x39, 0xef, 0xa1, 0x0, 0xa3, 0x31, 0x49, + 0x80, 0x17, 0x29, 0x2e, 0x92, 0x80, 0x1c, 0x2, + 0x31, 0xc0, 0xf, 0x8c, 0x80, 0x3f, 0xf8, 0x84, + 0x40, 0x2, 0x0, 0x4a, 0x18, 0x0, 0xd9, 0x69, + 0xa2, 0x50, 0x4, 0x8c, 0x32, 0xe0, 0x6, 0x8e, + 0xfa, 0x10, 0xf, 0xfe, 0x80, + + /* U+0071 "q" */ + 0x3, 0xcf, 0xe6, 0x5f, 0x11, 0x61, 0x44, 0x33, + 0x80, 0x12, 0x18, 0xed, 0x44, 0x0, 0x44, 0x10, + 0x5, 0x80, 0x2, 0x10, 0xf, 0xfe, 0x21, 0x8, + 0x7, 0xc8, 0x82, 0x0, 0xb0, 0x1, 0x21, 0x90, + 0xd4, 0x40, 0x1, 0xc2, 0x78, 0xce, 0x0, 0x8f, + 0x3f, 0x98, 0x3, 0xff, 0xa4, + + /* U+0072 "r" */ + 0x9f, 0xfa, 0xa, 0xff, 0xc3, 0x77, 0x8, 0xbb, + 0x2e, 0xc2, 0x48, 0x80, 0x4d, 0x74, 0x40, 0x7, + 0x38, 0x80, 0x7e, 0x20, 0xf, 0xfe, 0xb9, 0x22, + 0x0, 0x91, 0x20, 0x5, 0xdc, 0x2b, 0x77, 0x10, + 0x0, + + /* U+0073 "s" */ + 0x1, 0x9e, 0xfe, 0x92, 0x0, 0x62, 0x45, 0xc2, + 0xe0, 0x19, 0x73, 0x23, 0x66, 0x80, 0x82, 0x0, + 0x63, 0x2, 0x78, 0xeb, 0x85, 0x0, 0xa3, 0x60, + 0x8e, 0xb0, 0x2, 0x26, 0x9b, 0xc3, 0x53, 0x70, + 0xc, 0x40, 0x6b, 0x18, 0xc8, 0xd8, 0x2e, 0xd8, + 0xd1, 0x70, 0xf8, 0x20, + + /* U+0074 "t" */ + 0x0, 0x8b, 0xcc, 0x3, 0xff, 0x8a, 0xc0, 0x1e, + 0x4f, 0xf5, 0x86, 0x7f, 0xce, 0xb7, 0x68, 0x8, + 0xbb, 0x94, 0x51, 0x20, 0x44, 0xc4, 0x1, 0xff, + 0xe9, 0x20, 0x44, 0xc4, 0x1, 0xc, 0x42, 0xee, + 0x50, + + /* U+0075 "u" */ + 0x5f, 0x10, 0x9, 0xbc, 0x3, 0xff, 0xc4, 0x20, + 0x60, 0x1e, 0x60, 0x40, 0xb, 0x0, 0x4, 0x73, + 0xd, 0x4c, 0x1, 0x6a, 0xb4, 0x6d, 0x0, 0x0, + + /* U+0076 "v" */ + 0xac, 0x0, 0xee, 0x7f, 0x31, 0x0, 0x90, 0x55, + 0x40, 0xe0, 0x16, 0x99, 0x2, 0xe8, 0x4, 0xf6, + 0x0, 0xf4, 0x0, 0x18, 0xa8, 0x1, 0x41, 0x42, + 0xcc, 0x80, 0x25, 0xf0, 0x5b, 0x0, 0xde, 0xa6, + 0x4a, 0x1, 0x94, 0x3c, 0xc8, 0x3, 0x94, 0xec, + 0x2, + + /* U+0077 "w" */ + 0x6d, 0x0, 0x3f, 0xa0, 0x3, 0xcd, 0x48, 0x1, + 0xa2, 0x60, 0x40, 0x64, 0x2, 0xe, 0xda, 0xc, + 0x20, 0x20, 0x40, 0x56, 0xe0, 0x44, 0x0, 0x13, + 0x19, 0x19, 0x7, 0x28, 0x1, 0xc9, 0x5c, 0x8, + 0xcb, 0x80, 0x5, 0xdc, 0xd0, 0x75, 0x62, 0x0, + 0x71, 0x19, 0x83, 0x7c, 0x98, 0x0, 0xab, 0x80, + 0xd0, 0x4c, 0x0, 0x42, 0x2, 0xa, 0x42, 0x0, + + /* U+0078 "x" */ + 0x7f, 0x40, 0x9, 0x7d, 0x1c, 0xe4, 0x0, 0x36, + 0xc8, 0x1c, 0x52, 0x16, 0x72, 0x0, 0x29, 0x68, + 0x5e, 0x0, 0xcc, 0x8e, 0xe3, 0x0, 0xe6, 0x2, + 0x0, 0xe8, 0x39, 0x55, 0x0, 0x46, 0xf6, 0xcb, + 0x62, 0x0, 0xe4, 0x50, 0xb0, 0xa0, 0x92, 0xa0, + 0x0, 0xda, 0x30, + + /* U+0079 "y" */ + 0xd, 0x90, 0xe, 0xca, 0xa, 0x60, 0xc, 0x47, + 0xc0, 0xc0, 0xa0, 0x15, 0xa, 0x0, 0xa7, 0x0, + 0x4c, 0xc0, 0xb, 0x90, 0x41, 0x6, 0x80, 0x25, + 0x16, 0xd, 0x32, 0x0, 0xcf, 0x60, 0xd4, 0x1, + 0xd4, 0x70, 0xe, 0x1, 0xc6, 0x4e, 0xa2, 0x1, + 0xea, 0xe, 0x0, 0xfb, 0xc5, 0x0, 0x3e, 0x66, + 0x0, 0x71, 0x23, 0x8d, 0x0, 0x75, 0x5c, 0xc8, + 0x80, 0x30, + + /* U+007A "z" */ + 0x4f, 0xff, 0x85, 0x6e, 0xf8, 0x84, 0x64, 0x4c, + 0x65, 0x40, 0x1c, 0x91, 0x28, 0x1, 0x8a, 0xd9, + 0x80, 0x18, 0x7d, 0x24, 0x3, 0xb4, 0x28, 0x3, + 0xa4, 0xfc, 0x40, 0x32, 0xb3, 0xb2, 0x26, 0x12, + 0x7, 0xbb, 0xe5, + + /* U+007B "{" */ + 0x0, 0x97, 0xfe, 0xe0, 0xb, 0x9b, 0x32, 0x0, + 0x9f, 0x8c, 0xc4, 0x1, 0x37, 0x0, 0x7b, 0xd8, + 0x40, 0x39, 0x52, 0x80, 0x3d, 0xe8, 0x1, 0xc3, + 0xc6, 0x0, 0x1f, 0xf7, 0x6, 0x0, 0x7, 0x72, + 0x9a, 0x40, 0x22, 0x35, 0x86, 0x0, 0xf6, 0x98, + 0x7, 0x1b, 0x78, 0x7, 0x4a, 0x98, 0x7, 0x36, + 0x80, 0x7f, 0xf0, 0xae, 0x3f, 0xdc, 0x1, 0x3f, + 0x6e, 0xb0, + + /* U+007C "|" */ + 0x3f, 0x0, 0xff, 0xf2, 0x80, + + /* U+007D "}" */ + 0x1f, 0xfb, 0x8c, 0x0, 0x39, 0x92, 0xd8, 0x4, + 0x67, 0x8, 0x7, 0xcd, 0xe0, 0x1c, 0x86, 0xa0, + 0x1d, 0x50, 0x40, 0x18, 0xc5, 0x80, 0x38, 0xc6, + 0x44, 0x3, 0xb1, 0x3b, 0xf8, 0x2, 0xe6, 0xbd, + 0xc0, 0x1, 0x9a, 0xd0, 0x88, 0x0, 0x30, 0x40, + 0xf, 0x4d, 0x88, 0x7, 0x31, 0xb8, 0x7, 0x9f, + 0x80, 0x3f, 0xc3, 0xff, 0x2e, 0x0, 0x7, 0x76, + 0xe4, 0x0, 0x0, + + /* U+007E "~" */ + 0x0, 0xff, 0xe0, 0x4f, 0xd8, 0x4, 0x90, 0xc, + 0xf6, 0xd6, 0x0, 0xd1, 0xa, 0x84, 0xc5, 0x82, + 0x7d, 0x1, 0x40, 0x1d, 0x7d, 0x44, 0x6, 0x90, + 0x0, 0xfb, 0x68, 0x0, + + /* U+00A0 " " */ + + /* U+00A1 "¡" */ + 0xaf, 0x74, 0xc, 0xb6, 0x85, 0x93, 0x0, 0xd0, + 0x0, 0x50, 0x23, 0x1, 0x10, 0x30, 0x9, 0x81, + 0x80, 0x4, 0x3, 0xc0, + + /* U+00A2 "¢" */ + 0x0, 0xde, 0x40, 0x1f, 0xfc, 0x27, 0xe0, 0xda, + 0x10, 0x3, 0xc3, 0x4, 0x3e, 0x80, 0x22, 0x20, + 0x5e, 0x40, 0x40, 0x50, 0x8, 0xe0, 0x8, 0xc, + 0x3, 0xff, 0x86, 0x40, 0x60, 0x1e, 0x40, 0x50, + 0x8, 0x68, 0x42, 0x22, 0x4, 0xc7, 0x10, 0x78, + 0x60, 0x96, 0xc0, 0x9, 0xf8, 0x36, 0xc4, 0x3, + 0xfc, + + /* U+00A3 "£" */ + 0x0, 0x97, 0x3f, 0xd8, 0x80, 0x13, 0xd3, 0x44, + 0x2, 0xdc, 0x6, 0x1a, 0x9d, 0xd9, 0x6, 0x4, + 0x18, 0x1, 0x9d, 0x0, 0x80, 0xc0, 0x3e, 0x12, + 0xd0, 0xf, 0xf, 0x92, 0x7f, 0xce, 0x0, 0x1c, + 0x80, 0x9c, 0xc3, 0x0, 0x46, 0x60, 0x53, 0x30, + 0x80, 0x62, 0x3, 0x0, 0xf1, 0x43, 0x0, 0x7d, + 0x2, 0x11, 0x1c, 0xc0, 0x13, 0x3b, 0xf6, 0x80, + + /* U+00A4 "¤" */ + 0x39, 0x0, 0xea, 0x20, 0x63, 0x0, 0x9d, 0x4c, + 0xcb, 0xbd, 0xfb, 0x30, 0x20, 0x43, 0x16, 0xa2, + 0x60, 0x7, 0x86, 0x48, 0x58, 0x2, 0x3, 0x0, + 0xb4, 0xc0, 0x40, 0x3f, 0x18, 0x10, 0x5, 0xc4, + 0x0, 0x68, 0x41, 0x56, 0xb0, 0x0, 0x8a, 0xfa, + 0x80, 0xc0, 0x9e, 0xbb, 0x39, 0xec, 0x1c, 0xd4, + 0x4c, 0x64, 0xcc, 0xb8, 0x1, 0x87, 0x4c, + + /* U+00A5 "¥" */ + 0x5f, 0x40, 0xe, 0x7f, 0x25, 0x28, 0x0, 0xe9, + 0x52, 0x8, 0x34, 0x0, 0x98, 0x60, 0x0, 0xa5, + 0xe0, 0x14, 0x29, 0x0, 0x50, 0x86, 0xc, 0x30, + 0x1, 0x94, 0xa0, 0x2d, 0x48, 0x3, 0xd, 0xa4, + 0x14, 0x88, 0x5, 0x5d, 0x20, 0xe1, 0x9c, 0xe0, + 0x8, 0xbb, 0x40, 0x55, 0xd9, 0x80, 0x4, 0x88, + 0x20, 0x74, 0x40, 0x80, 0x7f, 0xf0, 0x4d, 0x55, + 0xa0, 0x8a, 0xa2, 0x0, 0x4d, 0x51, 0xc2, 0x6a, + 0x8a, 0x0, + + /* U+00A6 "¦" */ + 0x3f, 0x0, 0xff, 0xe2, 0x90, 0x1e, 0x80, 0x7c, + 0x58, 0x2, 0x60, 0x1f, 0xfc, 0x40, + + /* U+00A7 "§" */ + 0x0, 0x36, 0x7f, 0x51, 0x0, 0x1a, 0x56, 0xa5, + 0xd0, 0x1, 0x4b, 0x2a, 0xfa, 0xc0, 0x1, 0x0, + 0xfd, 0x4b, 0x26, 0x1, 0xce, 0x6f, 0x9d, 0x0, + 0x16, 0x57, 0x63, 0x3e, 0x3, 0x13, 0x1, 0xc5, + 0x9a, 0x8, 0x80, 0x32, 0x0, 0xa8, 0x61, 0x80, + 0x14, 0x94, 0xac, 0x73, 0xab, 0xb0, 0x0, 0x9b, + 0xa6, 0x44, 0x18, 0x6, 0x28, 0xe1, 0x80, 0xf, + 0x94, 0x40, 0xb, 0x4, 0x2c, 0x98, 0x0, 0x27, + 0xde, 0x97, 0x70, 0x1, 0xff, 0x73, 0xe0, 0x0, + + /* U+00A8 "¨" */ + 0x0, 0xf8, 0xbe, 0x43, 0x38, 0x2, 0x10, 0x1, + 0x80, + + /* U+00A9 "©" */ + 0x0, 0xc, 0x6f, 0x63, 0x80, 0x62, 0xf1, 0xec, + 0x8c, 0xc0, 0x80, 0x3e, 0x24, 0xc9, 0x2d, 0xac, + 0x18, 0x58, 0x6f, 0xac, 0x6d, 0xf, 0x64, 0x34, + 0xb0, 0x24, 0x5a, 0xd5, 0xc8, 0xe0, 0xae, 0x3, + 0x4c, 0x40, 0x40, 0x40, 0x39, 0xc4, 0x4, 0x3, + 0xe7, 0x26, 0x22, 0x38, 0x3c, 0x6, 0x95, 0xf0, + 0x74, 0xfc, 0xd8, 0x2e, 0xb2, 0x20, 0xfb, 0xa3, + 0x71, 0x41, 0xaa, 0x38, 0x8d, 0x32, 0xe0, 0x2, + 0x6e, 0x77, 0xf1, 0xf9, 0x0, 0x47, 0x7f, 0xdf, + 0x42, 0x0, + + /* U+00AA "ª" */ + 0x7, 0xee, 0x58, 0x80, 0xe7, 0xf3, 0xc8, 0xd, + 0x88, 0x21, 0x0, 0xd6, 0x61, 0x80, 0xc, 0x39, + 0x87, 0x10, 0x60, 0xca, 0xd6, 0x40, + + /* U+00AB "«" */ + 0x0, 0xff, 0xe0, 0x52, 0x0, 0x25, 0x80, 0x71, + 0xd4, 0x29, 0xd0, 0x70, 0xbc, 0x31, 0x74, 0xac, + 0xb4, 0xa4, 0xe8, 0xc0, 0xc1, 0x83, 0xc0, 0x80, + 0x12, 0xb4, 0x8e, 0xd0, 0xc0, 0xa, 0x6b, 0x39, + 0x89, 0x40, 0x4, 0xc0, 0x1, 0xe8, 0x40, + + /* U+00AC "¬" */ + 0x0, 0xfe, 0xef, 0xff, 0x9b, 0x73, 0x3c, 0x20, + 0x67, 0xf8, 0x3, 0xff, 0x8a, 0x84, + + /* U+00AD "­" */ + 0x19, 0x9e, 0x2, 0x66, 0xe0, + + /* U+00AE "®" */ + 0x3, 0xbc, 0xa2, 0x3, 0x87, 0x62, 0xe1, 0x89, + 0x7b, 0x29, 0x93, 0x13, 0xd7, 0xb1, 0x31, 0x3f, + 0xeb, 0x14, 0x4c, 0x26, 0x4c, 0x8e, 0x21, 0x9d, + 0xc1, + + /* U+00AF "¯" */ + 0x1, 0x1c, 0x5, 0xdd, 0xc5, 0x99, 0xb4, + + /* U+00B0 "°" */ + 0x0, 0xfc, 0x9b, 0xf6, 0x40, 0x77, 0x56, 0x58, + 0x36, 0x8e, 0x92, 0x8, 0x22, 0x0, 0x8c, 0xda, + 0xa8, 0xe, 0x48, 0x88, 0xbf, 0x9a, 0x20, 0x7f, + 0xde, 0x50, + + /* U+00B1 "±" */ + 0x0, 0x8f, 0x44, 0x3, 0xce, 0x40, 0x1f, 0xfc, + 0x41, 0x0, 0xee, 0xff, 0x50, 0x77, 0xfa, 0xf7, + 0x31, 0x1, 0x79, 0x89, 0x33, 0xc0, 0xa6, 0x62, + 0x0, 0xff, 0xe4, 0xa7, 0x88, 0x4, 0x89, 0xf8, + 0xea, 0xef, 0xe8, + + /* U+00B2 "²" */ + 0x4, 0xde, 0xb1, 0x9, 0xee, 0x4, 0x86, 0x98, + 0x8, 0x80, 0x24, 0x45, 0x80, 0x16, 0xb4, 0x81, + 0xe7, 0xcc, 0x1, 0x5, 0xfd, 0x80, + + /* U+00B3 "³" */ + 0x5, 0xee, 0x58, 0x86, 0xe7, 0x4, 0x84, 0x10, + 0x3f, 0x0, 0x27, 0xdd, 0x80, 0x13, 0xe3, 0x21, + 0x22, 0x0, 0x30, 0xef, 0xe1, 0xb0, + + /* U+00B4 "´" */ + 0x0, 0x40, 0x83, 0xb8, 0x1, 0x32, 0x12, 0x46, + 0x0, + + /* U+00B5 "µ" */ + 0x3f, 0x30, 0x9, 0xbc, 0x3, 0xff, 0xda, 0xa0, + 0x17, 0x0, 0x68, 0x52, 0x64, 0x0, 0xcf, 0x79, + 0xf4, 0x1, 0xaf, 0xb9, 0x2d, 0xe0, 0x10, 0x80, + 0x7f, 0xf3, 0x0, + + /* U+00B6 "¶" */ + 0x0, 0x1d, 0xff, 0xf0, 0x81, 0xe2, 0x0, 0x2a, + 0x40, 0x2f, 0x0, 0xce, 0xa0, 0x1, 0x30, 0xf, + 0xc6, 0x1, 0xfc, 0x40, 0x1f, 0xe6, 0x0, 0xfe, + 0x93, 0x0, 0xfe, 0xca, 0x50, 0xf, 0xcb, 0x0, + 0x1f, 0xff, 0x92, 0xc2, 0x3c, 0x10, + + /* U+00B7 "·" */ + 0x59, 0x3b, 0x68, 0x40, 0xc0, + + /* U+00B8 "¸" */ + 0x0, 0x28, 0x4, 0x52, 0x1, 0x97, 0x50, 0x6, + 0x5b, 0x3, 0xfc, 0xb6, 0x19, 0xdc, 0x30, + + /* U+00B9 "¹" */ + 0x2, 0xbf, 0x40, 0x0, 0xfe, 0x88, 0x4, 0x3a, + 0xc0, 0x1f, 0xfc, 0xec, 0xe1, 0xae, 0x0, + + /* U+00BA "º" */ + 0x3, 0xce, 0xb2, 0x1, 0xd8, 0xec, 0xe0, 0x43, + 0x40, 0x73, 0x22, 0x8, 0x6, 0x12, 0x10, 0xc, + 0x28, 0x48, 0xe, 0x64, 0x37, 0x3f, 0x9c, 0x0, + + /* U+00BB "»" */ + 0x0, 0xfe, 0x96, 0x0, 0x3c, 0x0, 0x5b, 0x30, + 0x8, 0xf4, 0x0, 0x4a, 0x7a, 0x3b, 0x5b, 0x0, + 0x2c, 0x2a, 0x92, 0x91, 0xc0, 0x1a, 0x2, 0x1a, + 0x2, 0x12, 0xb2, 0x8e, 0xf3, 0x3b, 0x5b, 0x2c, + 0x4c, 0x0, 0x31, 0x0, 0x16, 0xc0, 0x0, + + /* U+00BC "¼" */ + 0x0, 0xff, 0x8a, 0x7e, 0x0, 0x31, 0x79, 0x37, + 0xb0, 0x7, 0x42, 0x92, 0x30, 0x7, 0x1a, 0xc0, + 0x7, 0xe8, 0x62, 0x0, 0xf9, 0x11, 0x0, 0x4, + 0xf6, 0x7f, 0x7, 0xc1, 0x0, 0x27, 0xfe, 0x3, + 0x33, 0x28, 0x6, 0xb3, 0x0, 0x2c, 0x70, 0x4, + 0xcc, 0x30, 0x4, 0x48, 0x6, 0x88, 0x0, 0x22, + 0xc4, 0x2, 0x73, 0x50, 0x24, 0x60, 0xd, 0x30, + 0x0, 0x72, 0xe9, 0x47, 0x63, 0x50, 0x2, 0xf7, + 0x25, 0x5c, + + /* U+00BD "½" */ + 0x0, 0xff, 0x86, 0x7e, 0x0, 0x31, 0x79, 0xaf, + 0xb0, 0x7, 0x42, 0x9a, 0x30, 0x7, 0x1a, 0xc0, + 0x7, 0xe8, 0x62, 0x0, 0xf9, 0x11, 0x0, 0x4, + 0xf6, 0x7f, 0x7, 0xc1, 0x0, 0x27, 0xfb, 0x78, + 0x17, 0x79, 0x80, 0x22, 0xd3, 0x8, 0xda, 0x90, + 0xa, 0xd0, 0x83, 0x8d, 0xc0, 0x21, 0x68, 0x0, + 0xd2, 0xc0, 0x9, 0x33, 0x0, 0x52, 0x32, 0x0, + 0x68, 0x0, 0xa8, 0xec, 0x0, 0xc6, 0x80, 0x1, + 0x55, 0x7f, 0x94, + + /* U+00BE "¾" */ + 0xb, 0xfc, 0x20, 0x8, 0x7c, 0xc8, 0xfd, 0x60, + 0x2, 0xb5, 0x32, 0x80, 0x1, 0x80, 0xd, 0x6c, + 0x0, 0x5e, 0x96, 0x0, 0xf7, 0x10, 0x36, 0xfa, + 0xe0, 0x52, 0x80, 0x0, 0xc7, 0xd4, 0x83, 0xe0, + 0x4, 0x59, 0xfa, 0xa0, 0x44, 0x86, 0x0, 0xd8, + 0x60, 0x6, 0x66, 0x80, 0x4e, 0x26, 0x3, 0x72, + 0x1, 0xa6, 0x0, 0x11, 0x62, 0x1, 0x39, 0xa8, + 0x12, 0xb0, 0x6, 0x98, 0x0, 0x39, 0x74, 0xa3, + 0xb1, 0x28, 0x1, 0x7b, 0x92, 0xae, + + /* U+00BF "¿" */ + 0x0, 0x97, 0xf0, 0x3, 0xd8, 0x6, 0x1, 0xeb, + 0x69, 0x0, 0xf1, 0x4b, 0x0, 0x7f, 0xf1, 0x3a, + 0x0, 0x3f, 0xf8, 0x4f, 0xc2, 0x1, 0xd3, 0x10, + 0xe8, 0x0, 0x8d, 0xa5, 0x84, 0x3, 0x30, 0x28, + 0x6, 0x40, 0x10, 0x50, 0x9, 0xac, 0xd0, 0xe6, + 0x1a, 0x64, 0xa6, 0x1a, 0xcb, 0x29, 0x14, 0x0, + + /* U+00C0 "À" */ + 0x0, 0x8a, 0x0, 0x3f, 0x99, 0xd0, 0x3, 0xf2, + 0x34, 0x0, 0x7f, 0x43, 0x80, 0x7f, 0xd, 0x80, + 0x7f, 0x5f, 0xc0, 0x7, 0xe4, 0x15, 0x0, 0xf9, + 0x42, 0x8, 0x80, 0x1e, 0xf3, 0x60, 0x50, 0xf, + 0x22, 0x1, 0x30, 0x3, 0x8c, 0x6c, 0x31, 0x0, + 0x3a, 0xc4, 0xc1, 0x41, 0x40, 0x32, 0xa8, 0x44, + 0x63, 0xe0, 0x10, 0x92, 0x77, 0x42, 0x80, 0x13, + 0x85, 0x5d, 0xce, 0x26, 0x0, 0xd2, 0x54, 0x4a, + 0x42, 0xc0, 0x8, 0xa0, 0x18, 0x4d, 0x41, 0x3, + 0x0, 0x3a, 0xc8, 0x40, + + /* U+00C1 "Á" */ + 0x0, 0xf4, 0x8, 0x7, 0xe7, 0x70, 0x7, 0xf4, + 0xc8, 0x40, 0x3e, 0x24, 0x60, 0xf, 0xc5, 0x40, + 0x1f, 0xd7, 0xf0, 0x1, 0xf9, 0x5, 0x40, 0x3e, + 0x50, 0x82, 0x20, 0x7, 0xbc, 0xd8, 0x14, 0x3, + 0xc8, 0x80, 0x4c, 0x0, 0xe3, 0x1b, 0xc, 0x40, + 0xe, 0xb1, 0x30, 0x50, 0x50, 0xc, 0xaa, 0x11, + 0x18, 0xf8, 0x4, 0x24, 0x9d, 0xd0, 0xa0, 0x4, + 0xe1, 0x57, 0x73, 0x89, 0x80, 0x34, 0x95, 0x12, + 0x90, 0xb0, 0x2, 0x28, 0x6, 0x13, 0x50, 0x40, + 0xc0, 0xe, 0xb2, 0x10, + + /* U+00C2 "Â" */ + 0x0, 0xcb, 0x6, 0x1, 0xf1, 0xd2, 0xe8, 0x80, + 0x70, 0xeb, 0xea, 0xe8, 0x7, 0x1c, 0xc0, 0x5a, + 0x88, 0x6, 0x27, 0x0, 0xa4, 0x40, 0x3d, 0x7f, + 0x0, 0x1f, 0x90, 0x54, 0x3, 0xe5, 0x8, 0x22, + 0x0, 0x7b, 0xcd, 0x81, 0x40, 0x3c, 0x88, 0x4, + 0xc0, 0xe, 0x31, 0xb0, 0xc4, 0x0, 0xeb, 0x13, + 0x5, 0x5, 0x0, 0xca, 0xa1, 0x11, 0x8f, 0x80, + 0x42, 0x49, 0xdd, 0xa, 0x0, 0x4e, 0x15, 0x77, + 0x38, 0x98, 0x3, 0x49, 0x51, 0x29, 0xb, 0x0, + 0x22, 0x80, 0x61, 0x35, 0x4, 0xc, 0x0, 0xeb, + 0x21, + + /* U+00C3 "Ã" */ + 0x0, 0xff, 0xe0, 0x9f, 0x72, 0x5, 0x0, 0x39, + 0xe2, 0xd3, 0xa8, 0x80, 0x32, 0x32, 0xe6, 0xf9, + 0x0, 0x7e, 0x21, 0x0, 0xfa, 0xfe, 0x0, 0x3f, + 0x20, 0xa8, 0x7, 0xca, 0x10, 0x44, 0x0, 0xf7, + 0x9b, 0x2, 0x80, 0x79, 0x10, 0x9, 0x80, 0x1c, + 0x63, 0x61, 0x88, 0x1, 0xd6, 0x26, 0xa, 0xa, + 0x1, 0x95, 0x42, 0x23, 0x1f, 0x0, 0x84, 0x93, + 0xba, 0x14, 0x0, 0x9c, 0x2a, 0xee, 0x71, 0x30, + 0x6, 0x92, 0xa2, 0x52, 0x16, 0x0, 0x45, 0x0, + 0xc2, 0x6a, 0x8, 0x18, 0x1, 0xd6, 0x42, + + /* U+00C4 "Ä" */ + 0x0, 0x17, 0xc0, 0x66, 0x80, 0x7c, 0x20, 0x3, + 0x0, 0xe2, 0xf9, 0xc, 0xe0, 0xf, 0xfe, 0x35, + 0xfc, 0x0, 0x7e, 0x41, 0x50, 0xf, 0x94, 0x20, + 0x88, 0x1, 0xef, 0x36, 0x5, 0x0, 0xf2, 0x20, + 0x13, 0x0, 0x38, 0xc6, 0xc3, 0x10, 0x3, 0xac, + 0x4c, 0x14, 0x14, 0x3, 0x2a, 0x84, 0x46, 0x3e, + 0x1, 0x9, 0x27, 0x74, 0x28, 0x1, 0x38, 0x55, + 0xdc, 0xe2, 0x60, 0xd, 0x25, 0x44, 0xa4, 0x2c, + 0x0, 0x8a, 0x1, 0x84, 0xd4, 0x10, 0x30, 0x3, + 0xac, 0x84, + + /* U+00C5 "Å" */ + 0x0, 0xd5, 0xd0, 0x1, 0xf3, 0x9f, 0x22, 0x0, + 0x3d, 0xb8, 0xa, 0x60, 0x1e, 0xdc, 0x5, 0x30, + 0xf, 0x39, 0xf2, 0x20, 0x3, 0xea, 0xe8, 0x0, + 0xfd, 0x7f, 0x0, 0x1f, 0x90, 0x54, 0x3, 0xe5, + 0x8, 0x22, 0x0, 0x7b, 0xcd, 0x81, 0x40, 0x3c, + 0x88, 0x4, 0xc0, 0xe, 0x31, 0xb0, 0xc4, 0x0, + 0xeb, 0x13, 0x5, 0x5, 0x0, 0xca, 0xa1, 0x11, + 0x8f, 0x80, 0x42, 0x49, 0xdd, 0xa, 0x0, 0x4e, + 0x15, 0x77, 0x38, 0x98, 0x3, 0x49, 0x51, 0x29, + 0xb, 0x0, 0x22, 0x80, 0x61, 0x35, 0x4, 0xc, + 0x0, 0xeb, 0x21, + + /* U+00C6 "Æ" */ + 0x0, 0x87, 0xff, 0xc2, 0x1, 0x20, 0x30, 0x55, + 0xd8, 0x40, 0x2f, 0x4f, 0x4, 0x48, 0x3, 0x26, + 0x0, 0x7e, 0x31, 0x50, 0xf, 0xd6, 0x24, 0x1, + 0x8, 0x6, 0x47, 0x0, 0xbb, 0x9a, 0x0, 0x13, + 0xc0, 0xa, 0xed, 0xc0, 0x4, 0xa, 0xf9, 0x5, + 0x43, 0x0, 0x79, 0x66, 0x1c, 0x3, 0xc8, 0xa6, + 0x6e, 0x0, 0xe3, 0x1c, 0x0, 0xc8, 0xaa, 0xb, + 0x4, 0x0, 0xd5, 0x50, 0x80, + + /* U+00C7 "Ç" */ + 0x0, 0x2e, 0x7f, 0x48, 0x80, 0x1a, 0x9e, 0x59, + 0xf0, 0x6, 0x56, 0xda, 0x30, 0x9d, 0x2, 0x0, + 0x22, 0xc7, 0xc0, 0x20, 0xc, 0x60, 0x64, 0x1, + 0xf0, 0x80, 0x7f, 0xf1, 0x48, 0x80, 0x1f, 0x78, + 0x10, 0x6, 0x30, 0x40, 0x80, 0x8, 0xb1, 0xc9, + 0x96, 0xda, 0x30, 0x9c, 0x25, 0xd2, 0x59, 0x30, + 0x2, 0x8f, 0x2e, 0xa1, 0x0, 0xea, 0xe7, 0x0, + 0xe4, 0x67, 0xc0, 0xe, 0xde, 0x48, 0x0, 0x0, + + /* U+00C8 "È" */ + 0x0, 0xc, 0x0, 0x7c, 0x4e, 0xa0, 0x1e, 0x37, + 0x80, 0xf, 0xa1, 0x84, 0x3, 0xeb, 0x10, 0x9, + 0xbf, 0xfe, 0x50, 0x4, 0x4c, 0xf3, 0x80, 0x15, + 0x9b, 0x88, 0x3, 0xff, 0x90, 0x23, 0xe0, 0xb, + 0x3b, 0xb8, 0x80, 0x15, 0x77, 0xc4, 0x0, 0x74, + 0x4e, 0x0, 0xff, 0xe4, 0xab, 0x37, 0x10, 0x2, + 0x26, 0x79, 0xc0, + + /* U+00C9 "É" */ + 0x0, 0xe8, 0x10, 0xf, 0x33, 0x98, 0x7, 0xad, + 0xc8, 0x3, 0x85, 0xa0, 0x3, 0xc3, 0x60, 0x19, + 0xbf, 0xfe, 0x50, 0x4, 0x4c, 0xf3, 0x80, 0x15, + 0x9b, 0x88, 0x3, 0xff, 0x90, 0x23, 0xe0, 0xb, + 0x3b, 0xb8, 0x80, 0x15, 0x77, 0xc4, 0x0, 0x74, + 0x4e, 0x0, 0xff, 0xe4, 0xab, 0x37, 0x10, 0x2, + 0x26, 0x79, 0xc0, + + /* U+00CA "Ê" */ + 0x0, 0x92, 0x10, 0x3, 0x8a, 0xd6, 0xc8, 0x3, + 0x7b, 0x6a, 0xf8, 0x4, 0x4b, 0x21, 0x4a, 0x20, + 0x2, 0x80, 0xa, 0x4, 0x1b, 0xff, 0xe5, 0x0, + 0x44, 0xcf, 0x38, 0x1, 0x59, 0xb8, 0x80, 0x3f, + 0xf9, 0x2, 0x3e, 0x0, 0xb3, 0xbb, 0x88, 0x1, + 0x57, 0x7c, 0x40, 0x7, 0x44, 0xe0, 0xf, 0xfe, + 0x4a, 0xb3, 0x71, 0x0, 0x22, 0x67, 0x9c, + + /* U+00CB "Ë" */ + 0x1, 0xea, 0xa, 0xe1, 0x0, 0xff, 0x87, 0xa8, + 0x2b, 0x84, 0x3, 0xfc, 0xdf, 0xff, 0x28, 0x2, + 0x26, 0x79, 0xc0, 0xa, 0xcd, 0xc4, 0x1, 0xff, + 0xc8, 0x11, 0xf0, 0x5, 0x9d, 0xdc, 0x40, 0xa, + 0xbb, 0xe2, 0x0, 0x3a, 0x27, 0x0, 0x7f, 0xf2, + 0x55, 0x9b, 0x88, 0x1, 0x13, 0x3c, 0xe0, + + /* U+00CC "Ì" */ + 0x0, 0x14, 0x0, 0x7c, 0xce, 0x80, 0x1e, 0x46, + 0x80, 0xf, 0xa1, 0xc0, 0x3e, 0x1b, 0x0, 0xcf, + 0xff, 0xf2, 0x2d, 0xd9, 0xc2, 0x6e, 0xca, 0x48, + 0x8d, 0x4, 0x48, 0x40, 0x3f, 0xff, 0x84, 0x88, + 0xd0, 0x44, 0x85, 0x6e, 0xce, 0x13, 0x76, 0x50, + + /* U+00CD "Í" */ + 0x0, 0xe8, 0x10, 0xf, 0x3b, 0x80, 0x3e, 0x99, + 0x8, 0x7, 0x12, 0x30, 0x7, 0x8a, 0x80, 0x33, + 0xff, 0xfc, 0x8b, 0x76, 0x70, 0x9b, 0xb2, 0x92, + 0x23, 0x41, 0x12, 0x10, 0xf, 0xff, 0xe1, 0x22, + 0x34, 0x11, 0x21, 0x5b, 0xb3, 0x84, 0xdd, 0x94, + + /* U+00CE "Î" */ + 0x0, 0x96, 0xc, 0x3, 0x8e, 0x97, 0x44, 0x2, + 0x1d, 0x7d, 0x5d, 0x0, 0x8e, 0x60, 0x2d, 0x44, + 0x0, 0x4e, 0x1, 0x48, 0x83, 0xff, 0xfc, 0x8b, + 0x76, 0x70, 0x9b, 0xb2, 0x92, 0x23, 0x41, 0x12, + 0x10, 0xf, 0xff, 0xe1, 0x22, 0x34, 0x11, 0x21, + 0x5b, 0xb3, 0x84, 0xdd, 0x94, + + /* U+00CF "Ï" */ + 0x2, 0xf8, 0xc, 0xd0, 0xe, 0x10, 0x1, 0x80, + 0x45, 0xf2, 0x19, 0xc0, 0x1f, 0xf3, 0xff, 0xfc, + 0x8b, 0x76, 0x70, 0x9b, 0xb2, 0x92, 0x23, 0x41, + 0x12, 0x10, 0xf, 0xff, 0xe1, 0x22, 0x34, 0x11, + 0x21, 0x5b, 0xb3, 0x84, 0xdd, 0x94, + + /* U+00D0 "Ð" */ + 0x2, 0xff, 0xda, 0xc0, 0x1c, 0xd3, 0x26, 0x98, + 0x0, 0xd8, 0xcc, 0xb6, 0x71, 0x0, 0xfa, 0xc1, + 0x80, 0x3e, 0x21, 0xd9, 0xd0, 0xaf, 0xe0, 0x1, + 0x8c, 0x50, 0x46, 0x60, 0x2, 0x71, 0x70, 0x23, + 0x20, 0x9, 0xc0, 0x3f, 0x18, 0x80, 0x7c, 0x43, + 0xa0, 0x1f, 0x58, 0x30, 0x5, 0x8c, 0xcb, 0x67, + 0x10, 0x9, 0xa6, 0x4d, 0x30, 0x0, + + /* U+00D1 "Ñ" */ + 0x0, 0xff, 0x1f, 0x72, 0x5, 0x0, 0x27, 0x8b, + 0x4e, 0xa2, 0x0, 0x23, 0x2e, 0x6f, 0x90, 0x7, + 0x88, 0x40, 0x13, 0xf8, 0x1, 0xbd, 0x40, 0x6, + 0x40, 0x1e, 0x21, 0xa0, 0xf, 0xa5, 0x80, 0x3e, + 0x61, 0x50, 0xf, 0xa, 0x70, 0x7, 0xdc, 0x84, + 0x1, 0xe5, 0x1a, 0x0, 0xf9, 0x98, 0x1, 0xf5, + 0xa, 0x0, 0x78, 0x8f, 0xc0, 0x3e, 0x94, 0x0, + 0xf9, 0x40, 0x20, + + /* U+00D2 "Ò" */ + 0x0, 0x90, 0x3, 0xe6, 0xb2, 0x0, 0xf3, 0x1c, + 0x0, 0x7d, 0xc8, 0x1, 0xf1, 0x70, 0x7, 0xa3, + 0xbf, 0x5c, 0x2, 0xc7, 0x69, 0x78, 0x80, 0x29, + 0xd4, 0x35, 0x3b, 0x8b, 0xc5, 0x40, 0x29, 0x5, + 0x75, 0x0, 0xc2, 0x2d, 0x22, 0x0, 0x71, 0xb0, + 0x7, 0xff, 0x18, 0x88, 0x1, 0xc6, 0xce, 0xa0, + 0x18, 0x45, 0xbe, 0x2a, 0x1, 0x48, 0x2a, 0x8e, + 0xa1, 0xa9, 0xdc, 0x41, 0x8e, 0xd2, 0xf1, 0x0, + + /* U+00D3 "Ó" */ + 0x0, 0xe5, 0x0, 0xf9, 0x68, 0xc0, 0x3d, 0xc, + 0x60, 0x1c, 0x61, 0x20, 0x1e, 0x3d, 0x0, 0xf4, + 0x77, 0xeb, 0x80, 0x58, 0xed, 0x2f, 0x10, 0x5, + 0x3a, 0x86, 0xa7, 0x71, 0x78, 0xa8, 0x5, 0x20, + 0xae, 0xa0, 0x18, 0x45, 0xa4, 0x40, 0xe, 0x36, + 0x0, 0xff, 0xe3, 0x11, 0x0, 0x38, 0xd9, 0xd4, + 0x3, 0x8, 0xb7, 0xc5, 0x40, 0x29, 0x5, 0x51, + 0xd4, 0x35, 0x3b, 0x88, 0x31, 0xda, 0x5e, 0x20, + 0x0, + + /* U+00D4 "Ô" */ + 0x0, 0xb3, 0xe4, 0x3, 0x9c, 0xd9, 0x86, 0x1, + 0xc, 0x4c, 0x9b, 0x80, 0x23, 0x76, 0x9, 0x41, + 0x0, 0x14, 0x0, 0x52, 0x20, 0x14, 0x77, 0xeb, + 0x80, 0x58, 0xed, 0x2f, 0x10, 0x5, 0x3a, 0x86, + 0xa7, 0x71, 0x78, 0xa8, 0x5, 0x20, 0xae, 0xa0, + 0x18, 0x45, 0xa4, 0x40, 0xe, 0x36, 0x0, 0xff, + 0xe3, 0x11, 0x0, 0x38, 0xd9, 0xd4, 0x3, 0x8, + 0xb7, 0xc5, 0x40, 0x29, 0x5, 0x51, 0xd4, 0x35, + 0x3b, 0x88, 0x31, 0xda, 0x5e, 0x20, 0x0, + + /* U+00D5 "Õ" */ + 0x0, 0xff, 0x1f, 0x72, 0x5, 0x0, 0x27, 0x8b, + 0x4e, 0xa2, 0x0, 0x23, 0x2e, 0x6f, 0x90, 0x7, + 0x88, 0x40, 0x34, 0x77, 0xeb, 0x80, 0x58, 0xed, + 0x2f, 0x10, 0x5, 0x3a, 0x86, 0xa7, 0x71, 0x78, + 0xa8, 0x5, 0x20, 0xae, 0xa0, 0x18, 0x45, 0xa4, + 0x40, 0xe, 0x36, 0x0, 0xff, 0xe3, 0x11, 0x0, + 0x38, 0xd9, 0xd4, 0x3, 0x8, 0xb7, 0xc5, 0x40, + 0x29, 0x5, 0x51, 0xd4, 0x35, 0x3b, 0x88, 0x31, + 0xda, 0x5e, 0x20, 0x0, + + /* U+00D6 "Ö" */ + 0x2, 0xf8, 0xc, 0xd0, 0xe, 0x10, 0x1, 0x80, + 0x45, 0xf2, 0x19, 0xc0, 0x1f, 0xfc, 0x18, 0xef, + 0xd7, 0x0, 0xb1, 0xda, 0x5e, 0x20, 0xa, 0x75, + 0xd, 0x4e, 0xe2, 0xf1, 0x50, 0xa, 0x41, 0x5d, + 0x40, 0x30, 0x8b, 0x48, 0x80, 0x1c, 0x6c, 0x1, + 0xff, 0xc6, 0x22, 0x0, 0x71, 0xb3, 0xa8, 0x6, + 0x11, 0x6f, 0x8a, 0x80, 0x52, 0xa, 0xa3, 0xa8, + 0x6a, 0x77, 0x10, 0x63, 0xb4, 0xbc, 0x40, 0x0, + + /* U+00D7 "×" */ + 0x8, 0x0, 0xc3, 0x0, 0x4d, 0x40, 0x1, 0xc4, + 0x2, 0xc4, 0xa0, 0xd0, 0xd0, 0x1, 0x6a, 0xf0, + 0xe0, 0x80, 0x46, 0x61, 0x41, 0x0, 0xd4, 0x52, + 0xce, 0x1, 0x52, 0x6b, 0x54, 0x38, 0x12, 0x69, + 0x82, 0xcc, 0x80, 0xb8, 0xc0, 0x26, 0xe0, + + /* U+00D8 "Ø" */ + 0x0, 0xfc, 0x46, 0x1, 0x47, 0x7e, 0xb4, 0xc0, + 0x3, 0x1d, 0xa5, 0xe5, 0x24, 0x14, 0xea, 0x1a, + 0x88, 0x2, 0xf1, 0x50, 0x3, 0x80, 0x14, 0x1d, + 0x40, 0x3, 0x12, 0x3a, 0x4, 0x40, 0x5, 0xaa, + 0x1b, 0x0, 0x64, 0x5b, 0x0, 0xfa, 0x60, 0x40, + 0x31, 0x1b, 0x83, 0x80, 0xd, 0x81, 0xd2, 0x24, + 0x0, 0x22, 0xd0, 0xf1, 0x44, 0x0, 0x24, 0x14, + 0x2, 0x49, 0x6a, 0x77, 0x10, 0x5b, 0x3a, 0x4b, + 0xc4, 0x0, 0x17, 0x31, 0xbf, 0xae, 0x0, + + /* U+00D9 "Ù" */ + 0x0, 0x14, 0x0, 0x7c, 0xce, 0x80, 0x1e, 0x46, + 0x80, 0xf, 0xa1, 0xc0, 0x3e, 0x1b, 0x0, 0xd3, + 0xe0, 0x18, 0xbd, 0x40, 0x3f, 0xfd, 0x42, 0x1, + 0xff, 0xc2, 0x10, 0x7, 0x80, 0x73, 0x83, 0x18, + 0x28, 0x5, 0x80, 0x48, 0x55, 0xd, 0x2e, 0xa2, + 0x18, 0xad, 0x2e, 0xd4, 0x0, + + /* U+00DA "Ú" */ + 0x0, 0xe8, 0x10, 0xf, 0x3b, 0x80, 0x3e, 0x99, + 0x8, 0x7, 0x12, 0x30, 0x7, 0x8a, 0x80, 0x34, + 0xf8, 0x6, 0x2f, 0x50, 0xf, 0xff, 0x50, 0x80, + 0x7f, 0xf0, 0x84, 0x1, 0xe0, 0x1c, 0xe0, 0xc6, + 0xa, 0x1, 0x60, 0x12, 0x15, 0x43, 0x4b, 0xa8, + 0x86, 0x2b, 0x4b, 0xb5, 0x0, + + /* U+00DB "Û" */ + 0x0, 0xb3, 0xe8, 0x3, 0x9c, 0xdd, 0x50, 0x2, + 0x28, 0xb8, 0x7a, 0x0, 0x8e, 0x90, 0x22, 0x0, + 0x10, 0xa8, 0x4, 0xc0, 0x9, 0xf0, 0xc, 0x5e, + 0xa0, 0x1f, 0xfe, 0xa1, 0x0, 0xff, 0xe1, 0x8, + 0x3, 0xc0, 0x39, 0xc1, 0x8c, 0x14, 0x2, 0xc0, + 0x24, 0x2a, 0x86, 0x97, 0x51, 0xc, 0x56, 0x97, + 0x6a, 0x0, + + /* U+00DC "Ü" */ + 0x2, 0xf8, 0xc, 0xd0, 0xe, 0x10, 0x1, 0x80, + 0x45, 0xf2, 0x19, 0xc0, 0x1f, 0xf4, 0xf8, 0x6, + 0x2f, 0x50, 0xf, 0xff, 0x50, 0x80, 0x7f, 0xf0, + 0x84, 0x1, 0xe0, 0x1c, 0xe0, 0xc6, 0xa, 0x1, + 0x60, 0x12, 0x15, 0x43, 0x4b, 0xa8, 0x86, 0x2b, + 0x4b, 0xb5, 0x0, + + /* U+00DD "Ý" */ + 0x0, 0xf4, 0x8, 0x7, 0xe7, 0x70, 0x7, 0xf4, + 0xc8, 0x40, 0x3e, 0x24, 0x60, 0xf, 0xc5, 0x40, + 0x1c, 0xde, 0x80, 0x1c, 0xfe, 0x6c, 0x52, 0x1, + 0x86, 0x54, 0xc2, 0x9, 0x40, 0x29, 0x18, 0x0, + 0x29, 0xc0, 0x0, 0x5d, 0x88, 0x2, 0xf2, 0x60, + 0x80, 0x80, 0xc, 0x6b, 0x4, 0xd0, 0x20, 0x1d, + 0x3, 0x82, 0xe0, 0x1e, 0x26, 0x1b, 0x0, 0xfd, + 0x80, 0xe0, 0x1f, 0xfe, 0x40, + + /* U+00DE "Þ" */ + 0x6f, 0x20, 0xf, 0xfe, 0x28, 0x8c, 0x1, 0xec, + 0xee, 0x7e, 0xa8, 0x5, 0x57, 0x6a, 0x6a, 0x30, + 0x3, 0xa2, 0x16, 0xc6, 0x80, 0x3e, 0x22, 0x0, + 0x7c, 0x42, 0x0, 0x11, 0x8a, 0x7, 0xc0, 0x19, + 0xdc, 0xd7, 0x72, 0x80, 0x2a, 0xed, 0x9d, 0x0, + 0x13, 0xa2, 0xc, 0x40, 0x3f, 0xe0, + + /* U+00DF "ß" */ + 0x7, 0xef, 0xf2, 0x80, 0x74, 0x4d, 0xd9, 0x0, + 0x31, 0x1, 0xa2, 0x4, 0x3, 0xff, 0x89, 0x7f, + 0xfc, 0xe0, 0x14, 0x5d, 0xd2, 0x4, 0x1, 0x1a, + 0x25, 0xa6, 0xa0, 0x1e, 0x64, 0xe0, 0xf, 0x14, + 0xc8, 0x80, 0x3d, 0xc8, 0xc0, 0x1f, 0x1a, 0xe3, + 0x0, 0x7b, 0x75, 0x73, 0x20, 0xe, 0x23, 0x78, + 0x61, 0x0, 0xf8, 0xc1, 0x4b, 0xd0, 0x3, 0xce, + 0x1, 0xf6, 0x89, 0x80, 0x44, 0x8d, 0x6d, 0x0, + 0x1a, 0xed, 0x1, 0x6c, 0x0, + + /* U+00E0 "à" */ + 0x0, 0x23, 0x0, 0x7d, 0x90, 0x1, 0xf4, 0x1b, + 0x80, 0x7d, 0x3c, 0x1, 0xf2, 0x48, 0x7, 0x25, + 0xf7, 0x2c, 0xc0, 0x7, 0x63, 0x76, 0x7c, 0x20, + 0x3a, 0xa2, 0xab, 0x1, 0x40, 0xa, 0x1, 0x8b, + 0x80, 0xd, 0x9d, 0xfe, 0x20, 0x3, 0x4a, 0x6e, + 0xc4, 0x0, 0xd3, 0x83, 0x22, 0x0, 0x7f, 0x10, + 0x3, 0x4d, 0x44, 0xec, 0x88, 0x8d, 0x10, 0xee, + 0x7c, 0xad, 0x0, + + /* U+00E1 "á" */ + 0x0, 0xc8, 0xc0, 0x1f, 0x45, 0x0, 0x78, 0xcd, + 0xc0, 0x1e, 0x58, 0x20, 0xf, 0x33, 0x80, 0x72, + 0x5f, 0x72, 0xcc, 0x0, 0x76, 0x37, 0x67, 0xc2, + 0x3, 0xaa, 0x2a, 0xb0, 0x14, 0x0, 0xa0, 0x18, + 0xb8, 0x0, 0xd9, 0xdf, 0xe2, 0x0, 0x34, 0xa6, + 0xec, 0x40, 0xd, 0x38, 0x32, 0x20, 0x7, 0xf1, + 0x0, 0x34, 0xd4, 0x4e, 0xc8, 0x88, 0xd1, 0xe, + 0xe7, 0xca, 0xd0, + + /* U+00E2 "â" */ + 0x0, 0x9e, 0x4, 0x3, 0xa2, 0xd, 0x40, 0x19, + 0x1e, 0xf1, 0x5c, 0x2, 0x2b, 0x42, 0xcf, 0x0, + 0x99, 0x0, 0x5, 0x0, 0x12, 0x5f, 0x72, 0xcc, + 0x0, 0x76, 0x37, 0x67, 0xc2, 0x3, 0xaa, 0x2a, + 0xb0, 0x14, 0x0, 0xa0, 0x18, 0xb8, 0x0, 0xd9, + 0xdf, 0xe2, 0x0, 0x34, 0xa6, 0xec, 0x40, 0xd, + 0x38, 0x32, 0x20, 0x7, 0xf1, 0x0, 0x34, 0xd4, + 0x4e, 0xc8, 0x88, 0xd1, 0xe, 0xe7, 0xca, 0xd0, + + /* U+00E3 "ã" */ + 0x6, 0xfc, 0x50, 0x40, 0x8, 0x6a, 0xed, 0xf0, + 0x1, 0x3a, 0xbf, 0x66, 0x0, 0x3e, 0x30, 0xc, + 0x97, 0xdc, 0xb3, 0x0, 0x1d, 0x8d, 0xd9, 0xf0, + 0x80, 0xea, 0x8a, 0xac, 0x5, 0x0, 0x28, 0x6, + 0x2e, 0x0, 0x36, 0x77, 0xf8, 0x80, 0xd, 0x29, + 0xbb, 0x10, 0x3, 0x4e, 0xc, 0x88, 0x1, 0xfc, + 0x40, 0xd, 0x35, 0x13, 0xb2, 0x22, 0x34, 0x43, + 0xb9, 0xf2, 0xb4, + + /* U+00E4 "ä" */ + 0x6, 0xf4, 0x1e, 0x90, 0xf, 0x84, 0xc0, 0x26, + 0xf4, 0x1f, 0xa0, 0x9, 0x2f, 0xb9, 0x66, 0x0, + 0x3b, 0x1b, 0xb3, 0xe1, 0x1, 0xd5, 0x15, 0x58, + 0xa, 0x0, 0x50, 0xc, 0x5c, 0x0, 0x6c, 0xef, + 0xf1, 0x0, 0x1a, 0x53, 0x76, 0x20, 0x6, 0x9c, + 0x19, 0x10, 0x3, 0xf8, 0x80, 0x1a, 0x6a, 0x27, + 0x64, 0x44, 0x68, 0x87, 0x73, 0xe5, 0x68, + + /* U+00E5 "å" */ + 0x0, 0x26, 0xf3, 0x0, 0x74, 0xf7, 0xc0, 0x80, + 0x7f, 0xf0, 0x67, 0xbe, 0x4, 0x3, 0x26, 0xf3, + 0x0, 0x64, 0xbe, 0xe5, 0x98, 0x0, 0xec, 0x6e, + 0xcf, 0x84, 0x7, 0x54, 0x55, 0x60, 0x28, 0x1, + 0x40, 0x31, 0x70, 0x1, 0xb3, 0xbf, 0xc4, 0x0, + 0x69, 0x4d, 0xd8, 0x80, 0x1a, 0x70, 0x64, 0x40, + 0xf, 0xe2, 0x0, 0x69, 0xa8, 0x9d, 0x91, 0x11, + 0xa2, 0x1d, 0xcf, 0x95, 0xa0, + + /* U+00E6 "æ" */ + 0x7, 0xef, 0x81, 0xbf, 0xd4, 0x5, 0x9a, 0xc6, + 0xc2, 0xda, 0xa0, 0x2e, 0xa1, 0xb1, 0x49, 0x39, + 0xa0, 0x18, 0x3, 0xc8, 0x2, 0x62, 0x7, 0xde, + 0x82, 0xff, 0x90, 0x5a, 0x63, 0x1c, 0xb7, 0x73, + 0xeb, 0x29, 0x79, 0x91, 0x60, 0x70, 0xb, 0x84, + 0x80, 0x16, 0x48, 0xc8, 0xc0, 0x7c, 0x94, 0xc5, + 0x2b, 0x7b, 0x1a, 0xf6, 0x3e, 0x0, + + /* U+00E7 "ç" */ + 0x0, 0x2e, 0xff, 0x48, 0x4, 0xf4, 0x95, 0x2f, + 0x60, 0x8, 0x79, 0x57, 0xd2, 0x5, 0xa, 0x0, + 0x8e, 0x40, 0x80, 0xc0, 0x3c, 0x20, 0x1f, 0x88, + 0xc, 0x3, 0xc8, 0x34, 0x1, 0xd, 0x8, 0x4b, + 0xca, 0xb6, 0x38, 0x83, 0xca, 0x54, 0x3e, 0x0, + 0x4d, 0x87, 0xd4, 0x20, 0x1c, 0x9c, 0xa0, 0x1c, + 0x92, 0xb8, 0x1, 0xdf, 0xcd, 0x40, 0x0, + + /* U+00E8 "è" */ + 0x0, 0x14, 0x0, 0x7c, 0x2e, 0xa0, 0x1e, 0x36, + 0x90, 0xf, 0xa5, 0xc4, 0x3, 0xeb, 0x10, 0xe, + 0x7d, 0xfd, 0x60, 0xa, 0xa2, 0xae, 0xa6, 0x40, + 0x8b, 0x8e, 0x8f, 0x4c, 0x7a, 0x66, 0x0, 0x94, + 0x11, 0x4f, 0xff, 0x83, 0x80, 0xf3, 0x3d, 0xc, + 0x26, 0x7e, 0x1e, 0x24, 0x0, 0x8b, 0x5, 0x16, + 0xe1, 0x5f, 0x0, 0x42, 0xa1, 0x6a, 0x5b, 0x0, + + /* U+00E9 "é" */ + 0x0, 0xe8, 0x10, 0xf, 0x3b, 0x8c, 0x3, 0xd3, + 0x4, 0x1, 0xc4, 0xae, 0x1, 0xe2, 0xb0, 0xf, + 0x3e, 0xfe, 0xb0, 0x5, 0x51, 0x57, 0x53, 0x20, + 0x45, 0xc7, 0x47, 0xa6, 0x3d, 0x33, 0x0, 0x4a, + 0x8, 0xa7, 0xff, 0xc1, 0xc0, 0x79, 0x9e, 0x86, + 0x13, 0x3f, 0xf, 0x12, 0x0, 0x45, 0x82, 0x8b, + 0x70, 0xaf, 0x80, 0x21, 0x50, 0xb5, 0x2d, 0x80, + + /* U+00EA "ê" */ + 0x0, 0x96, 0x10, 0x3, 0x8e, 0x96, 0xc8, 0x2, + 0x1d, 0x7d, 0x5e, 0x0, 0x8d, 0xa0, 0x29, 0x44, + 0x0, 0x50, 0x1, 0x48, 0x80, 0x4f, 0xbf, 0xac, + 0x1, 0x54, 0x55, 0xd4, 0xc8, 0x11, 0x71, 0xd1, + 0xe9, 0x8f, 0x4c, 0xc0, 0x12, 0x82, 0x29, 0xff, + 0xf0, 0x70, 0x1e, 0x67, 0xa1, 0x84, 0xcf, 0xc3, + 0xc4, 0x80, 0x11, 0x60, 0xa2, 0xdc, 0x2b, 0xe0, + 0x8, 0x54, 0x2d, 0x4b, 0x60, 0x0, + + /* U+00EB "ë" */ + 0x2, 0xf9, 0xb, 0xe0, 0xf, 0xfe, 0x1, 0x7c, + 0x85, 0xf0, 0x6, 0x7d, 0xfd, 0x60, 0xa, 0xa2, + 0xae, 0xa6, 0x40, 0x8b, 0x8e, 0x8f, 0x4c, 0x7a, + 0x66, 0x0, 0x94, 0x11, 0x4f, 0xff, 0x83, 0x80, + 0xf3, 0x3d, 0xc, 0x26, 0x7e, 0x1e, 0x24, 0x0, + 0x8b, 0x5, 0x16, 0xe1, 0x5f, 0x0, 0x42, 0xa1, + 0x6a, 0x5b, 0x0, + + /* U+00EC "ì" */ + 0x0, 0xa5, 0xc0, 0x3e, 0x19, 0x0, 0xfa, 0xd, + 0x80, 0x3e, 0x93, 0x0, 0xf9, 0x14, 0x2, 0x1f, + 0xfd, 0x60, 0x10, 0xdd, 0xc4, 0x1, 0xc8, 0x90, + 0x80, 0x7f, 0xf9, 0x91, 0x21, 0x74, 0x41, 0x8d, + 0xdc, 0x47, 0x76, 0xe0, + + /* U+00ED "í" */ + 0x0, 0xe8, 0x80, 0x7, 0xce, 0x1, 0xf3, 0x94, + 0x0, 0x78, 0xb8, 0x3, 0xe5, 0x30, 0x8, 0x7f, + 0xf5, 0x80, 0x43, 0x77, 0x10, 0x7, 0x22, 0x42, + 0x1, 0xff, 0xe6, 0x44, 0x85, 0xd1, 0x6, 0x37, + 0x71, 0x1d, 0xdb, 0x80, + + /* U+00EE "î" */ + 0x0, 0xce, 0xe0, 0xf, 0x5d, 0x4d, 0x0, 0x67, + 0x6c, 0xc2, 0x30, 0x4, 0x58, 0x25, 0x86, 0x1, + 0x28, 0x80, 0x9, 0x40, 0x7f, 0xf5, 0x80, 0x43, + 0x77, 0x10, 0x7, 0x22, 0x42, 0x1, 0xff, 0xe6, + 0x44, 0x85, 0xd1, 0x6, 0x37, 0x71, 0x1d, 0xdb, + 0x80, + + /* U+00EF "ï" */ + 0x0, 0x47, 0x91, 0xfb, 0x80, 0x7f, 0xf0, 0x23, + 0xc8, 0xfd, 0xc0, 0x7f, 0xf5, 0x80, 0x43, 0x77, + 0x10, 0x7, 0x22, 0x42, 0x1, 0xff, 0xe6, 0x44, + 0x85, 0xd1, 0x6, 0x37, 0x71, 0x1d, 0xdb, 0x80, + + /* U+00F0 "ð" */ + 0x0, 0xf0, 0x80, 0x45, 0x7c, 0xd1, 0xe2, 0x0, + 0x2b, 0x69, 0x6f, 0x10, 0x9, 0x99, 0x2, 0xc0, + 0x19, 0xfd, 0xee, 0x8, 0x0, 0x31, 0xfd, 0xc5, + 0x80, 0x6, 0xbc, 0xd9, 0xba, 0x82, 0x9e, 0xba, + 0xcd, 0x82, 0xf9, 0x18, 0x4, 0xc0, 0x4c, 0xc0, + 0xe, 0x1e, 0x0, 0xfe, 0x66, 0x0, 0x70, 0xff, + 0x11, 0x80, 0x4c, 0x8, 0x96, 0xba, 0xc4, 0xb1, + 0x5, 0xb4, 0xd2, 0xc4, 0x80, + + /* U+00F1 "ñ" */ + 0x2, 0xde, 0x81, 0x41, 0x0, 0xd, 0x5a, 0xf5, + 0x90, 0x0, 0xdd, 0x77, 0x5c, 0x60, 0x1e, 0x21, + 0x0, 0x1f, 0xa5, 0x7f, 0x40, 0x6, 0xff, 0x6c, + 0x3c, 0x0, 0x5c, 0xa4, 0xce, 0xe0, 0x9, 0x80, + 0x2d, 0x1, 0x0, 0xf0, 0x81, 0x80, 0x7f, 0xf8, + 0x40, + + /* U+00F2 "ò" */ + 0x0, 0x14, 0x0, 0x7c, 0xce, 0x80, 0x1e, 0x45, + 0xf0, 0xf, 0xac, 0x3, 0xf0, 0xd8, 0x7, 0xa3, + 0xbf, 0x58, 0x2, 0xb6, 0x9a, 0x59, 0x80, 0x54, + 0xd7, 0x58, 0x97, 0x2f, 0x23, 0x0, 0x98, 0x15, + 0x98, 0x1, 0xc3, 0xc0, 0x1f, 0xcc, 0xc0, 0xe, + 0x1e, 0xe1, 0x18, 0x4, 0xc0, 0xa8, 0x8d, 0x75, + 0x89, 0x72, 0xb, 0x69, 0xa5, 0x98, 0x0, + + /* U+00F3 "ó" */ + 0x0, 0xe8, 0x10, 0xf, 0x3b, 0x80, 0x3e, 0x99, + 0x8, 0x7, 0x12, 0x30, 0x7, 0x8a, 0x80, 0x3d, + 0x1d, 0xfa, 0xc0, 0x15, 0xb4, 0xd2, 0xcc, 0x2, + 0xa6, 0xba, 0xc4, 0xb9, 0x79, 0x18, 0x4, 0xc0, + 0xac, 0xc0, 0xe, 0x1e, 0x0, 0xfe, 0x66, 0x0, + 0x70, 0xf7, 0x8, 0xc0, 0x26, 0x5, 0x44, 0x6b, + 0xac, 0x4b, 0x90, 0x5b, 0x4d, 0x2c, 0xc0, 0x0, + + /* U+00F4 "ô" */ + 0x0, 0x96, 0xc, 0x3, 0x8e, 0x97, 0x44, 0x2, + 0x1d, 0x7d, 0x5d, 0x0, 0x8e, 0x60, 0x2d, 0x44, + 0x0, 0x4e, 0x1, 0x48, 0x80, 0x51, 0xdf, 0xac, + 0x1, 0x5b, 0x4d, 0x2c, 0xc0, 0x2a, 0x6b, 0xac, + 0x4b, 0x97, 0x91, 0x80, 0x4c, 0xa, 0xcc, 0x0, + 0xe1, 0xe0, 0xf, 0xe6, 0x60, 0x7, 0xf, 0x70, + 0x8c, 0x2, 0x60, 0x54, 0x46, 0xba, 0xc4, 0xb9, + 0x5, 0xb4, 0xd2, 0xcc, 0x0, + + /* U+00F5 "õ" */ + 0x2, 0xde, 0x71, 0x40, 0x9, 0xaa, 0x2b, 0xac, + 0x80, 0x8, 0xed, 0xb9, 0xc4, 0x1, 0xe3, 0x10, + 0xd, 0x1d, 0xfa, 0xc0, 0x15, 0xb4, 0xd2, 0xcc, + 0x2, 0xa6, 0xba, 0xc4, 0xb9, 0x79, 0x18, 0x4, + 0xc0, 0xac, 0xc0, 0xe, 0x1e, 0x0, 0xfe, 0x66, + 0x0, 0x70, 0xf7, 0x8, 0xc0, 0x26, 0x5, 0x44, + 0x6b, 0xac, 0x4b, 0x90, 0x5b, 0x4d, 0x2c, 0xc0, + 0x0, + + /* U+00F6 "ö" */ + 0x2, 0xf8, 0xc, 0xd0, 0xe, 0x10, 0x1, 0x80, + 0x45, 0xf2, 0x19, 0xc0, 0x1a, 0x3b, 0xf5, 0x80, + 0x2b, 0x69, 0xa5, 0x98, 0x5, 0x4d, 0x75, 0x89, + 0x72, 0xf2, 0x30, 0x9, 0x81, 0x59, 0x80, 0x1c, + 0x3c, 0x1, 0xfc, 0xcc, 0x0, 0xe1, 0xee, 0x11, + 0x80, 0x4c, 0xa, 0x88, 0xd7, 0x58, 0x97, 0x20, + 0xb6, 0x9a, 0x59, 0x80, + + /* U+00F7 "÷" */ + 0x0, 0xc6, 0x1, 0xf5, 0x63, 0x0, 0x78, 0x80, + 0xc0, 0x3d, 0x1e, 0xa0, 0x1f, 0xfc, 0xe, 0xff, + 0xfa, 0xf7, 0x33, 0xe9, 0x33, 0xc0, 0x67, 0x10, + 0x5, 0x58, 0xc0, 0x1e, 0x20, 0x30, 0x8, + + /* U+00F8 "ø" */ + 0x0, 0xfe, 0x30, 0xa, 0x3b, 0xf1, 0x6a, 0x40, + 0x16, 0xd3, 0x49, 0x49, 0x40, 0xa9, 0xae, 0xb8, + 0x2, 0x20, 0xf2, 0x30, 0x1a, 0xb0, 0x70, 0x66, + 0x0, 0x36, 0x10, 0x78, 0x1c, 0x1, 0x4c, 0xc0, + 0x0, 0x81, 0x3b, 0x9a, 0x80, 0x3, 0xc1, 0xa7, + 0x3a, 0x0, 0x60, 0x50, 0x60, 0x3, 0x2c, 0x4b, + 0x90, 0x7c, 0x4d, 0x51, 0x66, 0x0, 0x12, 0xee, + 0xdf, 0xd6, 0x0, 0x0, + + /* U+00F9 "ù" */ + 0x0, 0x25, 0x80, 0x7c, 0xc8, 0xa0, 0x1e, 0x27, + 0x90, 0xf, 0xa1, 0x0, 0x3f, 0x40, 0x6, 0x5f, + 0x10, 0x9, 0xbc, 0x3, 0xff, 0xc4, 0x20, 0x60, + 0x1e, 0x60, 0x40, 0xb, 0x0, 0x4, 0x73, 0xd, + 0x4c, 0x1, 0x6a, 0xb4, 0x6d, 0x0, 0x0, + + /* U+00FA "ú" */ + 0x0, 0xc5, 0x84, 0x1, 0xe8, 0x12, 0x0, 0xe1, + 0x5e, 0x0, 0xf1, 0xc1, 0x80, 0x78, 0x9c, 0x3, + 0x2f, 0x88, 0x4, 0xde, 0x1, 0xff, 0xe2, 0x10, + 0x30, 0xf, 0x30, 0x20, 0x5, 0x80, 0x2, 0x39, + 0x86, 0xa6, 0x0, 0xb5, 0x5a, 0x36, 0x80, 0x0, + + /* U+00FB "û" */ + 0x0, 0x96, 0xc, 0x3, 0x92, 0x93, 0x44, 0x2, + 0x2a, 0x9c, 0x4d, 0x0, 0xd6, 0xc1, 0x54, 0x0, + 0x89, 0x40, 0x26, 0x0, 0x2f, 0x88, 0x4, 0xde, + 0x1, 0xff, 0xe2, 0x10, 0x30, 0xf, 0x30, 0x20, + 0x5, 0x80, 0x2, 0x39, 0x86, 0xa6, 0x0, 0xb5, + 0x5a, 0x36, 0x80, 0x0, + + /* U+00FC "ü" */ + 0x3, 0xf8, 0xc, 0xd0, 0xf, 0xfe, 0x1, 0xfc, + 0x6, 0x68, 0x1, 0x7c, 0x40, 0x26, 0xf0, 0xf, + 0xff, 0x10, 0x81, 0x80, 0x79, 0x81, 0x0, 0x2c, + 0x0, 0x11, 0xcc, 0x35, 0x30, 0x5, 0xaa, 0xd1, + 0xb4, 0x0, + + /* U+00FD "ý" */ + 0x0, 0xe1, 0xc2, 0x0, 0xfa, 0x8, 0x80, 0x1e, + 0x16, 0xa0, 0xf, 0x8e, 0x10, 0x3, 0xe2, 0x70, + 0xe, 0xd9, 0x0, 0xec, 0xa0, 0xa6, 0x0, 0xc4, + 0x7c, 0xc, 0xa, 0x1, 0x50, 0xa0, 0xa, 0x70, + 0x4, 0xcc, 0x0, 0xb9, 0x4, 0x10, 0x68, 0x2, + 0x51, 0x60, 0xd3, 0x20, 0xc, 0xf6, 0xd, 0x40, + 0x1d, 0x47, 0x0, 0xe0, 0x1c, 0x64, 0xea, 0x20, + 0x1e, 0xa0, 0xe0, 0xf, 0xbc, 0x50, 0x3, 0xe6, + 0x60, 0x7, 0x12, 0x38, 0xd0, 0x7, 0x55, 0xcc, + 0x88, 0x3, 0x0, + + /* U+00FE "þ" */ + 0x4f, 0x20, 0xf, 0xfe, 0xc4, 0xf7, 0xd0, 0x80, + 0x52, 0x30, 0xeb, 0x80, 0x16, 0xc3, 0x45, 0x12, + 0x80, 0x10, 0x2, 0x51, 0xc0, 0xf, 0x8c, 0x80, + 0x3f, 0xf8, 0x86, 0x40, 0x4, 0x0, 0x94, 0x70, + 0x1, 0xb0, 0xd3, 0x44, 0xa0, 0x9, 0x18, 0x65, + 0xc0, 0xd, 0x3d, 0xf4, 0x20, 0x1f, 0xfd, 0x0, + + /* U+00FF "ÿ" */ + 0x0, 0x17, 0xc0, 0x66, 0x80, 0x78, 0x40, 0x6, + 0x1, 0x8b, 0xe4, 0x33, 0x80, 0x2d, 0x90, 0xe, + 0xca, 0xa, 0x60, 0xc, 0x47, 0xc0, 0xc0, 0xa0, + 0x15, 0xa, 0x0, 0xa7, 0x0, 0x4c, 0xc0, 0xb, + 0x90, 0x41, 0x6, 0x80, 0x25, 0x16, 0xd, 0x32, + 0x0, 0xcf, 0x60, 0xd4, 0x1, 0xd4, 0x70, 0xe, + 0x1, 0xc6, 0x4e, 0xa2, 0x1, 0xea, 0xe, 0x0, + 0xfb, 0xc5, 0x0, 0x3e, 0x66, 0x0, 0x71, 0x23, + 0x8d, 0x0, 0x75, 0x5c, 0xc8, 0x80, 0x30, + + /* U+0100 "Ā" */ + 0x0, 0xff, 0xe0, 0x97, 0xfe, 0xe0, 0xe, 0x2c, + 0xcd, 0xa0, 0x1e, 0x33, 0xe0, 0xf, 0xfe, 0x35, + 0xfc, 0x0, 0x7e, 0x41, 0x50, 0xf, 0x94, 0x20, + 0x88, 0x1, 0xef, 0x36, 0x5, 0x0, 0xf2, 0x20, + 0x13, 0x0, 0x38, 0xc6, 0xc3, 0x10, 0x3, 0xac, + 0x4c, 0x14, 0x14, 0x3, 0x2a, 0x84, 0x46, 0x3e, + 0x1, 0x9, 0x27, 0x74, 0x28, 0x1, 0x38, 0x55, + 0xdc, 0xe2, 0x60, 0xd, 0x25, 0x44, 0xa4, 0x2c, + 0x0, 0x8a, 0x1, 0x84, 0xd4, 0x10, 0x30, 0x3, + 0xac, 0x84, + + /* U+0101 "ā" */ + 0x6, 0xff, 0xd4, 0x1, 0x3d, 0xde, 0x80, 0x8, + 0x51, 0x31, 0x0, 0x49, 0x7d, 0xcb, 0x30, 0x1, + 0xd8, 0xdd, 0x9f, 0x8, 0xe, 0xa8, 0xaa, 0xc0, + 0x50, 0x2, 0x80, 0x62, 0xe0, 0x3, 0x67, 0x7f, + 0x88, 0x0, 0xd2, 0x9b, 0xb1, 0x0, 0x34, 0xe0, + 0xc8, 0x80, 0x1f, 0xc4, 0x0, 0xd3, 0x51, 0x3b, + 0x22, 0x23, 0x44, 0x3b, 0x9f, 0x2b, 0x40, + + /* U+0102 "Ă" */ + 0x0, 0xff, 0xe0, 0xa5, 0x80, 0x5a, 0x40, 0x19, + 0x51, 0xc2, 0x80, 0x80, 0x30, 0xd4, 0x7a, 0x70, + 0x7, 0x97, 0xf7, 0x8c, 0x3, 0xf1, 0x0, 0x7f, + 0x5f, 0xc0, 0x7, 0xe4, 0x15, 0x0, 0xf9, 0x42, + 0x8, 0x80, 0x1e, 0xf3, 0x60, 0x50, 0xf, 0x22, + 0x1, 0x30, 0x3, 0x8c, 0x6c, 0x31, 0x0, 0x3a, + 0xc4, 0xc1, 0x41, 0x40, 0x32, 0xa8, 0x44, 0x63, + 0xe0, 0x10, 0x92, 0x77, 0x42, 0x80, 0x13, 0x85, + 0x5d, 0xce, 0x26, 0x0, 0xd2, 0x54, 0x4a, 0x42, + 0xc0, 0x8, 0xa0, 0x18, 0x4d, 0x41, 0x3, 0x0, + 0x3a, 0xc8, 0x40, + + /* U+0103 "ă" */ + 0x0, 0xff, 0x4b, 0x80, 0xf, 0x0, 0x2c, 0x83, + 0x1e, 0x40, 0x9, 0x5b, 0x39, 0xa0, 0x3, 0x4f, + 0x6d, 0x80, 0x64, 0xbf, 0xcb, 0x30, 0x1, 0xd8, + 0xdd, 0x9f, 0x8, 0xe, 0xa8, 0xaa, 0xc0, 0x50, + 0x2, 0x80, 0x62, 0xe0, 0x3, 0x67, 0x7f, 0x88, + 0x0, 0xd2, 0x9b, 0xb1, 0x0, 0x34, 0xe0, 0xc8, + 0x80, 0x1f, 0xc4, 0x0, 0xd3, 0x51, 0x3b, 0x22, + 0x23, 0x44, 0x3b, 0x9f, 0x2b, 0x40, + + /* U+0104 "Ą" */ + 0x0, 0xd7, 0xf0, 0x1, 0xf9, 0x5, 0x40, 0x3e, + 0x50, 0x82, 0x20, 0x7, 0xbc, 0xd8, 0x14, 0x3, + 0xc8, 0x80, 0x4c, 0x0, 0xe3, 0x1b, 0xc, 0x40, + 0xe, 0xb1, 0x30, 0x50, 0x50, 0xc, 0xaa, 0x11, + 0x18, 0xf8, 0x4, 0x24, 0x9d, 0xd0, 0xa0, 0x4, + 0xe1, 0x57, 0x73, 0x89, 0x80, 0x34, 0x95, 0x12, + 0x90, 0xb0, 0x2, 0x28, 0x6, 0x13, 0x50, 0x40, + 0xc0, 0xe, 0xb2, 0x14, 0xf4, 0x0, 0xee, 0x71, + 0x0, 0xfa, 0x86, 0x0, 0x3f, 0x2f, 0x20, 0x80, + 0x7d, 0x10, 0xa5, + + /* U+0105 "ą" */ + 0x4, 0xbe, 0xe5, 0x98, 0x4, 0x76, 0x37, 0x67, + 0xc2, 0x0, 0x1d, 0x51, 0x55, 0x80, 0xa0, 0x12, + 0x80, 0x62, 0xe0, 0x9, 0xb3, 0xbf, 0xc4, 0x1, + 0x34, 0xa6, 0xec, 0x40, 0x16, 0x9c, 0x19, 0x10, + 0x3, 0xf8, 0x48, 0x2, 0xd3, 0x85, 0x6d, 0x44, + 0x40, 0x34, 0x2d, 0x53, 0xe4, 0x54, 0x0, 0xfb, + 0xf8, 0x6e, 0x1e, 0x1, 0xe2, 0xae, 0x20, 0xf, + 0x30, 0x82, 0x80, 0x79, 0x17, 0x64, 0x40, + + /* U+0106 "Ć" */ + 0x0, 0xe3, 0x10, 0xf, 0xba, 0xc0, 0x3c, 0xa5, + 0x60, 0x1e, 0xe9, 0x10, 0xf, 0x5b, 0x0, 0x72, + 0xe7, 0xf4, 0x88, 0x1, 0xa9, 0xe5, 0x9f, 0x0, + 0x65, 0x6d, 0xa3, 0x9, 0xdc, 0x10, 0x1, 0x16, + 0x3f, 0x81, 0x0, 0x63, 0x3, 0x20, 0xf, 0x84, + 0x3, 0xf0, 0x80, 0x7e, 0x32, 0x0, 0xfb, 0xc0, + 0x80, 0x31, 0x83, 0x84, 0x0, 0x45, 0x8e, 0x30, + 0xb6, 0xd1, 0x84, 0xe0, 0xf4, 0xf2, 0xc9, 0x80, + + /* U+0107 "ć" */ + 0x0, 0xe8, 0x20, 0xf, 0x1b, 0xb0, 0x7, 0xa1, + 0x50, 0x3, 0xeb, 0x0, 0xfa, 0xc4, 0x3, 0x97, + 0x7f, 0xa4, 0x2, 0x7a, 0x4a, 0x97, 0xb0, 0x18, + 0x79, 0x57, 0xd2, 0x4, 0xa, 0x0, 0x8e, 0x40, + 0x80, 0xc0, 0x3f, 0xf8, 0x64, 0x6, 0x1, 0xe4, + 0xa, 0x0, 0x86, 0xc4, 0x50, 0xf4, 0xaf, 0xee, + 0x20, 0xf4, 0xf5, 0x2f, 0xa0, + + /* U+0108 "Ĉ" */ + 0x0, 0x97, 0xfc, 0x40, 0x1d, 0x68, 0x52, 0x1, + 0xa4, 0xef, 0xd1, 0x40, 0x21, 0xf0, 0x2b, 0x30, + 0xa, 0x8, 0x0, 0x8c, 0x1, 0x2e, 0x7f, 0x48, + 0x80, 0x1a, 0x9e, 0x59, 0xf0, 0x6, 0x56, 0xda, + 0x30, 0x9d, 0xc1, 0x0, 0x11, 0x63, 0xf8, 0x10, + 0x6, 0x30, 0x32, 0x0, 0xf8, 0x40, 0x3f, 0x8, + 0x7, 0xe3, 0x20, 0xf, 0xbc, 0x8, 0x3, 0x18, + 0x38, 0x40, 0x4, 0x58, 0xe3, 0xb, 0x6d, 0x18, + 0x4e, 0xf, 0x4f, 0x2c, 0x98, 0x0, + + /* U+0109 "ĉ" */ + 0x0, 0x8e, 0x14, 0x3, 0x87, 0x56, 0x90, 0x3, + 0x52, 0xeb, 0xd0, 0x80, 0x4, 0xac, 0x22, 0x48, + 0x0, 0x32, 0x1, 0x39, 0x80, 0x4b, 0xbf, 0xd2, + 0x1, 0x3d, 0x25, 0x4b, 0xd8, 0xc, 0x3c, 0xab, + 0xe9, 0x2, 0x5, 0x0, 0x47, 0x20, 0x40, 0x60, + 0x1f, 0xfc, 0x32, 0x3, 0x0, 0xf2, 0x5, 0x0, + 0x43, 0x62, 0x28, 0x7a, 0x57, 0xf7, 0x10, 0x7a, + 0x7a, 0x97, 0xd0, + + /* U+010A "Ċ" */ + 0x0, 0xc4, 0x20, 0x1e, 0x2d, 0xf0, 0xf, 0x18, + 0x90, 0x7, 0x87, 0xb0, 0x3, 0xff, 0x84, 0xb9, + 0xfd, 0x22, 0x0, 0x6a, 0x79, 0x67, 0xc0, 0x19, + 0x5b, 0x68, 0xc2, 0x77, 0x4, 0x0, 0x45, 0x8f, + 0xe0, 0x40, 0x18, 0xc0, 0xc8, 0x3, 0xe1, 0x0, + 0xfc, 0x20, 0x1f, 0x8c, 0x80, 0x3e, 0xf0, 0x20, + 0xc, 0x60, 0xe1, 0x0, 0x11, 0x63, 0x8c, 0x2d, + 0xb4, 0x61, 0x38, 0x3d, 0x3c, 0xb2, 0x60, 0x0, + + /* U+010B "ċ" */ + 0x0, 0x93, 0xe0, 0x3, 0xc4, 0x4, 0x1, 0xe6, + 0xca, 0x0, 0xf8, 0xc0, 0x3c, 0xbb, 0xfd, 0x20, + 0x13, 0xd2, 0x54, 0xbd, 0x80, 0xc3, 0xca, 0xbe, + 0x90, 0x20, 0x50, 0x4, 0x72, 0x4, 0x6, 0x1, + 0xff, 0xc3, 0x20, 0x30, 0xf, 0x20, 0x50, 0x4, + 0x36, 0x22, 0x87, 0xa5, 0x7f, 0x71, 0x7, 0xa7, + 0xa9, 0x7d, 0x0, + + /* U+010C "Č" */ + 0x0, 0x8, 0x6, 0x10, 0xb, 0x64, 0x1, 0x72, + 0x1, 0x73, 0x45, 0x34, 0x0, 0x45, 0x4e, 0xbc, + 0x1, 0xcb, 0xfe, 0x30, 0xf, 0xfe, 0xa, 0xe7, + 0xf4, 0x88, 0x1, 0xa9, 0xe5, 0x9f, 0x0, 0x65, + 0x6d, 0xa3, 0x9, 0xdc, 0x10, 0x1, 0x16, 0x3f, + 0x81, 0x0, 0x63, 0x3, 0x20, 0xf, 0x84, 0x3, + 0xf0, 0x80, 0x7e, 0x32, 0x0, 0xfb, 0xc0, 0x80, + 0x31, 0x83, 0x84, 0x0, 0x45, 0x8e, 0x30, 0xb6, + 0xd1, 0x84, 0xe0, 0xf4, 0xf2, 0xc9, 0x80, + + /* U+010D "č" */ + 0x1, 0x90, 0x9, 0xcc, 0x0, 0x25, 0x80, 0xf2, + 0x40, 0x15, 0x16, 0xc5, 0x8, 0x4, 0x3a, 0xb4, + 0x80, 0x1c, 0x70, 0xa0, 0x1c, 0xbb, 0xfd, 0x20, + 0x13, 0xd2, 0x54, 0xbd, 0x80, 0xc3, 0xca, 0xbe, + 0x90, 0x20, 0x50, 0x4, 0x72, 0x4, 0x6, 0x1, + 0xff, 0xc3, 0x20, 0x30, 0xf, 0x20, 0x50, 0x4, + 0x36, 0x22, 0x87, 0xa5, 0x7f, 0x71, 0x7, 0xa7, + 0xa9, 0x7d, 0x0, + + /* U+010E "Ď" */ + 0x7, 0x30, 0x1, 0xb8, 0x4, 0x58, 0x65, 0xa2, + 0x1, 0x2a, 0xb7, 0x11, 0x80, 0x35, 0x4c, 0xa8, + 0x3, 0xce, 0xe0, 0xc, 0x9f, 0xfb, 0x58, 0x3, + 0x3c, 0xc9, 0xa6, 0x0, 0x2b, 0x66, 0x5b, 0x38, + 0x80, 0x7a, 0xc1, 0x80, 0x3c, 0x43, 0xa0, 0x1f, + 0x18, 0x80, 0x7e, 0x70, 0xf, 0xce, 0x1, 0xf1, + 0x88, 0x7, 0x88, 0x74, 0x3, 0xd6, 0xc, 0x0, + 0xb6, 0x65, 0xb3, 0x88, 0x1, 0xe6, 0x4d, 0x30, + 0x0, + + /* U+010F "ď" */ + 0x0, 0xfb, 0xdc, 0xfe, 0x0, 0x3f, 0xec, 0x0, + 0xfe, 0x70, 0x40, 0xf, 0xe6, 0x80, 0x9, 0xfb, + 0xec, 0x40, 0x4, 0xe0, 0x5, 0x85, 0x9c, 0xb0, + 0xf, 0x41, 0xdb, 0x3c, 0x80, 0x70, 0x95, 0x80, + 0x46, 0x1, 0xc6, 0x4, 0x1, 0xff, 0xc9, 0x30, + 0x20, 0xf, 0xe1, 0x2b, 0x0, 0x8c, 0x3, 0xd0, + 0x76, 0xcf, 0x20, 0x1e, 0x58, 0x59, 0xcb, 0x0, + 0xe0, + + /* U+0110 "Đ" */ + 0x2, 0xff, 0xda, 0xc0, 0x1c, 0xd3, 0x26, 0x98, + 0x0, 0xd8, 0xcc, 0xb6, 0x71, 0x0, 0xfa, 0xc1, + 0x80, 0x3e, 0x21, 0xd9, 0xd0, 0xaf, 0xe0, 0x1, + 0x8c, 0x50, 0x46, 0x60, 0x2, 0x71, 0x70, 0x23, + 0x20, 0x9, 0xc0, 0x3f, 0x18, 0x80, 0x7c, 0x43, + 0xa0, 0x1f, 0x58, 0x30, 0x5, 0x8c, 0xcb, 0x67, + 0x10, 0x9, 0xa6, 0x4d, 0x30, 0x0, + + /* U+0111 "đ" */ + 0x0, 0xf2, 0xf8, 0x80, 0x61, 0xff, 0x50, 0x72, + 0x0, 0x43, 0xba, 0x80, 0xc4, 0x0, 0xc4, 0x50, + 0x10, 0x0, 0xf3, 0xf9, 0x80, 0x30, 0xe1, 0x44, + 0x33, 0x80, 0x29, 0xc, 0x76, 0xa2, 0x0, 0x91, + 0x4, 0x1, 0x60, 0x4, 0x42, 0x1, 0xff, 0xc6, + 0x21, 0x0, 0xfc, 0x88, 0x20, 0xb, 0x0, 0x29, + 0xc, 0x86, 0xa2, 0x0, 0x87, 0x9, 0xe3, 0x38, + 0x2, + + /* U+0112 "Ē" */ + 0x0, 0xff, 0xbf, 0xf8, 0x3, 0x66, 0x70, 0x6, + 0x33, 0xe0, 0xf, 0xf9, 0xbf, 0xfe, 0x50, 0x4, + 0x4c, 0xf3, 0x80, 0x15, 0x9b, 0x88, 0x3, 0xff, + 0x90, 0x23, 0xe0, 0xb, 0x3b, 0xb8, 0x80, 0x15, + 0x77, 0xc4, 0x0, 0x74, 0x4e, 0x0, 0xff, 0xe4, + 0xab, 0x37, 0x10, 0x2, 0x26, 0x79, 0xc0, + + /* U+0113 "ē" */ + 0x1, 0xff, 0xe0, 0x8, 0x6e, 0xf8, 0x3, 0x22, + 0x70, 0x6, 0x7d, 0xfd, 0x60, 0xa, 0xa2, 0xae, + 0xa6, 0x40, 0x8b, 0x8e, 0x8f, 0x4c, 0x7a, 0x66, + 0x0, 0x94, 0x11, 0x4f, 0xff, 0x83, 0x80, 0xf3, + 0x3d, 0xc, 0x26, 0x7e, 0x1e, 0x24, 0x0, 0x8b, + 0x5, 0x16, 0xe1, 0x5f, 0x0, 0x42, 0xa1, 0x6a, + 0x5b, 0x0, + + /* U+0114 "Ĕ" */ + 0x0, 0xff, 0x1e, 0x0, 0x58, 0x60, 0x3, 0x28, + 0x8, 0x23, 0x0, 0xa9, 0xfd, 0xb4, 0x3, 0x27, + 0xef, 0x18, 0x7, 0x88, 0x3, 0x37, 0xff, 0xca, + 0x0, 0x89, 0x9e, 0x70, 0x2, 0xb3, 0x71, 0x0, + 0x7f, 0xf2, 0x4, 0x7c, 0x1, 0x67, 0x77, 0x10, + 0x2, 0xae, 0xf8, 0x80, 0xe, 0x89, 0xc0, 0x1f, + 0xfc, 0x95, 0x66, 0xe2, 0x0, 0x44, 0xcf, 0x38, + + /* U+0115 "ĕ" */ + 0x0, 0xff, 0x26, 0x0, 0x5a, 0x40, 0x5, 0x37, + 0x9, 0x2, 0x0, 0xd, 0xc7, 0xbf, 0x0, 0x64, + 0xfd, 0xe3, 0x0, 0xf1, 0x0, 0x79, 0xf7, 0xf5, + 0x80, 0x2a, 0x8a, 0xba, 0x99, 0x2, 0x2e, 0x3a, + 0x3d, 0x31, 0xe9, 0x98, 0x2, 0x50, 0x45, 0x3f, + 0xfe, 0xe, 0x3, 0xcc, 0xf4, 0x30, 0x99, 0xf8, + 0x78, 0x90, 0x2, 0x2c, 0x14, 0x5b, 0x85, 0x7c, + 0x1, 0xa, 0x85, 0xa9, 0x6c, 0x0, + + /* U+0116 "Ė" */ + 0x0, 0x9b, 0xd8, 0x3, 0xdc, 0x1c, 0x1, 0xe8, + 0xc8, 0x0, 0xf8, 0xc0, 0x33, 0x7f, 0xfc, 0xa0, + 0x8, 0x99, 0xe7, 0x0, 0x2b, 0x37, 0x10, 0x7, + 0xff, 0x20, 0x47, 0xc0, 0x16, 0x77, 0x71, 0x0, + 0x2a, 0xef, 0x88, 0x0, 0xe8, 0x9c, 0x1, 0xff, + 0xc9, 0x56, 0x6e, 0x20, 0x4, 0x4c, 0xf3, 0x80, + + /* U+0117 "ė" */ + 0x0, 0x9f, 0xd4, 0x3, 0xdc, 0x4, 0x1, 0xe9, + 0xc7, 0x0, 0xf8, 0xc0, 0x3c, 0xfb, 0xfa, 0xc0, + 0x15, 0x45, 0x5d, 0x4c, 0x81, 0x17, 0x1d, 0x1e, + 0x98, 0xf4, 0xcc, 0x1, 0x28, 0x22, 0x9f, 0xff, + 0x7, 0x1, 0xe6, 0x7a, 0x18, 0x4c, 0xfc, 0x3c, + 0x48, 0x1, 0x16, 0xa, 0x2d, 0xc2, 0xbe, 0x0, + 0x85, 0x42, 0xd4, 0xb6, 0x0, + + /* U+0118 "Ę" */ + 0x6f, 0xff, 0x94, 0x1, 0x13, 0x3c, 0xe0, 0x5, + 0x66, 0xe2, 0x0, 0xff, 0xe4, 0x8, 0xf8, 0x2, + 0xce, 0xee, 0x20, 0x5, 0x5d, 0xf1, 0x0, 0x1d, + 0x13, 0x80, 0x3f, 0xf9, 0x2a, 0xcd, 0xc4, 0x0, + 0x89, 0x9e, 0x76, 0xff, 0xe6, 0x62, 0x80, 0x74, + 0x9c, 0x80, 0x79, 0x34, 0xc8, 0x3, 0xae, 0x71, + 0x0, + + /* U+0119 "ę" */ + 0x0, 0x3e, 0xfe, 0xb0, 0x5, 0x51, 0x57, 0x53, + 0x20, 0x45, 0xc7, 0x47, 0xa6, 0x3d, 0x33, 0x0, + 0x4a, 0x8, 0xa7, 0xff, 0xc1, 0xc0, 0x79, 0x9e, + 0x85, 0x13, 0x3f, 0xd, 0x12, 0x0, 0x45, 0x82, + 0x6b, 0x70, 0xaf, 0x80, 0x21, 0x50, 0xb5, 0x7, + 0xa0, 0x13, 0xed, 0x1e, 0x8, 0x6, 0x3b, 0xd1, + 0x0, 0xe5, 0x33, 0x28, 0x7, 0x38, 0xec, 0x80, + 0x0, + + /* U+011A "Ě" */ + 0x2, 0x80, 0xa, 0x4, 0x0, 0x4b, 0x41, 0x2a, + 0x20, 0x17, 0xae, 0xb7, 0x80, 0x62, 0xb5, 0xb2, + 0x0, 0xe4, 0x84, 0x0, 0x9b, 0xff, 0xe5, 0x0, + 0x44, 0xcf, 0x38, 0x1, 0x59, 0xb8, 0x80, 0x3f, + 0xf9, 0x2, 0x3e, 0x0, 0xb3, 0xbb, 0x88, 0x1, + 0x57, 0x7c, 0x40, 0x7, 0x44, 0xe0, 0xf, 0xfe, + 0x4a, 0xb3, 0x71, 0x0, 0x22, 0x67, 0x9c, + + /* U+011B "ě" */ + 0x2, 0x80, 0xa, 0x4, 0x0, 0x6d, 0x21, 0x48, + 0x20, 0x1, 0xd6, 0xd5, 0xe0, 0xc, 0x74, 0xb6, + 0x40, 0x1c, 0xb0, 0x80, 0x1c, 0xfb, 0xfa, 0xc0, + 0x15, 0x45, 0x5d, 0x4c, 0x81, 0x17, 0x1d, 0x1e, + 0x98, 0xf4, 0xcc, 0x1, 0x28, 0x22, 0x9f, 0xff, + 0x7, 0x1, 0xe6, 0x7a, 0x18, 0x4c, 0xfc, 0x3c, + 0x48, 0x1, 0x16, 0xa, 0x2d, 0xc2, 0xbe, 0x0, + 0x85, 0x42, 0xd4, 0xb6, 0x0, + + /* U+011C "Ĝ" */ + 0x0, 0xaf, 0xec, 0x3, 0x95, 0x19, 0x14, 0x2, + 0x1b, 0x89, 0x7b, 0x10, 0x1, 0xbb, 0x82, 0x1c, + 0xc0, 0x5, 0x20, 0x14, 0x90, 0x5, 0x1d, 0xfb, + 0x0, 0x15, 0xbb, 0xa5, 0x1e, 0xc1, 0x11, 0x52, + 0xd3, 0x68, 0x5c, 0x2a, 0x1, 0x27, 0x93, 0xa8, + 0x7, 0xc4, 0x40, 0xf, 0xfa, 0xbf, 0xe6, 0x0, + 0xd1, 0x98, 0x0, 0x11, 0x0, 0x4, 0x66, 0x0, + 0x3a, 0x80, 0x62, 0x0, 0x60, 0xa8, 0x5, 0x40, + 0x6, 0x2a, 0x86, 0xa2, 0x0, 0xb1, 0x5e, 0x2, + 0xc0, 0x0, + + /* U+011D "ĝ" */ + 0x0, 0x96, 0xc, 0x3, 0xc9, 0x4b, 0xa2, 0x1, + 0x86, 0xa3, 0x56, 0x80, 0x31, 0x4b, 0x85, 0x90, + 0x80, 0x46, 0xe0, 0x14, 0x88, 0x6, 0x7e, 0xff, + 0xe0, 0x4, 0xca, 0xf2, 0x1, 0x20, 0x5, 0xa1, + 0x4d, 0xd1, 0xdc, 0x4, 0x4, 0x1, 0x19, 0x80, + 0x2, 0x2, 0x1, 0x11, 0x0, 0x4, 0x70, 0x40, + 0x8b, 0x60, 0x16, 0x8e, 0xfd, 0xc3, 0x0, 0x50, + 0x9b, 0xaf, 0x70, 0xc, 0xc2, 0x88, 0x65, 0x20, + 0x9, 0xda, 0x66, 0xad, 0x60, 0x65, 0xef, 0xfb, + 0x9a, 0x42, 0x9c, 0x40, 0x21, 0xc0, 0x2, 0x2b, + 0x99, 0x91, 0xad, 0x2, 0x1e, 0xb3, 0x17, 0x53, + 0x60, + + /* U+011E "Ğ" */ + 0x0, 0xff, 0x1e, 0x0, 0x58, 0x60, 0x3, 0x28, + 0x8, 0x23, 0x0, 0xa9, 0xbd, 0xa8, 0x3, 0x27, + 0x6f, 0x20, 0x7, 0x88, 0x3, 0xd1, 0xdf, 0xb0, + 0x1, 0x5b, 0xba, 0x51, 0xec, 0x11, 0x15, 0x2d, + 0x36, 0x85, 0xc2, 0xa0, 0x12, 0x79, 0x3a, 0x80, + 0x7c, 0x44, 0x0, 0xff, 0xab, 0xfe, 0x60, 0xd, + 0x19, 0x80, 0x1, 0x10, 0x0, 0x46, 0x60, 0x3, + 0xa8, 0x6, 0x20, 0x6, 0xa, 0x80, 0x54, 0x0, + 0x62, 0xa8, 0x6a, 0x20, 0xb, 0x15, 0xe0, 0x2c, + 0x0, + + /* U+011F "ğ" */ + 0x0, 0xff, 0x96, 0xc0, 0x3, 0xa2, 0x1, 0x22, + 0x18, 0x2c, 0x44, 0x1, 0xd, 0x4f, 0xa7, 0x0, + 0x72, 0xf6, 0xf1, 0x0, 0x78, 0x48, 0x3, 0xe7, + 0xef, 0xfe, 0x0, 0x4c, 0xaf, 0x20, 0x12, 0x0, + 0x5a, 0x14, 0xdd, 0x1d, 0xc0, 0x40, 0x40, 0x11, + 0x98, 0x0, 0x20, 0x20, 0x11, 0x10, 0x0, 0x47, + 0x4, 0x8, 0xb6, 0x1, 0x68, 0xef, 0xdc, 0x30, + 0x5, 0x9, 0xba, 0xf7, 0x0, 0xcc, 0x28, 0x86, + 0x52, 0x0, 0x9d, 0xa6, 0x6a, 0xd6, 0x6, 0x5e, + 0xff, 0xb9, 0xa4, 0x29, 0xc4, 0x2, 0x1c, 0x0, + 0x22, 0xb9, 0x99, 0x1a, 0xd0, 0x21, 0xeb, 0x31, + 0x75, 0x36, 0x0, + + /* U+0120 "Ġ" */ + 0x0, 0xc6, 0x1, 0xf4, 0x64, 0x0, 0x7b, 0x83, + 0x80, 0x3c, 0xde, 0xc0, 0x1f, 0xfc, 0x28, 0xef, + 0xd8, 0x0, 0xad, 0xdd, 0x28, 0xf6, 0x8, 0x8a, + 0x96, 0x9b, 0x42, 0xe1, 0x50, 0x9, 0x3c, 0x9d, + 0x40, 0x3e, 0x22, 0x0, 0x7f, 0xd5, 0xff, 0x30, + 0x6, 0x8c, 0xc0, 0x0, 0x88, 0x0, 0x23, 0x30, + 0x1, 0xd4, 0x3, 0x10, 0x3, 0x5, 0x40, 0x2a, + 0x0, 0x31, 0x54, 0x35, 0x10, 0x5, 0x8a, 0xf0, + 0x16, 0x0, + + /* U+0121 "ġ" */ + 0x0, 0xa3, 0xd0, 0x3, 0xe2, 0x2, 0x0, 0xfa, + 0xb1, 0x80, 0x2, 0x1, 0xc6, 0x13, 0xfc, 0x1, + 0x3f, 0x7e, 0xa5, 0xd8, 0x1, 0x32, 0xbc, 0xa2, + 0x14, 0x1, 0x68, 0x53, 0x74, 0x80, 0x1, 0x1, + 0x0, 0x46, 0x40, 0x1, 0x1, 0x0, 0x88, 0x80, + 0x2, 0x38, 0x20, 0x45, 0xb0, 0xb, 0x47, 0x7e, + 0xe1, 0x80, 0x28, 0x4d, 0xd7, 0xb8, 0x6, 0x61, + 0x44, 0x32, 0x90, 0x4, 0xed, 0x33, 0x56, 0xb0, + 0x32, 0xf7, 0xfd, 0xcd, 0x21, 0x4e, 0x20, 0x10, + 0xe0, 0x1, 0x15, 0xcc, 0xc8, 0xd6, 0x81, 0xf, + 0x59, 0x8b, 0xa9, 0xb0, + + /* U+0122 "Ģ" */ + 0x0, 0x47, 0x7e, 0xc0, 0x5, 0x6e, 0xe9, 0x47, + 0xb0, 0x44, 0x54, 0xb4, 0xda, 0x17, 0xa, 0x80, + 0x49, 0xe4, 0xea, 0x1, 0xf1, 0x10, 0x3, 0xfe, + 0xaf, 0xf9, 0x80, 0x34, 0x66, 0x0, 0x4, 0x40, + 0x1, 0x19, 0x80, 0xe, 0xa0, 0x18, 0x80, 0x18, + 0x2a, 0x1, 0x50, 0x1, 0x8a, 0xa1, 0xa8, 0x80, + 0x2c, 0x57, 0x80, 0xb0, 0x8, 0x6b, 0xfa, 0x87, + 0xd8, 0x2, 0x9f, 0x30, 0xf, 0x11, 0xc, 0x3, + 0xcb, 0x40, 0x1f, 0x3b, 0x0, 0x60, + + /* U+0123 "ģ" */ + 0x0, 0xc6, 0xc0, 0x1f, 0xa3, 0x0, 0x3e, 0x14, + 0x20, 0xf, 0x98, 0x34, 0x0, 0x20, 0x19, 0xfd, + 0x67, 0xf8, 0x2, 0x7e, 0xfd, 0x4b, 0xb0, 0x2, + 0x65, 0x79, 0x44, 0x28, 0x2, 0xd0, 0xa6, 0xe9, + 0x0, 0x2, 0x2, 0x0, 0x8c, 0x80, 0x2, 0x2, + 0x1, 0x11, 0x0, 0x4, 0x70, 0x40, 0x8b, 0x60, + 0x16, 0x8e, 0xfd, 0xc3, 0x0, 0x50, 0x9b, 0xaf, + 0x70, 0xc, 0xc2, 0x88, 0x65, 0x20, 0x9, 0xda, + 0x66, 0xad, 0x60, 0x65, 0xef, 0xfb, 0x9a, 0x42, + 0x9c, 0x40, 0x21, 0xc0, 0x2, 0x2b, 0x99, 0x91, + 0xad, 0x2, 0x1e, 0xb3, 0x17, 0x53, 0x60, + + /* U+0124 "Ĥ" */ + 0x0, 0x96, 0xc, 0x3, 0x8e, 0x97, 0x44, 0x2, + 0x1d, 0x7d, 0x5d, 0x0, 0x8e, 0x60, 0x2d, 0x44, + 0x0, 0x4e, 0x1, 0x48, 0x84, 0xf8, 0x6, 0x2f, + 0x50, 0xf, 0xfe, 0xd0, 0x8f, 0x0, 0x6e, 0xed, + 0x80, 0x1a, 0xef, 0x48, 0x6, 0x44, 0xe0, 0xf, + 0xfe, 0xd0, + + /* U+0125 "ĥ" */ + 0x0, 0x5f, 0xe0, 0x7, 0xce, 0xb4, 0x6e, 0x1, + 0xc5, 0x3a, 0xb9, 0x4, 0x1, 0x8b, 0xc, 0xf, + 0x48, 0x3, 0x88, 0xfc, 0xc8, 0x3, 0xff, 0xbf, + 0x3d, 0xc8, 0x0, 0xf4, 0xc, 0x3b, 0xa0, 0x3, + 0xb6, 0x1a, 0x59, 0xc0, 0x39, 0x40, 0x2c, 0x1, + 0x0, 0xfc, 0x20, 0x60, 0x1f, 0xfe, 0xb0, + + /* U+0126 "Ħ" */ + 0x9, 0xf0, 0xc, 0x5e, 0xa0, 0x1f, 0xfc, 0xd, + 0x60, 0xff, 0xda, 0x15, 0x4f, 0x50, 0xcc, 0xd4, + 0x11, 0x2, 0x30, 0x33, 0xce, 0x4, 0x40, 0x8, + 0x47, 0x80, 0x3d, 0xdd, 0xb0, 0x3, 0xd7, 0x7a, + 0x40, 0x3c, 0x89, 0xc0, 0x1f, 0xfe, 0x30, + + /* U+0127 "ħ" */ + 0x3, 0xf3, 0x0, 0xf3, 0x60, 0x67, 0xf8, 0x3, + 0x3d, 0x5, 0x6e, 0x80, 0x30, 0xb0, 0x31, 0x10, + 0x3, 0xf4, 0xf7, 0x20, 0x3, 0xa0, 0x61, 0xdd, + 0x0, 0x1b, 0x61, 0xa5, 0x9c, 0x3, 0x28, 0x5, + 0x80, 0x20, 0x1f, 0x8, 0x18, 0x7, 0xff, 0x98, + + /* U+0128 "Ĩ" */ + 0x0, 0xff, 0x1f, 0x72, 0x5, 0x0, 0x27, 0x8b, + 0x4e, 0xa2, 0x0, 0x23, 0x2e, 0x6f, 0x90, 0x7, + 0x88, 0x40, 0xf, 0xff, 0xf2, 0x2d, 0xd9, 0xc2, + 0x6e, 0xca, 0x48, 0x8d, 0x4, 0x48, 0x40, 0x3f, + 0xff, 0x84, 0x88, 0xd0, 0x44, 0x85, 0x6e, 0xce, + 0x13, 0x76, 0x50, + + /* U+0129 "ĩ" */ + 0x0, 0x47, 0xe2, 0xa, 0x0, 0x6a, 0x4b, 0xee, + 0x0, 0x50, 0xb1, 0xd9, 0x40, 0x1e, 0x13, 0x0, + 0xf, 0xfe, 0xb0, 0x8, 0x6e, 0xe2, 0x0, 0xe4, + 0x48, 0x40, 0x3f, 0xfc, 0xc8, 0x90, 0xba, 0x20, + 0xc6, 0xee, 0x23, 0xbb, 0x70, + + /* U+012A "Ī" */ + 0x0, 0xff, 0x17, 0xfe, 0xe0, 0x8, 0xb3, 0x36, + 0x80, 0x63, 0x3e, 0x0, 0xff, 0x9f, 0xff, 0xe4, + 0x5b, 0xb3, 0x84, 0xdd, 0x94, 0x91, 0x1a, 0x8, + 0x90, 0x80, 0x7f, 0xff, 0x9, 0x11, 0xa0, 0x89, + 0xa, 0xdd, 0x9c, 0x26, 0xec, 0xa0, + + /* U+012B "ī" */ + 0x0, 0x47, 0xfe, 0x80, 0xa, 0xae, 0xf5, 0x0, + 0x44, 0x89, 0x88, 0x7, 0xff, 0x58, 0x4, 0x37, + 0x71, 0x0, 0x72, 0x24, 0x20, 0x1f, 0xfe, 0x64, + 0x48, 0x5d, 0x10, 0x63, 0x77, 0x11, 0xdd, 0xb8, + + /* U+012C "Ĭ" */ + 0x0, 0xff, 0x25, 0x80, 0x5a, 0x40, 0x5, 0x47, + 0xa, 0x2, 0x0, 0xd, 0x47, 0xa7, 0x0, 0x65, + 0xfd, 0xe3, 0x0, 0xf1, 0x0, 0x67, 0xff, 0xf9, + 0x16, 0xec, 0xe1, 0x37, 0x65, 0x24, 0x46, 0x82, + 0x24, 0x20, 0x1f, 0xff, 0xc2, 0x44, 0x68, 0x22, + 0x42, 0xb7, 0x67, 0x9, 0xbb, 0x28, + + /* U+012D "ĭ" */ + 0x0, 0xff, 0xad, 0x40, 0xd, 0x40, 0x16, 0x59, + 0x12, 0x30, 0x2, 0x75, 0xdd, 0x23, 0x0, 0x6b, + 0xdd, 0x50, 0x0, 0x7f, 0xed, 0x90, 0x8, 0x6e, + 0xe2, 0x0, 0xe4, 0x48, 0x40, 0x3f, 0xfc, 0xc8, + 0x90, 0xba, 0x20, 0xc6, 0xee, 0x23, 0xbb, 0x70, + + /* U+012E "Į" */ + 0x7f, 0xff, 0x91, 0x6e, 0xce, 0x13, 0x76, 0x52, + 0x44, 0x68, 0x22, 0x42, 0x1, 0xff, 0xfc, 0x24, + 0x46, 0x82, 0x24, 0x2b, 0x76, 0x70, 0x9b, 0xb2, + 0xbf, 0xff, 0x2b, 0xa0, 0x7, 0x51, 0x40, 0x7, + 0x93, 0x90, 0x40, 0x3a, 0x62, 0x90, + + /* U+012F "į" */ + 0x0, 0xdd, 0xa0, 0x1f, 0x8, 0x88, 0x3, 0xe1, + 0xdd, 0x0, 0x7e, 0x22, 0x0, 0x61, 0xff, 0xd6, + 0x1, 0x86, 0xee, 0x20, 0xf, 0x22, 0x42, 0x1, + 0xff, 0xec, 0x55, 0x85, 0xd5, 0x48, 0x3, 0x55, + 0x11, 0xd5, 0x24, 0x7, 0xff, 0xac, 0xf8, 0x3, + 0xc5, 0x5c, 0x60, 0x1e, 0x40, 0x2, 0x80, 0x79, + 0xcb, 0x64, 0x0, + + /* U+0130 "İ" */ + 0x0, 0xa3, 0xd4, 0x3, 0xc4, 0x6, 0x1, 0xea, + 0xc6, 0x0, 0xf8, 0xc0, 0x33, 0xff, 0xfc, 0x8b, + 0x76, 0x70, 0x9b, 0xb2, 0x92, 0x23, 0x41, 0x12, + 0x10, 0xf, 0xff, 0xe1, 0x22, 0x34, 0x11, 0x21, + 0x5b, 0xb3, 0x84, 0xdd, 0x94, + + /* U+0131 "ı" */ + 0x1f, 0xfd, 0x60, 0x10, 0xdd, 0xc4, 0x1, 0xc8, + 0x90, 0x80, 0x7f, 0xf9, 0x91, 0x21, 0x74, 0x41, + 0x8d, 0xdc, 0x47, 0x76, 0xe0, + + /* U+0132 "IJ" */ + 0x6f, 0x20, 0x9, 0x7c, 0xc0, 0x3f, 0xfd, 0x44, + 0x86, 0x1, 0xe4, 0xb1, 0x0, 0xff, 0xe0, 0x18, + 0xa, 0x7a, 0x80, 0x5a, 0x4, 0x85, 0x50, 0xf6, + 0x92, 0x0, 0xc6, 0x64, 0x35, 0x30, 0x0, + + /* U+0133 "ij" */ + 0x5f, 0x80, 0xa, 0xf8, 0x8c, 0x8, 0x2, 0x61, + 0x66, 0x65, 0x0, 0x59, 0xa8, 0x6, 0x1, 0x84, + 0x80, 0x7d, 0x40, 0x28, 0xe0, 0xf, 0xff, 0xf8, + 0x7, 0xc3, 0xea, 0x1, 0xff, 0xc5, 0x34, 0x48, + 0x88, 0x1, 0x4d, 0xdc, 0xfe, 0x0, + + /* U+0134 "Ĵ" */ + 0x0, 0xa7, 0xf4, 0x3, 0x23, 0x39, 0x40, 0x5, + 0x2f, 0x16, 0xe6, 0x0, 0xb8, 0x4, 0xa2, 0x0, + 0x30, 0x4, 0xa2, 0xd, 0xff, 0xd4, 0x9, 0x33, + 0x84, 0x0, 0x4c, 0xd8, 0x3, 0xff, 0xce, 0x40, + 0x1e, 0x8d, 0x10, 0x1, 0xe, 0xc8, 0xe3, 0xbb, + 0x5, 0x87, 0x1a, 0x25, 0x30, 0x40, + + /* U+0135 "ĵ" */ + 0x0, 0xe7, 0x70, 0x80, 0x74, 0xcd, 0x40, 0x19, + 0x9f, 0x30, 0xb0, 0x1, 0x1e, 0x11, 0x33, 0x40, + 0x25, 0x20, 0x1, 0x2a, 0xbf, 0xfb, 0x0, 0x9, + 0x77, 0x8c, 0x2, 0x14, 0x4c, 0xc0, 0x1f, 0xff, + 0xf0, 0xf, 0xf2, 0x26, 0x66, 0x0, 0xa, 0xef, + 0xc, 0x0, 0x0, + + /* U+0136 "Ķ" */ + 0x7f, 0x0, 0xc3, 0xba, 0x0, 0xfa, 0x8f, 0xc0, + 0x3d, 0x9, 0x44, 0x1, 0xca, 0xf0, 0x80, 0x1c, + 0x74, 0xae, 0x1, 0xc3, 0xc1, 0x60, 0x1e, 0xa3, + 0x6, 0x0, 0xf2, 0x63, 0x41, 0x0, 0x75, 0x14, + 0x4, 0x80, 0x72, 0x80, 0xca, 0x20, 0x3, 0xe6, + 0x29, 0x0, 0xfd, 0x25, 0x0, 0x1f, 0x22, 0xb9, + 0x3f, 0x80, 0x75, 0x79, 0x0, 0x47, 0xf4, 0x1, + 0xf3, 0x7, 0x0, 0x7c, 0x44, 0x40, 0xf, 0x8a, + 0x0, 0x38, + + /* U+0137 "ķ" */ + 0x1f, 0x50, 0xf, 0xfe, 0xfb, 0x7b, 0x0, 0x79, + 0xa6, 0x4c, 0x1, 0xcd, 0x32, 0x60, 0xe, 0x69, + 0x86, 0x0, 0xee, 0x90, 0xf, 0xc9, 0x6d, 0x4, + 0x1, 0xd2, 0x92, 0x5e, 0x1, 0xc6, 0x0, 0xe2, + 0x80, 0xf, 0x8d, 0xdc, 0x80, 0x1f, 0x41, 0xd0, + 0x8b, 0xd4, 0x3, 0x67, 0x8, 0x4, 0x7f, 0x40, + 0x1f, 0x30, 0x70, 0x7, 0xc4, 0x44, 0x0, 0xf8, + 0xa0, 0x3, 0x80, + + /* U+0138 "ĸ" */ + 0x1f, 0x50, 0x9, 0x3d, 0xc0, 0x3c, 0x96, 0xee, + 0x0, 0xe3, 0xb6, 0x80, 0xe, 0x2d, 0x59, 0x0, + 0xe2, 0xc1, 0xc0, 0xf, 0x42, 0x2c, 0x90, 0x7, + 0x2d, 0x50, 0xbc, 0x3, 0xbc, 0x1, 0xc5, 0x20, + 0x1f, 0x1c, 0x32, 0x80, 0x7c, 0xe9, 0x62, + + /* U+0139 "Ĺ" */ + 0x5, 0x50, 0x7, 0xaf, 0x0, 0x39, 0x82, 0x40, + 0x3b, 0xac, 0x3, 0xd0, 0xa0, 0x1e, 0xda, 0x0, + 0xff, 0xff, 0x80, 0x7f, 0xf0, 0x19, 0xbc, 0x60, + 0x73, 0x3e, + + /* U+013A "ĺ" */ + 0x0, 0xff, 0xe0, 0x9f, 0x18, 0x7, 0xa4, 0x8c, + 0x3, 0x89, 0x78, 0x3, 0xc5, 0x64, 0x1, 0x4f, + 0xfd, 0x64, 0x1, 0x5d, 0xce, 0x1, 0xc4, 0x88, + 0xd0, 0xf, 0xff, 0xf8, 0x7, 0xfc, 0x48, 0x8d, + 0x7, 0x44, 0xd, 0xdc, 0xe1, 0x57, 0x64, + + /* U+013B "Ļ" */ + 0xda, 0x0, 0xff, 0xff, 0x80, 0x7f, 0xf0, 0x19, + 0xbc, 0x60, 0x73, 0x3e, 0xdf, 0xff, 0x50, 0x0, + 0x7e, 0xc0, 0x39, 0x43, 0x80, 0x38, 0x85, 0x40, + 0x38, 0xa0, 0x3, 0x0, + + /* U+013C "ļ" */ + 0x9f, 0xfc, 0x40, 0x15, 0xdc, 0xe0, 0x1c, 0x48, + 0x8d, 0x0, 0xff, 0xff, 0x80, 0x7f, 0xc4, 0x88, + 0xd0, 0x74, 0x40, 0xdd, 0xce, 0x15, 0x76, 0x49, + 0xff, 0xf9, 0x40, 0x29, 0xf3, 0x0, 0xf1, 0x10, + 0xc0, 0x3c, 0xb4, 0x1, 0xf3, 0xb0, 0x6, + + /* U+013D "Ľ" */ + 0x0, 0xab, 0xc4, 0x1, 0xb4, 0x2, 0x42, 0x1, + 0xe4, 0x0, 0xf6, 0x50, 0x7, 0x9c, 0xc0, 0x3f, + 0xfe, 0xc, 0xde, 0x30, 0x39, 0x9f, 0x0, + + /* U+013E "ľ" */ + 0x9f, 0xfc, 0x47, 0xf1, 0x77, 0x38, 0x1, 0xc3, + 0x9, 0x11, 0xa0, 0x19, 0x0, 0x3c, 0xd0, 0x1, + 0xf1, 0x38, 0x7, 0xff, 0xd0, 0x91, 0x1a, 0xe, + 0x88, 0x1b, 0xb9, 0xc2, 0xae, 0xc8, + + /* U+013F "Ŀ" */ + 0xda, 0x0, 0xff, 0xef, 0x90, 0x80, 0x71, 0xef, + 0x80, 0x70, 0x88, 0x80, 0x38, 0xbb, 0x0, 0x3f, + 0xf8, 0xcc, 0xde, 0x30, 0x39, 0x9f, 0x0, + + /* U+0140 "ŀ" */ + 0x9f, 0xfc, 0x40, 0x1a, 0xee, 0x70, 0xf, 0x12, + 0x23, 0x40, 0x3f, 0xfd, 0x35, 0xe6, 0x1, 0xf3, + 0x3, 0x0, 0x7d, 0xba, 0x50, 0xf, 0x84, 0x80, + 0x3f, 0xe2, 0x44, 0x68, 0x3a, 0x20, 0x42, 0xee, + 0x70, 0xab, 0xb2, 0x0, + + /* U+0141 "Ł" */ + 0x0, 0x6d, 0x0, 0x7f, 0xf9, 0x85, 0xb1, 0xc0, + 0x22, 0x35, 0xeb, 0x45, 0x0, 0x26, 0x89, 0x66, + 0xc1, 0x0, 0x17, 0x49, 0xcc, 0x3, 0x84, 0x80, + 0x3f, 0xf9, 0x2c, 0xde, 0x30, 0x8, 0xe6, 0x7c, + + /* U+0142 "ł" */ + 0x9f, 0xfc, 0x40, 0x15, 0xdc, 0xe0, 0x1c, 0x48, + 0x8d, 0x0, 0xff, 0xec, 0xa4, 0xe8, 0x4, 0x9c, + 0x12, 0x72, 0x0, 0xea, 0x70, 0xda, 0x40, 0x5, + 0x74, 0x80, 0x79, 0x0, 0x3f, 0xf8, 0x84, 0x88, + 0xd0, 0x74, 0x40, 0xdd, 0xce, 0x15, 0x76, 0x40, + + /* U+0143 "Ń" */ + 0x0, 0xe8, 0x10, 0xf, 0x3b, 0x80, 0x3e, 0x99, + 0x8, 0x7, 0x12, 0x30, 0x7, 0x8a, 0x80, 0x34, + 0xfe, 0x0, 0x6f, 0x50, 0x1, 0x90, 0x7, 0x88, + 0x68, 0x3, 0xe9, 0x60, 0xf, 0x98, 0x54, 0x3, + 0xc2, 0x9c, 0x1, 0xf7, 0x21, 0x0, 0x79, 0x46, + 0x80, 0x3e, 0x66, 0x0, 0x7d, 0x42, 0x80, 0x1e, + 0x23, 0xf0, 0xf, 0xa5, 0x0, 0x3e, 0x50, 0x8, + + /* U+0144 "ń" */ + 0x0, 0xc3, 0x84, 0x1, 0xe9, 0x23, 0x0, 0xf3, + 0xd8, 0x80, 0x70, 0xc2, 0x80, 0x78, 0x5c, 0x3, + 0x1f, 0xa5, 0x7f, 0x40, 0x6, 0xff, 0x6c, 0x3c, + 0x0, 0x5c, 0xa4, 0xce, 0xe0, 0x9, 0x80, 0x2d, + 0x1, 0x0, 0xf0, 0x81, 0x80, 0x7f, 0xf8, 0x40, + + /* U+0145 "Ņ" */ + 0x9f, 0xc0, 0xd, 0xea, 0x0, 0x32, 0x0, 0xf1, + 0xd, 0x0, 0x7d, 0x2c, 0x1, 0xf3, 0xa, 0x80, + 0x78, 0x53, 0x80, 0x3e, 0xe4, 0x20, 0xf, 0x28, + 0xd0, 0x7, 0xcc, 0xc0, 0xf, 0xa8, 0x50, 0x3, + 0xc4, 0x7e, 0x1, 0xf4, 0xa0, 0x7, 0xca, 0x1, + 0x4e, 0x80, 0x6f, 0xf2, 0x80, 0x53, 0xe6, 0x1, + 0xe2, 0x21, 0x80, 0x79, 0x68, 0x3, 0xe7, 0x60, + 0xc, + + /* U+0146 "ņ" */ + 0x3f, 0x4a, 0xfe, 0x80, 0xd, 0xfe, 0xd8, 0x78, + 0x0, 0xb9, 0x49, 0x9d, 0xc0, 0x13, 0x0, 0x5a, + 0x2, 0x1, 0xe1, 0x3, 0x0, 0xff, 0xf0, 0x9f, + 0x98, 0x4, 0x9e, 0x40, 0x14, 0x7a, 0x0, 0x78, + 0xc5, 0x0, 0x3c, 0xcc, 0x0, 0xf9, 0x68, 0x3, + 0x0, + + /* U+0147 "Ň" */ + 0x2, 0x80, 0xa, 0x4, 0x0, 0x6d, 0x1, 0x48, + 0x20, 0x1, 0xa7, 0xd4, 0xd0, 0xc, 0x94, 0xba, + 0x20, 0x1c, 0xb0, 0x60, 0x14, 0xfe, 0x0, 0x6f, + 0x50, 0x1, 0x90, 0x7, 0x88, 0x68, 0x3, 0xe9, + 0x60, 0xf, 0x98, 0x54, 0x3, 0xc2, 0x9c, 0x1, + 0xf7, 0x21, 0x0, 0x79, 0x46, 0x80, 0x3e, 0x66, + 0x0, 0x7d, 0x42, 0x80, 0x1e, 0x23, 0xf0, 0xf, + 0xa5, 0x0, 0x3e, 0x50, 0x8, + + /* U+0148 "ň" */ + 0x2, 0x80, 0xa, 0x4, 0x0, 0x4b, 0x21, 0x48, + 0x20, 0x17, 0x36, 0xaf, 0x0, 0x63, 0xa5, 0xb2, + 0x0, 0xe5, 0x84, 0x0, 0x8f, 0xd2, 0xbf, 0xa0, + 0x3, 0x7f, 0xb6, 0x1e, 0x0, 0x2e, 0x52, 0x67, + 0x70, 0x4, 0xc0, 0x16, 0x80, 0x80, 0x78, 0x40, + 0xc0, 0x3f, 0xfc, 0x20, + + /* U+0149 "ʼn" */ + 0xdc, 0x0, 0xfc, 0x76, 0x1, 0xf8, 0x50, 0x3, + 0xf4, 0x18, 0x7, 0xe7, 0x3f, 0x4a, 0xfe, 0x80, + 0xe, 0xff, 0x6c, 0x3c, 0x0, 0x6e, 0x52, 0x67, + 0x70, 0x6, 0x60, 0xb, 0x40, 0x40, 0x3e, 0x10, + 0x30, 0xf, 0xff, 0x30, + + /* U+014A "Ŋ" */ + 0x9f, 0xc0, 0xd, 0xea, 0x0, 0x32, 0x0, 0xf1, + 0x12, 0x80, 0x3e, 0xa6, 0x0, 0xf9, 0x85, 0x40, + 0x3c, 0x29, 0xc0, 0x1f, 0x72, 0x10, 0x7, 0x94, + 0x68, 0x3, 0xe6, 0x60, 0x7, 0xd4, 0x2a, 0x1, + 0xe2, 0x3e, 0x0, 0xfa, 0x50, 0x3, 0xe5, 0x0, + 0xa7, 0x40, 0x37, 0x80, 0x7f, 0xf0, 0x51, 0x38, + 0x18, 0xa, 0xef, 0x98, 0xc0, + + /* U+014B "ŋ" */ + 0x3f, 0x4a, 0xfe, 0x80, 0xd, 0xfe, 0xd8, 0x78, + 0x0, 0xb9, 0x49, 0x9d, 0xc0, 0x13, 0x0, 0x5a, + 0x2, 0x1, 0xe1, 0x3, 0x0, 0xff, 0xf0, 0x9f, + 0x98, 0x7, 0xff, 0x14, 0xd1, 0x2c, 0x2, 0x0, + 0x7d, 0xdc, 0xd2, 0x0, + + /* U+014C "Ō" */ + 0x0, 0xff, 0x17, 0xfe, 0xe0, 0x8, 0xb3, 0x36, + 0x80, 0x63, 0x3e, 0x0, 0xff, 0xe0, 0xc7, 0x7e, + 0xb8, 0x5, 0x8e, 0xd2, 0xf1, 0x0, 0x53, 0xa8, + 0x6a, 0x77, 0x17, 0x8a, 0x80, 0x52, 0xa, 0xea, + 0x1, 0x84, 0x5a, 0x44, 0x0, 0xe3, 0x60, 0xf, + 0xfe, 0x31, 0x10, 0x3, 0x8d, 0x9d, 0x40, 0x30, + 0x8b, 0x7c, 0x54, 0x2, 0x90, 0x55, 0x1d, 0x43, + 0x53, 0xb8, 0x83, 0x1d, 0xa5, 0xe2, 0x0, + + /* U+014D "ō" */ + 0x2, 0xff, 0xdc, 0x1, 0x15, 0xde, 0xa0, 0xc, + 0x89, 0xc0, 0x1a, 0x3b, 0xf5, 0x80, 0x2b, 0x69, + 0xa5, 0x98, 0x5, 0x4d, 0x75, 0x89, 0x72, 0xf2, + 0x30, 0x9, 0x81, 0x59, 0x80, 0x1c, 0x3c, 0x1, + 0xfc, 0xcc, 0x0, 0xe1, 0xee, 0x11, 0x80, 0x4c, + 0xa, 0x88, 0xd7, 0x58, 0x97, 0x20, 0xb6, 0x9a, + 0x59, 0x80, + + /* U+014E "Ŏ" */ + 0x0, 0xff, 0x25, 0x80, 0x5a, 0x40, 0x5, 0x47, + 0xa, 0x2, 0x0, 0xd, 0x47, 0xa7, 0x0, 0x65, + 0xfd, 0xe3, 0x0, 0xf1, 0x0, 0x7a, 0x3b, 0xf5, + 0xc0, 0x2c, 0x76, 0x97, 0x88, 0x2, 0x9d, 0x43, + 0x53, 0xb8, 0xbc, 0x54, 0x2, 0x90, 0x57, 0x50, + 0xc, 0x22, 0xd2, 0x20, 0x7, 0x1b, 0x0, 0x7f, + 0xf1, 0x88, 0x80, 0x1c, 0x6c, 0xea, 0x1, 0x84, + 0x5b, 0xe2, 0xa0, 0x14, 0x82, 0xa8, 0xea, 0x1a, + 0x9d, 0xc4, 0x18, 0xed, 0x2f, 0x10, 0x0, + + /* U+014F "ŏ" */ + 0x0, 0xff, 0x25, 0x80, 0x5a, 0x40, 0x5, 0x47, + 0xa, 0x2, 0x0, 0xd, 0x47, 0xa7, 0x0, 0x65, + 0xfd, 0xe3, 0x0, 0xf1, 0x0, 0x7a, 0x3b, 0xf5, + 0x80, 0x2b, 0x69, 0xa5, 0x98, 0x5, 0x4d, 0x75, + 0x89, 0x72, 0xf2, 0x30, 0x9, 0x81, 0x59, 0x80, + 0x1c, 0x3c, 0x1, 0xfc, 0xcc, 0x0, 0xe1, 0xee, + 0x11, 0x80, 0x4c, 0xa, 0x88, 0xd7, 0x58, 0x97, + 0x20, 0xb6, 0x9a, 0x59, 0x80, + + /* U+0150 "Ő" */ + 0x0, 0x88, 0x80, 0x62, 0x1, 0xa6, 0x41, 0xd0, + 0x1, 0x1a, 0x4a, 0x9c, 0x0, 0x55, 0x5, 0xf6, + 0x20, 0x14, 0xb8, 0x52, 0x80, 0x68, 0xef, 0xd7, + 0x0, 0xb1, 0xda, 0x5e, 0x20, 0xa, 0x75, 0xd, + 0x4e, 0xe2, 0xf1, 0x50, 0xa, 0x41, 0x5d, 0x40, + 0x30, 0x8b, 0x48, 0x80, 0x1c, 0x6c, 0x1, 0xff, + 0xc6, 0x22, 0x0, 0x71, 0xb3, 0xa8, 0x6, 0x11, + 0x6f, 0x8a, 0x80, 0x52, 0xa, 0xa3, 0xa8, 0x6a, + 0x77, 0x10, 0x63, 0xb4, 0xbc, 0x40, 0x0, + + /* U+0151 "ő" */ + 0x0, 0x91, 0x41, 0x50, 0x3, 0x4e, 0x5, 0x98, + 0x4, 0xa7, 0xc, 0x6e, 0x1, 0x15, 0x8f, 0xf0, + 0x6, 0x75, 0x9, 0x30, 0xd, 0x1d, 0xfa, 0xc0, + 0x15, 0xb4, 0xd2, 0xcc, 0x2, 0xa6, 0xba, 0xc4, + 0xb9, 0x79, 0x18, 0x4, 0xc0, 0xac, 0xc0, 0xe, + 0x1e, 0x0, 0xfe, 0x66, 0x0, 0x70, 0xf7, 0x8, + 0xc0, 0x26, 0x5, 0x44, 0x6b, 0xac, 0x4b, 0x90, + 0x5b, 0x4d, 0x2c, 0xc0, 0x0, + + /* U+0152 "Œ" */ + 0x0, 0x15, 0x77, 0xff, 0x8, 0x16, 0x3c, 0x30, + 0x55, 0xd8, 0x42, 0x3, 0x5b, 0xc1, 0x12, 0x0, + 0x2c, 0x80, 0x7e, 0x20, 0x30, 0xf, 0xcc, 0xe, + 0x1, 0xc2, 0x0, 0x10, 0xf, 0x77, 0x34, 0x4, + 0x3, 0xd7, 0x6e, 0x6, 0x7, 0x0, 0xca, 0x86, + 0x4, 0x18, 0x1, 0xfc, 0xac, 0x1, 0xfd, 0x3, + 0xad, 0xe0, 0x8a, 0xa0, 0x1, 0x6b, 0x43, 0x5, + 0x54, 0x20, + + /* U+0153 "œ" */ + 0x5, 0xee, 0x38, 0x4f, 0xf2, 0x1, 0x54, 0x6c, + 0xd9, 0xec, 0xd8, 0x29, 0x31, 0xca, 0x41, 0x39, + 0x26, 0xb8, 0x0, 0x80, 0x80, 0xc, 0x42, 0x1, + 0x8, 0x7f, 0xc8, 0x20, 0x1e, 0xdd, 0xce, 0x20, + 0x10, 0x81, 0x91, 0x41, 0x8e, 0x0, 0x20, 0x50, + 0x5, 0x93, 0x93, 0x1c, 0xa4, 0x9c, 0x31, 0xd, + 0x46, 0xc5, 0xbe, 0x27, 0x80, + + /* U+0154 "Ŕ" */ + 0x0, 0xe8, 0x20, 0xf, 0x2b, 0x88, 0x7, 0xa5, + 0x8c, 0x3, 0x85, 0xe4, 0x3, 0xc3, 0x60, 0x19, + 0xbf, 0xfb, 0x10, 0x2, 0x89, 0x98, 0x2c, 0xc0, + 0xa, 0xcd, 0x60, 0x50, 0x7, 0xc4, 0x40, 0xf, + 0x8c, 0x40, 0x2, 0x31, 0x40, 0xf8, 0x3, 0x3b, + 0x9a, 0xf0, 0xa0, 0xa, 0xb3, 0x1f, 0x70, 0x9, + 0xd3, 0xc5, 0x80, 0x3c, 0x6b, 0x0, 0x1f, 0x40, + 0xb8, 0x7, 0x89, 0xa0, 0x40, 0x3d, 0x1, 0x0, + + /* U+0155 "ŕ" */ + 0x0, 0xe2, 0xc2, 0x0, 0xfa, 0x4, 0x80, 0x3c, + 0x2b, 0xc0, 0x1f, 0x1c, 0x18, 0x7, 0xc4, 0xe0, + 0x1a, 0x7f, 0xe8, 0x2b, 0xff, 0xd, 0xdc, 0x22, + 0xec, 0xbb, 0x9, 0x22, 0x1, 0x35, 0xd1, 0x0, + 0x1c, 0xe2, 0x1, 0xf8, 0x80, 0x3f, 0xfa, 0xe4, + 0x88, 0x2, 0x44, 0x80, 0x17, 0x70, 0xad, 0xdc, + 0x40, + + /* U+0156 "Ŗ" */ + 0x6f, 0xfe, 0xc4, 0x0, 0xa2, 0x66, 0xb, 0x30, + 0x2, 0xb3, 0x58, 0x14, 0x1, 0xf1, 0x10, 0x3, + 0xe3, 0x10, 0x0, 0x8c, 0x50, 0x3e, 0x0, 0xce, + 0xe6, 0xbc, 0x28, 0x2, 0xac, 0xc7, 0xdc, 0x2, + 0x74, 0xf1, 0x60, 0xf, 0x1a, 0xc0, 0x7, 0xd0, + 0x2e, 0x1, 0xe2, 0x68, 0x10, 0xf, 0x40, 0x43, + 0x79, 0x0, 0x43, 0xf2, 0x1, 0x3f, 0xa8, 0x7, + 0xb8, 0x14, 0x3, 0xc4, 0xc0, 0x1f, 0x65, 0x80, + 0x60, + + /* U+0157 "ŗ" */ + 0x9f, 0xfa, 0xa, 0xff, 0xc3, 0x77, 0x8, 0xbb, + 0x2e, 0xc2, 0x48, 0x80, 0x4d, 0x74, 0x40, 0x7, + 0x38, 0x80, 0x7e, 0x20, 0xf, 0xfe, 0xb9, 0x22, + 0x0, 0x91, 0x20, 0x5, 0xdc, 0x2b, 0x77, 0x10, + 0x4f, 0xff, 0xc4, 0x1, 0x17, 0xd0, 0x7, 0xce, + 0x1c, 0x1, 0xf1, 0xa, 0x0, 0x7c, 0x56, 0x1, + 0xe0, + + /* U+0158 "Ř" */ + 0x1, 0x90, 0xa, 0x8, 0x0, 0x2b, 0x41, 0xa, + 0x40, 0x17, 0x2e, 0xbf, 0x0, 0x62, 0xb5, 0xb3, + 0x0, 0xe4, 0x84, 0x0, 0x9b, 0xff, 0xb1, 0x0, + 0x28, 0x99, 0x82, 0xcc, 0x0, 0xac, 0xd6, 0x5, + 0x0, 0x7c, 0x44, 0x0, 0xf8, 0xc4, 0x0, 0x23, + 0x14, 0xf, 0x80, 0x33, 0xb9, 0xaf, 0xa, 0x0, + 0xab, 0x31, 0xf7, 0x0, 0x9d, 0x3c, 0x58, 0x3, + 0xc6, 0xb0, 0x1, 0xf4, 0xb, 0x80, 0x78, 0x9a, + 0x4, 0x3, 0xd0, 0x10, + + /* U+0159 "ř" */ + 0x0, 0x1b, 0x80, 0x52, 0x1, 0x8a, 0x60, 0x2c, + 0x80, 0x30, 0xd3, 0xea, 0xd0, 0x7, 0x25, 0x2e, + 0x88, 0x7, 0x96, 0xc, 0x2, 0x9f, 0xfa, 0xa, + 0xff, 0xc3, 0x77, 0x8, 0xbb, 0x2e, 0xc2, 0x48, + 0x80, 0x4d, 0x74, 0x40, 0x7, 0x38, 0x80, 0x7e, + 0x20, 0xf, 0xfe, 0xb9, 0x22, 0x0, 0x91, 0x20, + 0x5, 0xdc, 0x2b, 0x77, 0x10, 0x0, + + /* U+015A "Ś" */ + 0x0, 0xf2, 0x80, 0x7e, 0x6a, 0x20, 0xf, 0xad, + 0xc8, 0x3, 0xc6, 0x10, 0x1, 0xf1, 0xe8, 0x7, + 0x8a, 0xbb, 0xfa, 0x88, 0x0, 0x7a, 0xf3, 0x26, + 0x4d, 0x30, 0x90, 0xc7, 0x68, 0xf5, 0x10, 0x13, + 0x10, 0x8, 0x68, 0x80, 0x8c, 0x40, 0x3e, 0xb1, + 0xf8, 0x41, 0x0, 0xc5, 0xee, 0xb7, 0xda, 0x80, + 0x10, 0xce, 0xfd, 0xb5, 0xa0, 0x7, 0x92, 0xc7, + 0x80, 0x3f, 0x18, 0x86, 0x48, 0x6, 0x22, 0x18, + 0x5b, 0x6b, 0xb4, 0x60, 0xc8, 0x3d, 0xb4, 0xc9, + 0x5f, 0xc4, + + /* U+015B "ś" */ + 0x0, 0xc3, 0x2, 0x1, 0xe6, 0x70, 0xf, 0xa6, + 0x42, 0x1, 0xc6, 0x8c, 0x1, 0xe3, 0xa0, 0xe, + 0x19, 0xef, 0xe9, 0x20, 0x6, 0x24, 0x5c, 0x2e, + 0x1, 0x97, 0x32, 0x36, 0x68, 0x8, 0x20, 0x6, + 0x30, 0x27, 0x8e, 0xb8, 0x50, 0xa, 0x36, 0x8, + 0xeb, 0x0, 0x22, 0x69, 0xbc, 0x35, 0x37, 0x0, + 0xc4, 0x6, 0xb1, 0x8c, 0x8d, 0x82, 0xed, 0x8d, + 0x17, 0xf, 0x82, + + /* U+015C "Ŝ" */ + 0x0, 0xdb, 0xf2, 0x1, 0xe7, 0x26, 0x61, 0x80, + 0x62, 0x8a, 0x95, 0xe0, 0xc, 0x30, 0xa1, 0x48, + 0x20, 0x11, 0xb8, 0x5, 0x22, 0x1, 0x15, 0x77, + 0xf5, 0x10, 0x0, 0xf5, 0xe6, 0x4c, 0x9a, 0x61, + 0x21, 0x8e, 0xd1, 0xea, 0x20, 0x26, 0x20, 0x10, + 0xd1, 0x1, 0x18, 0x80, 0x7d, 0x63, 0xf0, 0x82, + 0x1, 0x8b, 0xdd, 0x6f, 0xb5, 0x0, 0x21, 0x9d, + 0xfb, 0x6b, 0x40, 0xf, 0x25, 0x8f, 0x0, 0x7e, + 0x31, 0xc, 0x90, 0xc, 0x44, 0x30, 0xb6, 0xd7, + 0x68, 0xc1, 0x90, 0x7b, 0x69, 0x92, 0xbf, 0x88, + + /* U+015D "ŝ" */ + 0x0, 0x96, 0xc, 0x3, 0x92, 0x97, 0x44, 0x2, + 0x1a, 0x8d, 0x5a, 0x0, 0x8a, 0x5c, 0x2c, 0x84, + 0x0, 0x6e, 0x1, 0x48, 0x80, 0x6, 0x7b, 0xfa, + 0x48, 0x1, 0x89, 0x17, 0xb, 0x80, 0x65, 0xcc, + 0x8d, 0x9a, 0x2, 0x8, 0x1, 0x8c, 0x9, 0xe3, + 0xae, 0x14, 0x2, 0x8d, 0x82, 0x3a, 0xc0, 0x8, + 0x9a, 0x6f, 0xd, 0x4d, 0xc0, 0x31, 0x1, 0xac, + 0x63, 0x23, 0x60, 0xbb, 0x63, 0x45, 0xc3, 0xe0, + 0x80, + + /* U+015E "Ş" */ + 0x0, 0x15, 0x77, 0xf5, 0x10, 0x0, 0xf5, 0xe6, + 0x4c, 0x9a, 0x61, 0x21, 0x8e, 0xd1, 0xea, 0x20, + 0x26, 0x20, 0x10, 0xd1, 0x1, 0x18, 0x80, 0x7d, + 0x61, 0xee, 0x62, 0x1, 0x8b, 0x52, 0x73, 0xb0, + 0xc0, 0x22, 0xae, 0xe5, 0x86, 0x18, 0x7, 0xa, + 0x60, 0xd0, 0x7, 0xe3, 0x20, 0xc8, 0x0, 0xc4, + 0x50, 0x2b, 0xe3, 0x2b, 0xe0, 0x58, 0x4c, 0x82, + 0x6a, 0x53, 0x48, 0x0, 0xd9, 0x85, 0xea, 0x20, + 0xe, 0x12, 0xe4, 0x0, 0xf3, 0xc9, 0x60, 0x7, + 0xbb, 0x81, 0x20, 0x0, + + /* U+015F "ş" */ + 0x1, 0x9e, 0xfe, 0x92, 0x0, 0x62, 0x45, 0xc2, + 0xe0, 0x19, 0x73, 0x23, 0x66, 0x80, 0x82, 0x0, + 0x63, 0x2, 0x78, 0xeb, 0x84, 0x0, 0xa3, 0x60, + 0x92, 0xf0, 0x2, 0x26, 0x9c, 0xc1, 0xa9, 0xb0, + 0x6, 0x20, 0x2, 0xcd, 0xb2, 0x2e, 0x8a, 0x36, + 0x4, 0x5d, 0x9b, 0x4, 0xe, 0xf5, 0x7a, 0x88, + 0x3, 0x9, 0x72, 0x0, 0x73, 0xc9, 0x60, 0x7, + 0x77, 0x2, 0x80, 0x0, + + /* U+0160 "Š" */ + 0x0, 0x84, 0x2, 0x10, 0xc, 0x9e, 0x40, 0x9e, + 0x20, 0x12, 0x23, 0x16, 0xe0, 0x40, 0x35, 0x1d, + 0x2b, 0x0, 0x7b, 0x7e, 0x80, 0x3f, 0xf8, 0x65, + 0x5d, 0xfd, 0x44, 0x0, 0x3d, 0x79, 0x93, 0x26, + 0x98, 0x48, 0x63, 0xb4, 0x7a, 0x88, 0x9, 0x88, + 0x4, 0x34, 0x40, 0x46, 0x20, 0x1f, 0x58, 0xfc, + 0x20, 0x80, 0x62, 0xf7, 0x5b, 0xed, 0x40, 0x8, + 0x67, 0x7e, 0xda, 0xd0, 0x3, 0xc9, 0x63, 0xc0, + 0x1f, 0x8c, 0x43, 0x24, 0x3, 0x11, 0xc, 0x2d, + 0xb5, 0xda, 0x30, 0x64, 0x1e, 0xda, 0x64, 0xaf, + 0xe2, + + /* U+0161 "š" */ + 0x3, 0x80, 0xa, 0x4, 0x0, 0x4d, 0x1, 0x46, + 0x20, 0x1, 0xa7, 0xd4, 0xa0, 0xc, 0x94, 0xba, + 0x20, 0x1c, 0xb0, 0x60, 0x18, 0x67, 0xbf, 0xa4, + 0x80, 0x18, 0x91, 0x70, 0xb8, 0x6, 0x5c, 0xc8, + 0xd9, 0xa0, 0x20, 0x80, 0x18, 0xc0, 0x9e, 0x3a, + 0xe1, 0x40, 0x28, 0xd8, 0x23, 0xac, 0x0, 0x89, + 0xa6, 0xf0, 0xd4, 0xdc, 0x3, 0x10, 0x1a, 0xc6, + 0x32, 0x36, 0xb, 0xb6, 0x34, 0x5c, 0x3e, 0x8, + + /* U+0162 "Ţ" */ + 0x8f, 0xff, 0xe5, 0xb9, 0x99, 0x82, 0x26, 0x67, + 0x36, 0x6b, 0x1, 0x59, 0xa2, 0x0, 0xff, 0xff, + 0x80, 0x7f, 0xf6, 0xdc, 0xc8, 0x3, 0xf9, 0x70, + 0xc0, 0x3e, 0x68, 0x7b, 0x0, 0xfb, 0xf9, 0xa8, + 0x2, + + /* U+0163 "ţ" */ + 0x0, 0x8b, 0xcc, 0x3, 0xff, 0x8a, 0xc0, 0x1e, + 0x4f, 0xf5, 0x86, 0x7f, 0xce, 0xb7, 0x68, 0x8, + 0xbb, 0x94, 0x51, 0x20, 0x44, 0xc4, 0x1, 0xff, + 0xe9, 0x20, 0x44, 0xc4, 0x1, 0xc, 0x42, 0xee, + 0x50, 0xc, 0xfe, 0x7d, 0xee, 0x1, 0xe4, 0xa8, + 0x0, 0xf1, 0xd6, 0xb0, 0x7, 0x97, 0x62, 0x0, + + /* U+0164 "Ť" */ + 0x0, 0x14, 0x0, 0x50, 0x20, 0x18, 0xda, 0x2, + 0x90, 0x40, 0x30, 0xd3, 0xea, 0x68, 0x7, 0x92, + 0x97, 0x44, 0x3, 0xe5, 0x83, 0x0, 0xd1, 0xff, + 0xfc, 0xb7, 0x33, 0x30, 0x44, 0xcc, 0xe6, 0xcd, + 0x60, 0x2b, 0x34, 0x40, 0x1f, 0xff, 0xf0, 0xf, + 0xfe, 0xb8, + + /* U+0165 "ť" */ + 0x0, 0xfb, 0xb4, 0x3, 0x17, 0x98, 0xa, 0x0, + 0x7f, 0x60, 0x6, 0x60, 0x9, 0x90, 0x13, 0xfd, + 0x61, 0x9e, 0xdc, 0xeb, 0x76, 0x80, 0x8b, 0xb9, + 0x45, 0x12, 0x4, 0x4c, 0x40, 0x1f, 0xfe, 0x92, + 0x4, 0x4c, 0x40, 0x10, 0xc4, 0x2e, 0xe5, + + /* U+0166 "Ŧ" */ + 0x8f, 0xff, 0xe5, 0xb9, 0x99, 0x82, 0x26, 0x67, + 0x36, 0x6b, 0x1, 0x59, 0xa2, 0x0, 0xff, 0xe5, + 0xbf, 0xfa, 0x83, 0x7f, 0xc8, 0x0, 0x6c, 0xc4, + 0x5, 0x66, 0x10, 0x0, 0x26, 0x62, 0x7, 0x33, + 0x0, 0x7f, 0xfb, 0x40, + + /* U+0167 "ŧ" */ + 0x0, 0x8b, 0xcc, 0x3, 0xff, 0x8a, 0xc0, 0x1e, + 0x4f, 0xf5, 0x86, 0x7f, 0xce, 0xb7, 0x68, 0x8, + 0xbb, 0x94, 0x51, 0x20, 0x44, 0xc4, 0x1, 0xff, + 0x17, 0xe0, 0x67, 0xf9, 0x40, 0x5, 0xb4, 0x15, + 0xba, 0x50, 0x8, 0x98, 0x18, 0x88, 0x1, 0xff, + 0xc3, 0x20, 0x44, 0xc2, 0x1, 0xc, 0x42, 0xee, + 0x60, + + /* U+0168 "Ũ" */ + 0x0, 0xff, 0x1f, 0x72, 0x5, 0x0, 0x27, 0x8b, + 0x4e, 0xa2, 0x0, 0x23, 0x2e, 0x6f, 0x90, 0x7, + 0x88, 0x40, 0x13, 0xe0, 0x18, 0xbd, 0x40, 0x3f, + 0xfd, 0x42, 0x1, 0xff, 0xc2, 0x10, 0x7, 0x80, + 0x73, 0x83, 0x18, 0x28, 0x5, 0x80, 0x48, 0x55, + 0xd, 0x2e, 0xa2, 0x18, 0xad, 0x2e, 0xd4, 0x0, + + /* U+0169 "ũ" */ + 0x3, 0xee, 0x38, 0xa0, 0x4, 0xf1, 0xa, 0xea, + 0x20, 0x2, 0x33, 0x37, 0x3c, 0x80, 0x3c, 0x62, + 0x0, 0x5f, 0x10, 0x9, 0xbc, 0x3, 0xff, 0xc4, + 0x20, 0x60, 0x1e, 0x60, 0x40, 0xb, 0x0, 0x4, + 0x73, 0xd, 0x4c, 0x1, 0x6a, 0xb4, 0x6d, 0x0, + 0x0, + + /* U+016A "Ū" */ + 0x0, 0xff, 0x17, 0xfe, 0xe0, 0x8, 0xb3, 0x36, + 0x80, 0x63, 0x3e, 0x0, 0xff, 0xa7, 0xc0, 0x31, + 0x7a, 0x80, 0x7f, 0xfa, 0x84, 0x3, 0xff, 0x84, + 0x20, 0xf, 0x0, 0xe7, 0x6, 0x30, 0x50, 0xb, + 0x0, 0x90, 0xaa, 0x1a, 0x5d, 0x44, 0x31, 0x5a, + 0x5d, 0xa8, 0x0, + + /* U+016B "ū" */ + 0x2, 0xff, 0xdc, 0x1, 0x15, 0xde, 0xa0, 0xc, + 0x89, 0xc0, 0x5, 0xf1, 0x0, 0x9b, 0xc0, 0x3f, + 0xfc, 0x42, 0x6, 0x1, 0xe6, 0x4, 0x0, 0xb0, + 0x0, 0x47, 0x30, 0xd4, 0xc0, 0x16, 0xab, 0x46, + 0xd0, 0x0, + + /* U+016C "Ŭ" */ + 0x0, 0xff, 0x25, 0x80, 0x5a, 0x40, 0x5, 0x47, + 0xa, 0x2, 0x0, 0xd, 0x47, 0xa7, 0x0, 0x65, + 0xfd, 0xe3, 0x0, 0xf1, 0x0, 0x69, 0xf0, 0xc, + 0x5e, 0xa0, 0x1f, 0xfe, 0xa1, 0x0, 0xff, 0xe1, + 0x8, 0x3, 0xc0, 0x39, 0xc1, 0x8c, 0x14, 0x2, + 0xc0, 0x24, 0x2a, 0x86, 0x97, 0x51, 0xc, 0x56, + 0x97, 0x6a, 0x0, + + /* U+016D "ŭ" */ + 0x0, 0xff, 0x2d, 0x80, 0x7, 0x44, 0x0, 0x88, + 0x60, 0xb1, 0x10, 0x0, 0x6a, 0x7d, 0x38, 0x3, + 0x2f, 0x6f, 0x10, 0x7, 0x9, 0x0, 0x65, 0xf1, + 0x0, 0x9b, 0xc0, 0x3f, 0xfc, 0x42, 0x6, 0x1, + 0xe6, 0x4, 0x0, 0xb0, 0x0, 0x47, 0x30, 0xd4, + 0xc0, 0x16, 0xab, 0x46, 0xd0, 0x0, + + /* U+016E "Ů" */ + 0x0, 0xab, 0xa0, 0x3, 0x9c, 0xf9, 0x10, 0x1, + 0xb7, 0x1, 0x4c, 0x3, 0x6e, 0x2, 0x98, 0x6, + 0x73, 0xe4, 0x40, 0x7, 0x57, 0x40, 0x5, 0x3e, + 0x1, 0x8b, 0xd4, 0x3, 0xff, 0xd4, 0x20, 0x1f, + 0xfc, 0x21, 0x0, 0x78, 0x7, 0x38, 0x31, 0x82, + 0x80, 0x58, 0x4, 0x85, 0x50, 0xd2, 0xea, 0x21, + 0x8a, 0xd2, 0xed, 0x40, + + /* U+016F "ů" */ + 0x0, 0xab, 0xa0, 0x3, 0xa0, 0xb9, 0xc, 0x3, + 0x16, 0x82, 0xa8, 0x3, 0x16, 0x82, 0xa8, 0x3, + 0x41, 0xf2, 0x18, 0x7, 0x5f, 0x40, 0x4, 0xbe, + 0x20, 0x13, 0x78, 0x7, 0xff, 0x88, 0x40, 0xc0, + 0x3c, 0xc0, 0x80, 0x16, 0x0, 0x8, 0xe6, 0x1a, + 0x98, 0x2, 0xd5, 0x68, 0xda, 0x0, 0x0, + + /* U+0170 "Ű" */ + 0x0, 0x91, 0x41, 0x90, 0x3, 0x4e, 0x4, 0x18, + 0x4, 0xa7, 0xc, 0x6e, 0x1, 0x15, 0x8f, 0xf0, + 0x6, 0x75, 0x9, 0x30, 0x4, 0xf8, 0x6, 0x2f, + 0x50, 0xf, 0xff, 0x50, 0x80, 0x7f, 0xf0, 0x84, + 0x1, 0xe0, 0x1c, 0xe0, 0xc6, 0xa, 0x1, 0x60, + 0x12, 0x15, 0x43, 0x4b, 0xa8, 0x86, 0x2b, 0x4b, + 0xb5, 0x0, + + /* U+0171 "ű" */ + 0x0, 0x9e, 0x2, 0x58, 0x3, 0x4f, 0xb, 0x18, + 0x4, 0xe4, 0xd0, 0xaa, 0x0, 0x8b, 0x83, 0xec, + 0x3, 0x29, 0x3, 0x8, 0x1, 0x7c, 0x40, 0x26, + 0xf0, 0xf, 0xff, 0x10, 0x81, 0x80, 0x79, 0x81, + 0x0, 0x2c, 0x0, 0x11, 0xcc, 0x35, 0x30, 0x5, + 0xaa, 0xd1, 0xb4, 0x0, + + /* U+0172 "Ų" */ + 0x9f, 0x0, 0xc5, 0xea, 0x1, 0xff, 0xea, 0x10, + 0xf, 0xfe, 0x10, 0x80, 0x3c, 0x3, 0x9c, 0x4, + 0xc1, 0x40, 0x2c, 0x5, 0x42, 0xa8, 0x69, 0x74, + 0x10, 0xf5, 0x69, 0x61, 0x90, 0x1, 0x57, 0x39, + 0xf9, 0x0, 0x68, 0x4c, 0x10, 0xe, 0x45, 0x33, + 0x0, 0x74, 0x3e, 0x20, 0x0, + + /* U+0173 "ų" */ + 0x5f, 0x10, 0x9, 0xbc, 0x3, 0xff, 0xc4, 0x20, + 0x60, 0x1e, 0x60, 0x40, 0xb, 0x0, 0x4, 0x73, + 0xd, 0x4c, 0x1, 0x6a, 0xb4, 0x6c, 0x80, 0x43, + 0x5d, 0xc4, 0x9c, 0x0, 0xf6, 0xf9, 0x80, 0x71, + 0x8, 0x28, 0x7, 0x12, 0xe4, 0x10, + + /* U+0174 "Ŵ" */ + 0x0, 0xcb, 0x6, 0x1, 0xf1, 0xd2, 0xe8, 0x80, + 0x70, 0xeb, 0xea, 0xe8, 0x7, 0x1c, 0xc0, 0x5a, + 0x88, 0x6, 0x27, 0x0, 0xa4, 0x40, 0xb, 0xe0, + 0x1e, 0x2f, 0x21, 0x1, 0x0, 0xe6, 0x2, 0x60, + 0x30, 0xe, 0x10, 0x1, 0x80, 0x84, 0x7a, 0x81, + 0x88, 0x8, 0x38, 0x18, 0x60, 0x9, 0x80, 0x44, + 0xa, 0xb0, 0x78, 0x80, 0x4, 0x77, 0x18, 0xb, + 0x0, 0xb, 0xd0, 0x90, 0x8, 0xcc, 0x0, 0x71, + 0xd7, 0x2, 0x71, 0x10, 0x0, 0x4c, 0xda, 0xf, + 0xad, 0xe0, 0x3, 0x15, 0x30, 0xd4, 0x31, 0x0, + 0x72, 0xa, 0x81, 0x98, 0x48, 0x0, 0x20, 0x22, + 0x5, 0x0, 0x38, 0x0, + + /* U+0175 "ŵ" */ + 0x0, 0xcb, 0x6, 0x1, 0xf2, 0x52, 0x69, 0x0, + 0x70, 0xd4, 0xe2, 0x70, 0x7, 0x1d, 0xb0, 0x55, + 0x0, 0x38, 0x94, 0x2, 0x60, 0x9, 0xb4, 0x0, + 0xfe, 0x80, 0xf, 0x35, 0x20, 0x6, 0x89, 0x81, + 0x1, 0x90, 0x8, 0x3b, 0x68, 0x30, 0x80, 0x81, + 0x1, 0x5b, 0x81, 0x10, 0x0, 0x4c, 0x64, 0x64, + 0x1c, 0xa0, 0x7, 0x25, 0x70, 0x23, 0x2e, 0x0, + 0x17, 0x73, 0x41, 0xd5, 0x88, 0x1, 0xc4, 0x66, + 0xd, 0xf2, 0x60, 0x2, 0xae, 0x3, 0x41, 0x30, + 0x1, 0x8, 0x8, 0x29, 0x8, 0x0, + + /* U+0176 "Ŷ" */ + 0x0, 0xcb, 0x6, 0x1, 0xf1, 0xd2, 0xe8, 0x80, + 0x70, 0xeb, 0xea, 0xe8, 0x7, 0x1c, 0xc0, 0x5a, + 0x88, 0x6, 0x27, 0x0, 0xa4, 0x40, 0xd, 0xe8, + 0x1, 0xcf, 0xe6, 0xc5, 0x20, 0x18, 0x65, 0x4c, + 0x20, 0x94, 0x2, 0x91, 0x80, 0x2, 0x9c, 0x0, + 0x5, 0xd8, 0x80, 0x2f, 0x26, 0x8, 0x8, 0x0, + 0xc6, 0xb0, 0x4d, 0x2, 0x1, 0xd0, 0x38, 0x2e, + 0x1, 0xe2, 0x61, 0xb0, 0xf, 0xd8, 0xe, 0x1, + 0xff, 0xe4, + + /* U+0177 "ŷ" */ + 0x0, 0xcb, 0x6, 0x1, 0xe4, 0xa4, 0xd2, 0x0, + 0xc3, 0x53, 0x89, 0xc0, 0x18, 0xed, 0x82, 0xa8, + 0x1, 0x89, 0x40, 0x26, 0x0, 0xb6, 0x40, 0x3b, + 0x28, 0x29, 0x80, 0x31, 0x1f, 0x3, 0x2, 0x80, + 0x54, 0x28, 0x2, 0x9c, 0x1, 0x33, 0x0, 0x2e, + 0x41, 0x4, 0x1a, 0x0, 0x94, 0x58, 0x34, 0xc8, + 0x3, 0x3d, 0x83, 0x50, 0x7, 0x51, 0xc0, 0x38, + 0x7, 0x19, 0x3a, 0x88, 0x7, 0xa8, 0x38, 0x3, + 0xef, 0x14, 0x0, 0xf9, 0x98, 0x1, 0xc4, 0x8e, + 0x34, 0x1, 0xd5, 0x73, 0x22, 0x0, 0xc0, + + /* U+0178 "Ÿ" */ + 0x0, 0x17, 0xc0, 0x66, 0x80, 0x7c, 0x20, 0x3, + 0x0, 0xe2, 0xf9, 0xc, 0xe0, 0xf, 0xfe, 0x13, + 0x7a, 0x0, 0x73, 0xf9, 0xb1, 0x48, 0x6, 0x19, + 0x53, 0x8, 0x25, 0x0, 0xa4, 0x60, 0x0, 0xa7, + 0x0, 0x1, 0x76, 0x20, 0xb, 0xc9, 0x82, 0x2, + 0x0, 0x31, 0xac, 0x13, 0x40, 0x80, 0x74, 0xe, + 0xb, 0x80, 0x78, 0x98, 0x6c, 0x3, 0xf6, 0x3, + 0x80, 0x7f, 0xf9, 0x0, + + /* U+0179 "Ź" */ + 0x0, 0xf4, 0x8, 0x7, 0xce, 0xe0, 0xf, 0xd3, + 0x21, 0x0, 0xf1, 0x23, 0x0, 0x7c, 0x54, 0x1, + 0xdb, 0xff, 0xf5, 0x4, 0xcf, 0x9c, 0x8, 0x11, + 0x9b, 0x90, 0xa0, 0x3, 0xd0, 0x7c, 0x1, 0xe3, + 0x78, 0x30, 0xf, 0x71, 0xb8, 0x7, 0xa4, 0xb4, + 0x3, 0xc8, 0xd4, 0x20, 0x1c, 0x34, 0xaa, 0x0, + 0xf5, 0x5, 0x80, 0x79, 0x92, 0x84, 0x3, 0x86, + 0x48, 0x8c, 0xdc, 0xa6, 0x5, 0x33, 0xea, + + /* U+017A "ź" */ + 0x0, 0xc3, 0x84, 0x1, 0xe8, 0x22, 0x0, 0x70, + 0xb5, 0x0, 0x78, 0xe1, 0x0, 0x3c, 0x4e, 0x1, + 0x93, 0xff, 0xe1, 0x5b, 0xbe, 0x21, 0x19, 0x13, + 0x19, 0x50, 0x7, 0x24, 0x4a, 0x0, 0x62, 0xb6, + 0x60, 0x6, 0x1f, 0x49, 0x0, 0xed, 0xa, 0x0, + 0xe9, 0x3f, 0x10, 0xc, 0xac, 0xec, 0x89, 0x84, + 0x81, 0xee, 0xf9, 0x40, + + /* U+017B "Ż" */ + 0x0, 0xd1, 0xea, 0x1, 0xf1, 0x1, 0x80, 0x7d, + 0x58, 0xc0, 0x1f, 0x8c, 0x3, 0xb7, 0xff, 0xea, + 0x9, 0x9f, 0x38, 0x10, 0x23, 0x37, 0x21, 0x40, + 0x7, 0xa0, 0xf8, 0x3, 0xc6, 0xf0, 0x60, 0x1e, + 0xe3, 0x70, 0xf, 0x49, 0x68, 0x7, 0x91, 0xa8, + 0x40, 0x38, 0x69, 0x54, 0x1, 0xea, 0xb, 0x0, + 0xf3, 0x25, 0x8, 0x7, 0xc, 0x91, 0x19, 0xb9, + 0x4c, 0xa, 0x67, 0xd4, + + /* U+017C "ż" */ + 0x0, 0xa3, 0xd4, 0x3, 0xc4, 0x6, 0x1, 0xea, + 0xc6, 0x0, 0xf8, 0xc0, 0x32, 0x7f, 0xfc, 0x2b, + 0x77, 0xc4, 0x23, 0x22, 0x63, 0x2a, 0x0, 0xe4, + 0x89, 0x40, 0xc, 0x56, 0xcc, 0x0, 0xc3, 0xe9, + 0x20, 0x1d, 0xa1, 0x40, 0x1d, 0x27, 0xe2, 0x1, + 0x95, 0x9d, 0x91, 0x30, 0x90, 0x3d, 0xdf, 0x28, + + /* U+017D "Ž" */ + 0x0, 0x14, 0x0, 0x50, 0x20, 0x11, 0xb4, 0x5, + 0x20, 0x80, 0x43, 0x4f, 0xa9, 0xa0, 0x1c, 0x94, + 0xba, 0x20, 0x1e, 0x58, 0x30, 0xd, 0xbf, 0xff, + 0x50, 0x4c, 0xf9, 0xc0, 0x81, 0x19, 0xb9, 0xa, + 0x0, 0x3d, 0x7, 0xc0, 0x1e, 0x37, 0x83, 0x0, + 0xf7, 0x1b, 0x80, 0x7a, 0x4b, 0x40, 0x3c, 0x8d, + 0x42, 0x1, 0xc3, 0x4a, 0xa0, 0xf, 0x50, 0x58, + 0x7, 0x99, 0x28, 0x40, 0x38, 0x64, 0x88, 0xcd, + 0xca, 0x60, 0x53, 0x3e, 0xa0, + + /* U+017E "ž" */ + 0x2, 0x80, 0xa, 0x4, 0x0, 0x6d, 0x1, 0x48, + 0x20, 0x1, 0xd7, 0xd4, 0xd0, 0xc, 0x74, 0xba, + 0x20, 0x1c, 0xb0, 0x60, 0x12, 0x7f, 0xfc, 0x2b, + 0x77, 0xc4, 0x23, 0x22, 0x63, 0x2a, 0x0, 0xe4, + 0x89, 0x40, 0xc, 0x56, 0xcc, 0x0, 0xc3, 0xe9, + 0x20, 0x1d, 0xa1, 0x40, 0x1d, 0x27, 0xe2, 0x1, + 0x95, 0x9d, 0x91, 0x30, 0x90, 0x3d, 0xdf, 0x28, + + /* U+017F "ſ" */ + 0x0, 0xd5, 0xff, 0x60, 0x4, 0x8b, 0x57, 0x6f, + 0x0, 0x84, 0x11, 0x23, 0x0, 0x8c, 0x3, 0xaf, + 0xfd, 0x20, 0x1d, 0x17, 0x67, 0x0, 0xe3, 0x44, + 0x70, 0x7, 0xff, 0x98, 0x91, 0x1c, 0x8, 0x90, + 0xd5, 0xd9, 0xc2, 0xae, 0xce, + + /* U+2013 "–" */ + 0x11, 0xfe, 0xfe, 0xef, 0xaa, 0x97, 0x7f, 0x40, + + /* U+2014 "—" */ + 0x11, 0xff, 0xbb, 0xbf, 0xdb, 0x77, 0xfe, 0xf0, + + /* U+2018 "‘" */ + 0x0, 0x4d, 0x80, 0x18, 0xc1, 0x83, 0x42, 0x81, + 0xcc, 0xc0, 0x44, 0xbc, 0x0, + + /* U+2019 "’" */ + 0x2f, 0xf0, 0xb0, 0x20, 0x98, 0x78, 0x68, 0xa0, + 0x3b, 0x0, 0x3a, 0x40, 0x0, + + /* U+201A "‚" */ + 0x1, 0x43, 0x9, 0xb9, 0x3, 0xe, 0x5, 0x24, + 0x1, 0xa0, 0x30, 0x60, + + /* U+201C "“" */ + 0x0, 0x55, 0x0, 0xb, 0xe0, 0x1, 0x5d, 0x0, + 0x71, 0x80, 0x18, 0xc, 0x9, 0x14, 0x1, 0x40, + 0xa1, 0x41, 0xe0, 0x64, 0x22, 0x6, 0x5, 0x2, + 0xbd, 0x0, 0x55, 0x98, 0x0, + + /* U+201D "”" */ + 0x2, 0xff, 0x8, 0x6f, 0xa8, 0x38, 0x28, 0x81, + 0xa, 0x86, 0x87, 0x1, 0x83, 0x80, 0xc, 0x90, + 0x14, 0x2c, 0x0, 0xb4, 0x0, 0xf3, 0x20, 0x6, + 0xb8, 0x2, 0xf8, 0x0, + + /* U+201E "„" */ + 0x0, 0x22, 0x0, 0x6, 0x84, 0x7, 0x74, 0x0, + 0xdb, 0x60, 0x50, 0x60, 0x11, 0x12, 0x7, 0x85, + 0x2, 0x85, 0x0, 0x8, 0xc8, 0xc, 0x18, 0x0, + 0xb4, 0x0, 0xd5, 0x10, + + /* U+2020 "†" */ + 0x0, 0x8f, 0xc0, 0x3f, 0xfa, 0x77, 0xfe, 0xc0, + 0xff, 0xa2, 0x73, 0x12, 0x19, 0x94, 0x91, 0x99, + 0x40, 0xce, 0x10, 0xf, 0xff, 0xf8, 0x7, 0xff, + 0x30, + + /* U+2021 "‡" */ + 0x0, 0x8f, 0xc0, 0x3f, 0xfa, 0x77, 0xfe, 0xc0, + 0xff, 0xa2, 0x73, 0x12, 0x19, 0x94, 0x91, 0x99, + 0x40, 0xce, 0x10, 0xf, 0xff, 0x9, 0x19, 0x94, + 0xc, 0xe1, 0x9c, 0xc4, 0x86, 0x65, 0x37, 0xfe, + 0xc0, 0xff, 0xa0, 0x3, 0xff, 0x9e, + + /* U+2022 "•" */ + 0x4, 0x63, 0x8, 0xb9, 0xc4, 0x40, 0x5, 0xae, + 0x1, 0xc, 0x10, 0x1c, 0x80, + + /* U+2026 "…" */ + 0x6, 0x50, 0x27, 0x10, 0x64, 0x2, 0x9a, 0x9, + 0x86, 0x39, 0xb0, 0x31, 0x20, 0x30, 0x22, 0x9, + 0x80, + + /* U+2030 "‰" */ + 0x3c, 0xfc, 0x30, 0xf, 0x75, 0xfd, 0xf8, 0x7, + 0x89, 0x1, 0xc, 0x3, 0xc6, 0x80, 0x86, 0x0, + 0x17, 0x40, 0xfb, 0xeb, 0xf4, 0xac, 0x14, 0x3, + 0xcf, 0xbe, 0xfe, 0xd6, 0x0, 0xa7, 0xfe, 0xb4, + 0x0, 0xea, 0xc3, 0xca, 0x3c, 0xea, 0x0, 0x1c, + 0x97, 0x16, 0x4f, 0xc, 0x0, 0x17, 0x42, 0xc1, + 0x43, 0xc, 0x3, 0xff, 0x82, 0xba, 0x16, 0xa, + 0x18, 0x60, 0x9, 0x5e, 0x3c, 0x9f, 0x18 +}; + + +/*--------------------- + * GLYPH DESCRIPTION + *--------------------*/ + +static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = { + {.bitmap_index = 0, .adv_w = 0, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0} /* id = 0 reserved */, + {.bitmap_index = 0, .adv_w = 173, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 0, .adv_w = 173, .box_w = 3, .box_h = 13, .ofs_x = 4, .ofs_y = 0}, + {.bitmap_index = 18, .adv_w = 173, .box_w = 5, .box_h = 6, .ofs_x = 3, .ofs_y = 8}, + {.bitmap_index = 27, .adv_w = 173, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 93, .adv_w = 173, .box_w = 10, .box_h = 18, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 166, .adv_w = 173, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 236, .adv_w = 173, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 302, .adv_w = 173, .box_w = 3, .box_h = 6, .ofs_x = 4, .ofs_y = 8}, + {.bitmap_index = 309, .adv_w = 173, .box_w = 6, .box_h = 17, .ofs_x = 3, .ofs_y = -3}, + {.bitmap_index = 353, .adv_w = 173, .box_w = 7, .box_h = 17, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 400, .adv_w = 173, .box_w = 11, .box_h = 10, .ofs_x = 0, .ofs_y = 1}, + {.bitmap_index = 442, .adv_w = 173, .box_w = 9, .box_h = 10, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 469, .adv_w = 173, .box_w = 4, .box_h = 6, .ofs_x = 3, .ofs_y = -3}, + {.bitmap_index = 481, .adv_w = 173, .box_w = 7, .box_h = 2, .ofs_x = 2, .ofs_y = 5}, + {.bitmap_index = 486, .adv_w = 173, .box_w = 3, .box_h = 3, .ofs_x = 4, .ofs_y = 0}, + {.bitmap_index = 491, .adv_w = 173, .box_w = 9, .box_h = 17, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 543, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 600, .adv_w = 173, .box_w = 10, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 629, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 680, .adv_w = 173, .box_w = 10, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 737, .adv_w = 173, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 788, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 838, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 891, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 936, .adv_w = 173, .box_w = 10, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 999, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 1053, .adv_w = 173, .box_w = 3, .box_h = 10, .ofs_x = 4, .ofs_y = 0}, + {.bitmap_index = 1066, .adv_w = 173, .box_w = 4, .box_h = 13, .ofs_x = 3, .ofs_y = -3}, + {.bitmap_index = 1089, .adv_w = 173, .box_w = 9, .box_h = 12, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 1132, .adv_w = 173, .box_w = 9, .box_h = 7, .ofs_x = 1, .ofs_y = 3}, + {.bitmap_index = 1150, .adv_w = 173, .box_w = 9, .box_h = 12, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 1195, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 1243, .adv_w = 173, .box_w = 9, .box_h = 16, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 1309, .adv_w = 173, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 1369, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 1421, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 1471, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 1516, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 1551, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 1579, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 1632, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 1653, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 1678, .adv_w = 173, .box_w = 8, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 1705, .adv_w = 173, .box_w = 10, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 1754, .adv_w = 173, .box_w = 8, .box_h = 13, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 1766, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 1805, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 1846, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 1896, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 1934, .adv_w = 173, .box_w = 9, .box_h = 17, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 2001, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 2049, .adv_w = 173, .box_w = 10, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 2108, .adv_w = 173, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 2128, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 2157, .adv_w = 173, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 2219, .adv_w = 173, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 2288, .adv_w = 173, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 2350, .adv_w = 173, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 2394, .adv_w = 173, .box_w = 10, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 2442, .adv_w = 173, .box_w = 6, .box_h = 18, .ofs_x = 4, .ofs_y = -4}, + {.bitmap_index = 2458, .adv_w = 173, .box_w = 9, .box_h = 17, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 2509, .adv_w = 173, .box_w = 6, .box_h = 18, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 2527, .adv_w = 173, .box_w = 10, .box_h = 8, .ofs_x = 0, .ofs_y = 5}, + {.bitmap_index = 2562, .adv_w = 173, .box_w = 9, .box_h = 3, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 2570, .adv_w = 173, .box_w = 4, .box_h = 4, .ofs_x = 3, .ofs_y = 11}, + {.bitmap_index = 2579, .adv_w = 173, .box_w = 9, .box_h = 10, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 2623, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 2664, .adv_w = 173, .box_w = 9, .box_h = 10, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 2703, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 2748, .adv_w = 173, .box_w = 9, .box_h = 10, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 2789, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 2831, .adv_w = 173, .box_w = 10, .box_h = 16, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 2906, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 2933, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 2967, .adv_w = 173, .box_w = 8, .box_h = 18, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 2998, .adv_w = 173, .box_w = 10, .box_h = 14, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 3039, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 3063, .adv_w = 173, .box_w = 10, .box_h = 10, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 3086, .adv_w = 173, .box_w = 9, .box_h = 10, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 3110, .adv_w = 173, .box_w = 9, .box_h = 10, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 3151, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 3196, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 3241, .adv_w = 173, .box_w = 10, .box_h = 10, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 3274, .adv_w = 173, .box_w = 9, .box_h = 10, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 3318, .adv_w = 173, .box_w = 10, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 3351, .adv_w = 173, .box_w = 9, .box_h = 10, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 3375, .adv_w = 173, .box_w = 9, .box_h = 10, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 3416, .adv_w = 173, .box_w = 11, .box_h = 10, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 3472, .adv_w = 173, .box_w = 9, .box_h = 10, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 3515, .adv_w = 173, .box_w = 10, .box_h = 14, .ofs_x = 0, .ofs_y = -4}, + {.bitmap_index = 3573, .adv_w = 173, .box_w = 9, .box_h = 10, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 3608, .adv_w = 173, .box_w = 8, .box_h = 18, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 3666, .adv_w = 173, .box_w = 3, .box_h = 17, .ofs_x = 4, .ofs_y = -3}, + {.bitmap_index = 3671, .adv_w = 173, .box_w = 8, .box_h = 18, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 3730, .adv_w = 173, .box_w = 10, .box_h = 6, .ofs_x = 0, .ofs_y = 3}, + {.bitmap_index = 3758, .adv_w = 173, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 3758, .adv_w = 173, .box_w = 3, .box_h = 14, .ofs_x = 4, .ofs_y = -4}, + {.bitmap_index = 3778, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 3827, .adv_w = 173, .box_w = 10, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 3883, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 3938, .adv_w = 173, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 4004, .adv_w = 173, .box_w = 3, .box_h = 17, .ofs_x = 4, .ofs_y = -3}, + {.bitmap_index = 4018, .adv_w = 173, .box_w = 9, .box_h = 17, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 4090, .adv_w = 173, .box_w = 7, .box_h = 3, .ofs_x = 2, .ofs_y = 12}, + {.bitmap_index = 4099, .adv_w = 173, .box_w = 11, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 4173, .adv_w = 173, .box_w = 7, .box_h = 6, .ofs_x = 2, .ofs_y = 7}, + {.bitmap_index = 4195, .adv_w = 173, .box_w = 9, .box_h = 9, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 4234, .adv_w = 173, .box_w = 9, .box_h = 6, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 4248, .adv_w = 173, .box_w = 7, .box_h = 2, .ofs_x = 2, .ofs_y = 5}, + {.bitmap_index = 4253, .adv_w = 173, .box_w = 7, .box_h = 7, .ofs_x = 2, .ofs_y = 6}, + {.bitmap_index = 4278, .adv_w = 173, .box_w = 6, .box_h = 3, .ofs_x = 2, .ofs_y = 11}, + {.bitmap_index = 4285, .adv_w = 173, .box_w = 7, .box_h = 8, .ofs_x = 2, .ofs_y = 6}, + {.bitmap_index = 4311, .adv_w = 173, .box_w = 9, .box_h = 12, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 4346, .adv_w = 173, .box_w = 6, .box_h = 7, .ofs_x = 2, .ofs_y = 6}, + {.bitmap_index = 4368, .adv_w = 173, .box_w = 6, .box_h = 7, .ofs_x = 2, .ofs_y = 6}, + {.bitmap_index = 4390, .adv_w = 173, .box_w = 4, .box_h = 4, .ofs_x = 4, .ofs_y = 11}, + {.bitmap_index = 4399, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 4426, .adv_w = 173, .box_w = 10, .box_h = 17, .ofs_x = 0, .ofs_y = -4}, + {.bitmap_index = 4464, .adv_w = 173, .box_w = 3, .box_h = 3, .ofs_x = 4, .ofs_y = 4}, + {.bitmap_index = 4469, .adv_w = 173, .box_w = 5, .box_h = 6, .ofs_x = 3, .ofs_y = -5}, + {.bitmap_index = 4484, .adv_w = 173, .box_w = 7, .box_h = 7, .ofs_x = 2, .ofs_y = 6}, + {.bitmap_index = 4499, .adv_w = 173, .box_w = 7, .box_h = 7, .ofs_x = 2, .ofs_y = 6}, + {.bitmap_index = 4523, .adv_w = 173, .box_w = 9, .box_h = 9, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 4562, .adv_w = 173, .box_w = 11, .box_h = 14, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 4628, .adv_w = 173, .box_w = 11, .box_h = 14, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 4695, .adv_w = 173, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 4765, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 4813, .adv_w = 173, .box_w = 11, .box_h = 18, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 4889, .adv_w = 173, .box_w = 11, .box_h = 18, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 4965, .adv_w = 173, .box_w = 11, .box_h = 18, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 5046, .adv_w = 173, .box_w = 11, .box_h = 18, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 5125, .adv_w = 173, .box_w = 11, .box_h = 17, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 5199, .adv_w = 173, .box_w = 11, .box_h = 19, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 5282, .adv_w = 173, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 5343, .adv_w = 173, .box_w = 9, .box_h = 17, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 5407, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 5458, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 5509, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 5564, .adv_w = 173, .box_w = 9, .box_h = 17, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 5611, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 5651, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 5691, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 5736, .adv_w = 173, .box_w = 9, .box_h = 17, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 5774, .adv_w = 173, .box_w = 10, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 5828, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 5887, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 5951, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 6016, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 6087, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 6155, .adv_w = 173, .box_w = 9, .box_h = 17, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 6219, .adv_w = 173, .box_w = 9, .box_h = 9, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 6258, .adv_w = 173, .box_w = 10, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 6329, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 6374, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 6419, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 6469, .adv_w = 173, .box_w = 9, .box_h = 17, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 6512, .adv_w = 173, .box_w = 11, .box_h = 18, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 6573, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 6619, .adv_w = 173, .box_w = 10, .box_h = 18, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 6688, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 6747, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 6806, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 6870, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 6929, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 6984, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 7045, .adv_w = 173, .box_w = 11, .box_h = 10, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 7099, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 7154, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 7210, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 7266, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 7328, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 7379, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 7415, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 7451, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 7492, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 7524, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 7585, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 7626, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 7681, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 7737, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 7798, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 7855, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 7907, .adv_w = 173, .box_w = 9, .box_h = 10, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 7938, .adv_w = 173, .box_w = 10, .box_h = 12, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 7998, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 8037, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 8077, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 8121, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 8155, .adv_w = 173, .box_w = 10, .box_h = 19, .ofs_x = 0, .ofs_y = -4}, + {.bitmap_index = 8230, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 8278, .adv_w = 173, .box_w = 10, .box_h = 17, .ofs_x = 0, .ofs_y = -4}, + {.bitmap_index = 8349, .adv_w = 173, .box_w = 11, .box_h = 18, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 8423, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 8478, .adv_w = 173, .box_w = 11, .box_h = 19, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 8561, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 8623, .adv_w = 173, .box_w = 11, .box_h = 17, .ofs_x = 0, .ofs_y = -4}, + {.bitmap_index = 8698, .adv_w = 173, .box_w = 10, .box_h = 14, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 8761, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 8825, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 8878, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 8948, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 9007, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 9071, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 9122, .adv_w = 173, .box_w = 9, .box_h = 19, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 9193, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 9252, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 9317, .adv_w = 173, .box_w = 12, .box_h = 14, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 9374, .adv_w = 173, .box_w = 10, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 9428, .adv_w = 173, .box_w = 10, .box_h = 14, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 9485, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 9532, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 9582, .adv_w = 173, .box_w = 9, .box_h = 19, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 9638, .adv_w = 173, .box_w = 9, .box_h = 16, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 9700, .adv_w = 173, .box_w = 9, .box_h = 17, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 9748, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 9801, .adv_w = 173, .box_w = 9, .box_h = 17, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 9850, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 9907, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 9962, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 10023, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 10097, .adv_w = 173, .box_w = 10, .box_h = 19, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 10186, .adv_w = 173, .box_w = 9, .box_h = 19, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 10259, .adv_w = 173, .box_w = 10, .box_h = 20, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 10350, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 10416, .adv_w = 173, .box_w = 10, .box_h = 18, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 10500, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = -5}, + {.bitmap_index = 10570, .adv_w = 173, .box_w = 10, .box_h = 19, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 10657, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 10699, .adv_w = 173, .box_w = 11, .box_h = 18, .ofs_x = -1, .ofs_y = 0}, + {.bitmap_index = 10746, .adv_w = 173, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 10785, .adv_w = 173, .box_w = 10, .box_h = 14, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 10825, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 10868, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 10905, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 10943, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 10975, .adv_w = 173, .box_w = 9, .box_h = 19, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 11021, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 11061, .adv_w = 173, .box_w = 9, .box_h = 17, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 11099, .adv_w = 173, .box_w = 10, .box_h = 18, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 11150, .adv_w = 173, .box_w = 9, .box_h = 17, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 11187, .adv_w = 173, .box_w = 9, .box_h = 10, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 11208, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 11239, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 11277, .adv_w = 173, .box_w = 8, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 11323, .adv_w = 173, .box_w = 9, .box_h = 19, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 11366, .adv_w = 173, .box_w = 10, .box_h = 18, .ofs_x = 1, .ofs_y = -5}, + {.bitmap_index = 11432, .adv_w = 173, .box_w = 10, .box_h = 19, .ofs_x = 1, .ofs_y = -5}, + {.bitmap_index = 11491, .adv_w = 173, .box_w = 10, .box_h = 10, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 11530, .adv_w = 173, .box_w = 8, .box_h = 18, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 11556, .adv_w = 173, .box_w = 9, .box_h = 19, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 11595, .adv_w = 173, .box_w = 8, .box_h = 18, .ofs_x = 2, .ofs_y = -5}, + {.bitmap_index = 11623, .adv_w = 173, .box_w = 9, .box_h = 19, .ofs_x = 1, .ofs_y = -5}, + {.bitmap_index = 11662, .adv_w = 173, .box_w = 8, .box_h = 14, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 11685, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 11715, .adv_w = 173, .box_w = 8, .box_h = 13, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 11738, .adv_w = 173, .box_w = 10, .box_h = 14, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 11774, .adv_w = 173, .box_w = 10, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 11806, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 11846, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 11902, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 11942, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = -5}, + {.bitmap_index = 11999, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = -5}, + {.bitmap_index = 12040, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 12101, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 12145, .adv_w = 173, .box_w = 10, .box_h = 14, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 12181, .adv_w = 173, .box_w = 9, .box_h = 17, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 12234, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 12270, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 12333, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 12383, .adv_w = 173, .box_w = 9, .box_h = 19, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 12454, .adv_w = 173, .box_w = 9, .box_h = 16, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 12515, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 12586, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 12647, .adv_w = 173, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 12705, .adv_w = 173, .box_w = 11, .box_h = 10, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 12758, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 12822, .adv_w = 173, .box_w = 10, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 12871, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = -5}, + {.bitmap_index = 12936, .adv_w = 173, .box_w = 10, .box_h = 15, .ofs_x = 1, .ofs_y = -5}, + {.bitmap_index = 12985, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 13053, .adv_w = 173, .box_w = 10, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 13107, .adv_w = 173, .box_w = 10, .box_h = 18, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 13181, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 13240, .adv_w = 173, .box_w = 10, .box_h = 18, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 13320, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 13385, .adv_w = 173, .box_w = 10, .box_h = 17, .ofs_x = 0, .ofs_y = -4}, + {.bitmap_index = 13461, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 13521, .adv_w = 173, .box_w = 10, .box_h = 19, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 13602, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 13666, .adv_w = 173, .box_w = 11, .box_h = 17, .ofs_x = 0, .ofs_y = -4}, + {.bitmap_index = 13699, .adv_w = 173, .box_w = 10, .box_h = 17, .ofs_x = 0, .ofs_y = -4}, + {.bitmap_index = 13747, .adv_w = 173, .box_w = 11, .box_h = 18, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 13789, .adv_w = 173, .box_w = 10, .box_h = 14, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 13828, .adv_w = 173, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 13864, .adv_w = 173, .box_w = 10, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 13913, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 13961, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 14002, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 14045, .adv_w = 173, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 14079, .adv_w = 173, .box_w = 9, .box_h = 19, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 14130, .adv_w = 173, .box_w = 9, .box_h = 16, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 14176, .adv_w = 173, .box_w = 9, .box_h = 19, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 14228, .adv_w = 173, .box_w = 9, .box_h = 16, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 14275, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 14325, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 14369, .adv_w = 173, .box_w = 9, .box_h = 17, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 14414, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 14452, .adv_w = 173, .box_w = 11, .box_h = 18, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 14544, .adv_w = 173, .box_w = 11, .box_h = 15, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 14622, .adv_w = 173, .box_w = 11, .box_h = 18, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 14688, .adv_w = 173, .box_w = 10, .box_h = 19, .ofs_x = 0, .ofs_y = -4}, + {.bitmap_index = 14767, .adv_w = 173, .box_w = 11, .box_h = 17, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 14827, .adv_w = 173, .box_w = 10, .box_h = 18, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 14890, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 14942, .adv_w = 173, .box_w = 10, .box_h = 17, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 15002, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 15050, .adv_w = 173, .box_w = 10, .box_h = 18, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 15119, .adv_w = 173, .box_w = 9, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 15175, .adv_w = 173, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 15212, .adv_w = 173, .box_w = 9, .box_h = 3, .ofs_x = 1, .ofs_y = 4}, + {.bitmap_index = 15220, .adv_w = 173, .box_w = 11, .box_h = 3, .ofs_x = 0, .ofs_y = 4}, + {.bitmap_index = 15228, .adv_w = 173, .box_w = 4, .box_h = 6, .ofs_x = 3, .ofs_y = 8}, + {.bitmap_index = 15241, .adv_w = 173, .box_w = 4, .box_h = 6, .ofs_x = 4, .ofs_y = 8}, + {.bitmap_index = 15254, .adv_w = 173, .box_w = 4, .box_h = 6, .ofs_x = 3, .ofs_y = -3}, + {.bitmap_index = 15266, .adv_w = 173, .box_w = 9, .box_h = 6, .ofs_x = 1, .ofs_y = 8}, + {.bitmap_index = 15295, .adv_w = 173, .box_w = 9, .box_h = 6, .ofs_x = 1, .ofs_y = 8}, + {.bitmap_index = 15323, .adv_w = 173, .box_w = 9, .box_h = 6, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 15351, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 15376, .adv_w = 173, .box_w = 9, .box_h = 18, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 15414, .adv_w = 173, .box_w = 5, .box_h = 5, .ofs_x = 3, .ofs_y = 4}, + {.bitmap_index = 15427, .adv_w = 173, .box_w = 11, .box_h = 3, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 15444, .adv_w = 173, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = 0} +}; + +/*--------------------- + * CHARACTER MAPPING + *--------------------*/ + +static const uint16_t unicode_list_2[] = { + 0x0, 0x1, 0x5, 0x6, 0x7, 0x9, 0xa, 0xb, + 0xd, 0xe, 0xf, 0x13, 0x1d +}; + +/*Collect the unicode lists and glyph_id offsets*/ +static const lv_font_fmt_txt_cmap_t cmaps[] = +{ + { + .range_start = 32, .range_length = 95, .glyph_id_start = 1, + .unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY + }, + { + .range_start = 160, .range_length = 224, .glyph_id_start = 96, + .unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY + }, + { + .range_start = 8211, .range_length = 30, .glyph_id_start = 320, + .unicode_list = unicode_list_2, .glyph_id_ofs_list = NULL, .list_length = 13, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY + } +}; + + + +/*-------------------- + * ALL CUSTOM DATA + *--------------------*/ + +#if LVGL_VERSION_MAJOR == 8 +/*Store all the custom data of the font*/ +static lv_font_fmt_txt_glyph_cache_t cache; +#endif + +#if LVGL_VERSION_MAJOR >= 8 +static const lv_font_fmt_txt_dsc_t font_dsc = { +#else +static lv_font_fmt_txt_dsc_t font_dsc = { +#endif + .glyph_bitmap = glyph_bitmap, + .glyph_dsc = glyph_dsc, + .cmaps = cmaps, + .kern_dsc = NULL, + .kern_scale = 0, + .cmap_num = 3, + .bpp = 4, + .kern_classes = 0, + .bitmap_format = 1, +#if LVGL_VERSION_MAJOR == 8 + .cache = &cache +#endif +}; + + + +/*----------------- + * PUBLIC FONT + *----------------*/ + +/*Initialize a public general font descriptor*/ +#if LVGL_VERSION_MAJOR >= 8 +const lv_font_t lv_font_ibmplexmono_18 = { +#else +lv_font_t lv_font_ibmplexmono_18 = { +#endif + .get_glyph_dsc = lv_font_get_glyph_dsc_fmt_txt, /*Function pointer to get glyph's data*/ + .get_glyph_bitmap = lv_font_get_bitmap_fmt_txt, /*Function pointer to get glyph's bitmap*/ + .line_height = 24, /*The maximum line height required by the font*/ + .base_line = 5, /*Baseline measured from the bottom of the line*/ +#if !(LVGL_VERSION_MAJOR == 6 && LVGL_VERSION_MINOR == 0) + .subpx = LV_FONT_SUBPX_NONE, +#endif +#if LV_VERSION_CHECK(7, 4, 0) || LVGL_VERSION_MAJOR >= 8 + .underline_position = -3, + .underline_thickness = 1, +#endif + .dsc = &font_dsc, /*The custom font data. Will be accessed by `get_glyph_bitmap/dsc` */ +#if LV_VERSION_CHECK(8, 2, 0) || LVGL_VERSION_MAJOR >= 9 + .fallback = NULL, +#endif + .user_data = NULL, +}; + + + +#endif /*#if LV_FONT_IBMPLEXMONO_18*/ + diff --git a/idf_zacus/components/display_ui/fonts/lv_font_orbitron_40.c b/idf_zacus/components/display_ui/fonts/lv_font_orbitron_40.c new file mode 100644 index 0000000..3df76cd --- /dev/null +++ b/idf_zacus/components/display_ui/fonts/lv_font_orbitron_40.c @@ -0,0 +1,3324 @@ +/******************************************************************************* + * Size: 40 px + * Bpp: 4 + * Opts: --font Orbitron-Bold.ttf --size 40 --bpp 4 --format lvgl --output lv_font_orbitron_40.c --lv-include lvgl.h --range 0x20-0x7E --range 0xA0-0x00FF --range 0x0100-0x017F --range 0x2010-0x2030 --symbols ’”“«»…•–—°œŒàâäçéèêëîïôöùûüÿÀÂÄÇÉÈÊËÎÏÔÖÙÛÜŸ + ******************************************************************************/ + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl.h" +#endif + +#ifndef LV_FONT_ORBITRON_40 +#define LV_FONT_ORBITRON_40 1 +#endif + +#if LV_FONT_ORBITRON_40 + +/*----------------- + * BITMAPS + *----------------*/ + +/*Store the image of the glyphs*/ +static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = { + /* U+0020 " " */ + + /* U+0021 "!" */ + 0xbf, 0xf4, 0x80, 0x7f, 0xff, 0xc3, 0x9d, 0xda, + 0xb1, 0x4, 0x0, 0xff, 0xe0, 0x12, 0x20, 0xa6, + 0xee, 0x0, 0xfc, + + /* U+0022 "\"" */ + 0xaf, 0xf5, 0x0, 0x1f, 0xfd, 0xa0, 0x1f, 0xfd, + 0xcf, 0x77, 0x78, 0x1, 0x1d, 0xd4, + + /* U+0023 "#" */ + 0x0, 0xff, 0xab, 0xfd, 0x0, 0x1f, 0xef, 0xf8, + 0xc0, 0x3f, 0xf8, 0xa, 0x0, 0xb0, 0xf, 0xe5, + 0x0, 0x11, 0x80, 0x7f, 0xca, 0x0, 0x13, 0x0, + 0xfe, 0xf0, 0x2, 0x80, 0x7f, 0xf0, 0x3c, 0x0, + 0xe0, 0x1f, 0xe5, 0x0, 0x58, 0x7, 0xff, 0x1, + 0x40, 0x1a, 0x1, 0xfc, 0xa0, 0x1, 0x30, 0xf, + 0xf9, 0x0, 0x2, 0x80, 0x1f, 0xde, 0x0, 0x70, + 0xf, 0xfe, 0x6, 0x80, 0x1c, 0x3, 0xfc, 0xa0, + 0xa, 0x0, 0xcf, 0xff, 0xf3, 0x0, 0x1b, 0xff, + 0xfc, 0x1, 0x67, 0xfb, 0x80, 0x3f, 0xfe, 0x2d, + 0x77, 0xe1, 0x0, 0x45, 0xdf, 0xf4, 0x0, 0x57, + 0x7b, 0x40, 0x51, 0x39, 0xc4, 0x0, 0xe8, 0x9f, + 0xdc, 0x0, 0x54, 0x4c, 0x60, 0x1f, 0x58, 0x1, + 0x0, 0x3f, 0xce, 0x0, 0xf0, 0xf, 0xfe, 0x2, + 0x80, 0x34, 0x3, 0xf8, 0xc4, 0x0, 0xa0, 0x1f, + 0xf1, 0x10, 0x0, 0xe0, 0x1f, 0xd6, 0x0, 0x40, + 0xf, 0xfe, 0x5, 0x0, 0xc, 0x40, 0x3f, 0x94, + 0x1, 0xc0, 0x1f, 0xfc, 0x5, 0x0, 0x58, 0x7, + 0xf1, 0x90, 0x1, 0x0, 0x3f, 0xe2, 0x20, 0x1, + 0x80, 0x3f, 0xac, 0x0, 0x82, 0x1, 0xff, 0x28, + 0x0, 0xc4, 0x3, 0xf9, 0x40, 0x1a, 0x1, 0xf5, + 0xff, 0xe8, 0x0, 0x1f, 0xff, 0xf8, 0x80, 0x13, + 0xff, 0xd2, 0x1, 0xff, 0xf1, 0x9b, 0xb8, 0x80, + 0xd, 0x77, 0xfd, 0x20, 0x15, 0x5d, 0xfc, 0x4, + 0x88, 0x52, 0x0, 0x5a, 0x27, 0xf0, 0x80, 0xd, + 0x51, 0x3c, 0x40, 0x19, 0xc0, 0x4, 0x40, 0xf, + 0xeb, 0x0, 0x58, 0x7, 0xff, 0x3, 0x40, 0xa, + 0x1, 0xfc, 0x26, 0x0, 0x50, 0xf, 0xf8, 0x50, + 0x1, 0x60, 0x1f, 0xce, 0x0, 0x22, 0x0, 0x7f, + 0xc8, 0x0, 0x13, 0x0, 0xfe, 0xd0, 0x2, 0x80, + 0x7f, 0xf0, 0x3c, 0x0, 0xe0, 0x1f, 0xc2, 0x80, + 0xb, 0x0, 0xff, 0x0, + + /* U+0024 "$" */ + 0x0, 0xff, 0xe0, 0xdf, 0xfa, 0x40, 0x3f, 0xff, + 0xe0, 0x1f, 0xfd, 0x22, 0xae, 0xff, 0xfc, 0x80, + 0x6, 0xff, 0xfd, 0xd2, 0x20, 0x12, 0x6a, 0x88, + 0x7, 0xff, 0x20, 0x5b, 0xc8, 0x6, 0x80, 0x3f, + 0xf9, 0xa3, 0xe0, 0xe2, 0x0, 0x9c, 0xcf, 0xc6, + 0x0, 0x5c, 0xcf, 0xcc, 0x0, 0x23, 0xf0, 0x2, + 0xb1, 0x9f, 0xe7, 0x0, 0x19, 0xff, 0x48, 0x80, + 0x14, 0xc0, 0x6, 0x1, 0xff, 0xca, 0x30, 0xf, + 0xfe, 0x8e, 0xff, 0x98, 0x3, 0xff, 0xfe, 0x1, + 0xff, 0xe1, 0x30, 0xf, 0xfe, 0x8f, 0x0, 0x1c, + 0xc0, 0x3f, 0xf9, 0xaa, 0x0, 0x1c, 0xff, 0xfc, + 0x80, 0x6, 0xff, 0xfd, 0xd2, 0x20, 0x2, 0x70, + 0xf, 0xfe, 0x58, 0xb7, 0x90, 0x2, 0x24, 0x40, + 0x3f, 0xf9, 0x63, 0xe0, 0x13, 0x76, 0x5d, 0xfe, + 0x30, 0x2, 0x5d, 0xfd, 0x4a, 0x0, 0x24, 0x0, + 0xc6, 0x89, 0xf9, 0xc0, 0x4, 0x89, 0xf2, 0xd0, + 0x80, 0xc, 0x3, 0xff, 0x9c, 0x60, 0x1f, 0xff, + 0xf0, 0xf, 0xff, 0x24, 0xf7, 0x20, 0x3, 0xff, + 0x9e, 0x22, 0x20, 0xf, 0xfe, 0x51, 0x80, 0x5c, + 0x0, 0x56, 0x33, 0xfc, 0xe0, 0x3, 0x3f, 0xe9, + 0x10, 0x1, 0xb0, 0x5, 0x39, 0x9f, 0x8c, 0x0, + 0xb9, 0x9f, 0x98, 0x0, 0x48, 0x34, 0x1, 0xff, + 0xcd, 0x1e, 0x0, 0x2e, 0x28, 0x80, 0x7f, 0xf2, + 0x5, 0xb0, 0xc0, 0x23, 0xae, 0xff, 0xfc, 0x80, + 0x6, 0xff, 0xfd, 0xd2, 0x40, 0x1f, 0xff, 0xf0, + 0xf, 0xfe, 0x97, 0x3b, 0xb4, 0x3, 0xff, 0x82, + + /* U+0025 "%" */ + 0x0, 0xff, 0xee, 0x2e, 0x7f, 0xf6, 0xc0, 0x80, + 0x7f, 0xf1, 0xc9, 0xc0, 0x3a, 0x68, 0xc0, 0x38, + 0x9f, 0x4, 0x3, 0xff, 0x8a, 0x9b, 0xc0, 0x19, + 0x98, 0xb1, 0x1f, 0x8a, 0x80, 0x3f, 0xf8, 0x8d, + 0x60, 0x1e, 0xb0, 0xf7, 0x7f, 0xc0, 0x84, 0x1, + 0xff, 0xc2, 0x89, 0x0, 0xf8, 0x80, 0x3f, 0xf8, + 0xe, 0x1, 0xff, 0xc1, 0xb7, 0x0, 0xb0, 0x2, + 0x10, 0xf, 0xfe, 0x9, 0x80, 0x7f, 0xc3, 0xa8, + 0x0, 0x1c, 0x50, 0xf, 0xfe, 0x28, 0x80, 0x7f, + 0x8f, 0xc8, 0x0, 0x58, 0x60, 0x1f, 0xfd, 0x5, + 0xc1, 0x0, 0x26, 0x10, 0x7, 0x8, 0x7, 0xff, + 0x4, 0x40, 0x3f, 0x3d, 0x0, 0x4d, 0x62, 0x1, + 0xf0, 0x80, 0x7f, 0xf0, 0x8, 0x3, 0xe9, 0x80, + 0xa, 0x24, 0x3, 0xf9, 0xc0, 0x3f, 0xe1, 0x40, + 0xe, 0x1c, 0x60, 0xa, 0xdc, 0x3, 0xfd, 0x3, + 0x5f, 0xff, 0x82, 0xc0, 0x38, 0xb0, 0xc0, 0x3, + 0x88, 0x1, 0xff, 0xe, 0x18, 0x7, 0xc3, 0x4a, + 0x1, 0x8f, 0x8, 0x0, 0x7e, 0x60, 0x1f, 0xfc, + 0x12, 0xcc, 0x4c, 0xf5, 0xf2, 0x80, 0x66, 0xc1, + 0x0, 0x2e, 0x8, 0x7, 0xff, 0x10, 0xd9, 0xb9, + 0x0, 0x3a, 0x24, 0x2, 0x7a, 0x0, 0xff, 0xe8, + 0xd3, 0x80, 0x53, 0x0, 0x1f, 0xfd, 0x1, 0xc5, + 0x0, 0xe, 0x30, 0x6, 0x7d, 0xff, 0xed, 0x70, + 0xf, 0xfe, 0x19, 0x79, 0x80, 0xb, 0xc, 0x3, + 0x64, 0x10, 0x7, 0x14, 0x60, 0x7, 0xff, 0x5, + 0x34, 0x40, 0x9, 0x84, 0x1, 0xa4, 0xde, 0x67, + 0xe7, 0x39, 0x0, 0xff, 0x9a, 0xc0, 0x26, 0xb1, + 0x0, 0xe6, 0xb, 0x66, 0xfa, 0x81, 0x84, 0x3, + 0xfa, 0x64, 0x1, 0x44, 0x80, 0x78, 0x80, 0x3f, + 0xf8, 0x24, 0x1, 0xfa, 0xd8, 0x2, 0xb7, 0x0, + 0xf8, 0x40, 0x3f, 0xf8, 0x2e, 0x1, 0xe2, 0xd4, + 0x0, 0xe, 0x20, 0x7, 0xff, 0x40, 0xf0, 0x80, + 0x7, 0xe6, 0x1, 0xff, 0xd0, 0x4c, 0x10, 0x2, + 0xe0, 0x80, 0x7f, 0x84, 0x3, 0xff, 0x82, 0xe0, + 0x1b, 0x0, 0x27, 0xa0, 0xf, 0xfe, 0x1, 0x0, + 0x7f, 0xf0, 0x48, 0x3, 0xe9, 0x80, 0xf, 0xfe, + 0x13, 0x5, 0xb3, 0x7d, 0x40, 0xc2, 0x1, 0xc3, + 0x8c, 0x1, 0xff, 0xc3, 0x93, 0x79, 0x9f, 0x9c, + 0xe4, 0x3, 0xb7, 0xc, 0x3, 0xff, 0x8b, 0x90, + 0x40, 0x1c, 0x51, 0x80, 0x0, + + /* U+0026 "&" */ + 0x0, 0xcf, 0xbf, 0xff, 0xfc, 0x6b, 0x30, 0xf, + 0xfe, 0x5, 0xc1, 0x0, 0x7f, 0xf1, 0x93, 0x14, + 0x3, 0xfd, 0x8, 0x1, 0xff, 0xca, 0xa3, 0x0, + 0xfe, 0x70, 0x1, 0x4d, 0x57, 0xff, 0x12, 0x0, + 0x28, 0x0, 0xfc, 0x40, 0x17, 0xb2, 0xaf, 0xff, + 0x11, 0xe0, 0x0, 0x80, 0x1f, 0x84, 0x2, 0x30, + 0xf, 0xfe, 0x31, 0x0, 0x7f, 0xf5, 0x92, 0xee, + 0x0, 0xff, 0xe9, 0x8a, 0x24, 0x1, 0xff, 0xdf, + 0x10, 0xf, 0xfe, 0xb1, 0x10, 0x0, 0xa0, 0x1f, + 0xfd, 0x39, 0x0, 0x5d, 0x8c, 0x3, 0xff, 0xa2, + 0x92, 0x0, 0x4c, 0xb3, 0x0, 0xff, 0xe7, 0x3f, + 0x48, 0x8, 0x1, 0x32, 0x88, 0x3, 0xff, 0x97, + 0x10, 0x13, 0xfe, 0xa2, 0x0, 0x2e, 0xd1, 0x0, + 0x7f, 0xf2, 0x54, 0x1, 0x40, 0x5, 0xd9, 0x20, + 0x2, 0xed, 0x10, 0x7, 0xf9, 0x9a, 0x10, 0xe, + 0x30, 0xf, 0x9b, 0x64, 0x40, 0xb, 0xb2, 0x20, + 0x1f, 0xc, 0xcc, 0x40, 0x1f, 0xfc, 0x36, 0xe9, + 0x10, 0x3, 0x74, 0x88, 0x7, 0xff, 0x3d, 0xba, + 0x4, 0x0, 0xdd, 0x2, 0x1, 0xff, 0xcf, 0x7e, + 0x81, 0x0, 0x3f, 0x40, 0x80, 0x7f, 0xf3, 0xdf, + 0xa0, 0x40, 0xf, 0xd0, 0x60, 0x1f, 0xfc, 0xf7, + 0xf7, 0x0, 0x9f, 0x40, 0x3f, 0xfa, 0x3, 0x1c, + 0xe0, 0x1e, 0xb0, 0xf, 0xfe, 0x70, 0xc7, 0x38, + 0x6, 0x7e, 0x50, 0x8, 0xc0, 0x2, 0x1, 0xff, + 0xc7, 0x18, 0xb0, 0xc, 0x35, 0x64, 0xe, 0x0, + 0x95, 0x44, 0xff, 0xe4, 0x48, 0x0, 0x5c, 0x0, + 0x9a, 0xb2, 0x0, 0x1a, 0xbb, 0xff, 0xe4, 0x10, + 0x2, 0x21, 0xca, 0x0, 0xc3, 0x80, 0xf, 0xfe, + 0x6b, 0x30, 0x6, 0xb0, 0xc0, 0xf, 0x68, 0x1, + 0xff, 0xc9, 0x29, 0x90, 0x6, 0x3c, 0x80, + + /* U+0027 "'" */ + 0xaf, 0xf5, 0x0, 0x7f, 0xf1, 0x3d, 0xdd, 0xe0, + + /* U+0028 "(" */ + 0x0, 0x25, 0xff, 0x84, 0x1e, 0xd0, 0x2, 0x48, + 0x0, 0xef, 0x0, 0xc, 0xd8, 0xb0, 0x2, 0x59, + 0x0, 0xc0, 0x4, 0x1, 0xff, 0xff, 0x0, 0xff, + 0xf5, 0x18, 0x0, 0xc0, 0x26, 0x0, 0x42, 0xa0, + 0x78, 0x0, 0x6a, 0xc5, 0x20, 0x3, 0xcf, 0x66, + 0x1, 0x0, + + /* U+0029 ")" */ + 0xcf, 0xd7, 0x0, 0xe2, 0x8c, 0x0, 0xf1, 0xc8, + 0x7d, 0x18, 0x1, 0x80, 0xd7, 0x80, 0x22, 0x0, + 0x18, 0x4, 0x20, 0x1f, 0xff, 0xf0, 0xf, 0xff, + 0x49, 0x80, 0x42, 0x69, 0xc0, 0x11, 0x7d, 0x98, + 0x1, 0x80, 0x38, 0xe4, 0x2, 0x28, 0xc0, 0x0, + + /* U+002A "*" */ + 0x0, 0xf1, 0x7f, 0xc4, 0x1, 0xff, 0xed, 0x2a, + 0x50, 0xf, 0xf9, 0x68, 0x15, 0x55, 0xf4, 0xa2, + 0x1, 0xa, 0xdf, 0xd2, 0xb5, 0x80, 0x4b, 0x58, + 0x1, 0x65, 0x20, 0x5, 0xb6, 0x60, 0x1f, 0xfc, + 0x13, 0xc7, 0xce, 0x93, 0x0, 0xf9, 0x27, 0xb1, + 0xc0, 0x2, 0xd9, 0x0, 0x1d, 0x16, 0xc2, 0x1, + 0xe3, 0x80, 0xe, 0x82, 0x0, 0xfd, 0xc0, 0x14, + 0x0, 0x5e, 0x1, 0xf4, 0x90, 0x2, 0x1d, 0xc0, + 0x2, 0x90, 0xe, 0x56, 0x0, 0x23, 0x84, 0x18, + 0x1, 0x90, 0x3, 0xe1, 0xa0, 0xb, 0x44, 0x4, + 0x3, 0x97, 0x8e, 0x84, 0x2, 0x1a, 0x4f, 0x40, + 0xe, 0x1c, 0x40, 0xe, 0x4b, 0x10, 0x8, + + /* U+002B "+" */ + 0x0, 0xf0, 0xbb, 0xc0, 0x1f, 0xfc, 0x12, 0x88, + 0x80, 0x3f, 0xff, 0xe2, 0x89, 0xca, 0x1, 0x22, + 0x78, 0x52, 0xef, 0xa4, 0x2, 0xbb, 0xf2, 0x80, + 0x7f, 0xf5, 0x97, 0xff, 0xb0, 0x2, 0xff, 0xf2, + 0x0, 0x7f, 0xff, 0xc0, 0x3f, 0xc0, + + /* U+002C "," */ + 0xdf, 0xf3, 0x80, 0x7f, 0xc2, 0x1, 0x18, 0x4, + 0xe0, 0x5, 0x20, 0x4a, 0xe, 0xb0, 0x0, + + /* U+002D "-" */ + 0x24, 0x4f, 0xfe, 0x11, 0xc5, 0xdf, 0xff, 0xb, + 0x0, 0x3f, 0xfa, 0x80, + + /* U+002E "." */ + 0x34, 0x40, 0xf5, 0xd9, 0x80, 0x3f, 0x0, + + /* U+002F "/" */ + 0x0, 0xff, 0xe4, 0x10, 0x7, 0xff, 0x1d, 0x94, + 0x3, 0xff, 0x8c, 0x92, 0x1, 0xff, 0xc6, 0x2a, + 0x0, 0xff, 0xe3, 0xf8, 0x80, 0x80, 0x7f, 0xf1, + 0x2c, 0x81, 0x18, 0x3, 0xff, 0x87, 0x28, 0x5, + 0x60, 0x1f, 0xfc, 0x36, 0x60, 0xf, 0x80, 0x7f, + 0xf0, 0xce, 0x40, 0x14, 0x40, 0x1f, 0xfc, 0x22, + 0xd0, 0x4, 0xa0, 0x7, 0xff, 0xf, 0xc4, 0x19, + 0x80, 0x1f, 0xfc, 0x3b, 0x20, 0x49, 0x0, 0xff, + 0xe1, 0xc2, 0x1, 0x50, 0x7, 0xff, 0xd, 0x9c, + 0x7, 0xc4, 0x3, 0xff, 0x84, 0x72, 0x0, 0xa2, + 0x0, 0xff, 0xe1, 0xe, 0x80, 0x25, 0x0, 0x3f, + 0xf8, 0x78, 0x20, 0xcc, 0x0, 0xff, 0xe1, 0xd9, + 0x1, 0xc8, 0x7, 0xff, 0xe, 0x10, 0xb, 0x40, + 0x3f, 0xf8, 0x6a, 0xe0, 0xf, 0x10, 0xf, 0xfe, + 0x11, 0xd0, 0x2, 0xc8, 0x3, 0xff, 0x84, 0x3a, + 0x0, 0x84, 0x0, 0xff, 0xe1, 0xe0, 0x82, 0xb8, + 0x7, 0xff, 0xe, 0x88, 0xe, 0x80, 0x3f, 0xf8, + 0x70, 0xa0, 0x3a, 0x1, 0xff, 0xc4, 0x40, 0x6, + 0x8, 0x7, 0xff, 0x1a, 0x88, 0x3, 0xff, 0x8d, + 0xa, 0x1, 0xff, 0xc6, 0xa7, 0x0, 0xff, 0xe3, + 0x80, + + /* U+0030 "0" */ + 0x0, 0x1d, 0x77, 0xff, 0xff, 0x23, 0x60, 0x3, + 0x2e, 0x28, 0x80, 0x7f, 0xf2, 0x9, 0xf4, 0x40, + 0x68, 0x3, 0xff, 0x9a, 0x54, 0xc, 0x1, 0x45, + 0xdf, 0xff, 0x1e, 0x88, 0x0, 0x85, 0xa0, 0x5, + 0x74, 0x4f, 0xfe, 0x32, 0xf1, 0x0, 0x4c, 0x20, + 0x3, 0x0, 0xff, 0xe3, 0x16, 0x20, 0x6, 0x10, + 0xf, 0xfe, 0x4a, 0x61, 0x0, 0x7f, 0xf3, 0x9a, + 0xc4, 0x0, 0x60, 0x1f, 0xfc, 0xb9, 0x90, 0x4, + 0xd8, 0x20, 0x1f, 0xfc, 0x9b, 0x60, 0xa, 0x24, + 0x3, 0xff, 0x92, 0x5a, 0x80, 0x15, 0xb8, 0x7, + 0xff, 0x24, 0xf0, 0x80, 0x5, 0xa8, 0x1, 0xff, + 0xc9, 0x6c, 0x10, 0x1, 0xe1, 0x0, 0x7f, 0xf2, + 0x62, 0x40, 0x26, 0xc1, 0x0, 0xff, 0xe4, 0xdb, + 0x80, 0x51, 0x20, 0x1f, 0xfc, 0x92, 0xd4, 0x0, + 0xad, 0xc0, 0x3f, 0xf9, 0x27, 0x84, 0x0, 0x2d, + 0x40, 0xf, 0xfe, 0x4b, 0x60, 0x80, 0xf, 0x8, + 0x3, 0xff, 0x93, 0x12, 0x1, 0x36, 0x8, 0x7, + 0xff, 0x26, 0xdc, 0x2, 0x89, 0x0, 0xff, 0xe4, + 0x8e, 0xa0, 0x5, 0x6e, 0x1, 0xff, 0xc9, 0x5f, + 0x20, 0x1, 0x6a, 0x0, 0x7f, 0xf2, 0x8c, 0x40, + 0x7, 0x84, 0x1, 0xff, 0xce, 0x5c, 0x10, 0xf, + 0xfe, 0x48, 0x80, 0x68, 0xa0, 0xf, 0xfe, 0x38, + 0x80, 0x7, 0x40, 0x27, 0x34, 0x4f, 0xfe, 0x3d, + 0x0, 0x4c, 0xc0, 0x9, 0xee, 0xff, 0xf9, 0xa, + 0x0, 0x42, 0x1a, 0x0, 0xff, 0xe6, 0x95, 0x0, + 0x17, 0x10, 0x40, 0x3f, 0xf9, 0x4, 0xfa, 0x20, + + /* U+0031 "1" */ + 0x0, 0xfb, 0x3f, 0xf0, 0x80, 0x7a, 0x8c, 0x3, + 0xfc, 0xea, 0x1, 0xfe, 0x58, 0x0, 0xff, 0x1d, + 0x0, 0x52, 0x1, 0xe1, 0xd0, 0xa, 0xd4, 0x3, + 0xda, 0x20, 0x8, 0x40, 0xf, 0x51, 0x80, 0x19, + 0xc0, 0x3c, 0xea, 0x0, 0x39, 0x0, 0xf9, 0xfb, + 0xac, 0x0, 0xfe, 0x11, 0x80, 0x3f, 0xff, 0xe0, + 0x1f, 0xff, 0xf0, 0xf, 0xff, 0xf8, 0x7, 0xff, + 0x18, + + /* U+0032 "2" */ + 0x0, 0x1d, 0x77, 0xff, 0xff, 0x23, 0x60, 0x3, + 0x2e, 0x28, 0x80, 0x7f, 0xf2, 0x9, 0xf4, 0x40, + 0x68, 0x3, 0xff, 0x9a, 0x56, 0xc, 0x1, 0x45, + 0xdf, 0xff, 0x1e, 0x90, 0x0, 0xa3, 0xa0, 0x5, + 0x74, 0x4f, 0xfe, 0x3a, 0xd8, 0x4, 0xa2, 0x0, + 0x30, 0xf, 0xfe, 0x50, 0x80, 0x6, 0xb7, 0x50, + 0x1, 0xff, 0xce, 0x12, 0x20, 0x80, 0x7f, 0xff, + 0xc0, 0x3f, 0xff, 0xe0, 0x1f, 0xfc, 0xa1, 0x0, + 0x8, 0x6, 0x23, 0x44, 0xff, 0xe3, 0xd0, 0x4, + 0xa0, 0x7, 0xed, 0xcb, 0xbf, 0xfe, 0x3a, 0x80, + 0x14, 0x42, 0x60, 0x40, 0x3f, 0xf9, 0x65, 0x60, + 0x8c, 0x1, 0xff, 0xcb, 0x27, 0xd1, 0xd, 0x0, + 0xe, 0xff, 0xff, 0xf2, 0x36, 0x0, 0x22, 0x0, + 0x39, 0x0, 0x7f, 0xff, 0xc0, 0x3f, 0xff, 0xe0, + 0x1f, 0xfe, 0xe6, 0x62, 0x27, 0xff, 0x30, 0x40, + 0x34, 0xdd, 0xff, 0xf3, 0x10, 0x3, 0xff, 0xe0, + + /* U+0033 "3" */ + 0x0, 0x25, 0xf7, 0xff, 0xff, 0x1f, 0x14, 0x3, + 0x9e, 0xd0, 0x40, 0x3f, 0xf8, 0xe7, 0x50, 0x1, + 0x1c, 0x0, 0x7f, 0xf3, 0x1d, 0x0, 0x12, 0x1, + 0x4d, 0xdf, 0xff, 0x1a, 0x44, 0x1, 0xe0, 0x7, + 0x0, 0x3b, 0x22, 0x7f, 0xf1, 0x9a, 0x0, 0xa, + 0x1, 0xdc, 0x1, 0xff, 0xc8, 0x30, 0x0, 0x80, + 0x37, 0xfc, 0xc0, 0x1f, 0xff, 0xf0, 0xf, 0xff, + 0xf8, 0x7, 0xff, 0x20, 0xc0, 0x2, 0x1, 0xf0, + 0xa2, 0x7f, 0xf1, 0x56, 0x0, 0xa, 0x1, 0xf2, + 0x5d, 0xff, 0xf1, 0x68, 0x40, 0x3f, 0xfa, 0x88, + 0xc0, 0x1f, 0xfd, 0x1b, 0x0, 0xf2, 0xff, 0xff, + 0xf1, 0xdc, 0x0, 0x42, 0x1, 0xff, 0xcd, 0x90, + 0x8, 0x80, 0x3f, 0xf9, 0xa2, 0x1, 0xff, 0xff, + 0x0, 0xff, 0xf2, 0xb3, 0xb8, 0xc0, 0x3f, 0xf9, + 0xd7, 0x10, 0xb0, 0xf, 0xfe, 0x48, 0x80, 0x67, + 0x0, 0x33, 0x11, 0x3f, 0xf9, 0x14, 0x1, 0x1c, + 0x80, 0x6, 0x6e, 0xff, 0xf9, 0x8, 0x0, 0x60, + 0x38, 0x0, 0xff, 0xe6, 0x9c, 0x80, 0x1e, 0xd0, + 0x3, 0xff, 0x92, 0x51, 0x80, 0x0, + + /* U+0034 "4" */ + 0x0, 0xff, 0xe3, 0xcf, 0xfd, 0x20, 0x1f, 0xfc, + 0xbb, 0x60, 0xf, 0xfe, 0x68, 0xe2, 0x0, 0x7f, + 0xf3, 0x4b, 0xc, 0x3, 0xff, 0x9a, 0x78, 0x40, + 0x11, 0x0, 0x7f, 0xf2, 0x53, 0x4, 0x0, 0x5a, + 0xc0, 0x1f, 0xfc, 0x85, 0xb0, 0x8, 0xf0, 0x80, + 0x3f, 0xf9, 0xf, 0x40, 0x12, 0xe0, 0x80, 0x7f, + 0xf2, 0x26, 0x0, 0x27, 0xa0, 0xf, 0xfe, 0x4d, + 0x30, 0x5, 0x30, 0x1, 0xff, 0xc8, 0x1c, 0x50, + 0x0, 0xdb, 0x0, 0x7f, 0xf2, 0x7, 0xc, 0x0, + 0x58, 0x80, 0x1f, 0xfc, 0x82, 0xf2, 0x0, 0x1e, + 0x10, 0x7, 0xff, 0x20, 0xf0, 0x40, 0xb, 0x82, + 0x1, 0xff, 0xc8, 0x5c, 0x10, 0x3, 0xd0, 0x7, + 0xff, 0x25, 0xa8, 0x2, 0x98, 0x0, 0xff, 0xe4, + 0xc4, 0x80, 0x6, 0xd8, 0x3, 0xff, 0x93, 0xe, + 0x0, 0x2c, 0x40, 0xf, 0xfe, 0x52, 0x0, 0x45, + 0xbf, 0xff, 0xfc, 0x24, 0x0, 0x37, 0xfe, 0x10, + 0xf, 0xff, 0x77, 0xdd, 0xff, 0xf1, 0xcc, 0x0, + 0x97, 0x78, 0x4d, 0x13, 0xff, 0x8e, 0xe0, 0x2, + 0x44, 0xc0, 0x1f, 0xff, 0xf0, 0xf, 0xff, 0xf8, + 0x7, 0xff, 0x34, + + /* U+0035 "5" */ + 0xbf, 0xff, 0xfe, 0x82, 0x80, 0x7f, 0xfd, 0x22, + 0xef, 0xff, 0x98, 0x80, 0x12, 0xba, 0x27, 0xff, + 0x30, 0x40, 0x23, 0x0, 0xff, 0xff, 0x80, 0x7f, + 0xff, 0xc0, 0x3f, 0xfa, 0x26, 0x1, 0xff, 0xd1, + 0x57, 0x44, 0xff, 0xe3, 0x98, 0x80, 0x7e, 0x8b, + 0xbf, 0xfe, 0x3e, 0x76, 0x18, 0x7, 0xff, 0x40, + 0xf0, 0xc0, 0x3f, 0xfa, 0x3c, 0x17, 0xff, 0xff, + 0xcc, 0x80, 0x1, 0x18, 0x7, 0xff, 0x35, 0xc0, + 0x26, 0x0, 0xff, 0xe7, 0x8, 0x7, 0xff, 0xfc, + 0x3, 0xff, 0xc9, 0x5f, 0xe8, 0x0, 0xff, 0xe8, + 0x90, 0x7, 0xff, 0x28, 0x40, 0x22, 0x0, 0x2b, + 0x22, 0x7f, 0xf2, 0x28, 0x2, 0x7a, 0x0, 0xa6, + 0xef, 0xff, 0x90, 0xa0, 0x4, 0x22, 0x50, 0x7, + 0xff, 0x34, 0xa8, 0x0, 0xb8, 0x82, 0x1, 0xff, + 0xc8, 0x27, 0xd1, 0x0, + + /* U+0036 "6" */ + 0x0, 0x1d, 0x77, 0xff, 0xff, 0x1d, 0x40, 0x3c, + 0xb8, 0xa2, 0x1, 0xff, 0xcd, 0x1a, 0x0, 0xff, + 0xe8, 0x30, 0x5, 0x17, 0x7f, 0xfc, 0x74, 0x0, + 0xed, 0x0, 0x2b, 0xa2, 0x7f, 0xf1, 0xc4, 0x3, + 0x84, 0x0, 0x60, 0x1f, 0xff, 0xf0, 0xf, 0xff, + 0xf8, 0x7, 0xff, 0x44, 0xc0, 0x3f, 0xfa, 0x2a, + 0xe8, 0x9f, 0xfc, 0x73, 0x10, 0xf, 0xd1, 0x77, + 0xff, 0xc7, 0xce, 0xc3, 0x0, 0xff, 0xe8, 0x1e, + 0x20, 0x7, 0xff, 0x46, 0x40, 0x33, 0x7f, 0xff, + 0xf2, 0x64, 0x0, 0x46, 0x1, 0xff, 0xcd, 0x60, + 0x9, 0x80, 0x3f, 0xf9, 0xc2, 0x1, 0xff, 0xff, + 0x0, 0xff, 0xff, 0x80, 0x42, 0x0, 0x30, 0xf, + 0xfe, 0x50, 0x80, 0x44, 0x0, 0x56, 0x44, 0xff, + 0xe4, 0x50, 0x4, 0xf2, 0x1, 0x4d, 0xdf, 0xff, + 0x21, 0x40, 0x8, 0x43, 0x40, 0x1f, 0xfc, 0xd2, + 0xa0, 0x2, 0xe2, 0x8, 0x7, 0xff, 0x20, 0x9f, + 0x4, 0x0, + + /* U+0037 "7" */ + 0xef, 0xff, 0xfe, 0x3e, 0xb0, 0x7, 0xff, 0x2c, + 0xa6, 0xc0, 0x3f, 0xf9, 0x88, 0xe1, 0x57, 0x7f, + 0xfc, 0x6a, 0x20, 0x4, 0x82, 0x27, 0xff, 0x1d, + 0x78, 0x0, 0x22, 0x0, 0xff, 0xe4, 0x8, 0x4, + 0x60, 0x1f, 0xff, 0xf0, 0xf, 0xff, 0xf8, 0x7, + 0xff, 0xfc, 0x3, 0xff, 0xfe, 0x1, 0xff, 0xff, + 0x0, 0xff, 0xff, 0x80, 0x7f, 0xff, 0xc0, 0x3f, + 0xfc, 0xc0, + + /* U+0038 "8" */ + 0x0, 0x1d, 0x77, 0xff, 0xff, 0x23, 0x58, 0x3, + 0x2e, 0x28, 0x80, 0x7f, 0xf2, 0xa, 0x6c, 0x0, + 0x34, 0x1, 0xff, 0xcd, 0x49, 0x6, 0x0, 0x9e, + 0xab, 0xff, 0x90, 0x60, 0x6, 0x2d, 0x0, 0x2c, + 0x2a, 0xff, 0xf2, 0x30, 0x2, 0x71, 0x0, 0x18, + 0x7, 0xff, 0x28, 0x40, 0x3f, 0xff, 0xe0, 0x1f, + 0xff, 0xf0, 0x8, 0x40, 0x3f, 0xf9, 0xa2, 0x0, + 0x1d, 0x0, 0x20, 0x80, 0x7f, 0xf2, 0x10, 0x2, + 0x57, 0x10, 0x2e, 0xdd, 0xff, 0xe4, 0x58, 0x1, + 0x44, 0x10, 0x2, 0x22, 0xff, 0xe4, 0x0, 0x5e, + 0x4, 0xa0, 0x1f, 0xfc, 0xe8, 0xb, 0x0, 0xe, + 0xff, 0xff, 0xf2, 0x20, 0x0, 0x46, 0x40, 0x7, + 0x20, 0xf, 0xfe, 0x43, 0x80, 0x4c, 0x1, 0xff, + 0xce, 0x10, 0xf, 0xff, 0xf8, 0x7, 0xff, 0xfc, + 0x2, 0x10, 0x1, 0x80, 0x7f, 0xf2, 0x84, 0x2, + 0x20, 0x2, 0xb2, 0x27, 0xff, 0x22, 0x80, 0x27, + 0xa0, 0xa, 0x6e, 0xff, 0xf9, 0xa, 0x0, 0x42, + 0x25, 0x0, 0x7f, 0xf3, 0x4a, 0x80, 0xb, 0x88, + 0x20, 0x1f, 0xfc, 0x82, 0x7d, 0x10, + + /* U+0039 "9" */ + 0x0, 0x26, 0x7f, 0xff, 0xf2, 0x75, 0x80, 0x33, + 0xd9, 0x80, 0x7f, 0xf2, 0x4a, 0x6c, 0x0, 0x90, + 0x1, 0xff, 0xcd, 0x47, 0xf, 0x0, 0xc, 0xdd, + 0xff, 0xf1, 0xe8, 0x80, 0x12, 0xa, 0x0, 0x96, + 0x44, 0xff, 0xe3, 0xaf, 0x0, 0x4, 0x60, 0x1, + 0x0, 0x7f, 0xf2, 0x44, 0x2, 0x30, 0xf, 0xff, + 0xf8, 0x7, 0xff, 0xfc, 0x3, 0xff, 0xa4, 0x20, + 0x2, 0x0, 0xff, 0xe7, 0x28, 0x2, 0x55, 0x13, + 0xff, 0x90, 0xe0, 0x1b, 0xc0, 0x3, 0x57, 0x7f, + 0xfc, 0x8a, 0x0, 0xc8, 0xe0, 0x1f, 0xfd, 0x18, + 0xb3, 0x0, 0xff, 0xe8, 0x26, 0x7f, 0xff, 0xf2, + 0x58, 0x3, 0xff, 0xa3, 0x60, 0x1f, 0xff, 0xf0, + 0xf, 0xff, 0xf8, 0x7, 0xff, 0x44, 0x40, 0x23, + 0x24, 0x4f, 0xfe, 0x67, 0x80, 0x4, 0x4f, 0x77, + 0xff, 0xcc, 0x30, 0x4, 0x82, 0xb0, 0x7, 0xff, + 0x35, 0x1c, 0x1, 0x34, 0x60, 0x1f, 0xfc, 0x92, + 0x9b, 0x0, 0x0, + + /* U+003A ":" */ + 0xdf, 0xf3, 0x80, 0x7e, 0xeb, 0xb3, 0x1a, 0x20, + 0x40, 0x3f, 0xfd, 0x86, 0x88, 0x1e, 0xbb, 0x30, + 0x7, 0xe0, + + /* U+003B ";" */ + 0xff, 0x94, 0x3, 0xf6, 0x5d, 0x90, 0xd1, 0x2, + 0x1, 0xff, 0xf0, 0xbc, 0xc1, 0xa1, 0x99, 0x80, + 0x3f, 0xe1, 0x0, 0x94, 0x0, 0xa2, 0x5, 0x61, + 0x1a, 0x20, + + /* U+003C "<" */ + 0x0, 0xff, 0xe1, 0xb1, 0x80, 0x7f, 0xf0, 0x53, + 0x65, 0xc0, 0x3f, 0xe2, 0xab, 0x20, 0xf, 0xfe, + 0x4, 0x6a, 0x80, 0x7f, 0xf0, 0x1b, 0x9c, 0x2, + 0x1a, 0x40, 0xf, 0x26, 0xc8, 0x80, 0x4f, 0xca, + 0x1, 0xc5, 0x76, 0x20, 0x9, 0x7a, 0x0, 0x3d, + 0x1a, 0x80, 0x11, 0xe5, 0x8, 0x7, 0x37, 0x38, + 0x4, 0x33, 0x86, 0x1, 0xeb, 0x91, 0x0, 0x9f, + 0x98, 0x3, 0xf3, 0x80, 0x4b, 0xb0, 0x1, 0xff, + 0xc1, 0x28, 0x20, 0xf, 0xfe, 0x11, 0x6c, 0x88, + 0x7, 0xfa, 0x44, 0x2, 0x6f, 0x60, 0xf, 0xe5, + 0xe8, 0x0, 0x86, 0x75, 0x0, 0x3f, 0x9f, 0x94, + 0x2, 0x2b, 0xb1, 0x0, 0x7e, 0x1a, 0xc3, 0x0, + 0x93, 0x64, 0x40, 0x3f, 0x1e, 0x50, 0x80, 0x4d, + 0xec, 0x1, 0xfc, 0xbd, 0x0, 0x10, 0xce, 0x90, + 0x7, 0xf3, 0xf2, 0x80, 0x44, 0xc0, 0x1f, 0xe1, + 0xac, 0x30, 0xf, 0xfe, 0x19, 0xe5, 0x8, 0x7, + 0xff, 0xd, 0x79, 0x40, + + /* U+003D "=" */ + 0x24, 0x4f, 0xfe, 0x40, 0x45, 0xdf, 0xff, 0x20, + 0x3, 0xff, 0xc3, 0x5f, 0xff, 0xfc, 0x80, 0xf, + 0xff, 0xb9, 0x22, 0x7f, 0xf2, 0x2, 0x2e, 0xff, + 0xf9, 0x0, 0x1f, 0xfe, 0x10, + + /* U+003E ">" */ + 0x52, 0x0, 0xff, 0xe1, 0xfe, 0xc0, 0x7, 0xff, + 0x11, 0xf9, 0x80, 0x3f, 0xf8, 0x63, 0x3a, 0x80, + 0x1f, 0xeb, 0x50, 0x8, 0xae, 0xc4, 0x1, 0xf8, + 0x6b, 0xc, 0x2, 0x4d, 0x80, 0xf, 0xe3, 0xc9, + 0x10, 0x9, 0xf9, 0x80, 0x3f, 0x9b, 0x9c, 0x2, + 0x19, 0xd4, 0x0, 0xfe, 0x8d, 0x50, 0x8, 0xae, + 0xc4, 0x1, 0xf8, 0xab, 0xc, 0x2, 0x4d, 0x50, + 0xf, 0xe3, 0xc9, 0x10, 0x8, 0x80, 0x3f, 0xe6, + 0xb0, 0xf, 0xfe, 0x11, 0x5d, 0x0, 0x7f, 0xf0, + 0x6, 0x35, 0x0, 0x23, 0x50, 0xf, 0x9b, 0xdc, + 0x2, 0x1a, 0xc2, 0x0, 0xe4, 0xd9, 0x10, 0x9, + 0xf9, 0x40, 0x38, 0xae, 0xc4, 0x1, 0x2f, 0x40, + 0x7, 0xa3, 0x50, 0x2, 0x3c, 0xa1, 0x0, 0xe5, + 0xe7, 0x0, 0x86, 0xb0, 0xc0, 0x3e, 0xf1, 0x0, + 0x9f, 0x94, 0x3, 0xff, 0x80, 0xbd, 0x0, 0x1f, + 0xfc, 0x3, 0xca, 0x10, 0xf, 0xfe, 0x4, 0x61, + 0x80, 0x7f, 0xf0, 0x80, + + /* U+003F "?" */ + 0xcf, 0xff, 0xfe, 0x47, 0x51, 0x0, 0x7f, 0xf2, + 0xc5, 0x75, 0x40, 0x3f, 0xf9, 0xb4, 0x3f, 0x77, + 0xff, 0xc8, 0x80, 0x9, 0x8d, 0x13, 0xff, 0x90, + 0xec, 0x0, 0xc0, 0xf, 0xfe, 0x50, 0x80, 0x1c, + 0x3, 0xff, 0xfe, 0x1, 0xff, 0xff, 0x0, 0xff, + 0xf5, 0x8, 0x1, 0xc0, 0x3f, 0x8d, 0x13, 0xfe, + 0x66, 0x0, 0xc, 0x3, 0xe8, 0xfc, 0xbb, 0xff, + 0xa4, 0x2, 0x90, 0xf, 0x5b, 0x80, 0x7f, 0xf1, + 0x28, 0x40, 0x39, 0x90, 0x3, 0xff, 0x84, 0x2b, + 0x8a, 0x1, 0xed, 0x0, 0x17, 0x7f, 0xff, 0xdd, + 0x46, 0x1, 0xf3, 0x0, 0x2c, 0x40, 0x3f, 0xf9, + 0x26, 0x1, 0xff, 0xcd, 0xfd, 0xd3, 0x0, 0x7f, + 0xf2, 0x84, 0x88, 0x1, 0xff, 0xff, 0x34, 0x40, + 0x80, 0x7f, 0xf2, 0xb6, 0xec, 0xe0, 0x1f, 0xff, + 0x90, + + /* U+0040 "@" */ + 0x0, 0x25, 0xf7, 0xff, 0xff, 0x23, 0x5c, 0x3, + 0x3d, 0xa0, 0x80, 0x7f, 0xf2, 0xa, 0x30, 0x0, + 0x70, 0x1, 0xff, 0xcd, 0x38, 0xa, 0x0, 0xa6, + 0xef, 0xff, 0x8f, 0x46, 0x0, 0x70, 0x50, 0x3, + 0xb2, 0x27, 0xff, 0x1d, 0x78, 0x2, 0x21, 0x0, + 0x78, 0x7, 0xff, 0x24, 0x80, 0x21, 0x0, 0xff, + 0xfd, 0x9b, 0x37, 0x20, 0x7, 0xff, 0x24, 0xb7, + 0x26, 0x7a, 0xfd, 0x80, 0x3f, 0xf8, 0xc3, 0x84, + 0x1, 0xfa, 0x58, 0x3, 0xff, 0x8b, 0x62, 0x5d, + 0xff, 0xe7, 0x9, 0x10, 0xf, 0xfe, 0x22, 0x5, + 0x88, 0x7, 0xa0, 0x0, 0x80, 0x1f, 0xfc, 0x41, + 0x0, 0xff, 0xe0, 0x18, 0x7, 0xff, 0x44, 0x40, + 0x3f, 0xff, 0xe0, 0x1f, 0xfd, 0x35, 0xa, 0x10, + 0xf, 0xfe, 0x74, 0x1, 0xf7, 0xff, 0xc0, 0x8, + 0xff, 0xc0, 0x1f, 0xfc, 0x2, 0xc2, 0x0, 0xff, + 0xe8, 0x1e, 0xdc, 0xc4, 0x7f, 0xf1, 0x8, 0x3, + 0xfe, 0x46, 0x77, 0xff, 0xe2, 0x8, 0x7, 0xff, + 0x48, 0x40, 0x1e, 0x1, 0xff, 0xce, 0x50, 0x3, + 0x31, 0x13, 0xff, 0x98, 0x14, 0x0, 0x19, 0xbb, + 0xff, 0xe6, 0x19, 0xa0, 0x3, 0xff, 0xa2, 0xf6, + 0x80, 0x1f, 0xfc, 0xe0, + + /* U+0041 "A" */ + 0x0, 0x1d, 0xf7, 0xff, 0xff, 0x23, 0x5c, 0x3, + 0x2e, 0x20, 0x80, 0x7f, 0xf2, 0xa, 0x2c, 0x0, + 0x34, 0x1, 0xff, 0xcd, 0x47, 0x6, 0x0, 0xa7, + 0x33, 0xff, 0x8f, 0x66, 0x0, 0x90, 0xd0, 0x2, + 0xb1, 0x9f, 0xff, 0x1d, 0x3c, 0x0, 0x23, 0x0, + 0xc, 0x3, 0xff, 0x92, 0x20, 0x11, 0x80, 0x7f, + 0xff, 0xc0, 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0, + 0xf, 0xff, 0x23, 0x7f, 0xff, 0xf2, 0x74, 0x3, + 0xff, 0xee, 0xb9, 0x9f, 0xfc, 0x9a, 0x0, 0xf8, + 0xcf, 0xff, 0x94, 0xe0, 0x1f, 0xff, 0xf0, 0xf, + 0xff, 0xf8, 0x7, 0xff, 0xfc, 0x3, 0xff, 0xca, + + /* U+0042 "B" */ + 0xaf, 0xff, 0xfe, 0x5e, 0x28, 0x7, 0xff, 0x40, + 0xea, 0x0, 0x3f, 0xfa, 0x2e, 0x80, 0x1e, 0x9c, + 0xcf, 0xfe, 0x35, 0x88, 0x3, 0xc0, 0x39, 0x58, + 0xcf, 0xff, 0x8c, 0x90, 0x0, 0x60, 0xe, 0x30, + 0xf, 0xfe, 0x41, 0x80, 0xc, 0x3, 0xff, 0xfe, + 0x1, 0xff, 0xff, 0x0, 0xe3, 0x0, 0xff, 0xe4, + 0x18, 0x7, 0xe5, 0x63, 0x3f, 0xfe, 0x32, 0x40, + 0x1, 0x40, 0x3d, 0x39, 0x9f, 0xfc, 0x6b, 0x10, + 0x1, 0x80, 0x7f, 0xf4, 0x5c, 0x80, 0x3f, 0xfa, + 0x30, 0x1, 0x87, 0x3f, 0xff, 0xf9, 0xc, 0x0, + 0x50, 0xc, 0xe6, 0x1, 0xff, 0xc8, 0x80, 0x8, + 0x80, 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0xf, + 0xfe, 0xa9, 0x80, 0x7f, 0xf2, 0x4c, 0x2, 0x30, + 0x9, 0x58, 0xcf, 0xff, 0x8e, 0x92, 0x1, 0x8, + 0x6, 0x9c, 0xcf, 0xfe, 0x3d, 0xa0, 0x1, 0xc0, + 0x3f, 0xfa, 0x7, 0x0, 0x1f, 0xfc, 0xe2, 0x8c, + 0x0, 0x0, + + /* U+0043 "C" */ + 0x0, 0x25, 0xf7, 0xff, 0xff, 0x30, 0x1a, 0xd0, + 0x40, 0x3f, 0xf9, 0x85, 0x20, 0x1f, 0xfc, 0xfa, + 0x0, 0xab, 0x33, 0xff, 0x98, 0x60, 0x7, 0x53, + 0x3f, 0xfe, 0x63, 0x80, 0x3c, 0x3, 0xff, 0xfe, + 0x1, 0xff, 0xff, 0x0, 0xff, 0xff, 0x80, 0x7f, + 0xff, 0xc0, 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0, + 0xf, 0xff, 0xf8, 0x7, 0xff, 0x29, 0xc0, 0x1e, + 0x1, 0xff, 0xcd, 0x30, 0x3, 0xa9, 0x9f, 0xff, + 0x32, 0x80, 0x2a, 0xcc, 0xff, 0xe6, 0x14, 0x80, + 0x7f, 0xf4, 0x1a, 0xd0, 0x40, 0x3f, 0xf9, 0x80, + + /* U+0044 "D" */ + 0xbf, 0xff, 0xfe, 0x66, 0xb8, 0x7, 0xff, 0x40, + 0xa2, 0xc0, 0x3f, 0xfa, 0x28, 0xe0, 0x1d, 0x39, + 0x9f, 0xfc, 0x7b, 0x30, 0x4, 0x80, 0x65, 0x63, + 0x3f, 0xfe, 0x3a, 0x78, 0x0, 0x44, 0x1, 0x18, + 0x7, 0xff, 0x24, 0x40, 0x23, 0x0, 0xff, 0xff, + 0x80, 0x7f, 0xff, 0xc0, 0x3f, 0xff, 0xe0, 0x1f, + 0xff, 0xf0, 0xf, 0xff, 0xf8, 0x7, 0xff, 0xfc, + 0x3, 0xff, 0xfe, 0x1, 0xff, 0xc6, 0x30, 0xf, + 0xfe, 0x48, 0x80, 0x46, 0x1, 0x2b, 0x19, 0xff, + 0xf1, 0xd3, 0xc0, 0x2, 0x20, 0xd, 0x39, 0x9f, + 0xfc, 0x7b, 0x30, 0x3, 0x0, 0x7f, 0xf4, 0x12, + 0x0, 0x3f, 0xf9, 0xc5, 0x16, 0x0, + + /* U+0045 "E" */ + 0xbf, 0xff, 0xfe, 0x6c, 0x80, 0x7f, 0xfb, 0x57, + 0x33, 0xff, 0x95, 0x0, 0x11, 0x9f, 0xff, 0x2c, + 0x40, 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0xf, + 0xff, 0xcb, 0x7f, 0xff, 0xf1, 0x84, 0x3, 0xff, + 0xe2, 0xb7, 0x7f, 0xfc, 0x61, 0x0, 0xf8, 0xd1, + 0x3f, 0xf8, 0xc0, 0x1f, 0xff, 0xf0, 0xf, 0xff, + 0xf8, 0x7, 0xff, 0x8c, 0xcf, 0xff, 0x96, 0x40, + 0x12, 0xe6, 0x7f, 0xf2, 0xac, 0x3, 0xff, 0xd4, + + /* U+0046 "F" */ + 0xbf, 0xff, 0xfe, 0x6c, 0x80, 0x7f, 0xfb, 0x57, + 0x33, 0xff, 0x95, 0x0, 0x11, 0x9f, 0xff, 0x2c, + 0x40, 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0xf, + 0xff, 0xcb, 0x7f, 0xff, 0xf1, 0x84, 0x3, 0xff, + 0xe2, 0xb7, 0x7f, 0xfc, 0x61, 0x0, 0xf8, 0xd1, + 0x3f, 0xf8, 0xc0, 0x1f, 0xff, 0xf0, 0xf, 0xff, + 0xf8, 0x7, 0xff, 0xfc, 0x3, 0xff, 0xfe, 0x0, + + /* U+0047 "G" */ + 0x0, 0x25, 0xf7, 0xff, 0xff, 0x23, 0x18, 0x3, + 0x35, 0xa0, 0x80, 0x7f, 0xf2, 0xe, 0x68, 0x0, + 0x52, 0x1, 0xff, 0xcd, 0x56, 0xa, 0x0, 0xab, + 0x33, 0xff, 0x8f, 0x64, 0x0, 0xb0, 0x30, 0x3, + 0xa9, 0x9f, 0xff, 0x1d, 0x24, 0x0, 0x40, 0xe0, + 0xf, 0x0, 0xff, 0xe4, 0xb8, 0x7, 0xff, 0x44, + 0xff, 0xe0, 0xf, 0xff, 0xf8, 0x7, 0xff, 0xfc, + 0x3, 0xff, 0xfe, 0x1, 0x97, 0xff, 0xfc, 0x1, + 0xff, 0xff, 0x0, 0xc9, 0x77, 0xe9, 0x0, 0xff, + 0xe5, 0xa, 0x27, 0x94, 0x3, 0xff, 0xfe, 0x1, + 0xff, 0xff, 0x0, 0xce, 0x0, 0xf0, 0xf, 0xfe, + 0x4b, 0x80, 0x63, 0x0, 0x3a, 0x99, 0xff, 0xf1, + 0xd2, 0x40, 0x4, 0x14, 0x1, 0x56, 0x67, 0xff, + 0x1e, 0xc8, 0x1, 0x40, 0x52, 0x1, 0xff, 0xcd, + 0x47, 0x0, 0x35, 0xa0, 0x80, 0x7f, 0xf2, 0xe, + 0x6c, 0x0, + + /* U+0048 "H" */ + 0xbf, 0xf4, 0x0, 0x7f, 0xf2, 0x9f, 0xfd, 0x80, + 0x1f, 0xff, 0xf0, 0xf, 0xff, 0xf8, 0x7, 0xff, + 0xfc, 0x3, 0xff, 0xfe, 0x1, 0xff, 0xf3, 0x7f, + 0xff, 0xfe, 0x54, 0x0, 0x7f, 0xfd, 0x96, 0xef, + 0xff, 0x94, 0xc0, 0x1e, 0x24, 0x4f, 0xfe, 0x57, + 0x0, 0x7f, 0xff, 0xc0, 0x3f, 0xff, 0xe0, 0x1f, + 0xff, 0xf0, 0xf, 0xff, 0xf8, 0x7, 0xff, 0x54, + + /* U+0049 "I" */ + 0x5f, 0xf7, 0x0, 0x7f, 0xff, 0xc0, 0x3f, 0xfb, + 0x20, + + /* U+004A "J" */ + 0x0, 0xff, 0xe6, 0xa7, 0xfc, 0x1, 0xff, 0xff, + 0x0, 0xff, 0xff, 0x80, 0x7f, 0xff, 0xc0, 0x3f, + 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0xf, 0xff, 0xf8, + 0x7, 0xff, 0xfc, 0x3, 0xff, 0xd6, 0x23, 0x0, + 0x7f, 0xf3, 0xb3, 0xb8, 0xc0, 0x1f, 0xfe, 0xa1, + 0x0, 0x8, 0x7, 0xff, 0x24, 0x40, 0x2, 0xc0, + 0x9, 0x53, 0x3f, 0xfe, 0x3a, 0x40, 0x1, 0x64, + 0x0, 0x35, 0x99, 0xff, 0xc7, 0xb2, 0x0, 0x71, + 0xc0, 0x7, 0xff, 0x35, 0x94, 0x1e, 0xd0, 0x3, + 0xff, 0x92, 0x75, 0x20, + + /* U+004B "K" */ + 0xbf, 0xf4, 0x80, 0x7f, 0xf1, 0xf3, 0xfe, 0x50, + 0xf, 0xfe, 0x55, 0x18, 0x1, 0xd4, 0x3, 0xff, + 0x93, 0xa, 0x0, 0x58, 0x0, 0xff, 0xe4, 0xab, + 0x80, 0xa, 0x80, 0x3f, 0xf9, 0x27, 0x40, 0x1, + 0xc0, 0xf, 0xfe, 0x48, 0xe8, 0x5, 0xa2, 0x1, + 0xff, 0xc9, 0xd1, 0x0, 0x49, 0x80, 0x7f, 0xf2, + 0x64, 0xc0, 0xe, 0xc0, 0x1f, 0xfc, 0x97, 0x60, + 0x2, 0x40, 0x7, 0xff, 0x25, 0x20, 0x0, 0x54, + 0x1, 0xff, 0xc9, 0x2b, 0x0, 0xf, 0x88, 0x7, + 0xff, 0x20, 0x7c, 0x2, 0xa2, 0x0, 0xff, 0xe4, + 0xd1, 0x0, 0x21, 0x0, 0x3f, 0xf8, 0xd, 0xff, + 0xfc, 0x80, 0x6, 0x70, 0xf, 0xfe, 0x68, 0xc8, + 0x7, 0xff, 0x38, 0x58, 0x3, 0xff, 0x84, 0xb7, + 0x7f, 0xc4, 0x0, 0x94, 0x0, 0xff, 0xe0, 0x9a, + 0x27, 0xf7, 0x80, 0x56, 0x40, 0x1f, 0xfc, 0xa2, + 0xb0, 0xb, 0xc4, 0x3, 0xff, 0x94, 0x92, 0x0, + 0x2d, 0x0, 0xff, 0xe5, 0xb3, 0x0, 0x7, 0x20, + 0x1f, 0xfc, 0xb9, 0x40, 0x3, 0x38, 0x7, 0xff, + 0x2e, 0x88, 0x1, 0x8, 0x1, 0xff, 0xca, 0x1f, + 0x10, 0x5, 0x98, 0x7, 0xff, 0x28, 0xb4, 0x2, + 0xd1, 0x0, 0xff, 0xe5, 0x1c, 0x80, 0x7, 0x40, + 0x3f, 0xf9, 0x6c, 0xe0, 0x3, 0xa0, 0xf, 0xfe, + 0x5c, 0x20, 0x1, 0x60, 0x3, 0xff, 0x97, 0x66, + 0x0, 0x75, + + /* U+004C "L" */ + 0xbf, 0xf4, 0x0, 0x7f, 0xff, 0xc0, 0x3f, 0xff, + 0xe0, 0x1f, 0xff, 0xf0, 0xf, 0xff, 0xf8, 0x7, + 0xff, 0xfc, 0x3, 0xff, 0xfe, 0x1, 0xff, 0xff, + 0x0, 0xff, 0xff, 0x80, 0x7f, 0xff, 0xc0, 0x3f, + 0xff, 0xc2, 0x67, 0xff, 0xcd, 0x0, 0xcd, 0x99, + 0xff, 0xcd, 0x10, 0xf, 0xff, 0x80, + + /* U+004D "M" */ + 0xcf, 0xfc, 0x40, 0x1f, 0xfc, 0xac, 0xff, 0xc0, + 0x1e, 0xf1, 0x0, 0xff, 0xe4, 0x51, 0x80, 0x7f, + 0x8a, 0x80, 0x3f, 0xf8, 0xee, 0xa0, 0x1f, 0xfc, + 0x4, 0x90, 0xf, 0xfe, 0x2a, 0xc0, 0x7, 0xf9, + 0xcc, 0x0, 0xcc, 0x0, 0xff, 0xe1, 0x9d, 0x0, + 0x52, 0x1, 0xf7, 0xe8, 0x80, 0x25, 0x0, 0x3f, + 0xf8, 0x23, 0xa0, 0x15, 0xa8, 0x7, 0xe1, 0xd0, + 0xa, 0xc8, 0x3, 0xff, 0x81, 0xa2, 0x0, 0x84, + 0x0, 0xff, 0x1d, 0x0, 0x5e, 0x20, 0x1f, 0xea, + 0x30, 0x2, 0xb8, 0x7, 0xff, 0x1, 0x5c, 0x0, + 0x54, 0x1, 0xfc, 0xea, 0x0, 0x3a, 0x0, 0xff, + 0xe1, 0x42, 0x80, 0x12, 0x40, 0x3e, 0x58, 0x0, + 0x16, 0x80, 0x7f, 0xf1, 0x28, 0xc0, 0xc, 0xc0, + 0xe, 0x3a, 0x0, 0xbc, 0x40, 0x3f, 0xf8, 0xba, + 0x20, 0x9, 0x40, 0x8, 0x74, 0x2, 0xb2, 0x0, + 0xff, 0xe3, 0xe, 0x80, 0x56, 0x40, 0xd, 0x10, + 0x4, 0x20, 0x7, 0xff, 0x20, 0xe8, 0x2, 0xf1, + 0xa3, 0x0, 0x33, 0x80, 0x7f, 0xf2, 0x95, 0xc0, + 0x5, 0xea, 0x0, 0x39, 0x0, 0xff, 0xe6, 0x42, + 0x80, 0x4, 0x0, 0x5a, 0x1, 0xff, 0xce, 0xa3, + 0x0, 0xde, 0x20, 0x1f, 0xfc, 0xfd, 0x10, 0x5, + 0x90, 0x7, 0xff, 0x40, 0x74, 0x25, 0x0, 0x3f, + 0xfa, 0x47, 0xac, 0x1, 0xff, 0xd4, 0x20, 0xf, + 0xff, 0xf8, 0x7, 0xff, 0xfc, 0x3, 0xff, 0xfe, + 0x1, 0xff, 0xf4, + + /* U+004E "N" */ + 0xcf, 0xfc, 0x40, 0x1f, 0xfc, 0x73, 0xff, 0x80, + 0x3d, 0xe2, 0x1, 0xff, 0xd0, 0x2d, 0x0, 0xff, + 0xe8, 0x9c, 0x80, 0x7f, 0xf3, 0x5c, 0xc0, 0xc, + 0xe0, 0x1f, 0xfc, 0xcf, 0xd1, 0x0, 0x42, 0x0, + 0x7f, 0xf3, 0x7, 0x40, 0x2b, 0x20, 0xf, 0xfe, + 0x61, 0xd0, 0x5, 0xe2, 0x1, 0xff, 0xcc, 0x57, + 0x0, 0x16, 0x80, 0x7f, 0xf3, 0x61, 0x40, 0x7, + 0x20, 0x1f, 0xfc, 0xda, 0x30, 0x3, 0x38, 0x7, + 0xff, 0x37, 0x44, 0x1, 0x8, 0x1, 0xff, 0xcc, + 0x1d, 0x0, 0xac, 0x80, 0x3f, 0xf9, 0x87, 0x40, + 0x17, 0x88, 0x7, 0xff, 0x31, 0x5c, 0x0, 0x5a, + 0x1, 0xff, 0xcd, 0x85, 0x0, 0x1c, 0x80, 0x7f, + 0xf3, 0x68, 0xc0, 0xc, 0xe0, 0x1f, 0xfc, 0xdd, + 0x10, 0x4, 0x20, 0x7, 0xff, 0x30, 0x74, 0x2, + 0xb2, 0x0, 0xff, 0xe6, 0x1d, 0x0, 0x5e, 0x20, + 0x1f, 0xfc, 0xc5, 0x70, 0x1, 0x68, 0x7, 0xff, + 0x36, 0x14, 0x0, 0x74, 0x1, 0xff, 0xcd, 0xa3, + 0x0, 0x2b, 0x80, 0x7f, 0xf3, 0x74, 0x40, 0x10, + 0x80, 0x1f, 0xfc, 0xc1, 0xd0, 0xa, 0xd4, 0x3, + 0xff, 0x98, 0x74, 0x1, 0x48, 0x7, 0xff, 0x35, + 0x60, 0x3, 0xff, 0xa2, 0xea, 0x1, 0xff, 0xd1, + 0xa3, 0x0, 0xe0, + + /* U+004F "O" */ + 0x0, 0x25, 0xff, 0xff, 0xf2, 0x71, 0x40, 0x27, + 0xb4, 0x0, 0xff, 0xe4, 0x9d, 0x48, 0x1c, 0x0, + 0x7f, 0xf3, 0x59, 0x64, 0x0, 0x35, 0x99, 0xff, + 0xc7, 0xb2, 0x0, 0x73, 0x0, 0x25, 0x4c, 0xff, + 0xf8, 0xe9, 0x0, 0x5, 0x10, 0x0, 0x80, 0x7f, + 0xf2, 0x44, 0x0, 0x20, 0x1f, 0xff, 0xf0, 0xf, + 0xff, 0xf8, 0x7, 0xff, 0xfc, 0x3, 0xff, 0xfe, + 0x1, 0xff, 0xff, 0x0, 0xff, 0xff, 0x80, 0x7f, + 0xff, 0x4, 0x0, 0x20, 0x1f, 0xfc, 0x91, 0x0, + 0xb, 0x0, 0x25, 0x4c, 0xff, 0xf8, 0xe9, 0x0, + 0x5, 0x90, 0x0, 0xd6, 0x67, 0xff, 0x1e, 0xc8, + 0x1, 0xc7, 0x0, 0x1f, 0xfc, 0xd6, 0x50, 0x7b, + 0x40, 0xf, 0xfe, 0x49, 0xd4, 0x80, + + /* U+0050 "P" */ + 0xcf, 0xff, 0xfe, 0x66, 0x30, 0x7, 0xff, 0x40, + 0xe6, 0x80, 0x3f, 0xfa, 0x2a, 0xc0, 0x1d, 0x39, + 0x9f, 0xfc, 0x7b, 0x20, 0x5, 0x80, 0x67, 0x63, + 0x3f, 0xfe, 0x3a, 0x48, 0x0, 0x80, 0x37, 0x80, + 0x7f, 0xf2, 0x5c, 0x3, 0xff, 0xfe, 0x1, 0xff, + 0xff, 0x0, 0xff, 0xfb, 0xf8, 0x7, 0xff, 0x45, + 0x48, 0x3, 0xff, 0x8e, 0x32, 0x0, 0x10, 0xc, + 0x5b, 0xff, 0xff, 0xc7, 0xe5, 0x0, 0x30, 0x7, + 0xff, 0x40, 0xa0, 0x3, 0xff, 0x9e, 0xba, 0x1, + 0xcd, 0x77, 0xff, 0xc8, 0xce, 0xa0, 0xf, 0x72, + 0x27, 0xff, 0x20, 0xc4, 0x3, 0xff, 0xfe, 0x1, + 0xff, 0xff, 0x0, 0xff, 0xff, 0x80, 0x7f, 0xf9, + 0x40, + + /* U+0051 "Q" */ + 0x0, 0x25, 0xff, 0xff, 0xf2, 0x71, 0x40, 0x3e, + 0x7b, 0x40, 0xf, 0xfe, 0x49, 0xd4, 0x80, 0x71, + 0xc0, 0x7, 0xff, 0x35, 0x94, 0x3, 0x48, 0x0, + 0x6b, 0x33, 0xff, 0x8f, 0x64, 0x0, 0xe0, 0xc, + 0xc0, 0x9, 0x53, 0x3f, 0xfe, 0x3a, 0x40, 0x1, + 0x40, 0x30, 0x80, 0x4, 0x3, 0xff, 0x92, 0x20, + 0x1, 0x0, 0xff, 0xff, 0x80, 0x7f, 0xff, 0xc0, + 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0xf, 0xff, + 0xf8, 0x7, 0xff, 0xfc, 0x3, 0xff, 0xfe, 0x1, + 0xff, 0xfd, 0x10, 0x0, 0x80, 0x7f, 0xf2, 0x44, + 0x3, 0xe6, 0x0, 0x4a, 0x99, 0xff, 0xf1, 0xd2, + 0x0, 0x23, 0x38, 0xa4, 0x0, 0x35, 0x99, 0xff, + 0xc7, 0xb2, 0x0, 0xb3, 0x29, 0x38, 0x0, 0xff, + 0xea, 0x3d, 0xa0, 0x7, 0xff, 0x44, + + /* U+0052 "R" */ + 0xcf, 0xff, 0xfe, 0x66, 0x30, 0x7, 0xff, 0x40, + 0xe6, 0x80, 0x3f, 0xfa, 0x2a, 0xc0, 0x1d, 0x39, + 0x9f, 0xfc, 0x7b, 0x20, 0x5, 0x80, 0x67, 0x63, + 0x3f, 0xfe, 0x3a, 0x48, 0x0, 0x80, 0x37, 0x80, + 0x7f, 0xf2, 0x5c, 0x3, 0xff, 0xfe, 0x1, 0xff, + 0xff, 0x0, 0xff, 0xfb, 0xf8, 0x7, 0xff, 0x45, + 0x48, 0x3, 0xff, 0x8e, 0x32, 0x0, 0x10, 0xc, + 0x5b, 0xff, 0xff, 0xc7, 0xe5, 0x0, 0x30, 0x7, + 0xff, 0x40, 0xa0, 0x3, 0xff, 0x9e, 0xba, 0x1, + 0xcd, 0x77, 0xff, 0x38, 0x6, 0xab, 0xbb, 0x3a, + 0x80, 0x3d, 0xc8, 0x9f, 0xf4, 0x48, 0x5, 0x8, + 0x91, 0x88, 0x7, 0xff, 0x21, 0x9c, 0x0, 0x56, + 0x1, 0xff, 0xcd, 0x84, 0x0, 0x24, 0x0, 0x7f, + 0xf3, 0x6c, 0x80, 0xe, 0xc0, 0x1f, 0xfc, 0xdf, + 0x10, 0x4, 0x98, 0x7, 0xff, 0x30, 0xb4, 0x2, + 0xd2, 0x0, 0xff, 0xe6, 0x1d, 0x0, 0x7, 0xc0, + 0x3f, 0xf9, 0xaa, 0xe0, 0x2, 0xb0, 0xf, 0xfe, + 0x6c, 0x20, 0x1, 0x24, 0x3, 0xff, 0x9b, 0x66, + 0x0, 0x66, 0x0, + + /* U+0053 "S" */ + 0x0, 0x2e, 0x7f, 0xff, 0xf2, 0x6d, 0x0, 0x29, + 0xa3, 0x0, 0xff, 0xe4, 0xa5, 0xb8, 0x2b, 0x0, + 0x7f, 0xf3, 0x60, 0xf8, 0x0, 0x57, 0x99, 0xff, + 0xc7, 0xa1, 0x0, 0x4a, 0x80, 0x21, 0xc, 0xff, + 0xf8, 0xeb, 0x20, 0x6, 0x10, 0x0, 0x80, 0x7f, + 0xf2, 0x44, 0x0, 0x20, 0x1f, 0xfc, 0xd6, 0xff, + 0x68, 0x7, 0xff, 0xfc, 0x3, 0xff, 0xba, 0x20, + 0x1f, 0xfd, 0x2, 0x0, 0x78, 0x80, 0x7f, 0xf3, + 0x2c, 0x0, 0x9d, 0xff, 0xff, 0xc8, 0xb4, 0x0, + 0x39, 0x80, 0x7f, 0xf2, 0xd2, 0xdc, 0x1, 0x8c, + 0x1, 0xff, 0xcc, 0x83, 0x0, 0x4f, 0x65, 0xdf, + 0xff, 0x1e, 0x44, 0x1, 0x20, 0x10, 0x9a, 0x27, + 0xff, 0x1d, 0xa4, 0x0, 0xc0, 0x1f, 0xfc, 0xd1, + 0x0, 0x8, 0x7, 0xff, 0xfc, 0x3, 0xff, 0xbb, + 0xbd, 0xc4, 0x0, 0xff, 0xe6, 0x98, 0x8c, 0x1, + 0xff, 0xc9, 0x10, 0x0, 0x90, 0x2, 0x10, 0xcf, + 0xff, 0x8e, 0xb2, 0x0, 0x69, 0x0, 0x15, 0xe6, + 0x7f, 0xf1, 0xe8, 0x40, 0x12, 0xaa, 0x0, 0xff, + 0xe6, 0xb9, 0x85, 0x49, 0x80, 0x7f, 0xf2, 0x4e, + 0xe0, 0x0, + + /* U+0054 "T" */ + 0x3f, 0xff, 0xfe, 0x84, 0x80, 0x7f, 0xfc, 0xf, + 0x33, 0xff, 0x80, 0xe0, 0x1, 0xcc, 0xff, 0xe0, + 0x40, 0x19, 0xff, 0xf0, 0x38, 0x0, 0x67, 0xff, + 0xc1, 0x10, 0xf, 0xff, 0xf8, 0x7, 0xff, 0xfc, + 0x3, 0xff, 0xfe, 0x1, 0xff, 0xff, 0x0, 0xff, + 0xff, 0x80, 0x7f, 0xff, 0xc0, 0x3f, 0xff, 0xe0, + 0x1f, 0xff, 0xf0, 0xf, 0xff, 0xf8, 0x7, 0xff, + 0x84, + + /* U+0055 "U" */ + 0xdf, 0xf3, 0x0, 0x7f, 0xf2, 0x53, 0xfe, 0x0, + 0xff, 0xff, 0x80, 0x7f, 0xff, 0xc0, 0x3f, 0xff, + 0xe0, 0x1f, 0xff, 0xf0, 0xf, 0xff, 0xf8, 0x7, + 0xff, 0xfc, 0x3, 0xff, 0xfe, 0x1, 0xff, 0xff, + 0x0, 0xff, 0xfb, 0x88, 0x0, 0x40, 0x3f, 0xf9, + 0x22, 0x0, 0x16, 0x0, 0x4a, 0x99, 0xff, 0xf1, + 0xd2, 0x0, 0xb, 0x20, 0x1, 0xac, 0xcf, 0xfe, + 0x3d, 0x90, 0x3, 0x8e, 0x0, 0x3f, 0xf9, 0xac, + 0xa0, 0xf6, 0x80, 0x1f, 0xfc, 0x93, 0xa9, 0x0, + + /* U+0056 "V" */ + 0x4f, 0xf9, 0xc0, 0x3f, 0xfa, 0x13, 0xff, 0x1a, + 0x20, 0x1, 0x2, 0x1, 0xff, 0xce, 0x26, 0x0, + 0x31, 0x84, 0x80, 0x56, 0x1, 0xff, 0xce, 0x90, + 0xa, 0x0, 0x4, 0xc0, 0x5, 0x30, 0xf, 0xfe, + 0x62, 0xa0, 0x2, 0x4, 0x2, 0x81, 0x0, 0x78, + 0x7, 0xff, 0x32, 0x0, 0x4, 0xe0, 0x18, 0x60, + 0x0, 0x6a, 0x1, 0xff, 0xca, 0x72, 0x0, 0x40, + 0x7, 0x98, 0x80, 0x16, 0x1, 0xff, 0xc9, 0x18, + 0x0, 0x1a, 0x80, 0x7d, 0x20, 0x1, 0x70, 0xf, + 0xfe, 0x44, 0x0, 0x5e, 0x1, 0xf9, 0x10, 0x0, + 0x81, 0x0, 0xff, 0xe3, 0x13, 0x0, 0x18, 0xc0, + 0x3f, 0xa4, 0x2, 0xb0, 0xf, 0xfe, 0x34, 0x80, + 0x50, 0x1, 0xfe, 0x27, 0x0, 0x29, 0x80, 0x7f, + 0xf1, 0x11, 0x0, 0x8, 0x10, 0xf, 0xfa, 0x44, + 0x1, 0xe0, 0x1f, 0xfc, 0x49, 0x0, 0x13, 0x80, + 0x7f, 0xf0, 0x6, 0x0, 0x6, 0xa0, 0x1f, 0xfc, + 0x27, 0x20, 0x4, 0x80, 0x7f, 0xf0, 0x98, 0x80, + 0x16, 0x1, 0xff, 0xc1, 0x18, 0x0, 0x22, 0x0, + 0x3f, 0xf8, 0x72, 0x0, 0x17, 0x0, 0xff, 0xe0, + 0x40, 0x5, 0x20, 0x1f, 0xfc, 0x44, 0x50, 0x4, + 0x8, 0x7, 0xf8, 0x98, 0x0, 0xc4, 0x1, 0xff, + 0xc5, 0x80, 0xa, 0xc0, 0x3f, 0xd2, 0x0, 0x18, + 0x0, 0xff, 0xe3, 0x13, 0x80, 0x14, 0xc0, 0x3f, + 0x22, 0x0, 0x12, 0x20, 0x1f, 0xfc, 0x78, 0x10, + 0x7, 0x80, 0x7e, 0x90, 0x1, 0x38, 0x7, 0xff, + 0x26, 0x0, 0x6, 0xa0, 0x1e, 0x72, 0x0, 0x48, + 0x7, 0xff, 0x29, 0x8c, 0x1, 0x60, 0x1c, 0x32, + 0x0, 0x44, 0x0, 0x7f, 0xf2, 0xfc, 0x0, 0x2e, + 0x1, 0xa0, 0x40, 0x12, 0x1, 0xff, 0xcc, 0x35, + 0x0, 0x40, 0x80, 0x9, 0x80, 0xc, 0x40, 0x1f, + 0xfc, 0xdb, 0x0, 0xac, 0x1, 0x20, 0x1, 0x80, + 0xf, 0xfe, 0x70, 0xb8, 0x1, 0x4d, 0x10, 0x0, + 0x81, 0x0, 0xff, 0xe7, 0xc0, 0x80, 0x3e, 0x40, + 0x4, 0xc0, 0x1f, 0xfd, 0x1b, 0x0, 0x19, 0x0, + 0x24, 0x3, 0xff, 0xa4, 0xa6, 0x1, 0x91, 0x0, + 0x1f, 0xfd, 0x3f, 0x0, 0xd2, 0x1, 0xff, 0xc4, + + /* U+0057 "W" */ + 0x6f, 0xf8, 0x3, 0xff, 0x88, 0xff, 0xf6, 0x0, + 0x7f, 0xf1, 0x33, 0xfd, 0x6e, 0x1, 0x30, 0x7, + 0xff, 0xe, 0x80, 0x23, 0x20, 0xf, 0xfe, 0x11, + 0x18, 0x3, 0x45, 0x0, 0x1a, 0x1, 0xff, 0xc2, + 0x32, 0x0, 0xca, 0x1, 0xff, 0xc2, 0x50, 0x9, + 0x83, 0x80, 0x8, 0x20, 0x1f, 0xfc, 0x1a, 0x0, + 0xea, 0x0, 0xff, 0xe1, 0x50, 0x1, 0x40, 0xa, + 0x1, 0x30, 0x7, 0xff, 0x5, 0xc0, 0x6, 0x0, + 0x23, 0x0, 0xff, 0xe0, 0x19, 0x0, 0x38, 0x2, + 0x60, 0x5, 0x0, 0x7f, 0xf0, 0x10, 0x40, 0x1e, + 0x20, 0xa, 0x0, 0xff, 0xe0, 0x50, 0x0, 0x50, + 0x2, 0xd0, 0x1, 0x18, 0x7, 0xfd, 0xc0, 0x2, + 0x36, 0x0, 0x38, 0x7, 0xff, 0x1, 0xc0, 0xe, + 0x1, 0x90, 0x40, 0x16, 0x1, 0xff, 0x28, 0x2, + 0x82, 0x80, 0x2, 0xa0, 0x1f, 0xe4, 0x10, 0x5, + 0x0, 0x73, 0x0, 0x18, 0x3, 0xfc, 0xc0, 0x13, + 0x1, 0x10, 0x1, 0xe0, 0x1f, 0xee, 0x0, 0x11, + 0x80, 0x75, 0x0, 0x5, 0x0, 0x3f, 0xb4, 0x0, + 0x82, 0x0, 0xa0, 0x2, 0x88, 0x7, 0xf2, 0x80, + 0x28, 0x3, 0xc4, 0x60, 0xe, 0x0, 0xfc, 0x28, + 0x0, 0xe0, 0x9, 0x80, 0x27, 0x0, 0xfc, 0xa0, + 0x12, 0x80, 0x7d, 0x60, 0x5, 0x0, 0xfc, 0xc0, + 0x12, 0x80, 0x42, 0x80, 0xa, 0x0, 0xfd, 0xc0, + 0x3, 0x20, 0xf, 0x98, 0x2, 0x50, 0xf, 0xa8, + 0x0, 0xa0, 0x1d, 0xc0, 0x3, 0x20, 0xf, 0xa, + 0x0, 0x28, 0x3, 0xf0, 0xa0, 0x3, 0x80, 0x3c, + 0x64, 0x0, 0xe0, 0xe, 0x50, 0x9, 0x40, 0x3c, + 0xc0, 0x13, 0x80, 0x7f, 0x70, 0x1, 0x4, 0x3, + 0xac, 0x0, 0x28, 0x1, 0xe5, 0x0, 0x50, 0x7, + 0xac, 0x0, 0xa2, 0x1, 0xfc, 0xa0, 0x13, 0x0, + 0x73, 0x0, 0x1c, 0x3, 0xee, 0x0, 0x11, 0x80, + 0x62, 0x30, 0x7, 0x80, 0x7f, 0xca, 0x0, 0xa0, + 0xc, 0x82, 0x0, 0xa0, 0xf, 0x90, 0x40, 0x14, + 0x1, 0xa8, 0x0, 0x2a, 0x1, 0xff, 0x70, 0x0, + 0x88, 0x1, 0x70, 0x0, 0x8c, 0x3, 0xf3, 0x80, + 0x1c, 0x3, 0x28, 0x1, 0xc0, 0x3f, 0xf8, 0x8, + 0x20, 0xa, 0x0, 0x94, 0x1, 0x40, 0x1f, 0xd4, + 0x0, 0x14, 0x0, 0x19, 0x0, 0x28, 0x3, 0xff, + 0x82, 0xc0, 0x6, 0x0, 0x28, 0x4, 0xa0, 0x1f, + 0xc6, 0x40, 0xe, 0x0, 0x50, 0x0, 0x8c, 0x3, + 0xff, 0x83, 0x60, 0x1, 0x40, 0xe0, 0x1, 0x90, + 0x7, 0xfa, 0x80, 0xa, 0x20, 0xe0, 0x5, 0x0, + 0xff, 0xe1, 0x19, 0x0, 0x38, 0x50, 0x1, 0x40, + 0x1f, 0xf3, 0x0, 0x4e, 0xa2, 0x0, 0xa0, 0xf, + 0xfe, 0x1d, 0x0, 0x15, 0x80, 0x27, 0x0, 0xff, + 0x84, 0xc0, 0x15, 0xe0, 0x3, 0x20, 0xf, 0xfe, + 0x1b, 0x0, 0x50, 0x0, 0x51, 0x0, 0xff, 0xe0, + 0x50, 0x0, 0xd4, 0x1, 0x40, 0x1f, 0xfc, 0x41, + 0x30, 0xe, 0xf0, 0xf, 0xfe, 0xb, 0x80, 0x79, + 0xc0, 0x3f, 0xf8, 0xb4, 0x1, 0x85, 0x40, 0x3f, + 0xf8, 0x22, 0xa0, 0x19, 0x4, 0x3, 0xff, 0x8a, + 0xc0, 0x19, 0xc0, 0x3f, 0xf8, 0x7e, 0x1, 0xb8, + 0x3, 0xff, 0x8e, 0xa0, 0x15, 0x0, 0x7f, 0xf0, + 0xd4, 0x40, 0x25, 0x0, 0xff, 0xe3, 0xf0, 0x0, + 0x8c, 0x3, 0xff, 0x88, 0xe0, 0x6, 0x0, 0xff, + 0x0, + + /* U+0058 "X" */ + 0xb, 0xff, 0x98, 0x3, 0xff, 0x8c, 0xbf, 0xf6, + 0x0, 0x28, 0x80, 0x12, 0x80, 0x1f, 0xfc, 0x43, + 0xa0, 0x0, 0xe8, 0x0, 0x7c, 0x40, 0x16, 0x40, + 0x1f, 0xfc, 0x22, 0xd0, 0xb, 0x4, 0x2, 0x2a, + 0x0, 0xbc, 0x40, 0x3f, 0xf8, 0x3e, 0x20, 0xb, + 0x20, 0xe, 0x49, 0x0, 0x16, 0x80, 0x7f, 0xf0, + 0x2c, 0x80, 0x10, 0x80, 0x1f, 0x33, 0x0, 0x7, + 0x40, 0x1f, 0xe9, 0x40, 0x2, 0xb8, 0x7, 0xf4, + 0xa0, 0x1, 0x5c, 0x3, 0xf3, 0x30, 0x0, 0x74, + 0x1, 0xff, 0x59, 0x0, 0x21, 0x40, 0x3c, 0x92, + 0x0, 0x1d, 0x0, 0xff, 0xe0, 0xf8, 0x80, 0x28, + 0xc0, 0x31, 0x50, 0x5, 0x82, 0x1, 0xff, 0xc1, + 0x2a, 0x0, 0xb4, 0x40, 0x3, 0xe2, 0x0, 0xb2, + 0x0, 0xff, 0xe1, 0xa4, 0x80, 0x7, 0x40, 0x14, + 0x40, 0x8, 0x40, 0xf, 0xfe, 0x2b, 0x30, 0x0, + 0x75, 0x28, 0x0, 0x67, 0x0, 0xff, 0xe3, 0xca, + 0x0, 0x15, 0x80, 0x7, 0x20, 0x1f, 0xfc, 0x9b, + 0x20, 0xc, 0x5a, 0x1, 0xff, 0xcb, 0x90, 0xd, + 0x2, 0x1, 0xff, 0xca, 0x19, 0x0, 0xd6, 0x20, + 0x1f, 0xfc, 0xaa, 0x20, 0xc, 0x3a, 0x1, 0xff, + 0xc9, 0x94, 0x0, 0x2b, 0x80, 0xe, 0x80, 0x3f, + 0xf8, 0xec, 0xc0, 0x1, 0xd4, 0x20, 0x1, 0x5c, + 0x3, 0xff, 0x8a, 0x92, 0x0, 0x1d, 0x0, 0x59, + 0x0, 0x21, 0x40, 0x3f, 0xf8, 0x65, 0x60, 0x16, + 0x8, 0x5, 0xe2, 0x0, 0xa3, 0x0, 0xff, 0xe0, + 0x8f, 0x80, 0x56, 0x40, 0x18, 0xb4, 0x2, 0xd1, + 0x0, 0xff, 0xe0, 0x51, 0x0, 0x21, 0x0, 0x3c, + 0x72, 0x0, 0x1d, 0x0, 0xff, 0xa5, 0x0, 0xc, + 0xe0, 0x1f, 0x99, 0xc0, 0x7, 0x40, 0x1f, 0xce, + 0xc0, 0x3, 0x90, 0xf, 0xf4, 0x20, 0x1, 0x5c, + 0x3, 0xe4, 0x80, 0x1, 0x68, 0x7, 0xff, 0x2, + 0xc8, 0x1, 0xa, 0x1, 0xc5, 0x60, 0x17, 0x88, + 0x7, 0xff, 0x7, 0xc4, 0x1, 0x46, 0x1, 0xf, + 0x80, 0x56, 0x40, 0x1f, 0xfc, 0x22, 0xd0, 0xb, + 0x44, 0x1, 0xa4, 0x0, 0x94, 0x0, 0xff, 0xe2, + 0x1d, 0x0, 0x7, 0x40, + + /* U+0059 "Y" */ + 0x5f, 0xf9, 0x80, 0x3f, 0xf9, 0x7, 0xff, 0x52, + 0xb0, 0x2, 0x4c, 0x3, 0xff, 0x8c, 0x3a, 0x0, + 0x2a, 0x9, 0x20, 0x6, 0x88, 0x7, 0xff, 0x16, + 0x84, 0x1, 0xc0, 0x17, 0x80, 0x6, 0x80, 0x3f, + 0xf8, 0x90, 0x80, 0x9, 0x30, 0x8, 0xa4, 0x0, + 0x90, 0x1, 0xff, 0xc2, 0x47, 0x0, 0x23, 0x0, + 0x73, 0x20, 0x1, 0xd4, 0x3, 0xff, 0x80, 0x56, + 0x0, 0x1a, 0x0, 0xfa, 0x84, 0x1, 0x44, 0x1, + 0xff, 0x78, 0x5, 0x42, 0x1, 0xf0, 0xe8, 0x5, + 0xe0, 0x1f, 0xea, 0x20, 0x3, 0xa0, 0x7, 0xf1, + 0xc0, 0x0, 0xa8, 0x3, 0xf3, 0x28, 0x0, 0xe0, + 0x3, 0xfe, 0x73, 0x0, 0x2b, 0x80, 0x78, 0xe4, + 0x0, 0x3a, 0x1, 0xff, 0xc1, 0xd1, 0x0, 0x42, + 0x0, 0x61, 0xd0, 0xa, 0xc4, 0x3, 0xff, 0x82, + 0x34, 0x1, 0x51, 0x0, 0x54, 0x20, 0x6, 0x50, + 0xf, 0xfe, 0x1a, 0x30, 0x0, 0x78, 0x1, 0x8, + 0x0, 0x29, 0x0, 0xff, 0xe2, 0xc9, 0x80, 0xe, + 0x55, 0xc0, 0x2f, 0x0, 0xff, 0xe3, 0xf0, 0x4, + 0xd4, 0x1, 0x49, 0x0, 0x7f, 0xf1, 0xca, 0x80, + 0x3c, 0xac, 0x1, 0xff, 0xc9, 0x55, 0x0, 0x62, + 0xa0, 0xf, 0xfe, 0x5d, 0x10, 0x5, 0xc0, 0x1f, + 0xfc, 0xda, 0x0, 0x19, 0x80, 0x3f, 0xff, 0xe0, + 0x1f, 0xff, 0xf0, 0xf, 0xff, 0xf8, 0x7, 0xff, + 0xfc, 0x3, 0xff, 0x80, + + /* U+005A "Z" */ + 0xff, 0xff, 0xe8, 0x68, 0x7, 0xff, 0xbb, 0x33, + 0xff, 0x99, 0x0, 0x18, 0xcf, 0xff, 0x96, 0xb0, + 0x1, 0x38, 0x7, 0xff, 0x2a, 0x24, 0x2, 0x6a, + 0x0, 0xff, 0xe4, 0xd3, 0x80, 0x53, 0x20, 0xf, + 0xfe, 0x40, 0xe2, 0x80, 0x56, 0xc0, 0x1f, 0xfc, + 0x82, 0xf3, 0x0, 0xe, 0xa0, 0x7, 0xff, 0x21, + 0x34, 0x40, 0x7, 0xe4, 0x1, 0xff, 0xc8, 0x6b, + 0x0, 0x97, 0x4, 0x3, 0xff, 0x91, 0x32, 0x0, + 0x9e, 0x80, 0x3f, 0xf9, 0x36, 0xc0, 0x15, 0x40, + 0x7, 0xff, 0x20, 0x75, 0x0, 0x3, 0x8a, 0x1, + 0xff, 0xc8, 0x3f, 0x20, 0x1, 0x61, 0x80, 0x7f, + 0xf2, 0x17, 0x4, 0x0, 0x98, 0x40, 0x1f, 0xfc, + 0x87, 0xa0, 0x9, 0xac, 0x40, 0x3f, 0xf9, 0x15, + 0x0, 0x14, 0x48, 0x7, 0xff, 0x20, 0x71, 0x40, + 0x2a, 0x70, 0xf, 0xfe, 0x41, 0x61, 0x80, 0x7, + 0x14, 0x3, 0xff, 0x90, 0x78, 0x40, 0x2, 0xf3, + 0x0, 0xff, 0xe4, 0x36, 0x8, 0x1, 0x34, 0x40, + 0x3f, 0xf9, 0x11, 0x20, 0x13, 0x58, 0x7, 0xff, + 0x26, 0x9c, 0x2, 0x99, 0x0, 0x7f, 0xf2, 0x71, + 0x40, 0x2b, 0x60, 0xf, 0xfe, 0x51, 0x80, 0x52, + 0xe6, 0x7f, 0xfc, 0xa2, 0x0, 0xd3, 0x99, 0xff, + 0xcb, 0xf0, 0xf, 0xff, 0x70, + + /* U+005B "[" */ + 0xdf, 0xfe, 0x20, 0xf, 0xfe, 0x2b, 0x5d, 0x88, + 0x2, 0xe4, 0x40, 0x7, 0xff, 0xfc, 0x3, 0xff, + 0xf3, 0xc8, 0x80, 0xc, 0xd7, 0x62, 0x0, 0xff, + 0xe1, 0x0, + + /* U+005C "\\" */ + 0x40, 0xf, 0xfe, 0x44, 0x20, 0x7, 0xff, 0x22, + 0xc8, 0x3, 0xff, 0x91, 0xe2, 0x1, 0xff, 0xc5, + 0x60, 0x2a, 0x0, 0xff, 0xe2, 0xd1, 0x2, 0x48, + 0x7, 0xff, 0x17, 0xc4, 0x19, 0x80, 0x1f, 0xfc, + 0x42, 0xa0, 0x4, 0xa0, 0x7, 0xff, 0x11, 0x24, + 0x1, 0x44, 0x1, 0xff, 0xc4, 0x66, 0x0, 0xf8, + 0x7, 0xff, 0x16, 0x4c, 0xa, 0xc0, 0x3f, 0xf8, + 0xba, 0x20, 0x90, 0x1, 0xff, 0xc4, 0x1d, 0x0, + 0x3a, 0x80, 0x7f, 0xf1, 0xe, 0x80, 0x14, 0x60, + 0x1f, 0xfc, 0x45, 0x70, 0x6, 0x88, 0x7, 0xff, + 0x12, 0x10, 0x7, 0x40, 0x3f, 0xf8, 0xb6, 0x40, + 0x74, 0x1, 0xff, 0xc5, 0xf1, 0x5, 0x70, 0xf, + 0xfe, 0x21, 0x50, 0x2, 0x10, 0x3, 0xff, 0x88, + 0x90, 0x0, 0xb2, 0x0, 0xff, 0xe2, 0x3a, 0x80, + 0x3c, 0x40, 0x3f, 0xf8, 0x94, 0x60, 0x54, 0x1, + 0xff, 0xc5, 0xd1, 0x4, 0x90, 0xf, 0xfe, 0x20, + 0xe8, 0x1, 0x98, 0x1, 0xff, 0xc4, 0x3a, 0x0, + 0x49, 0x80, 0x7f, 0xf1, 0x15, 0xc0, 0x8, 0x1, + 0xff, 0xc5, 0x84, 0x0, 0xff, 0xe4, 0x51, 0x0, + 0x7f, 0xf1, 0xc7, 0xc4, + + /* U+005D "]" */ + 0xff, 0xf0, 0x7, 0xff, 0xe, 0xed, 0x0, 0x19, + 0x10, 0x60, 0x1f, 0xff, 0xf0, 0xf, 0xff, 0xc9, + 0xa1, 0x80, 0x6c, 0xb8, 0x0, 0xff, 0xe3, 0x0, + + /* U+005F "_" */ + 0x34, 0x4f, 0xfe, 0x80, 0xf5, 0xdf, 0xff, 0x41, + 0x40, 0x3f, 0xfe, 0x0, + + /* U+0060 "`" */ + 0x78, 0x81, 0x86, 0xbb, 0xa8, 0x30, 0x0, 0x80, + 0x80, 0x2, 0x12, 0x10, 0x2, 0x2, 0x0, 0x30, + + /* U+0061 "a" */ + 0xef, 0xff, 0xfe, 0x37, 0x51, 0x80, 0x7f, 0xf2, + 0x45, 0x71, 0x40, 0x3f, 0xf9, 0x74, 0x3b, 0x77, + 0xff, 0xc6, 0x80, 0xa, 0x4d, 0x13, 0xff, 0x8c, + 0xea, 0x0, 0x20, 0xf, 0xfe, 0x41, 0x80, 0x4, + 0x3, 0xff, 0xfe, 0x1, 0xff, 0xc9, 0xef, 0xff, + 0xfe, 0x3b, 0x0, 0x7f, 0xfa, 0x9e, 0xef, 0xff, + 0x88, 0x80, 0x1e, 0xd4, 0x4f, 0xfe, 0x21, 0x0, + 0x7f, 0xff, 0xc0, 0x23, 0x0, 0xff, 0xe4, 0x28, + 0x2, 0x15, 0x13, 0xff, 0x86, 0x40, 0x17, 0x80, + 0x6, 0xae, 0xff, 0xf8, 0x68, 0x1, 0x23, 0x80, + 0x7f, 0xf2, 0xe2, 0x8c, 0x3, 0xff, 0x90, + + /* U+0062 "b" */ + 0xdf, 0xf3, 0x80, 0x7f, 0xff, 0xc0, 0x3f, 0xff, + 0xe0, 0x1f, 0xfd, 0xd8, 0xff, 0xff, 0xe1, 0xf5, + 0x98, 0x7, 0xff, 0x24, 0x53, 0x18, 0x3, 0xff, + 0x97, 0x24, 0x1, 0xa6, 0xef, 0xff, 0x85, 0x20, + 0x15, 0x80, 0x4e, 0xc8, 0x9f, 0xfc, 0x26, 0x70, + 0x2, 0x80, 0x5e, 0x1, 0xff, 0xc4, 0xf0, 0xf, + 0xff, 0xf8, 0x7, 0xff, 0xfc, 0x3, 0xff, 0xfe, + 0x1, 0xff, 0xf9, 0xf0, 0xf, 0xfe, 0x27, 0x80, + 0x79, 0xd9, 0x13, 0xff, 0x84, 0xce, 0x0, 0x50, + 0xd, 0x37, 0x7f, 0xfc, 0x29, 0x0, 0xac, 0x3, + 0xff, 0x95, 0x24, 0x1, 0xff, 0xc7, 0x14, 0xc6, + 0x0, + + /* U+0063 "c" */ + 0x0, 0x2e, 0x7f, 0xff, 0xf1, 0xe4, 0x22, 0x8c, + 0x3, 0xff, 0x90, 0x8e, 0x1, 0xff, 0xca, 0xf0, + 0x0, 0xd5, 0xdf, 0xff, 0x1d, 0x40, 0x10, 0xa8, + 0x9f, 0xfc, 0x62, 0x10, 0x1, 0x80, 0x7f, 0xff, + 0xc0, 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0xf, + 0xff, 0xf8, 0x7, 0xe1, 0x0, 0x18, 0x7, 0xff, + 0x21, 0x40, 0x10, 0xa8, 0x9f, 0xfc, 0x62, 0xe0, + 0x0, 0xd5, 0xdf, 0xff, 0x1a, 0x55, 0xc0, 0x3f, + 0xf9, 0x71, 0x46, 0x1, 0xff, 0xc8, + + /* U+0064 "d" */ + 0x0, 0xff, 0xe4, 0xdf, 0xfa, 0x40, 0x3f, 0xff, + 0xe0, 0x1f, 0xff, 0xf0, 0xf, 0xfe, 0x83, 0x67, + 0xff, 0xff, 0x11, 0x0, 0x3a, 0xa4, 0xc0, 0x3f, + 0xf9, 0x2c, 0xa0, 0x1f, 0xfc, 0xbb, 0x0, 0x15, + 0x5d, 0xff, 0xf0, 0x9c, 0x3, 0x88, 0x1, 0x2a, + 0x89, 0xff, 0xc2, 0x83, 0x0, 0x84, 0x2, 0x70, + 0xf, 0xfe, 0x23, 0x80, 0x7f, 0xff, 0xc0, 0x3f, + 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0xf, 0xff, 0xf8, + 0x4, 0x20, 0x13, 0x80, 0x7f, 0xf1, 0x1c, 0x3, + 0x10, 0x2, 0x55, 0x13, 0xff, 0x85, 0x6, 0x1, + 0xa8, 0x0, 0x55, 0x77, 0xff, 0xc2, 0x70, 0xe, + 0x75, 0x0, 0xff, 0xe6, 0x54, 0x98, 0x7, 0xff, + 0x20, + + /* U+0065 "e" */ + 0x0, 0x2e, 0x7f, 0xff, 0xf0, 0xfa, 0x88, 0x2, + 0x8a, 0x30, 0xf, 0xfe, 0x18, 0xae, 0xa0, 0x23, + 0x80, 0x7f, 0xf2, 0x28, 0x7c, 0x0, 0x35, 0x77, + 0xff, 0xc2, 0x80, 0x0, 0xb2, 0x80, 0x21, 0x51, + 0x3f, 0xf8, 0x4e, 0x80, 0xd, 0x10, 0x1, 0x80, + 0x7f, 0xf1, 0x4, 0x0, 0x20, 0x1f, 0xff, 0xf0, + 0xf, 0xfe, 0x65, 0x7f, 0xff, 0xf1, 0x14, 0x3, + 0xff, 0xd5, 0x17, 0x7f, 0xfc, 0x79, 0x0, 0x89, + 0x13, 0xff, 0x8e, 0x40, 0x1f, 0xff, 0xc1, 0x0, + 0x18, 0x7, 0xff, 0x21, 0x40, 0x10, 0xa8, 0x9f, + 0xfc, 0x62, 0xe0, 0x0, 0xd5, 0xdf, 0xff, 0x1a, + 0x55, 0xc0, 0x3f, 0xf9, 0x71, 0x46, 0x1, 0xff, + 0xc8, + + /* U+0066 "f" */ + 0x0, 0x2e, 0x7f, 0xff, 0x38, 0x45, 0x18, 0x7, + 0xf9, 0x1c, 0x3, 0xff, 0x81, 0xc0, 0x14, 0xdd, + 0xfe, 0x67, 0x0, 0x3b, 0x22, 0x7e, 0x13, 0x0, + 0x70, 0x7, 0xff, 0x76, 0x7f, 0xff, 0x9c, 0x3, + 0xff, 0xa6, 0xf7, 0x7f, 0xcc, 0x1, 0x72, 0x27, + 0xf0, 0x80, 0x7f, 0xff, 0xc0, 0x3f, 0xff, 0xe0, + 0x1f, 0xff, 0xf0, 0xf, 0xfe, 0x78, + + /* U+0067 "g" */ + 0x0, 0xa3, 0x7f, 0xff, 0xf8, 0x7a, 0xe0, 0x18, + 0x75, 0xc8, 0x3, 0xff, 0x86, 0x51, 0x80, 0x15, + 0x10, 0x7, 0xff, 0x1c, 0xe4, 0x9, 0x0, 0xb, + 0x77, 0xff, 0xc3, 0x40, 0x3, 0x3, 0x80, 0x54, + 0x89, 0xff, 0xc3, 0xa0, 0x8, 0xc0, 0x21, 0x0, + 0xff, 0xe2, 0x8, 0x7, 0xff, 0xfc, 0x3, 0xff, + 0xfe, 0x1, 0xff, 0xff, 0x0, 0xff, 0xff, 0x80, + 0x7c, 0x20, 0x1f, 0xfc, 0x41, 0x0, 0xce, 0x1, + 0x52, 0x27, 0xff, 0xe, 0x80, 0x31, 0x20, 0x1, + 0x6e, 0xff, 0xf8, 0x68, 0x1, 0xd4, 0x40, 0x1f, + 0xfc, 0xb1, 0xd7, 0x20, 0xf, 0xfe, 0x5c, 0x6f, + 0xff, 0xff, 0x10, 0x3, 0xff, 0xfe, 0x1, 0xff, + 0xcc, 0x10, 0xf, 0xe4, 0x4f, 0xfe, 0x12, 0xe8, + 0x4, 0x60, 0x18, 0x6e, 0xff, 0xf8, 0x54, 0x60, + 0x6, 0x0, 0xff, 0xe5, 0x9c, 0x80, 0x7f, 0xf2, + 0x4a, 0x30, 0x0, + + /* U+0068 "h" */ + 0xdf, 0xf3, 0x80, 0x7f, 0xff, 0xc0, 0x3f, 0xff, + 0xe0, 0x1f, 0xfd, 0xd8, 0xff, 0xff, 0xe1, 0xf5, + 0x98, 0x7, 0xff, 0x24, 0x53, 0x18, 0x3, 0xff, + 0x97, 0x24, 0x1, 0xa6, 0xef, 0xff, 0x85, 0x20, + 0x15, 0x80, 0x4e, 0xc8, 0x9f, 0xfc, 0x26, 0x70, + 0x2, 0x80, 0x5e, 0x1, 0xff, 0xc4, 0xf0, 0xf, + 0xff, 0xf8, 0x7, 0xff, 0xfc, 0x3, 0xff, 0xfe, + 0x1, 0xff, 0xff, 0x0, 0xff, 0xff, 0x80, 0x7f, + 0xf6, 0x0, + + /* U+0069 "i" */ + 0xef, 0xf2, 0x80, 0x7e, 0xdb, 0xb2, 0x1a, 0x20, + 0x40, 0x3f, 0xf8, 0x1d, 0xfe, 0x50, 0xf, 0xff, + 0xf8, 0x7, 0xff, 0x4, + + /* U+006A "j" */ + 0x0, 0xff, 0x8b, 0xfe, 0x30, 0xf, 0xfe, 0xf9, + 0x5d, 0xc6, 0x1, 0xff, 0xc0, 0x44, 0x80, 0x3f, + 0xfe, 0x5, 0xff, 0x18, 0x7, 0xff, 0xfc, 0x3, + 0xff, 0xfe, 0x1, 0xff, 0xff, 0x0, 0xff, 0xff, + 0x80, 0x7f, 0xff, 0xc0, 0x3f, 0xfd, 0x82, 0x1, + 0xff, 0x58, 0x4, 0x6f, 0xff, 0xfe, 0x60, 0x2, + 0x8, 0x7, 0xff, 0x4, 0x64, 0x3, 0xff, 0x82, + 0x78, 0x40, 0xb7, 0x7f, 0xed, 0xc2, 0x0, 0x0, + + /* U+006B "k" */ + 0xdf, 0xf3, 0x80, 0x7f, 0xff, 0xc0, 0x3f, 0xff, + 0xe0, 0x1f, 0xfe, 0xc8, 0xff, 0xb0, 0x3, 0xff, + 0x8c, 0xce, 0x0, 0x2d, 0x0, 0xff, 0xe2, 0xac, + 0x80, 0x7, 0xc4, 0x3, 0xff, 0x88, 0x94, 0x1, + 0x69, 0x0, 0x7f, 0xf1, 0xe, 0xc0, 0x2b, 0x30, + 0xf, 0xfe, 0x21, 0x68, 0x5, 0x48, 0x1, 0xff, + 0xc4, 0x1f, 0x10, 0x4, 0xa8, 0x7, 0xff, 0x17, + 0x8, 0x0, 0xec, 0x1, 0xff, 0xc5, 0xc2, 0x0, + 0x34, 0x0, 0x7f, 0xf1, 0x68, 0xc0, 0xb, 0x20, + 0x1f, 0xf4, 0x7f, 0xf9, 0x40, 0x7, 0x40, 0x1f, + 0xfc, 0xad, 0x0, 0xff, 0xe5, 0xc0, 0x7, 0xff, + 0x5, 0xae, 0xfc, 0x40, 0x6, 0x70, 0xf, 0xfe, + 0x7, 0x22, 0x7b, 0x4, 0x1, 0xc, 0x1, 0xff, + 0xc6, 0x1c, 0x10, 0x4, 0xa8, 0x7, 0xff, 0x18, + 0xb0, 0x40, 0x14, 0x80, 0x1f, 0xfc, 0x62, 0xd0, + 0xa, 0xcc, 0x3, 0xff, 0x8c, 0x76, 0x1, 0x69, + 0x0, 0x7f, 0xf1, 0x92, 0x80, 0x3, 0x82, 0x1, + 0xff, 0xc6, 0x59, 0x0, 0xe, 0x8, 0x7, 0xff, + 0x19, 0xa0, 0x0, 0x5a, 0x1, 0xff, 0xc7, 0x77, + 0x0, 0xe, 0xc0, + + /* U+006C "l" */ + 0xef, 0xf2, 0x80, 0x7f, 0xff, 0xc0, 0x3f, 0xff, + 0xe0, 0x1f, 0xff, 0xf0, 0xe, 0x30, 0x1, 0x80, + 0x73, 0x0, 0x21, 0x51, 0x23, 0xf0, 0x0, 0xd5, + 0xdd, 0xc9, 0x0, 0x1f, 0x9e, 0xcc, 0x3, 0x80, + + /* U+006D "m" */ + 0xdf, 0xff, 0xfe, 0x97, 0x51, 0x0, 0x7f, 0xf5, + 0x45, 0x75, 0x0, 0x3f, 0xfa, 0xf4, 0x20, 0x1a, + 0x6e, 0xff, 0xce, 0x1, 0xa2, 0xef, 0xfd, 0x0, + 0x1, 0x70, 0x9, 0xd9, 0x13, 0xfd, 0x6, 0x0, + 0x57, 0x44, 0xff, 0x3a, 0x80, 0x3c, 0x2, 0xf0, + 0xf, 0xfe, 0x3, 0x80, 0xc, 0x3, 0xff, 0x80, + 0x60, 0x3, 0x0, 0xff, 0xff, 0x80, 0x7f, 0xff, + 0xc0, 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0xf, + 0xff, 0xf8, 0x7, 0xff, 0xfc, 0x3, 0xff, 0xfe, + 0x1, 0xff, 0xff, 0x0, 0xff, 0xe0, 0x0, + + /* U+006E "n" */ + 0xdf, 0xff, 0xfe, 0x37, 0x59, 0x80, 0x7f, 0xf2, + 0x45, 0x31, 0x80, 0x3f, 0xf9, 0x72, 0x40, 0x1a, + 0x6e, 0xff, 0xf8, 0x52, 0x1, 0x58, 0x4, 0xec, + 0x89, 0xff, 0xc2, 0x67, 0x0, 0x28, 0x5, 0xe0, + 0x1f, 0xfc, 0x4f, 0x0, 0xff, 0xff, 0x80, 0x7f, + 0xff, 0xc0, 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0, + 0xf, 0xff, 0xf8, 0x7, 0xff, 0x60, + + /* U+006F "o" */ + 0x0, 0x2e, 0x7f, 0xff, 0xf0, 0xfa, 0x88, 0x2, + 0x8a, 0x30, 0xf, 0xfe, 0x18, 0xae, 0xa0, 0x23, + 0x80, 0x7f, 0xf2, 0x2c, 0x7c, 0x0, 0x35, 0x77, + 0xff, 0xc2, 0x80, 0x9, 0x94, 0x1, 0xa, 0x89, + 0xff, 0xc2, 0x74, 0x0, 0x68, 0x80, 0xc, 0x3, + 0xff, 0x88, 0x20, 0x1, 0x0, 0xff, 0xff, 0x80, + 0x7f, 0xff, 0xc0, 0x3f, 0xff, 0xe0, 0x1f, 0xff, + 0x31, 0x0, 0x18, 0x7, 0xff, 0x10, 0x40, 0x2, + 0xa0, 0x8, 0x54, 0x4f, 0xfe, 0x13, 0xa0, 0x3, + 0x78, 0x0, 0x35, 0x77, 0xff, 0xc2, 0x80, 0x9, + 0x95, 0xc0, 0x3f, 0xf9, 0x16, 0x21, 0x14, 0x60, + 0x1f, 0xfc, 0x31, 0x5d, 0x40, + + /* U+0070 "p" */ + 0xdf, 0xff, 0xfe, 0x37, 0x59, 0x80, 0x7f, 0xf2, + 0x45, 0x31, 0x80, 0x3f, 0xf9, 0x72, 0x40, 0x1a, + 0x6e, 0xff, 0xf8, 0x52, 0x1, 0x58, 0x4, 0xec, + 0x89, 0xff, 0xc2, 0x67, 0x0, 0x28, 0x5, 0xe0, + 0x1f, 0xfc, 0x4f, 0x0, 0xff, 0xff, 0x80, 0x7f, + 0xff, 0xc0, 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0x9f, + 0x0, 0xff, 0xe2, 0x78, 0x7, 0x9d, 0x91, 0x3f, + 0xf8, 0x4c, 0xe0, 0x5, 0x0, 0xd3, 0x77, 0xff, + 0xc2, 0x90, 0xa, 0xc0, 0x3f, 0xf9, 0x52, 0x40, + 0x1f, 0xfc, 0x71, 0x4c, 0x60, 0xd, 0x1f, 0xff, + 0xfc, 0x3e, 0xb3, 0x0, 0xff, 0xff, 0x80, 0x7f, + 0xff, 0xc0, 0x3f, 0xfc, 0x0, + + /* U+0071 "q" */ + 0x0, 0x9f, 0x7f, 0xff, 0xf8, 0xee, 0x0, 0xc8, + 0x20, 0xf, 0xfe, 0x4c, 0x18, 0x7, 0xff, 0x2d, + 0xc0, 0x7, 0x77, 0xff, 0xc3, 0x60, 0xc, 0x40, + 0x17, 0xa2, 0x7f, 0xf0, 0xe4, 0x40, 0x21, 0x0, + 0x84, 0x3, 0xff, 0x88, 0x60, 0x1f, 0xff, 0xf0, + 0xf, 0xff, 0xf8, 0x7, 0xff, 0xfc, 0x3, 0xff, + 0xfe, 0x1, 0x8, 0x4, 0x20, 0x1f, 0xfc, 0x43, + 0x0, 0x88, 0x2, 0xf4, 0x4f, 0xfe, 0x1c, 0x88, + 0x6, 0x60, 0x1, 0xdd, 0xff, 0xf0, 0xd8, 0x3, + 0xa4, 0xc0, 0x3f, 0xf9, 0x99, 0x4, 0x1, 0xff, + 0xcb, 0x7d, 0xff, 0xff, 0xe2, 0x10, 0x7, 0xff, + 0xfc, 0x3, 0xff, 0xfe, 0x1, 0xff, 0xe9, + + /* U+0072 "r" */ + 0x0, 0x26, 0x7f, 0xff, 0xf0, 0x82, 0x2c, 0xc0, + 0x3f, 0xf8, 0x48, 0xe0, 0x1f, 0xfc, 0x4f, 0x0, + 0xd, 0x5d, 0xff, 0xf0, 0x31, 0x40, 0x10, 0xa8, + 0x9f, 0xfc, 0x3, 0x0, 0x8c, 0x3, 0xff, 0xfe, + 0x1, 0xff, 0xff, 0x0, 0xff, 0xff, 0x80, 0x7f, + 0xff, 0xc0, 0x3f, 0xf9, 0x0, + + /* U+0073 "s" */ + 0x0, 0x9b, 0x3f, 0xff, 0xf8, 0x7d, 0x22, 0x1, + 0xaa, 0x4c, 0x3, 0xff, 0x86, 0x2d, 0xe6, 0x0, + 0x65, 0x0, 0xff, 0xe3, 0x8f, 0x0, 0x2c, 0x0, + 0x55, 0x77, 0xff, 0xc2, 0x60, 0x1, 0x28, 0x10, + 0x2, 0x55, 0x13, 0xff, 0x85, 0x24, 0x0, 0xd1, + 0x0, 0x9c, 0x3, 0xff, 0x89, 0x9b, 0xa9, 0x0, + 0xff, 0xe4, 0x89, 0x10, 0x40, 0x3f, 0xf9, 0xa2, + 0x1, 0xff, 0xcd, 0x10, 0x4, 0x80, 0x7f, 0xf2, + 0x58, 0x0, 0xbf, 0xff, 0xfc, 0x3e, 0x92, 0x0, + 0xa0, 0x80, 0x3f, 0xf8, 0xa2, 0xd8, 0x60, 0x16, + 0x28, 0x7, 0xff, 0x18, 0x74, 0x2, 0x1a, 0xec, + 0xbb, 0xff, 0xe1, 0x38, 0x0, 0x54, 0x3, 0x9, + 0xa2, 0x7f, 0xf0, 0xa0, 0x80, 0x1a, 0x1, 0xff, + 0xc9, 0x10, 0x0, 0x80, 0x7f, 0xf3, 0x84, 0x60, + 0xf, 0xfe, 0x57, 0x74, 0x80, 0x1f, 0xfc, 0x41, + 0x0, 0x8, 0x10, 0x2, 0x51, 0x3f, 0xf8, 0x70, + 0x40, 0xd, 0xa, 0x0, 0x15, 0xdf, 0xff, 0xd, + 0xc0, 0x2, 0xa0, 0xea, 0x1, 0xff, 0xc8, 0xd0, + 0xa, 0xa4, 0xc0, 0x3f, 0xf8, 0x62, 0xba, 0x60, + + /* U+0074 "t" */ + 0xef, 0xf3, 0x0, 0x7f, 0xff, 0xc0, 0x3f, 0xfb, + 0x13, 0xff, 0xfc, 0xe0, 0x1f, 0xfd, 0x37, 0xbb, + 0xfe, 0x60, 0xb, 0x91, 0x3f, 0x84, 0x3, 0xff, + 0xfe, 0x1, 0xff, 0xff, 0x0, 0xff, 0xea, 0x18, + 0x0, 0x40, 0x3f, 0xce, 0x0, 0x95, 0x44, 0xfc, + 0x32, 0x0, 0x1a, 0xbb, 0xfc, 0xc7, 0x0, 0x1f, + 0xfc, 0x17, 0xb3, 0x0, 0xff, 0x0, + + /* U+0075 "u" */ + 0xef, 0xf3, 0x0, 0x7f, 0xf1, 0x23, 0xfd, 0x80, + 0x1f, 0xff, 0xf0, 0xf, 0xff, 0xf8, 0x7, 0xff, + 0xfc, 0x3, 0xff, 0xfe, 0x1, 0xff, 0xff, 0x0, + 0xff, 0xea, 0x98, 0x0, 0x40, 0x3f, 0xf8, 0x82, + 0x0, 0x77, 0x0, 0x25, 0x51, 0x3f, 0xf8, 0x4c, + 0xc0, 0x1, 0xc8, 0x0, 0x6a, 0xef, 0xff, 0x85, + 0x20, 0x15, 0x1c, 0x0, 0x7f, 0xf2, 0x28, 0x81, + 0xed, 0x0, 0x3f, 0xf8, 0x62, 0x98, 0xa0, + + /* U+0076 "v" */ + 0xd, 0xff, 0x70, 0x7, 0xff, 0x27, 0x7f, 0xdc, + 0x21, 0x0, 0x1, 0x80, 0xf, 0xfe, 0x3b, 0x10, + 0x2, 0x44, 0x14, 0xc0, 0xe, 0x20, 0x1f, 0xfc, + 0x68, 0x0, 0x13, 0x80, 0x5e, 0x1, 0x40, 0x7, + 0xff, 0x15, 0xc4, 0x1, 0x0, 0x18, 0xd4, 0x0, + 0xc4, 0x1, 0xff, 0xc3, 0x18, 0x0, 0x1a, 0x80, + 0x74, 0x0, 0x52, 0x1, 0xff, 0xc3, 0x80, 0xb, + 0xc0, 0x3c, 0x4e, 0x0, 0x44, 0x0, 0x7f, 0xf0, + 0x49, 0x80, 0xa, 0x60, 0x1f, 0x40, 0x80, 0x20, + 0x3, 0xff, 0x83, 0x0, 0x15, 0x80, 0x7f, 0x40, + 0x0, 0xd4, 0x3, 0xfe, 0x35, 0x0, 0x38, 0x80, + 0x7f, 0x31, 0x0, 0x20, 0x3, 0xfe, 0xf0, 0x0, + 0xc0, 0x7, 0xfd, 0x0, 0x2, 0x70, 0xf, 0xe5, + 0x30, 0x4, 0x0, 0x7f, 0xf0, 0x14, 0xc0, 0x12, + 0x20, 0x1f, 0xa0, 0x0, 0x4c, 0x1, 0xff, 0xc1, + 0xf0, 0x0, 0xc8, 0x7, 0xcc, 0x40, 0x9, 0x0, + 0xff, 0xe1, 0x1a, 0x80, 0x1c, 0x40, 0x3d, 0x0, + 0x4, 0x40, 0x7, 0xff, 0xe, 0xc0, 0x2b, 0x0, + 0xe8, 0x10, 0x4, 0x0, 0x7f, 0xf1, 0x5, 0xc0, + 0xa, 0x60, 0x10, 0xb8, 0x1, 0x8c, 0x3, 0xff, + 0x8b, 0x2, 0x0, 0x80, 0xa, 0x0, 0x28, 0x0, + 0xff, 0xe3, 0xc0, 0x1, 0x10, 0x4, 0xc0, 0x7, + 0x10, 0xf, 0xfe, 0x3b, 0x10, 0x2, 0x42, 0x40, + 0x3, 0x0, 0x1f, 0xfc, 0x99, 0x0, 0x14, 0xa0, + 0x2, 0x0, 0x3f, 0xf9, 0x48, 0x60, 0x6, 0x0, + 0x13, 0x0, 0x7f, 0xf2, 0xfc, 0x3, 0xa4, 0x3, + 0xff, 0x98, 0x6a, 0x1, 0x22, 0x0, 0x3f, 0xf8, + 0x0, + + /* U+0077 "w" */ + 0x6f, 0xf8, 0x80, 0x3f, 0xf8, 0x39, 0xff, 0x80, + 0x3f, 0xf8, 0x5d, 0xfe, 0xa6, 0x0, 0xa8, 0x3, + 0xff, 0x80, 0x66, 0x0, 0xce, 0x1, 0xff, 0xc0, + 0x41, 0x0, 0x70, 0x30, 0x1, 0x80, 0x3f, 0xf8, + 0x12, 0x1, 0xd2, 0x1, 0xff, 0xc0, 0xe0, 0x0, + 0xa0, 0x50, 0x0, 0x54, 0x3, 0xfc, 0x2a, 0x1, + 0xc2, 0xc0, 0x1f, 0xe1, 0x50, 0x3, 0x0, 0xc, + 0x80, 0x12, 0x1, 0xfe, 0x90, 0x9, 0xc0, 0x2b, + 0x0, 0xff, 0x30, 0x5, 0x40, 0x15, 0x80, 0xc, + 0x80, 0x3f, 0x98, 0x0, 0x30, 0x80, 0x2, 0x50, + 0xf, 0xea, 0x0, 0x21, 0x0, 0x4c, 0x1, 0x58, + 0x7, 0xe6, 0x10, 0x3, 0x7, 0x80, 0x52, 0x1, + 0xf8, 0xc8, 0x1, 0xc0, 0x1c, 0xa0, 0x6, 0x0, + 0xfd, 0x60, 0x14, 0x82, 0x10, 0x0, 0xd0, 0x3, + 0xea, 0x0, 0x94, 0x3, 0xa4, 0x2, 0x60, 0xf, + 0x21, 0x0, 0x14, 0x40, 0x10, 0x1, 0x78, 0x7, + 0xcc, 0x0, 0x60, 0xf, 0x19, 0x0, 0x2c, 0x3, + 0xde, 0x1, 0x48, 0x4, 0xa2, 0x0, 0x43, 0x0, + 0xe5, 0x0, 0xa8, 0x3, 0xea, 0x0, 0x11, 0x80, + 0x62, 0x40, 0x1, 0x98, 0x3, 0x48, 0x5, 0x20, + 0x1d, 0x20, 0x2, 0x30, 0xf, 0x98, 0x2, 0x90, + 0xd, 0x60, 0x14, 0x80, 0x73, 0x80, 0x4a, 0x40, + 0x11, 0x18, 0x2, 0x80, 0x3f, 0xa, 0x80, 0x14, + 0x40, 0x2, 0xc0, 0x1, 0x50, 0xf, 0x38, 0x5, + 0x60, 0x15, 0x0, 0x4c, 0x1, 0xfd, 0x20, 0x13, + 0x0, 0x18, 0x2, 0x90, 0xf, 0xa4, 0x2, 0x61, + 0x0, 0x30, 0x1, 0x4, 0x3, 0xf8, 0xc8, 0x1, + 0x40, 0x9, 0x0, 0x98, 0x3, 0xe1, 0x50, 0xa, + 0x41, 0x4, 0x1, 0xc0, 0x1f, 0xf5, 0x0, 0x9, + 0x14, 0x40, 0xa, 0x20, 0x1f, 0xa0, 0x2, 0x60, + 0xe0, 0x0, 0xa8, 0x7, 0xfc, 0xc0, 0x17, 0xc8, + 0x5, 0x20, 0x1f, 0xc4, 0x80, 0x1, 0x85, 0x0, + 0x30, 0x7, 0xff, 0x0, 0x54, 0x0, 0x86, 0x0, + 0x33, 0x0, 0x7f, 0xbc, 0x2, 0x70, 0xa, 0x80, + 0x3f, 0xf8, 0x3c, 0x1, 0xe9, 0x0, 0xff, 0x90, + 0xc0, 0x38, 0xc8, 0x3, 0xff, 0x82, 0x84, 0x1, + 0x85, 0x40, 0x3f, 0xf8, 0x12, 0x1, 0xd4, 0x1, + 0xff, 0xc3, 0xa0, 0xd, 0x20, 0x1f, 0xfc, 0x15, + 0x10, 0xc, 0xe0, 0x1f, 0xfc, 0x36, 0x0, 0xcc, + 0x1, 0xff, 0xc2, 0x90, 0x9, 0x44, 0x3, 0xff, + 0x86, 0x2a, 0x0, 0x61, 0x0, 0xff, 0xe1, 0x38, + 0x5, 0xc0, 0x1f, 0x80, + + /* U+0078 "x" */ + 0xb, 0xff, 0xa4, 0x3, 0xff, 0x82, 0x7f, 0xf8, + 0x82, 0xc8, 0x0, 0xce, 0x1, 0xff, 0x16, 0x80, + 0x56, 0x40, 0xc, 0x10, 0x4, 0x28, 0x7, 0xf0, + 0xf8, 0x80, 0x25, 0x0, 0x21, 0xd0, 0xa, 0x8c, + 0x3, 0xf6, 0x90, 0x1, 0xd8, 0x3, 0x8e, 0xc0, + 0x2d, 0x20, 0xf, 0x51, 0x80, 0x16, 0x0, 0x3e, + 0x48, 0x0, 0xf, 0x88, 0x6, 0x85, 0x0, 0x1d, + 0x0, 0x7f, 0x3b, 0x0, 0xb, 0x40, 0x26, 0x70, + 0x1, 0x68, 0x7, 0xfd, 0x28, 0x0, 0x3a, 0x4, + 0x90, 0x0, 0xf8, 0x80, 0x7f, 0xf0, 0x2c, 0x80, + 0xb, 0x36, 0x1, 0x51, 0x0, 0x7f, 0xf0, 0xb0, + 0x40, 0xc, 0x1, 0x4a, 0x0, 0x7f, 0xf0, 0xc7, + 0x40, 0x39, 0xd8, 0x3, 0xff, 0x8a, 0x68, 0x1, + 0xa4, 0x3, 0xff, 0x8c, 0xe8, 0x1, 0xb4, 0x3, + 0xff, 0x8a, 0xb0, 0x1, 0xc7, 0x40, 0x1f, 0xfc, + 0x33, 0xa0, 0xb, 0x50, 0x0, 0xb0, 0x1, 0xff, + 0xc1, 0x1d, 0x0, 0xac, 0xac, 0x80, 0xe, 0xa0, + 0x1f, 0xfc, 0xd, 0x10, 0x4, 0x20, 0x3, 0xc4, + 0x1, 0x46, 0x1, 0xfe, 0xa3, 0x0, 0x2b, 0x80, + 0x45, 0x40, 0x16, 0x88, 0x7, 0xe7, 0x50, 0x1, + 0xd0, 0x7, 0x24, 0x0, 0x7, 0x40, 0x3e, 0x48, + 0x0, 0xe, 0x80, 0x7c, 0xea, 0x0, 0x3a, 0x0, + 0xe2, 0xb0, 0xb, 0x44, 0x3, 0xf5, 0x18, 0x1, + 0x5c, 0x2, 0x1f, 0x0, 0xa4, 0xc0, 0x3f, 0xda, + 0x20, 0x8, 0x50, 0x6, 0x90, 0x1, 0x98, 0x1, + 0xff, 0xe, 0x80, 0x54, 0x60, + + /* U+0079 "y" */ + 0x5f, 0xf8, 0x3, 0xff, 0x8b, 0xff, 0x20, 0x7, + 0xff, 0xfc, 0x3, 0xff, 0xfe, 0x1, 0xff, 0xff, + 0x0, 0xff, 0xff, 0x80, 0x7f, 0xff, 0xc0, 0x3f, + 0xfc, 0xc2, 0x1, 0xff, 0xc7, 0x10, 0xc, 0xa0, + 0x15, 0x22, 0x7f, 0xf0, 0xe8, 0x3, 0xa, 0x80, + 0x16, 0xef, 0xff, 0x86, 0x80, 0x1d, 0x64, 0x1, + 0xff, 0xcb, 0x1d, 0x72, 0x0, 0xff, 0xe5, 0xc6, + 0xff, 0xff, 0xf1, 0x0, 0x3f, 0xff, 0xe0, 0x1f, + 0xfc, 0xc1, 0x0, 0x9c, 0x3, 0x91, 0x3f, 0xf8, + 0x4b, 0x40, 0x11, 0x0, 0x75, 0xdf, 0xff, 0xa, + 0x90, 0x0, 0xa2, 0x1, 0xff, 0xca, 0x2b, 0x0, + 0xff, 0xe4, 0x93, 0xe8, 0x80, + + /* U+007A "z" */ + 0xdf, 0xff, 0xfe, 0x56, 0x0, 0x7f, 0xf9, 0x3a, + 0xef, 0xff, 0x8d, 0x0, 0x18, 0xd1, 0x3f, 0xf8, + 0xa7, 0x0, 0x12, 0x80, 0x7f, 0xf1, 0x6a, 0x0, + 0x29, 0x90, 0x7, 0xff, 0xc, 0xf5, 0x40, 0x3, + 0x8c, 0x1, 0xff, 0xc3, 0x6c, 0x20, 0x2, 0x79, + 0x80, 0x7f, 0xf0, 0xea, 0x40, 0x28, 0xb1, 0x0, + 0xff, 0xe1, 0x16, 0xa8, 0x0, 0x6d, 0xc0, 0x3f, + 0xf8, 0x6b, 0xa4, 0x0, 0x3f, 0x40, 0xf, 0xfe, + 0x1c, 0xd0, 0x4, 0xf8, 0x20, 0x1f, 0xfc, 0x21, + 0xc6, 0x0, 0xd, 0xc0, 0x7, 0xff, 0xd, 0x3c, + 0xc0, 0x7, 0x88, 0x1, 0xff, 0xc3, 0x8b, 0x10, + 0x3, 0x61, 0x0, 0x7f, 0xf0, 0x86, 0xdc, 0x2, + 0xa9, 0x0, 0xff, 0xe1, 0x9f, 0xa0, 0x0, 0xb5, + 0x40, 0x3f, 0xf8, 0x6f, 0x82, 0x0, 0x5d, 0x20, + 0xf, 0xfe, 0x1d, 0x40, 0x5, 0x34, 0x1, 0xff, + 0xc5, 0x70, 0xa, 0x9, 0x13, 0xff, 0x8a, 0x60, + 0x1a, 0x2e, 0xff, 0xf8, 0xde, 0x1, 0xff, 0xe4, + + /* U+007B "{" */ + 0x0, 0xe7, 0xdf, 0xc0, 0xd, 0x70, 0x40, 0x1d, + 0x8, 0x1, 0xf3, 0x80, 0xe, 0xbc, 0x0, 0x40, + 0x17, 0x29, 0x80, 0x4, 0x2, 0x30, 0xf, 0xff, + 0x2a, 0x80, 0x7d, 0x92, 0x3, 0x62, 0x0, 0x13, + 0x2, 0xc4, 0x0, 0xe1, 0xc2, 0x0, 0xf0, 0xe8, + 0x80, 0x61, 0x30, 0x3f, 0x30, 0xd, 0x90, 0x3, + 0x80, 0x1c, 0x80, 0x10, 0x80, 0x7f, 0xf9, 0x44, + 0x2, 0x30, 0xc, 0x40, 0x17, 0x21, 0x80, 0x4e, + 0x0, 0x3b, 0xf0, 0xa, 0x10, 0x3, 0xf5, 0xc1, + 0x0, 0x0, + + /* U+007C "|" */ + 0xdf, 0xf3, 0x80, 0x7f, 0xff, 0xc0, 0x3f, 0xff, + 0xa0, + + /* U+007D "}" */ + 0xff, 0x62, 0x80, 0x78, 0xea, 0x40, 0x3e, 0x65, + 0x0, 0x5d, 0x8, 0x2, 0x40, 0x8, 0xb6, 0x0, + 0x30, 0xc, 0xc0, 0x1f, 0xfe, 0x73, 0x0, 0x8e, + 0x0, 0x19, 0x20, 0x13, 0xd8, 0x83, 0x80, 0x64, + 0xd0, 0xf, 0x17, 0x0, 0x72, 0x69, 0x2, 0x0, + 0xa, 0xc0, 0x15, 0x40, 0x0, 0x80, 0x4a, 0x1, + 0xff, 0xdf, 0x70, 0xe, 0x35, 0x90, 0x1, 0x0, + 0x32, 0x88, 0x1, 0x60, 0x1e, 0x66, 0x0, 0x63, + 0xa9, 0x0, 0x80, + + /* U+007E "~" */ + 0x4, 0x67, 0x51, 0x0, 0xff, 0x5c, 0xc5, 0x75, + 0x18, 0x7, 0xec, 0xff, 0x6c, 0xbe, 0x59, 0x80, + 0x61, 0x3, 0x0, 0x13, 0x6d, 0x36, 0x6c, 0x4e, + 0xc8, 0x7, 0xcb, 0xb4, 0xce, 0xc3, 0x80, + + /* U+00A0 " " */ + + /* U+00A1 "¡" */ + 0xef, 0xf3, 0x0, 0x7e, 0xdb, 0xb3, 0x9a, 0x20, + 0x40, 0x3f, 0xf8, 0xf, 0x10, 0x39, 0x77, 0x28, + 0x7, 0xff, 0xfc, 0x0, + + /* U+00A2 "¢" */ + 0x0, 0xff, 0x47, 0xfb, 0x0, 0x3f, 0xff, 0xe0, + 0x1f, 0xfe, 0xd3, 0xbe, 0xff, 0xe7, 0x0, 0x1f, + 0xff, 0xf6, 0x82, 0xe2, 0x8, 0x7, 0xff, 0x1c, + 0x68, 0x3, 0xff, 0x94, 0xc0, 0x14, 0x5d, 0xf2, + 0x80, 0xa, 0xef, 0xfa, 0x74, 0x0, 0xae, 0x89, + 0xc4, 0x0, 0x14, 0x4f, 0xf0, 0x80, 0xc, 0x3, + 0xff, 0xfe, 0x1, 0xff, 0xff, 0x0, 0xff, 0xff, + 0x80, 0x7f, 0xff, 0xc0, 0x3f, 0x8, 0x0, 0xc0, + 0x3f, 0xf9, 0x1a, 0x0, 0x57, 0x44, 0xe2, 0x0, + 0xa, 0x27, 0xf1, 0xb0, 0x5, 0x17, 0x7c, 0xa0, + 0x2, 0xbb, 0xfe, 0xe1, 0xa0, 0xf, 0xfe, 0x5a, + 0xe2, 0x8, 0x7, 0xff, 0x24, 0xef, 0xbf, 0xf9, + 0xc0, 0x7, 0xff, 0xfd, 0xa0, 0x1f, 0xff, 0xf0, + 0xf, 0xaa, 0xa8, 0x0, 0xff, 0x0, + + /* U+00A3 "£" */ + 0x0, 0xf9, 0xf7, 0xff, 0xff, 0x83, 0xd2, 0x40, + 0x1f, 0xae, 0x8, 0x3, 0xff, 0x82, 0x2d, 0x86, + 0x1, 0xe8, 0x40, 0xf, 0xfe, 0x28, 0xe8, 0x7, + 0x9c, 0x0, 0x75, 0x77, 0xff, 0xc0, 0x60, 0x0, + 0xb0, 0x6, 0x20, 0xb, 0x95, 0x13, 0xff, 0x81, + 0x26, 0x0, 0xf0, 0xc, 0x20, 0x11, 0x80, 0x7f, + 0xf0, 0xa9, 0x99, 0x80, 0x1f, 0xfc, 0xb6, 0x99, + 0x28, 0x7, 0xff, 0xfc, 0x3, 0xff, 0xfe, 0x1, + 0xff, 0xc0, 0x7f, 0xfb, 0x0, 0x2e, 0xff, 0xff, + 0xe0, 0x90, 0x7, 0xff, 0xb9, 0xae, 0xe9, 0x0, + 0xae, 0xff, 0xf8, 0x44, 0x1, 0x85, 0x12, 0x50, + 0x9, 0x51, 0x3f, 0xf8, 0x20, 0x1f, 0xff, 0xf0, + 0xf, 0xff, 0xf8, 0x7, 0xff, 0x80, 0x51, 0x25, + 0x0, 0x95, 0x13, 0xff, 0x8a, 0x4d, 0x77, 0x48, + 0x5, 0x77, 0xff, 0xc6, 0x80, 0xf, 0xff, 0x50, + + /* U+00A8 "¨" */ + 0x44, 0x84, 0x2, 0x24, 0x41, 0x5d, 0xc8, 0x1, + 0x4d, 0xda, 0x0, 0x3f, 0xf9, 0x40, + + /* U+00AF "¯" */ + 0x24, 0x4f, 0xfe, 0x0, 0xd5, 0xdf, 0xff, 0x1, + 0x80, 0x3f, 0xfa, 0x0, + + /* U+00B0 "°" */ + 0x0, 0x9f, 0x7f, 0xfb, 0x60, 0x3, 0xb2, 0x8, + 0x3, 0x89, 0xf4, 0x40, 0x10, 0x62, 0xf1, 0x1c, + 0xe2, 0x56, 0x0, 0x70, 0xb8, 0x77, 0xe8, 0xa0, + 0x51, 0x20, 0x3, 0x0, 0x7c, 0xc0, 0x5, 0x10, + 0xf, 0xfe, 0x8, 0x80, 0x7f, 0xf4, 0x84, 0x3, + 0xff, 0x82, 0x24, 0x0, 0x60, 0xf, 0x98, 0x0, + 0xa0, 0xc1, 0x6e, 0xcd, 0xcf, 0x40, 0xa2, 0x12, + 0x63, 0x13, 0x3d, 0x2, 0x56, 0x1, 0x64, 0x10, + 0x7, 0x13, 0xe8, 0x80, + + /* U+00B4 "´" */ + 0x2, 0x88, 0x38, 0x2b, 0xbb, 0x3, 0x0, 0x18, + 0x8, 0x0, 0x43, 0x0, 0x8d, 0x0, 0x8, 0x0, + + /* U+00B6 "¶" */ + 0x0, 0x1d, 0xf7, 0xff, 0xff, 0x31, 0x41, 0x71, + 0x4, 0x3, 0xff, 0x9a, 0x54, 0x1, 0xff, 0xd0, + 0xa0, 0xa, 0x2e, 0xff, 0xf8, 0x4, 0x0, 0x5b, + 0xbe, 0x0, 0xc6, 0x0, 0x67, 0x44, 0xff, 0xe0, + 0x8, 0x0, 0x91, 0x38, 0x3, 0x38, 0x0, 0x40, + 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0xf, 0xff, + 0xf8, 0x7, 0xfc, 0xe0, 0x1f, 0xfd, 0x12, 0x0, + 0x31, 0x0, 0x7f, 0xf3, 0x68, 0x0, 0x3b, 0xff, + 0xff, 0xc0, 0x30, 0x3, 0xff, 0xf0, 0x6, 0x36, + 0x0, 0xff, 0xe8, 0xcc, 0x8, 0x7, 0xff, 0x41, + 0xfb, 0x72, 0xef, 0xfe, 0x20, 0x2, 0xdd, 0xf0, + 0x7, 0xe2, 0x34, 0x4f, 0xf8, 0x40, 0x4, 0x89, + 0xc0, 0x1f, 0xff, 0xf0, 0xf, 0xff, 0xf8, 0x7, + 0xff, 0xfc, 0x3, 0xff, 0xca, + + /* U+00B8 "¸" */ + 0x0, 0x9, 0x10, 0x40, 0x15, 0xba, 0x70, 0x2, + 0x0, 0x10, 0xc, 0x0, 0x24, 0x8, 0x0, 0x50, + 0x6, 0x0, 0x30, 0x0, 0x80, 0x4, 0x0, + + /* U+00BF "¿" */ + 0x0, 0xff, 0xe3, 0x77, 0xf9, 0x40, 0x3f, 0xff, + 0xe0, 0xa, 0xbb, 0x20, 0x7, 0xff, 0x2d, 0x12, + 0x10, 0xf, 0xff, 0xf8, 0x7, 0xff, 0x47, 0x37, + 0x48, 0x1, 0xff, 0xcb, 0x22, 0x84, 0x3, 0xff, + 0x96, 0x20, 0x1, 0x0, 0xfe, 0x24, 0x4f, 0xf9, + 0x6c, 0x2, 0x60, 0xf, 0x8b, 0x36, 0xef, 0xfe, + 0xa4, 0x0, 0x21, 0x0, 0x78, 0xb4, 0xc0, 0x3f, + 0xf8, 0x65, 0x40, 0x1f, 0x70, 0x7, 0xff, 0xc, + 0x9f, 0x4, 0x3, 0xc2, 0x60, 0x7, 0xff, 0xff, + 0xe0, 0x6c, 0x8, 0x7, 0xc4, 0x1, 0x48, 0x7, + 0xff, 0x38, 0x40, 0x3f, 0xff, 0xe0, 0x1f, 0xff, + 0xf0, 0xf, 0xfe, 0xd0, 0x80, 0x7f, 0xf2, 0x8c, + 0x2, 0xa4, 0x4f, 0xfe, 0x48, 0x1, 0x80, 0x9, + 0x77, 0xff, 0xc9, 0x30, 0x93, 0x0, 0xff, 0xe7, + 0x64, 0x10, 0x7, 0xff, 0x28, + + /* U+00C0 "À" */ + 0x0, 0xff, 0xe0, 0x3c, 0x40, 0x80, 0x3f, 0xf9, + 0xd8, 0xee, 0x50, 0xf, 0xfe, 0x76, 0x0, 0x30, + 0x3, 0xff, 0x9c, 0x80, 0x4, 0x0, 0xff, 0xe7, + 0x18, 0x4, 0x60, 0x1f, 0xfc, 0xe4, 0x0, 0x20, + 0x7, 0xff, 0x3a, 0xff, 0xce, 0x1, 0xff, 0xe1, + 0x3b, 0xef, 0xff, 0xfe, 0x46, 0xb8, 0x6, 0x5c, + 0x41, 0x0, 0xff, 0xe4, 0x14, 0x58, 0x0, 0x68, + 0x3, 0xff, 0x9a, 0x8e, 0xc, 0x1, 0x4e, 0x67, + 0xff, 0x1e, 0xcc, 0x1, 0x21, 0xa0, 0x5, 0x63, + 0x3f, 0xfe, 0x3a, 0x78, 0x0, 0x46, 0x0, 0x18, + 0x7, 0xff, 0x24, 0x40, 0x23, 0x0, 0xff, 0xff, + 0x80, 0x7f, 0xff, 0xc0, 0x3f, 0xff, 0xe0, 0x1f, + 0xfe, 0x46, 0xff, 0xff, 0xe4, 0xe8, 0x7, 0xff, + 0xdd, 0x73, 0x3f, 0xf9, 0x34, 0x1, 0xf1, 0x9f, + 0xff, 0x29, 0xc0, 0x3f, 0xff, 0xe0, 0x1f, 0xff, + 0xf0, 0xf, 0xff, 0xf8, 0x7, 0xff, 0x94, + + /* U+00C1 "Á" */ + 0x0, 0xff, 0xe1, 0x34, 0x40, 0x80, 0x3f, 0xf9, + 0xd2, 0xee, 0x0, 0xff, 0xe7, 0x20, 0x0, 0x48, + 0x3, 0xff, 0x9b, 0x80, 0x4, 0x0, 0xff, 0xe7, + 0x20, 0x3, 0x0, 0x3f, 0xf9, 0xc6, 0x0, 0x40, + 0xf, 0xfe, 0x77, 0xfc, 0x40, 0x1f, 0xfe, 0x13, + 0xbe, 0xff, 0xff, 0xe4, 0x6b, 0x80, 0x65, 0xc4, + 0x10, 0xf, 0xfe, 0x41, 0x45, 0x80, 0x6, 0x80, + 0x3f, 0xf9, 0xa8, 0xe0, 0xc0, 0x14, 0xe6, 0x7f, + 0xf1, 0xec, 0xc0, 0x12, 0x1a, 0x0, 0x56, 0x33, + 0xff, 0xe3, 0xa7, 0x80, 0x4, 0x60, 0x1, 0x80, + 0x7f, 0xf2, 0x44, 0x2, 0x30, 0xf, 0xff, 0xf8, + 0x7, 0xff, 0xfc, 0x3, 0xff, 0xfe, 0x1, 0xff, + 0xe4, 0x6f, 0xff, 0xfe, 0x4e, 0x80, 0x7f, 0xfd, + 0xd7, 0x33, 0xff, 0x93, 0x40, 0x1f, 0x19, 0xff, + 0xf2, 0x9c, 0x3, 0xff, 0xfe, 0x1, 0xff, 0xff, + 0x0, 0xff, 0xff, 0x80, 0x7f, 0xf9, 0x40, + + /* U+00C2 "Â" */ + 0x0, 0xff, 0xe0, 0xbc, 0x40, 0xc0, 0x3f, 0xf9, + 0xad, 0xe, 0xee, 0x0, 0xff, 0xe6, 0xc, 0x5, + 0x11, 0x1c, 0x3, 0xff, 0x97, 0x2, 0x6b, 0x21, + 0x2, 0x1, 0xff, 0xc9, 0x36, 0xf, 0x4, 0x40, + 0x58, 0x7, 0xff, 0x27, 0xc1, 0x4c, 0x1, 0x0, + 0xa8, 0x1, 0xff, 0xc8, 0xcf, 0xa0, 0x8, 0xff, + 0xc8, 0x1, 0xff, 0xfc, 0x3b, 0xef, 0xff, 0xfe, + 0x46, 0xb8, 0x6, 0x5c, 0x41, 0x0, 0xff, 0xe4, + 0x14, 0x58, 0x0, 0x68, 0x3, 0xff, 0x9a, 0x8e, + 0xc, 0x1, 0x4e, 0x67, 0xff, 0x1e, 0xcc, 0x1, + 0x21, 0xa0, 0x5, 0x63, 0x3f, 0xfe, 0x3a, 0x78, + 0x0, 0x46, 0x0, 0x18, 0x7, 0xff, 0x24, 0x40, + 0x23, 0x0, 0xff, 0xff, 0x80, 0x7f, 0xff, 0xc0, + 0x3f, 0xff, 0xe0, 0x1f, 0xfe, 0x46, 0xff, 0xff, + 0xe4, 0xe8, 0x7, 0xff, 0xdd, 0x73, 0x3f, 0xf9, + 0x34, 0x1, 0xf1, 0x9f, 0xff, 0x29, 0xc0, 0x3f, + 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0xf, 0xff, 0xf8, + 0x7, 0xff, 0x94, + + /* U+00C3 "Ã" */ + 0x0, 0xfd, 0x32, 0x85, 0x0, 0xff, 0xe6, 0x8b, + 0x31, 0xeb, 0x9c, 0x3, 0xff, 0xa0, 0x31, 0xa8, + 0x1, 0xff, 0xc9, 0x1f, 0xf6, 0xc9, 0x1, 0x5e, + 0x28, 0x80, 0x7f, 0xf2, 0x49, 0xb6, 0x84, 0xe, + 0xbb, 0xfd, 0x0, 0x1f, 0xfc, 0x95, 0xe7, 0x10, + 0xf, 0xfe, 0x84, 0x75, 0x3b, 0x33, 0x0, 0x3f, + 0xf9, 0xab, 0x13, 0x24, 0x0, 0xff, 0xfd, 0x1d, + 0xf7, 0xff, 0xff, 0x23, 0x5c, 0x3, 0x2e, 0x20, + 0x80, 0x7f, 0xf2, 0xa, 0x2c, 0x0, 0x34, 0x1, + 0xff, 0xcd, 0x47, 0x6, 0x0, 0xa7, 0x33, 0xff, + 0x8f, 0x66, 0x0, 0x90, 0xd0, 0x2, 0xb1, 0x9f, + 0xff, 0x1d, 0x3c, 0x0, 0x23, 0x0, 0xc, 0x3, + 0xff, 0x92, 0x20, 0x11, 0x80, 0x7f, 0xff, 0xc0, + 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0xf, 0xff, + 0x23, 0x7f, 0xff, 0xf2, 0x74, 0x3, 0xff, 0xee, + 0xb9, 0x9f, 0xfc, 0x9a, 0x0, 0xf8, 0xcf, 0xff, + 0x94, 0xe0, 0x1f, 0xff, 0xf0, 0xf, 0xff, 0xf8, + 0x7, 0xff, 0xfc, 0x3, 0xff, 0xca, + + /* U+00C4 "Ä" */ + 0x0, 0xfe, 0x24, 0x41, 0x0, 0x42, 0x89, 0x0, + 0x7f, 0xf1, 0xee, 0xe9, 0x0, 0x96, 0xee, 0x0, + 0xff, 0xff, 0x80, 0x69, 0xff, 0x58, 0x4, 0x9f, + 0xf0, 0x7, 0xff, 0x78, 0xef, 0xbf, 0xff, 0xf9, + 0x1a, 0xe0, 0x19, 0x71, 0x4, 0x3, 0xff, 0x90, + 0x51, 0x60, 0x1, 0xa0, 0xf, 0xfe, 0x6a, 0x38, + 0x30, 0x5, 0x39, 0x9f, 0xfc, 0x7b, 0x30, 0x4, + 0x86, 0x80, 0x15, 0x8c, 0xff, 0xf8, 0xe9, 0xe0, + 0x1, 0x18, 0x0, 0x60, 0x1f, 0xfc, 0x91, 0x0, + 0x8c, 0x3, 0xff, 0xfe, 0x1, 0xff, 0xff, 0x0, + 0xff, 0xff, 0x80, 0x7f, 0xf9, 0x1b, 0xff, 0xff, + 0x93, 0xa0, 0x1f, 0xff, 0x75, 0xcc, 0xff, 0xe4, + 0xd0, 0x7, 0xc6, 0x7f, 0xfc, 0xa7, 0x0, 0xff, + 0xff, 0x80, 0x7f, 0xff, 0xc0, 0x3f, 0xff, 0xe0, + 0x1f, 0xfe, 0x50, + + /* U+00C5 "Å" */ + 0x0, 0xff, 0xe0, 0x57, 0xfd, 0xa6, 0x1, 0xff, + 0xcb, 0x75, 0x53, 0x30, 0x34, 0x3, 0xff, 0x97, + 0xc1, 0x53, 0x24, 0x10, 0xf, 0xff, 0x4f, 0x5, + 0x4c, 0x90, 0x40, 0x3f, 0xf9, 0x6e, 0xaa, 0x66, + 0x5, 0x0, 0x7f, 0xf3, 0x2b, 0xfe, 0xd4, 0x0, + 0xff, 0xff, 0x1d, 0xf7, 0xff, 0xff, 0x23, 0x5c, + 0x3, 0x2e, 0x20, 0x80, 0x7f, 0xf2, 0xa, 0x2c, + 0x0, 0x34, 0x1, 0xff, 0xcd, 0x47, 0x6, 0x0, + 0xa7, 0x33, 0xff, 0x8f, 0x66, 0x0, 0x90, 0xd0, + 0x2, 0xb1, 0x9f, 0xff, 0x1d, 0x3c, 0x0, 0x23, + 0x0, 0xc, 0x3, 0xff, 0x92, 0x20, 0x11, 0x80, + 0x7f, 0xff, 0xc0, 0x3f, 0xff, 0xe0, 0x1f, 0xff, + 0xf0, 0xf, 0xff, 0x23, 0x7f, 0xff, 0xf2, 0x74, + 0x3, 0xff, 0xee, 0xb9, 0x9f, 0xfc, 0x9a, 0x0, + 0xf8, 0xcf, 0xff, 0x94, 0xe0, 0x1f, 0xff, 0xf0, + 0xf, 0xff, 0xf8, 0x7, 0xff, 0xfc, 0x3, 0xff, + 0xca, + + /* U+00C6 "Æ" */ + 0x0, 0x25, 0xff, 0xff, 0xf9, 0x1, 0xed, 0x0, + 0x3f, 0xfc, 0x87, 0x0, 0x1f, 0xfe, 0x69, 0x0, + 0xd, 0x66, 0x7f, 0xf1, 0xec, 0x80, 0x2c, 0xcf, + 0xfe, 0x5b, 0x0, 0x25, 0x4c, 0xff, 0xf8, 0xe9, + 0x0, 0x11, 0x9f, 0xff, 0x2c, 0x40, 0x2, 0x1, + 0xff, 0xc9, 0x10, 0xf, 0xff, 0xf8, 0x7, 0xff, + 0xfc, 0x3, 0xff, 0xfe, 0x1, 0xff, 0xff, 0x0, + 0xff, 0xff, 0x80, 0x7f, 0xf8, 0x3f, 0xff, 0xf8, + 0xce, 0x1, 0xff, 0xef, 0x9f, 0xff, 0xfe, 0x4d, + 0x80, 0x7f, 0xfa, 0xee, 0xff, 0xf8, 0xca, 0x1, + 0xff, 0xd5, 0x44, 0xff, 0xe3, 0x10, 0x7, 0xcf, + 0x99, 0xff, 0xc9, 0x80, 0xf, 0xfe, 0x8f, 0x19, + 0xff, 0xf2, 0x80, 0x3f, 0xff, 0xe0, 0x1f, 0xff, + 0xf0, 0xf, 0xff, 0xf8, 0x7, 0xff, 0xfc, 0x3, + 0xff, 0x86, 0x67, 0xff, 0xcb, 0x0, 0xff, 0xe8, + 0xe6, 0x7f, 0xf2, 0xc0, 0x3f, 0xff, 0xe0, 0x1f, + 0xfd, 0x10, + + /* U+00C7 "Ç" */ + 0x0, 0x25, 0xf7, 0xff, 0xff, 0x30, 0x1a, 0xd0, + 0x40, 0x3f, 0xf9, 0x85, 0x20, 0x1f, 0xfc, 0xfa, + 0x0, 0xab, 0x33, 0xff, 0x98, 0x60, 0x7, 0x53, + 0x3f, 0xfe, 0x63, 0x80, 0x3c, 0x3, 0xff, 0xfe, + 0x1, 0xff, 0xff, 0x0, 0xff, 0xff, 0x80, 0x7f, + 0xff, 0xc0, 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0, + 0xf, 0xff, 0xf8, 0x7, 0xff, 0x29, 0xc0, 0x1e, + 0x1, 0xff, 0xcd, 0x30, 0x3, 0xa9, 0x9f, 0xff, + 0x32, 0x80, 0x2a, 0xcc, 0xff, 0xe6, 0x14, 0x80, + 0x7f, 0xf4, 0x1a, 0xd0, 0x40, 0x3f, 0xf9, 0xc9, + 0x7d, 0xff, 0xfb, 0x80, 0x2e, 0xff, 0xff, 0x80, + 0x3f, 0xf8, 0x2a, 0x0, 0x21, 0x0, 0xff, 0xe6, + 0x60, 0x1, 0x0, 0x3f, 0xf9, 0xa8, 0x0, 0xc0, + 0xf, 0xfe, 0x60, 0x98, 0x1, 0x0, 0x3f, 0xf9, + 0x86, 0xee, 0x81, 0x0, 0xff, 0xe0, 0x0, + + /* U+00C8 "È" */ + 0x0, 0xff, 0x9a, 0x20, 0x80, 0x1f, 0xfc, 0xbe, + 0x77, 0x70, 0x7, 0xff, 0x2f, 0x0, 0x8, 0x1, + 0xff, 0xcb, 0x40, 0x0, 0x90, 0x7, 0xff, 0x2c, + 0xc0, 0x8, 0x1, 0xff, 0xcb, 0x40, 0x6, 0x0, + 0x7f, 0xf2, 0xe3, 0xfd, 0x40, 0x1f, 0xfd, 0xab, + 0xff, 0xff, 0xe6, 0xc8, 0x7, 0xff, 0xb5, 0x73, + 0x3f, 0xf9, 0x50, 0x1, 0x19, 0xff, 0xf2, 0xc4, + 0x3, 0xff, 0xfe, 0x1, 0xff, 0xff, 0x0, 0xff, + 0xfc, 0xb7, 0xff, 0xff, 0x18, 0x40, 0x3f, 0xfe, + 0x2b, 0x77, 0xff, 0xc6, 0x10, 0xf, 0x8d, 0x13, + 0xff, 0x8c, 0x1, 0xff, 0xff, 0x0, 0xff, 0xff, + 0x80, 0x7f, 0xf8, 0xcc, 0xff, 0xf9, 0x64, 0x1, + 0x2e, 0x67, 0xff, 0x2a, 0xc0, 0x3f, 0xfd, 0x40, + + /* U+00C9 "É" */ + 0x0, 0xff, 0xe0, 0xac, 0x41, 0x0, 0x3f, 0xf9, + 0x70, 0xee, 0x10, 0xf, 0xfe, 0x50, 0x90, 0x1, + 0x0, 0x3f, 0xf9, 0x48, 0x0, 0x21, 0x0, 0xff, + 0xe5, 0x60, 0x1, 0x0, 0x3f, 0xf9, 0x68, 0x0, + 0xc0, 0xf, 0xfe, 0x5e, 0xff, 0x94, 0x3, 0xff, + 0xb5, 0x7f, 0xff, 0xfc, 0xd9, 0x0, 0xff, 0xf6, + 0xae, 0x67, 0xff, 0x2a, 0x0, 0x23, 0x3f, 0xfe, + 0x58, 0x80, 0x7f, 0xff, 0xc0, 0x3f, 0xff, 0xe0, + 0x1f, 0xff, 0x96, 0xff, 0xff, 0xe3, 0x8, 0x7, + 0xff, 0xc5, 0x6e, 0xff, 0xf8, 0xc2, 0x1, 0xf1, + 0xa2, 0x7f, 0xf1, 0x80, 0x3f, 0xff, 0xe0, 0x1f, + 0xff, 0xf0, 0xf, 0xff, 0x19, 0x9f, 0xff, 0x2c, + 0x80, 0x25, 0xcc, 0xff, 0xe5, 0x58, 0x7, 0xff, + 0xa8, + + /* U+00CA "Ê" */ + 0x0, 0xff, 0xe0, 0x34, 0x41, 0x40, 0x3f, 0xf9, + 0x47, 0x2e, 0xea, 0x20, 0xf, 0xfe, 0x4f, 0x83, + 0xa8, 0x48, 0x7, 0xff, 0x21, 0x8c, 0x62, 0x0, + 0x88, 0x0, 0xff, 0xe3, 0xc, 0x4, 0x1, 0x38, + 0x48, 0x7, 0xff, 0x1a, 0x4, 0x98, 0x1, 0x22, + 0x4e, 0x1, 0xff, 0xc5, 0x9f, 0xd0, 0x8, 0x7b, + 0xdc, 0x3, 0xff, 0xe5, 0x7f, 0xff, 0xfc, 0xd9, + 0x0, 0xff, 0xf6, 0xae, 0x67, 0xff, 0x2a, 0x0, + 0x23, 0x3f, 0xfe, 0x58, 0x80, 0x7f, 0xff, 0xc0, + 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0x96, 0xff, 0xff, + 0xe3, 0x8, 0x7, 0xff, 0xc5, 0x6e, 0xff, 0xf8, + 0xc2, 0x1, 0xf1, 0xa2, 0x7f, 0xf1, 0x80, 0x3f, + 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0xf, 0xff, 0x19, + 0x9f, 0xff, 0x2c, 0x80, 0x25, 0xcc, 0xff, 0xe5, + 0x58, 0x7, 0xff, 0xa8, + + /* U+00CB "Ë" */ + 0x0, 0xfc, 0x28, 0x83, 0x0, 0xc8, 0x90, 0x7, + 0xff, 0x11, 0xee, 0xda, 0x1, 0xd, 0xdc, 0x60, + 0x1f, 0xff, 0x96, 0xff, 0x70, 0x4, 0x3f, 0xf1, + 0x80, 0x7f, 0xf5, 0xaf, 0xff, 0xff, 0x9b, 0x20, + 0x1f, 0xfe, 0xd5, 0xcc, 0xff, 0xe5, 0x40, 0x4, + 0x67, 0xff, 0xcb, 0x10, 0xf, 0xff, 0xf8, 0x7, + 0xff, 0xfc, 0x3, 0xff, 0xf2, 0xdf, 0xff, 0xfc, + 0x61, 0x0, 0xff, 0xf8, 0xad, 0xdf, 0xff, 0x18, + 0x40, 0x3e, 0x34, 0x4f, 0xfe, 0x30, 0x7, 0xff, + 0xfc, 0x3, 0xff, 0xfe, 0x1, 0xff, 0xe3, 0x33, + 0xff, 0xe5, 0x90, 0x4, 0xb9, 0x9f, 0xfc, 0xab, + 0x0, 0xff, 0xf5, 0x0, + + /* U+00CC "Ì" */ + 0x28, 0x83, 0x80, 0xd, 0xdd, 0x2, 0x2, 0x40, + 0x4, 0x0, 0x20, 0x3, 0x0, 0x18, 0x0, 0x40, + 0x2, 0x0, 0x8, 0x40, 0x7f, 0xe1, 0x0, 0xf9, + 0x7f, 0xdc, 0x1, 0xff, 0xff, 0x0, 0xff, 0xff, + 0x80, 0x7f, 0xf1, 0xc0, + + /* U+00CD "Í" */ + 0x1, 0x88, 0x80, 0xe, 0xf2, 0x0, 0x30, 0x0, + 0x80, 0x4, 0x0, 0x60, 0x10, 0x80, 0x14, 0x10, + 0x0, 0x62, 0xd, 0xfe, 0xc0, 0xf, 0xcb, 0xfe, + 0xe0, 0xf, 0xff, 0xf8, 0x7, 0xff, 0xfc, 0x3, + 0xff, 0x90, + + /* U+00CE "Î" */ + 0x0, 0x8a, 0x20, 0xe0, 0x1f, 0x73, 0xba, 0x20, + 0x1, 0xcc, 0x63, 0x60, 0xe4, 0x1, 0xa0, 0x21, + 0x14, 0x24, 0x2, 0x81, 0x26, 0x8, 0x4, 0x50, + 0x27, 0x9, 0x0, 0x13, 0x84, 0x1, 0x7f, 0x90, + 0x2, 0x8f, 0xd0, 0xf, 0xfe, 0x72, 0xff, 0xb8, + 0x3, 0xff, 0xfe, 0x1, 0xff, 0xff, 0x0, 0xff, + 0xff, 0x80, 0x7f, 0xff, 0xc0, 0x3f, 0xf8, 0x0, + + /* U+00CF "Ï" */ + 0x34, 0x40, 0x80, 0x44, 0x88, 0x2c, 0xbb, 0x20, + 0x5, 0x17, 0x69, 0x0, 0xff, 0xe5, 0x7f, 0xca, + 0x1, 0x57, 0xfa, 0xc0, 0x3f, 0xf8, 0x4b, 0xfe, + 0xe0, 0xf, 0xff, 0xf8, 0x7, 0xff, 0xfc, 0x3, + 0xff, 0xfe, 0x1, 0xff, 0xff, 0x0, 0xff, 0xe0, + 0x0, + + /* U+00D1 "Ñ" */ + 0x0, 0xf9, 0xa6, 0x4e, 0x40, 0x1f, 0xfc, 0xdd, + 0x66, 0x46, 0xd9, 0x0, 0x7f, 0xf4, 0x13, 0x60, + 0x3, 0xff, 0x95, 0x7f, 0xd6, 0xa0, 0x7, 0xe8, + 0x30, 0xf, 0xfe, 0x48, 0xa5, 0x6a, 0x0, 0xbe, + 0x7f, 0xb8, 0x3, 0xff, 0x92, 0x57, 0x63, 0x0, + 0xff, 0xe8, 0x26, 0x62, 0x19, 0xa0, 0xf, 0xfe, + 0x69, 0xbc, 0xca, 0x0, 0x3f, 0xff, 0x39, 0xff, + 0x88, 0x3, 0xff, 0x8e, 0x7f, 0xf0, 0x7, 0xbc, + 0x40, 0x3f, 0xfa, 0x5, 0xa0, 0x1f, 0xfd, 0x13, + 0x90, 0xf, 0xfe, 0x6b, 0x98, 0x1, 0x9c, 0x3, + 0xff, 0x99, 0xfa, 0x20, 0x8, 0x40, 0xf, 0xfe, + 0x60, 0xe8, 0x5, 0x64, 0x1, 0xff, 0xcc, 0x3a, + 0x0, 0xbc, 0x40, 0x3f, 0xf9, 0x8a, 0xe0, 0x2, + 0xd0, 0xf, 0xfe, 0x6c, 0x28, 0x0, 0xe4, 0x3, + 0xff, 0x9b, 0x46, 0x0, 0x67, 0x0, 0xff, 0xe6, + 0xe8, 0x80, 0x21, 0x0, 0x3f, 0xf9, 0x83, 0xa0, + 0x15, 0x90, 0x7, 0xff, 0x30, 0xe8, 0x2, 0xf1, + 0x0, 0xff, 0xe6, 0x2b, 0x80, 0xb, 0x40, 0x3f, + 0xf9, 0xb0, 0xa0, 0x3, 0x90, 0xf, 0xfe, 0x6d, + 0x18, 0x1, 0x9c, 0x3, 0xff, 0x9b, 0xa2, 0x0, + 0x84, 0x0, 0xff, 0xe6, 0xe, 0x80, 0x56, 0x40, + 0x1f, 0xfc, 0xc3, 0xa0, 0xb, 0xc4, 0x3, 0xff, + 0x98, 0xae, 0x0, 0x2d, 0x0, 0xff, 0xe6, 0xc2, + 0x80, 0xe, 0x80, 0x3f, 0xf9, 0xb4, 0x60, 0x5, + 0x70, 0xf, 0xfe, 0x6e, 0x88, 0x2, 0x10, 0x3, + 0xff, 0x98, 0x3a, 0x1, 0x5a, 0x80, 0x7f, 0xf3, + 0xe, 0x80, 0x29, 0x0, 0xff, 0xe6, 0xac, 0x0, + 0x7f, 0xf4, 0x5d, 0x40, 0x3f, 0xfa, 0x34, 0x60, + 0x1c, + + /* U+00D2 "Ò" */ + 0x0, 0xff, 0xe0, 0x44, 0x42, 0x1, 0xff, 0xcd, + 0x67, 0x72, 0x0, 0x7f, 0xf3, 0x50, 0x1, 0x80, + 0x1f, 0xfc, 0xdc, 0x0, 0x20, 0x7, 0xff, 0x35, + 0x0, 0x4, 0x20, 0x1f, 0xfc, 0xc2, 0x10, 0x2, + 0x0, 0x7f, 0xf3, 0x7b, 0xfc, 0xa0, 0x1f, 0xfd, + 0xf4, 0xbf, 0xff, 0xfe, 0x4e, 0x28, 0x4, 0xf6, + 0x80, 0x1f, 0xfc, 0x93, 0xa9, 0x3, 0x80, 0xf, + 0xfe, 0x6b, 0x2c, 0x80, 0x6, 0xb3, 0x3f, 0xf8, + 0xf6, 0x40, 0xe, 0x60, 0x4, 0xa9, 0x9f, 0xff, + 0x1d, 0x20, 0x0, 0xa2, 0x0, 0x10, 0xf, 0xfe, + 0x48, 0x80, 0x4, 0x3, 0xff, 0xfe, 0x1, 0xff, + 0xff, 0x0, 0xff, 0xff, 0x80, 0x7f, 0xff, 0xc0, + 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0xf, 0xff, + 0xe0, 0x80, 0x4, 0x3, 0xff, 0x92, 0x20, 0x1, + 0x60, 0x4, 0xa9, 0x9f, 0xff, 0x1d, 0x20, 0x0, + 0xb2, 0x0, 0x1a, 0xcc, 0xff, 0xe3, 0xd9, 0x0, + 0x38, 0xe0, 0x3, 0xff, 0x9a, 0xca, 0xf, 0x68, + 0x1, 0xff, 0xc9, 0x3a, 0x90, + + /* U+00D3 "Ó" */ + 0x0, 0xff, 0xe1, 0x44, 0x42, 0x1, 0xff, 0xcc, + 0x27, 0x78, 0x40, 0x3f, 0xf9, 0x88, 0x0, 0x40, + 0xf, 0xfe, 0x6e, 0x0, 0x30, 0x3, 0xff, 0x9a, + 0x80, 0x4, 0x0, 0xff, 0xe6, 0x18, 0x80, 0xc, + 0x3, 0xff, 0x98, 0x7f, 0xf0, 0x7, 0xff, 0x81, + 0x2f, 0xff, 0xff, 0x93, 0x8a, 0x1, 0x3d, 0xa0, + 0x7, 0xff, 0x24, 0xea, 0x40, 0xe0, 0x3, 0xff, + 0x9a, 0xcb, 0x20, 0x1, 0xac, 0xcf, 0xfe, 0x3d, + 0x90, 0x3, 0x98, 0x1, 0x2a, 0x67, 0xff, 0xc7, + 0x48, 0x0, 0x28, 0x80, 0x4, 0x3, 0xff, 0x92, + 0x20, 0x1, 0x0, 0xff, 0xff, 0x80, 0x7f, 0xff, + 0xc0, 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0xf, + 0xff, 0xf8, 0x7, 0xff, 0xfc, 0x3, 0xff, 0xf8, + 0x20, 0x1, 0x0, 0xff, 0xe4, 0x88, 0x0, 0x58, + 0x1, 0x2a, 0x67, 0xff, 0xc7, 0x48, 0x0, 0x2c, + 0x80, 0x6, 0xb3, 0x3f, 0xf8, 0xf6, 0x40, 0xe, + 0x38, 0x0, 0xff, 0xe6, 0xb2, 0x83, 0xda, 0x0, + 0x7f, 0xf2, 0x4e, 0xa4, 0x0, + + /* U+00D4 "Ô" */ + 0x0, 0xff, 0xe0, 0xc, 0x44, 0x40, 0x1f, 0xfc, + 0xc8, 0x77, 0xa4, 0x3, 0xff, 0x96, 0x6c, 0x16, + 0x28, 0xa0, 0x1f, 0xfc, 0xaf, 0x6, 0x49, 0xb, + 0x0, 0xff, 0xe4, 0xa9, 0x84, 0x3, 0x88, 0xa0, + 0x3, 0xff, 0x91, 0x61, 0x2, 0x0, 0xb0, 0x72, + 0x0, 0xff, 0xe3, 0xf7, 0xb8, 0x4, 0xbf, 0xe2, + 0x0, 0xff, 0xfc, 0xa5, 0xff, 0xff, 0xf2, 0x71, + 0x40, 0x27, 0xb4, 0x0, 0xff, 0xe4, 0x9d, 0x48, + 0x1c, 0x0, 0x7f, 0xf3, 0x59, 0x64, 0x0, 0x35, + 0x99, 0xff, 0xc7, 0xb2, 0x0, 0x73, 0x0, 0x25, + 0x4c, 0xff, 0xf8, 0xe9, 0x0, 0x5, 0x10, 0x0, + 0x80, 0x7f, 0xf2, 0x44, 0x0, 0x20, 0x1f, 0xff, + 0xf0, 0xf, 0xff, 0xf8, 0x7, 0xff, 0xfc, 0x3, + 0xff, 0xfe, 0x1, 0xff, 0xff, 0x0, 0xff, 0xff, + 0x80, 0x7f, 0xff, 0x4, 0x0, 0x20, 0x1f, 0xfc, + 0x91, 0x0, 0xb, 0x0, 0x25, 0x4c, 0xff, 0xf8, + 0xe9, 0x0, 0x5, 0x90, 0x0, 0xd6, 0x67, 0xff, + 0x1e, 0xc8, 0x1, 0xc7, 0x0, 0x1f, 0xfc, 0xd6, + 0x50, 0x7b, 0x40, 0xf, 0xfe, 0x49, 0xd4, 0x80, + + /* U+00D5 "Õ" */ + 0x0, 0xf8, 0xa6, 0x50, 0x80, 0x1f, 0xfc, 0xc1, + 0x66, 0x3d, 0xf3, 0x0, 0x7f, 0xf3, 0xc6, 0x70, + 0xc0, 0x3f, 0xf9, 0x7, 0xfe, 0xd8, 0x20, 0x3c, + 0xb5, 0x10, 0xf, 0xfe, 0x41, 0x3e, 0xc0, 0x1, + 0x2b, 0xbf, 0xca, 0x1, 0xff, 0xc8, 0x7e, 0x60, + 0xf, 0xfe, 0x78, 0xcf, 0x53, 0xb3, 0x40, 0x1f, + 0xfc, 0xb1, 0x58, 0x99, 0x18, 0x7, 0xff, 0xdd, + 0x2f, 0xff, 0xff, 0x93, 0x8a, 0x1, 0x3d, 0xa0, + 0x7, 0xff, 0x24, 0xea, 0x40, 0xe0, 0x3, 0xff, + 0x9a, 0xcb, 0x20, 0x1, 0xac, 0xcf, 0xfe, 0x3d, + 0x90, 0x3, 0x98, 0x1, 0x2a, 0x67, 0xff, 0xc7, + 0x48, 0x0, 0x28, 0x80, 0x4, 0x3, 0xff, 0x92, + 0x20, 0x1, 0x0, 0xff, 0xff, 0x80, 0x7f, 0xff, + 0xc0, 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0xf, + 0xff, 0xf8, 0x7, 0xff, 0xfc, 0x3, 0xff, 0xf8, + 0x20, 0x1, 0x0, 0xff, 0xe4, 0x88, 0x0, 0x58, + 0x1, 0x2a, 0x67, 0xff, 0xc7, 0x48, 0x0, 0x2c, + 0x80, 0x6, 0xb3, 0x3f, 0xf8, 0xf6, 0x40, 0xe, + 0x38, 0x0, 0xff, 0xe6, 0xb2, 0x83, 0xda, 0x0, + 0x7f, 0xf2, 0x4e, 0xa4, 0x0, + + /* U+00D6 "Ö" */ + 0x0, 0xfe, 0x34, 0x41, 0x0, 0x42, 0x88, 0x30, + 0xf, 0xfe, 0x37, 0xdd, 0xc0, 0x13, 0x5d, 0xb4, + 0x3, 0xff, 0xfe, 0x19, 0xfe, 0x90, 0x9, 0xff, + 0xdc, 0x1, 0xff, 0xdc, 0x4b, 0xff, 0xff, 0xe4, + 0xe2, 0x80, 0x4f, 0x68, 0x1, 0xff, 0xc9, 0x3a, + 0x90, 0x38, 0x0, 0xff, 0xe6, 0xb2, 0xc8, 0x0, + 0x6b, 0x33, 0xff, 0x8f, 0x64, 0x0, 0xe6, 0x0, + 0x4a, 0x99, 0xff, 0xf1, 0xd2, 0x0, 0xa, 0x20, + 0x1, 0x0, 0xff, 0xe4, 0x88, 0x0, 0x40, 0x3f, + 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0xf, 0xff, 0xf8, + 0x7, 0xff, 0xfc, 0x3, 0xff, 0xfe, 0x1, 0xff, + 0xff, 0x0, 0xff, 0xfe, 0x8, 0x0, 0x40, 0x3f, + 0xf9, 0x22, 0x0, 0x16, 0x0, 0x4a, 0x99, 0xff, + 0xf1, 0xd2, 0x0, 0xb, 0x20, 0x1, 0xac, 0xcf, + 0xfe, 0x3d, 0x90, 0x3, 0x8e, 0x0, 0x3f, 0xf9, + 0xac, 0xa0, 0xf6, 0x80, 0x1f, 0xfc, 0x93, 0xa9, + 0x0, + + /* U+00D7 "×" */ + 0x23, 0x38, 0x40, 0x3f, 0x19, 0xe0, 0x1c, 0xcb, + 0xc4, 0x3, 0xd5, 0x99, 0x58, 0x1c, 0x80, 0x6, + 0x80, 0x39, 0x94, 0x0, 0x90, 0x0, 0x65, 0x0, + 0x23, 0x80, 0x47, 0x20, 0x1, 0xa0, 0xd, 0x44, + 0x0, 0x83, 0x0, 0x70, 0x5, 0xa2, 0x1, 0xde, + 0x1, 0x68, 0xd1, 0x0, 0x24, 0xc0, 0x3c, 0x54, + 0x0, 0x1f, 0x50, 0x3, 0x30, 0x3, 0xf2, 0xb8, + 0x0, 0x40, 0x7, 0x20, 0x1f, 0xe8, 0x30, 0xd, + 0xc0, 0x1f, 0xf0, 0x90, 0x6, 0x70, 0xf, 0xfb, + 0x44, 0x3, 0x51, 0x0, 0x7f, 0x49, 0x80, 0x14, + 0x2, 0xf0, 0xf, 0xca, 0xc0, 0x4, 0xa8, 0x0, + 0x14, 0x80, 0x78, 0xa8, 0x0, 0x34, 0xe, 0x80, + 0x6, 0x60, 0x7, 0x78, 0x5, 0x42, 0x0, 0xa1, + 0x0, 0x49, 0x80, 0x54, 0x40, 0x7, 0x40, 0x8, + 0x68, 0x2, 0xe1, 0x7, 0x50, 0x1, 0xc0, 0x7, + 0x24, 0x0, 0xa, 0x80, + + /* U+00D9 "Ù" */ + 0x0, 0xff, 0xe0, 0x44, 0x42, 0x1, 0xff, 0xcd, + 0x67, 0x72, 0x0, 0x7f, 0xf3, 0x50, 0x1, 0x80, + 0x1f, 0xfc, 0xdc, 0x0, 0x20, 0x7, 0xff, 0x35, + 0x0, 0x4, 0x20, 0x1f, 0xfc, 0xc2, 0x10, 0x2, + 0x0, 0x7f, 0xf3, 0x7b, 0xfc, 0xa0, 0x1f, 0xfd, + 0xdd, 0xff, 0x30, 0x7, 0xff, 0x25, 0x3f, 0xe0, + 0xf, 0xff, 0xf8, 0x7, 0xff, 0xfc, 0x3, 0xff, + 0xfe, 0x1, 0xff, 0xff, 0x0, 0xff, 0xff, 0x80, + 0x7f, 0xff, 0xc0, 0x3f, 0xff, 0xe0, 0x1f, 0xff, + 0xf0, 0xf, 0xff, 0xb8, 0x80, 0x4, 0x3, 0xff, + 0x92, 0x20, 0x1, 0x60, 0x4, 0xa9, 0x9f, 0xff, + 0x1d, 0x20, 0x0, 0xb2, 0x0, 0x1a, 0xcc, 0xff, + 0xe3, 0xd9, 0x0, 0x38, 0xe0, 0x3, 0xff, 0x9a, + 0xca, 0xf, 0x68, 0x1, 0xff, 0xc9, 0x3a, 0x90, + + /* U+00DA "Ú" */ + 0x0, 0xff, 0xe1, 0x44, 0x42, 0x1, 0xff, 0xcc, + 0x27, 0x78, 0x40, 0x3f, 0xf9, 0x88, 0x0, 0x40, + 0xf, 0xfe, 0x6e, 0x0, 0x30, 0x3, 0xff, 0x9a, + 0x80, 0x4, 0x0, 0xff, 0xe6, 0x18, 0x80, 0xc, + 0x3, 0xff, 0x98, 0x7f, 0xf0, 0x7, 0xff, 0x7b, + 0x7f, 0xcc, 0x1, 0xff, 0xc9, 0x4f, 0xf8, 0x3, + 0xff, 0xfe, 0x1, 0xff, 0xff, 0x0, 0xff, 0xff, + 0x80, 0x7f, 0xff, 0xc0, 0x3f, 0xff, 0xe0, 0x1f, + 0xff, 0xf0, 0xf, 0xff, 0xf8, 0x7, 0xff, 0xfc, + 0x3, 0xff, 0xee, 0x20, 0x1, 0x0, 0xff, 0xe4, + 0x88, 0x0, 0x58, 0x1, 0x2a, 0x67, 0xff, 0xc7, + 0x48, 0x0, 0x2c, 0x80, 0x6, 0xb3, 0x3f, 0xf8, + 0xf6, 0x40, 0xe, 0x38, 0x0, 0xff, 0xe6, 0xb2, + 0x83, 0xda, 0x0, 0x7f, 0xf2, 0x4e, 0xa4, 0x0, + + /* U+00DB "Û" */ + 0x0, 0xff, 0xe0, 0xc, 0x44, 0x40, 0x1f, 0xfc, + 0xc8, 0x77, 0xa4, 0x3, 0xff, 0x96, 0x6c, 0x16, + 0x28, 0xa0, 0x1f, 0xfc, 0xaf, 0x6, 0x49, 0xb, + 0x0, 0xff, 0xe4, 0xa9, 0x84, 0x3, 0x88, 0xa0, + 0x3, 0xff, 0x91, 0x61, 0x2, 0x0, 0xb0, 0x72, + 0x0, 0xff, 0xe3, 0xf7, 0xb8, 0x4, 0xbf, 0xe2, + 0x0, 0xff, 0xfb, 0xef, 0xf9, 0x80, 0x3f, 0xf9, + 0x29, 0xff, 0x0, 0x7f, 0xff, 0xc0, 0x3f, 0xff, + 0xe0, 0x1f, 0xff, 0xf0, 0xf, 0xff, 0xf8, 0x7, + 0xff, 0xfc, 0x3, 0xff, 0xfe, 0x1, 0xff, 0xff, + 0x0, 0xff, 0xff, 0x80, 0x7f, 0xfd, 0xc4, 0x0, + 0x20, 0x1f, 0xfc, 0x91, 0x0, 0xb, 0x0, 0x25, + 0x4c, 0xff, 0xf8, 0xe9, 0x0, 0x5, 0x90, 0x0, + 0xd6, 0x67, 0xff, 0x1e, 0xc8, 0x1, 0xc7, 0x0, + 0x1f, 0xfc, 0xd6, 0x50, 0x7b, 0x40, 0xf, 0xfe, + 0x49, 0xd4, 0x80, + + /* U+00DC "Ü" */ + 0x0, 0xfe, 0x34, 0x41, 0x0, 0x42, 0x88, 0x30, + 0xf, 0xfe, 0x37, 0xdd, 0xc0, 0x13, 0x5d, 0xb4, + 0x3, 0xff, 0xfe, 0x19, 0xfe, 0x90, 0x9, 0xff, + 0xdc, 0x1, 0xff, 0xda, 0xdf, 0xf3, 0x0, 0x7f, + 0xf2, 0x53, 0xfe, 0x0, 0xff, 0xff, 0x80, 0x7f, + 0xff, 0xc0, 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0, + 0xf, 0xff, 0xf8, 0x7, 0xff, 0xfc, 0x3, 0xff, + 0xfe, 0x1, 0xff, 0xff, 0x0, 0xff, 0xfb, 0x88, + 0x0, 0x40, 0x3f, 0xf9, 0x22, 0x0, 0x16, 0x0, + 0x4a, 0x99, 0xff, 0xf1, 0xd2, 0x0, 0xb, 0x20, + 0x1, 0xac, 0xcf, 0xfe, 0x3d, 0x90, 0x3, 0x8e, + 0x0, 0x3f, 0xf9, 0xac, 0xa0, 0xf6, 0x80, 0x1f, + 0xfc, 0x93, 0xa9, 0x0, + + /* U+00DD "Ý" */ + 0x0, 0xff, 0xe1, 0x1c, 0x41, 0x40, 0x3f, 0xf9, + 0xd2, 0xee, 0xd0, 0xf, 0xfe, 0x72, 0x0, 0x30, + 0x3, 0xff, 0x9a, 0x42, 0x0, 0x40, 0xf, 0xfe, + 0x6a, 0x0, 0xc, 0x3, 0xff, 0x9d, 0x80, 0x4, + 0x0, 0xff, 0xe7, 0x57, 0xfa, 0x0, 0x3f, 0xfb, + 0xeb, 0xff, 0x30, 0x7, 0xff, 0x20, 0xff, 0xea, + 0x56, 0x0, 0x49, 0x80, 0x7f, 0xf1, 0x87, 0x40, + 0x5, 0x41, 0x24, 0x0, 0xd1, 0x0, 0xff, 0xe2, + 0xd0, 0x80, 0x38, 0x2, 0xf0, 0x0, 0xd0, 0x7, + 0xff, 0x12, 0x10, 0x1, 0x26, 0x1, 0x14, 0x80, + 0x12, 0x0, 0x3f, 0xf8, 0x48, 0xe0, 0x4, 0x60, + 0xe, 0x64, 0x0, 0x3a, 0x80, 0x7f, 0xf0, 0xa, + 0xc0, 0x3, 0x40, 0x1f, 0x50, 0x80, 0x28, 0x80, + 0x3f, 0xef, 0x0, 0xa8, 0x40, 0x3e, 0x1d, 0x0, + 0xbc, 0x3, 0xfd, 0x44, 0x0, 0x74, 0x0, 0xfe, + 0x38, 0x0, 0x15, 0x0, 0x7e, 0x65, 0x0, 0x1c, + 0x0, 0x7f, 0xce, 0x60, 0x5, 0x70, 0xf, 0x1c, + 0x80, 0x7, 0x40, 0x3f, 0xf8, 0x3a, 0x20, 0x8, + 0x40, 0xc, 0x3a, 0x1, 0x58, 0x80, 0x7f, 0xf0, + 0x46, 0x80, 0x2a, 0x20, 0xa, 0x84, 0x0, 0xca, + 0x1, 0xff, 0xc3, 0x46, 0x0, 0xf, 0x0, 0x21, + 0x0, 0x5, 0x20, 0x1f, 0xfc, 0x59, 0x30, 0x1, + 0xca, 0xb8, 0x5, 0xe0, 0x1f, 0xfc, 0x7e, 0x0, + 0x9a, 0x80, 0x29, 0x20, 0xf, 0xfe, 0x39, 0x50, + 0x7, 0x95, 0x80, 0x3f, 0xf9, 0x2a, 0xa0, 0xc, + 0x54, 0x1, 0xff, 0xcb, 0xa2, 0x0, 0xb8, 0x3, + 0xff, 0x9b, 0x40, 0x3, 0x30, 0x7, 0xff, 0xfc, + 0x3, 0xff, 0xfe, 0x1, 0xff, 0xff, 0x0, 0xff, + 0xff, 0x80, 0x7f, 0xf0, 0x0, + + /* U+00DF "ß" */ + 0x0, 0x1d, 0x77, 0xff, 0xff, 0x23, 0x5c, 0x3, + 0x2e, 0x28, 0x80, 0x7f, 0xf2, 0xa, 0x2c, 0x0, + 0x34, 0x1, 0xff, 0xcd, 0x49, 0x6, 0x0, 0x9e, + 0xab, 0xff, 0x90, 0x80, 0x6, 0x2d, 0x0, 0x2c, + 0x2a, 0xff, 0xf2, 0x2c, 0x2, 0x71, 0x0, 0x18, + 0x7, 0xff, 0x28, 0x40, 0x3f, 0xff, 0xe0, 0x1f, + 0xff, 0xf0, 0xf, 0xfe, 0x88, 0x80, 0x4, 0x3, + 0xff, 0x9a, 0xe0, 0x12, 0x80, 0x7f, 0x57, 0xff, + 0xff, 0xe, 0x0, 0xc, 0x20, 0x1f, 0xfd, 0xf, + 0x0, 0xff, 0x11, 0x7f, 0xf1, 0x0, 0x28, 0x0, + 0xff, 0x46, 0xef, 0xff, 0xe, 0xc0, 0x2, 0x60, + 0x1f, 0xfc, 0xd4, 0x10, 0x3, 0x0, 0x7f, 0xff, + 0xc0, 0x3f, 0xff, 0xe0, 0x1f, 0xfe, 0xc1, 0x0, + 0xff, 0xe0, 0x12, 0x27, 0xff, 0xe, 0x80, 0x27, + 0x0, 0xfe, 0x8b, 0xbf, 0xfe, 0x1a, 0x80, 0x10, + 0x80, 0x3f, 0xf9, 0xe5, 0x40, 0x1f, 0xfc, 0xe2, + 0x7d, 0x10, + + /* U+00E0 "à" */ + 0x0, 0xff, 0x77, 0xf9, 0x40, 0x3f, 0xf9, 0x8, + 0x0, 0xc0, 0xf, 0xfe, 0x46, 0x0, 0x10, 0x3, + 0xff, 0x90, 0x80, 0x2, 0x10, 0xf, 0xfe, 0x39, + 0x8, 0x1, 0x40, 0x3f, 0xf9, 0x16, 0xee, 0x0, + 0xff, 0xe4, 0xac, 0x41, 0x0, 0x3f, 0xfa, 0xbd, + 0xff, 0xff, 0xc6, 0xea, 0x30, 0xf, 0xfe, 0x48, + 0xae, 0x28, 0x7, 0xff, 0x2e, 0x87, 0x6e, 0xff, + 0xf8, 0xd0, 0x1, 0x49, 0xa2, 0x7f, 0xf1, 0x9d, + 0x40, 0x4, 0x1, 0xff, 0xc8, 0x30, 0x0, 0x80, + 0x7f, 0xff, 0xc0, 0x3f, 0xf9, 0x3d, 0xff, 0xff, + 0xc7, 0x60, 0xf, 0xff, 0x53, 0xdd, 0xff, 0xf1, + 0x10, 0x3, 0xda, 0x89, 0xff, 0xc4, 0x20, 0xf, + 0xff, 0xf8, 0x4, 0x60, 0x1f, 0xfc, 0x85, 0x0, + 0x42, 0xa2, 0x7f, 0xf0, 0xc8, 0x2, 0xf0, 0x0, + 0xd5, 0xdf, 0xff, 0xd, 0x0, 0x24, 0x70, 0xf, + 0xfe, 0x5c, 0x51, 0x80, 0x7f, 0xf2, 0x0, + + /* U+00E1 "á" */ + 0x0, 0xff, 0xe0, 0x77, 0xf8, 0xc0, 0x3f, 0xf8, + 0xe4, 0x20, 0x3, 0x0, 0xff, 0xe3, 0xa0, 0x1, + 0x0, 0x3f, 0xf9, 0x1a, 0x0, 0xc0, 0xf, 0xfe, + 0x42, 0x80, 0x10, 0x3, 0xff, 0x8e, 0x2c, 0xee, + 0x30, 0xf, 0xfe, 0x38, 0xc4, 0x40, 0x1f, 0xfd, + 0x6e, 0xff, 0xff, 0xe3, 0x75, 0x18, 0x7, 0xff, + 0x24, 0x57, 0x14, 0x3, 0xff, 0x97, 0x43, 0xb7, + 0x7f, 0xfc, 0x68, 0x0, 0xa4, 0xd1, 0x3f, 0xf8, + 0xce, 0xa0, 0x2, 0x0, 0xff, 0xe4, 0x18, 0x0, + 0x40, 0x3f, 0xff, 0xe0, 0x1f, 0xfc, 0x9e, 0xff, + 0xff, 0xe3, 0xb0, 0x7, 0xff, 0xa9, 0xee, 0xff, + 0xf8, 0x88, 0x1, 0xed, 0x44, 0xff, 0xe2, 0x10, + 0x7, 0xff, 0xfc, 0x2, 0x30, 0xf, 0xfe, 0x42, + 0x80, 0x21, 0x51, 0x3f, 0xf8, 0x64, 0x1, 0x78, + 0x0, 0x6a, 0xef, 0xff, 0x86, 0x80, 0x12, 0x38, + 0x7, 0xff, 0x2e, 0x28, 0xc0, 0x3f, 0xf9, 0x0, + + /* U+00E2 "â" */ + 0x0, 0xff, 0x17, 0xfd, 0x20, 0x1f, 0xfc, 0x7e, + 0x4, 0x6, 0x20, 0xf, 0xfe, 0x2a, 0x98, 0x53, + 0x4, 0x80, 0x7f, 0xf1, 0x6c, 0x20, 0x60, 0x11, + 0x40, 0x3f, 0xf8, 0x70, 0x24, 0xe0, 0x2e, 0x16, + 0x1, 0xff, 0xc3, 0xf7, 0xe0, 0xa, 0x20, 0xc4, + 0x1, 0xff, 0xc2, 0x78, 0x30, 0xc, 0xf0, 0x40, + 0x1f, 0xfe, 0xae, 0xff, 0xff, 0xe3, 0x75, 0x18, + 0x7, 0xff, 0x24, 0x57, 0x14, 0x3, 0xff, 0x97, + 0x43, 0xb7, 0x7f, 0xfc, 0x68, 0x0, 0xa4, 0xd1, + 0x3f, 0xf8, 0xce, 0xa0, 0x2, 0x0, 0xff, 0xe4, + 0x18, 0x0, 0x40, 0x3f, 0xff, 0xe0, 0x1f, 0xfc, + 0x9e, 0xff, 0xff, 0xe3, 0xb0, 0x7, 0xff, 0xa9, + 0xee, 0xff, 0xf8, 0x88, 0x1, 0xed, 0x44, 0xff, + 0xe2, 0x10, 0x7, 0xff, 0xfc, 0x2, 0x30, 0xf, + 0xfe, 0x42, 0x80, 0x21, 0x51, 0x3f, 0xf8, 0x64, + 0x1, 0x78, 0x0, 0x6a, 0xef, 0xff, 0x86, 0x80, + 0x12, 0x38, 0x7, 0xff, 0x2e, 0x28, 0xc0, 0x3f, + 0xf9, 0x0, + + /* U+00E3 "ã" */ + 0x0, 0xe2, 0xff, 0xb1, 0x80, 0x3f, 0xf9, 0x47, + 0x3c, 0xa0, 0x1f, 0xfc, 0x43, 0x66, 0x20, 0x0, + 0x6b, 0x10, 0x3, 0xff, 0x84, 0x33, 0x2b, 0xf9, + 0x20, 0x3b, 0xea, 0x99, 0x20, 0x7, 0xff, 0x9, + 0xb6, 0x0, 0x2, 0xac, 0xc3, 0x0, 0xff, 0xe2, + 0x3f, 0x41, 0x80, 0x7f, 0xf2, 0x45, 0xf3, 0xfe, + 0x70, 0xf, 0xff, 0xf8, 0x77, 0xff, 0xff, 0x1b, + 0xa8, 0xc0, 0x3f, 0xf9, 0x22, 0xb8, 0xa0, 0x1f, + 0xfc, 0xba, 0x1d, 0xbb, 0xff, 0xe3, 0x40, 0x5, + 0x26, 0x89, 0xff, 0xc6, 0x75, 0x0, 0x10, 0x7, + 0xff, 0x20, 0xc0, 0x2, 0x1, 0xff, 0xff, 0x0, + 0xff, 0xe4, 0xf7, 0xff, 0xff, 0x1d, 0x80, 0x3f, + 0xfd, 0x4f, 0x77, 0xff, 0xc4, 0x40, 0xf, 0x6a, + 0x27, 0xff, 0x10, 0x80, 0x3f, 0xff, 0xe0, 0x11, + 0x80, 0x7f, 0xf2, 0x14, 0x1, 0xa, 0x89, 0xff, + 0xc3, 0x20, 0xb, 0xc0, 0x3, 0x57, 0x7f, 0xfc, + 0x34, 0x0, 0x91, 0xc0, 0x3f, 0xf9, 0x71, 0x46, + 0x1, 0xff, 0xc8, + + /* U+00E4 "ä" */ + 0x0, 0xfa, 0xbf, 0xd6, 0x1, 0x2f, 0xfc, 0x1, + 0xff, 0xf1, 0x8b, 0xb4, 0x80, 0x49, 0x77, 0x0, + 0x7f, 0xf0, 0x49, 0x10, 0x40, 0x10, 0xa2, 0x40, + 0x1f, 0xfd, 0x2e, 0xff, 0xff, 0xe3, 0x75, 0x18, + 0x7, 0xff, 0x24, 0x57, 0x14, 0x3, 0xff, 0x97, + 0x43, 0xb7, 0x7f, 0xfc, 0x68, 0x0, 0xa4, 0xd1, + 0x3f, 0xf8, 0xce, 0xa0, 0x2, 0x0, 0xff, 0xe4, + 0x18, 0x0, 0x40, 0x3f, 0xff, 0xe0, 0x1f, 0xfc, + 0x9e, 0xff, 0xff, 0xe3, 0xb0, 0x7, 0xff, 0xa9, + 0xee, 0xff, 0xf8, 0x88, 0x1, 0xed, 0x44, 0xff, + 0xe2, 0x10, 0x7, 0xff, 0xfc, 0x2, 0x30, 0xf, + 0xfe, 0x42, 0x80, 0x21, 0x51, 0x3f, 0xf8, 0x64, + 0x1, 0x78, 0x0, 0x6a, 0xef, 0xff, 0x86, 0x80, + 0x12, 0x38, 0x7, 0xff, 0x2e, 0x28, 0xc0, 0x3f, + 0xf9, 0x0, + + /* U+00E5 "å" */ + 0x0, 0xff, 0x24, 0x4c, 0x98, 0x3, 0xff, 0x8c, + 0x96, 0xec, 0xc9, 0xa0, 0xf, 0xfe, 0x2e, 0x87, + 0xfc, 0xa8, 0x1, 0xff, 0xcb, 0x10, 0xf, 0xfe, + 0x58, 0x80, 0x7f, 0xf1, 0x74, 0x3f, 0xe5, 0x40, + 0xf, 0xfe, 0x2a, 0x5b, 0x35, 0x34, 0x1, 0xff, + 0xc6, 0x49, 0x99, 0x80, 0x3f, 0xfd, 0xbd, 0xff, + 0xff, 0xc6, 0xea, 0x30, 0xf, 0xfe, 0x48, 0xae, + 0x28, 0x7, 0xff, 0x2e, 0x87, 0x6e, 0xff, 0xf8, + 0xd0, 0x1, 0x49, 0xa2, 0x7f, 0xf1, 0x9d, 0x40, + 0x4, 0x1, 0xff, 0xc8, 0x30, 0x0, 0x80, 0x7f, + 0xff, 0xc0, 0x3f, 0xf9, 0x3d, 0xff, 0xff, 0xc7, + 0x60, 0xf, 0xff, 0x53, 0xdd, 0xff, 0xf1, 0x10, + 0x3, 0xda, 0x89, 0xff, 0xc4, 0x20, 0xf, 0xff, + 0xf8, 0x4, 0x60, 0x1f, 0xfc, 0x85, 0x0, 0x42, + 0xa2, 0x7f, 0xf0, 0xc8, 0x2, 0xf0, 0x0, 0xd5, + 0xdf, 0xff, 0xd, 0x0, 0x24, 0x70, 0xf, 0xfe, + 0x5c, 0x51, 0x80, 0x7f, 0xf2, 0x0, + + /* U+00E6 "æ" */ + 0xef, 0xff, 0xfe, 0xde, 0x28, 0x7, 0xff, 0x7c, + 0xea, 0x80, 0x1f, 0xfe, 0x5, 0x60, 0xdb, 0xbf, + 0xfe, 0x34, 0x0, 0x67, 0xbb, 0xff, 0xe1, 0x51, + 0x0, 0x2c, 0xd, 0x13, 0xff, 0x8c, 0xea, 0x0, + 0x38, 0x44, 0xff, 0xe1, 0x2c, 0x0, 0x8, 0x3, + 0xff, 0x92, 0x60, 0x7, 0x0, 0xff, 0xe2, 0x30, + 0x7, 0xff, 0xfc, 0x3, 0xff, 0xfe, 0x1, 0xff, + 0xd6, 0xef, 0xff, 0xfe, 0x3b, 0x0, 0x13, 0xff, + 0xff, 0x89, 0x80, 0x1f, 0xff, 0xf0, 0xf, 0xfe, + 0x53, 0xdd, 0xff, 0xf1, 0x14, 0x0, 0x77, 0x7f, + 0xfc, 0x80, 0xd, 0xc8, 0x9f, 0xfc, 0x43, 0x0, + 0x3a, 0x27, 0xff, 0x20, 0x3, 0xff, 0xfe, 0x1, + 0xff, 0xf1, 0x30, 0x0, 0x80, 0x7f, 0xf1, 0xdc, + 0x3, 0xff, 0x92, 0xe0, 0x9, 0x54, 0x4f, 0xfe, + 0x19, 0x80, 0xe, 0x11, 0x3f, 0xf8, 0xe1, 0xc0, + 0x1, 0xab, 0xbf, 0xfe, 0x1a, 0x80, 0x4f, 0x77, + 0xff, 0xc7, 0x4, 0x70, 0xf, 0xff, 0x4, 0x51, + 0x80, 0x7f, 0xf7, 0x40, + + /* U+00E7 "ç" */ + 0x0, 0x2e, 0x7f, 0xff, 0xf1, 0xe4, 0x22, 0x8c, + 0x3, 0xff, 0x90, 0x8e, 0x1, 0xff, 0xca, 0xf0, + 0x0, 0xd5, 0xdf, 0xff, 0x1d, 0x40, 0x10, 0xa8, + 0x9f, 0xfc, 0x62, 0x10, 0x1, 0x80, 0x7f, 0xff, + 0xc0, 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0xf, + 0xff, 0xf8, 0x7, 0xe1, 0x0, 0x18, 0x7, 0xff, + 0x21, 0x40, 0x10, 0xa8, 0x9f, 0xfc, 0x62, 0xe0, + 0x0, 0xd5, 0xdf, 0xff, 0x1a, 0x55, 0xc0, 0x3f, + 0xf9, 0x71, 0x46, 0x1, 0xff, 0xca, 0x5c, 0xff, + 0xf6, 0x80, 0x5f, 0xff, 0xd6, 0x1, 0xfe, 0x40, + 0x1, 0x80, 0x7f, 0xf2, 0x30, 0x0, 0x80, 0x1f, + 0xfc, 0x84, 0x0, 0x60, 0x7, 0xff, 0x1c, 0x84, + 0x0, 0x80, 0x1f, 0xfc, 0x87, 0x74, 0x80, 0x7f, + 0xc0, + + /* U+00E8 "è" */ + 0x0, 0xfe, 0xbf, 0xf4, 0x0, 0x7f, 0xf2, 0x30, + 0x0, 0x80, 0x1f, 0xfc, 0x84, 0x0, 0x18, 0x7, + 0xff, 0x20, 0xc0, 0x24, 0x0, 0xff, 0xe4, 0x20, + 0x3, 0x0, 0x3f, 0xf9, 0x1e, 0xee, 0xd0, 0xf, + 0xfe, 0x42, 0x44, 0x14, 0x3, 0xff, 0xb0, 0xb9, + 0xff, 0xff, 0xc3, 0xea, 0x20, 0xa, 0x28, 0xc0, + 0x3f, 0xf8, 0x62, 0xba, 0x80, 0x8e, 0x1, 0xff, + 0xc8, 0xa1, 0xf0, 0x0, 0xd5, 0xdf, 0xff, 0xa, + 0x0, 0x2, 0xca, 0x0, 0x85, 0x44, 0xff, 0xe1, + 0x3a, 0x0, 0x34, 0x40, 0x6, 0x1, 0xff, 0xc4, + 0x10, 0x0, 0x80, 0x7f, 0xff, 0xc0, 0x3f, 0xf9, + 0x95, 0xff, 0xff, 0xc4, 0x50, 0xf, 0xff, 0x54, + 0x5d, 0xff, 0xf1, 0xe4, 0x2, 0x24, 0x4f, 0xfe, + 0x39, 0x0, 0x7f, 0xff, 0x4, 0x0, 0x60, 0x1f, + 0xfc, 0x85, 0x0, 0x42, 0xa2, 0x7f, 0xf1, 0x8b, + 0x80, 0x3, 0x57, 0x7f, 0xfc, 0x69, 0x57, 0x0, + 0xff, 0xe5, 0xc5, 0x18, 0x7, 0xff, 0x20, + + /* U+00E9 "é" */ + 0x0, 0xff, 0xaf, 0xfc, 0xc0, 0x1f, 0xfc, 0x84, + 0x0, 0x28, 0x7, 0xff, 0x1c, 0xc0, 0x2, 0x60, + 0x1f, 0xfc, 0x74, 0x0, 0x28, 0x7, 0xff, 0x23, + 0x0, 0x18, 0x1, 0xff, 0xc8, 0xc7, 0x72, 0x80, + 0x7f, 0xf2, 0x1e, 0x20, 0x40, 0x1f, 0xfd, 0x85, + 0xcf, 0xff, 0xfe, 0x1f, 0x51, 0x0, 0x51, 0x46, + 0x1, 0xff, 0xc3, 0x15, 0xd4, 0x4, 0x70, 0xf, + 0xfe, 0x45, 0xf, 0x80, 0x6, 0xae, 0xff, 0xf8, + 0x50, 0x0, 0x16, 0x50, 0x4, 0x2a, 0x27, 0xff, + 0x9, 0xd0, 0x1, 0xa2, 0x0, 0x30, 0xf, 0xfe, + 0x20, 0x80, 0x4, 0x3, 0xff, 0xfe, 0x1, 0xff, + 0xcc, 0xaf, 0xff, 0xfe, 0x22, 0x80, 0x7f, 0xfa, + 0xa2, 0xef, 0xff, 0x8f, 0x20, 0x11, 0x22, 0x7f, + 0xf1, 0xc8, 0x3, 0xff, 0xf8, 0x20, 0x3, 0x0, + 0xff, 0xe4, 0x28, 0x2, 0x15, 0x13, 0xff, 0x8c, + 0x5c, 0x0, 0x1a, 0xbb, 0xff, 0xe3, 0x4a, 0xb8, + 0x7, 0xff, 0x2e, 0x28, 0xc0, 0x3f, 0xf9, 0x0, + + /* U+00EA "ê" */ + 0x0, 0xfe, 0x1e, 0xff, 0x60, 0x7, 0xff, 0x1e, + 0x4, 0xc4, 0xd4, 0x3, 0xff, 0x8a, 0x4c, 0x1f, + 0x1, 0x60, 0x1f, 0xfc, 0x59, 0x5, 0x36, 0x21, + 0x80, 0xf, 0xfe, 0x1a, 0xa0, 0x58, 0x2, 0x1, + 0xc8, 0x3, 0xff, 0x84, 0x6f, 0x42, 0x0, 0x58, + 0x76, 0x0, 0xff, 0xe1, 0x34, 0x28, 0x6, 0x78, + 0x40, 0xf, 0xff, 0x6a, 0xe7, 0xff, 0xff, 0xf, + 0xa8, 0x80, 0x28, 0xa3, 0x0, 0xff, 0xe1, 0x8a, + 0xea, 0x2, 0x38, 0x7, 0xff, 0x22, 0x87, 0xc0, + 0x3, 0x57, 0x7f, 0xfc, 0x28, 0x0, 0xb, 0x28, + 0x2, 0x15, 0x13, 0xff, 0x84, 0xe8, 0x0, 0xd1, + 0x0, 0x18, 0x7, 0xff, 0x10, 0x40, 0x2, 0x1, + 0xff, 0xff, 0x0, 0xff, 0xe6, 0x57, 0xff, 0xff, + 0x11, 0x40, 0x3f, 0xfd, 0x51, 0x77, 0xff, 0xc7, + 0x90, 0x8, 0x91, 0x3f, 0xf8, 0xe4, 0x1, 0xff, + 0xfc, 0x10, 0x1, 0x80, 0x7f, 0xf2, 0x14, 0x1, + 0xa, 0x89, 0xff, 0xc6, 0x2e, 0x0, 0xd, 0x5d, + 0xff, 0xf1, 0xa5, 0x5c, 0x3, 0xff, 0x97, 0x14, + 0x60, 0x1f, 0xfc, 0x80, + + /* U+00EB "ë" */ + 0x0, 0xf3, 0xff, 0xb8, 0x2, 0x2f, 0xf8, 0xc0, + 0x3f, 0xfe, 0xd, 0x76, 0xd0, 0x8, 0xae, 0xe3, + 0x0, 0xff, 0xe0, 0xa, 0x20, 0xc0, 0x32, 0x24, + 0x1, 0xff, 0xd5, 0x5c, 0xff, 0xff, 0xe1, 0xf5, + 0x10, 0x5, 0x14, 0x60, 0x1f, 0xfc, 0x31, 0x5d, + 0x40, 0x47, 0x0, 0xff, 0xe4, 0x50, 0xf8, 0x0, + 0x6a, 0xef, 0xff, 0x85, 0x0, 0x1, 0x65, 0x0, + 0x42, 0xa2, 0x7f, 0xf0, 0x9d, 0x0, 0x1a, 0x20, + 0x3, 0x0, 0xff, 0xe2, 0x8, 0x0, 0x40, 0x3f, + 0xff, 0xe0, 0x1f, 0xfc, 0xca, 0xff, 0xff, 0xe2, + 0x28, 0x7, 0xff, 0xaa, 0x2e, 0xff, 0xf8, 0xf2, + 0x1, 0x12, 0x27, 0xff, 0x1c, 0x80, 0x3f, 0xff, + 0x82, 0x0, 0x30, 0xf, 0xfe, 0x42, 0x80, 0x21, + 0x51, 0x3f, 0xf8, 0xc5, 0xc0, 0x1, 0xab, 0xbf, + 0xfe, 0x34, 0xab, 0x80, 0x7f, 0xf2, 0xe2, 0x8c, + 0x3, 0xff, 0x90, + + /* U+00EC "ì" */ + 0x5f, 0xf7, 0x0, 0x10, 0x0, 0x24, 0x2, 0x60, + 0x4, 0x0, 0x20, 0x3, 0x0, 0x18, 0x0, 0x40, + 0x2, 0x3b, 0x98, 0x2, 0x88, 0x80, 0x3f, 0x2f, + 0xfb, 0x80, 0x3f, 0xff, 0xe0, 0x1f, 0xfe, 0xe0, + + /* U+00ED "í" */ + 0x5, 0xff, 0x68, 0x60, 0x1, 0x1, 0x0, 0x18, + 0x24, 0x0, 0x44, 0x0, 0x8, 0x44, 0xee, 0x80, + 0x48, 0x82, 0x80, 0x79, 0x7f, 0xdc, 0x1, 0xff, + 0xff, 0x0, 0xff, 0xec, 0x0, + + /* U+00EE "î" */ + 0x0, 0xa7, 0xfe, 0x20, 0xe, 0x26, 0x4, 0xe, + 0x0, 0xee, 0x6, 0xa0, 0x35, 0x0, 0x94, 0xc2, + 0x6, 0x2, 0xc0, 0x2b, 0x8, 0x10, 0x71, 0x14, + 0x1, 0x34, 0x38, 0x5, 0xaf, 0xe0, 0x50, 0xe0, + 0x18, 0xe1, 0xc0, 0x3f, 0xf9, 0xcb, 0xfe, 0xe0, + 0xf, 0xff, 0xf8, 0x7, 0xff, 0xfc, 0x3, 0xff, + 0xfe, 0x1, 0xff, 0xc9, + + /* U+00EF "ï" */ + 0xf, 0xf9, 0x0, 0x2b, 0xff, 0x48, 0x7, 0xff, + 0x36, 0xee, 0x50, 0xa, 0x6e, 0xe0, 0x44, 0x84, + 0x2, 0x24, 0x41, 0x0, 0x7f, 0xf1, 0x17, 0xfd, + 0xc0, 0x1f, 0xff, 0xf0, 0xf, 0xff, 0xf8, 0x7, + 0xff, 0xfc, 0x3, 0xff, 0xbe, + + /* U+00F1 "ñ" */ + 0x0, 0xef, 0xfb, 0x1c, 0x40, 0x3f, 0xf9, 0x27, + 0x1c, 0xe0, 0x1f, 0xfc, 0x57, 0x65, 0x10, 0x4, + 0x6a, 0x80, 0x7f, 0xf0, 0xe2, 0x6b, 0xac, 0xc0, + 0xab, 0xee, 0x64, 0xa0, 0x1f, 0xfc, 0x24, 0xc9, + 0x10, 0x2, 0x33, 0x3c, 0x3, 0xff, 0x88, 0xdd, + 0x28, 0x20, 0x1f, 0xfc, 0x96, 0xbe, 0xff, 0x50, + 0x7, 0xff, 0xfc, 0x1, 0xbf, 0xff, 0xfc, 0x6e, + 0xb3, 0x0, 0xff, 0xe4, 0x8a, 0x63, 0x0, 0x7f, + 0xf2, 0xe4, 0x80, 0x34, 0xdd, 0xff, 0xf0, 0xa4, + 0x2, 0xb0, 0x9, 0xd9, 0x13, 0xff, 0x84, 0xce, + 0x0, 0x50, 0xb, 0xc0, 0x3f, 0xf8, 0x9e, 0x1, + 0xff, 0xff, 0x0, 0xff, 0xff, 0x80, 0x7f, 0xff, + 0xc0, 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0xf, + 0xfe, 0xc0, + + /* U+00F2 "ò" */ + 0x0, 0xfe, 0xcf, 0xf3, 0x0, 0x7f, 0xf2, 0x10, + 0x1, 0x80, 0x1f, 0xfc, 0x8c, 0x0, 0x20, 0x7, + 0xff, 0x21, 0x0, 0x2, 0x40, 0x1f, 0xfc, 0x83, + 0x0, 0x20, 0x7, 0xff, 0x22, 0x1d, 0xc6, 0x1, + 0xff, 0xc8, 0x48, 0x82, 0x80, 0x7f, 0xf6, 0x17, + 0x3f, 0xff, 0xf8, 0x7d, 0x44, 0x1, 0x45, 0x18, + 0x7, 0xff, 0xc, 0x57, 0x50, 0x11, 0xc0, 0x3f, + 0xf9, 0x16, 0x3e, 0x0, 0x1a, 0xbb, 0xff, 0xe1, + 0x40, 0x4, 0xca, 0x0, 0x85, 0x44, 0xff, 0xe1, + 0x3a, 0x0, 0x34, 0x40, 0x6, 0x1, 0xff, 0xc4, + 0x10, 0x0, 0x80, 0x7f, 0xff, 0xc0, 0x3f, 0xff, + 0xe0, 0x1f, 0xff, 0xf0, 0xf, 0xff, 0x98, 0x80, + 0xc, 0x3, 0xff, 0x88, 0x20, 0x1, 0x50, 0x4, + 0x2a, 0x27, 0xff, 0x9, 0xd0, 0x1, 0xbc, 0x0, + 0x1a, 0xbb, 0xff, 0xe1, 0x40, 0x4, 0xca, 0xe0, + 0x1f, 0xfc, 0x8b, 0x10, 0x8a, 0x30, 0xf, 0xfe, + 0x18, 0xae, 0xa0, 0x0, + + /* U+00F3 "ó" */ + 0x0, 0xff, 0xb7, 0xfc, 0xa0, 0x1f, 0xfc, 0x71, + 0x20, 0x2, 0x0, 0x7f, 0xf1, 0xd0, 0x0, 0x42, + 0x1, 0xff, 0xc7, 0xc0, 0x2, 0x0, 0x7f, 0xf2, + 0x10, 0x1, 0x80, 0x1f, 0xfc, 0x85, 0x77, 0x20, + 0x7, 0xff, 0x22, 0x22, 0x10, 0xf, 0xfe, 0xc2, + 0xe7, 0xff, 0xff, 0xf, 0xa8, 0x80, 0x28, 0xa3, + 0x0, 0xff, 0xe1, 0x8a, 0xea, 0x2, 0x38, 0x7, + 0xff, 0x22, 0xc7, 0xc0, 0x3, 0x57, 0x7f, 0xfc, + 0x28, 0x0, 0x99, 0x40, 0x10, 0xa8, 0x9f, 0xfc, + 0x27, 0x40, 0x6, 0x88, 0x0, 0xc0, 0x3f, 0xf8, + 0x82, 0x0, 0x10, 0xf, 0xff, 0xf8, 0x7, 0xff, + 0xfc, 0x3, 0xff, 0xfe, 0x1, 0xff, 0xf3, 0x10, + 0x1, 0x80, 0x7f, 0xf1, 0x4, 0x0, 0x2a, 0x0, + 0x85, 0x44, 0xff, 0xe1, 0x3a, 0x0, 0x37, 0x80, + 0x3, 0x57, 0x7f, 0xfc, 0x28, 0x0, 0x99, 0x5c, + 0x3, 0xff, 0x91, 0x62, 0x11, 0x46, 0x1, 0xff, + 0xc3, 0x15, 0xd4, 0x0, + + /* U+00F4 "ô" */ + 0x0, 0xfe, 0x1f, 0xfa, 0xc0, 0x3f, 0xf8, 0xf6, + 0x6, 0x8, 0x80, 0xf, 0xfe, 0x29, 0xa8, 0x74, + 0x4, 0x80, 0x7f, 0xf1, 0x7c, 0x18, 0x9c, 0x49, + 0xc0, 0x3f, 0xf8, 0x6c, 0x63, 0x0, 0x8, 0x8, + 0x10, 0xf, 0xfe, 0x1b, 0xd0, 0x80, 0x1a, 0x1c, + 0x80, 0x3f, 0xf8, 0x4d, 0x8, 0x1, 0x9e, 0xc, + 0x3, 0xff, 0xda, 0xb9, 0xff, 0xff, 0xc3, 0xea, + 0x20, 0xa, 0x28, 0xc0, 0x3f, 0xf8, 0x62, 0xba, + 0x80, 0x8e, 0x1, 0xff, 0xc8, 0xb1, 0xf0, 0x0, + 0xd5, 0xdf, 0xff, 0xa, 0x0, 0x26, 0x50, 0x4, + 0x2a, 0x27, 0xff, 0x9, 0xd0, 0x1, 0xa2, 0x0, + 0x30, 0xf, 0xfe, 0x20, 0x80, 0x4, 0x3, 0xff, + 0xfe, 0x1, 0xff, 0xff, 0x0, 0xff, 0xff, 0x80, + 0x7f, 0xfc, 0xc4, 0x0, 0x60, 0x1f, 0xfc, 0x41, + 0x0, 0xa, 0x80, 0x21, 0x51, 0x3f, 0xf8, 0x4e, + 0x80, 0xd, 0xe0, 0x0, 0xd5, 0xdf, 0xff, 0xa, + 0x0, 0x26, 0x57, 0x0, 0xff, 0xe4, 0x58, 0x84, + 0x51, 0x80, 0x7f, 0xf0, 0xc5, 0x75, 0x0, + + /* U+00F5 "õ" */ + 0x0, 0xef, 0xfb, 0x1c, 0x3, 0xff, 0x94, 0x71, + 0xcc, 0x1, 0xff, 0xc5, 0x66, 0x20, 0x80, 0xce, + 0xa8, 0x7, 0xff, 0xe, 0x65, 0x7d, 0x44, 0x5, + 0x5d, 0x73, 0x25, 0x0, 0xff, 0xe1, 0x2e, 0xc8, + 0x80, 0xa3, 0x33, 0x0, 0x3f, 0xf8, 0x8d, 0xd2, + 0x82, 0x1, 0xff, 0xc9, 0x6b, 0xef, 0xf4, 0x80, + 0x7f, 0xff, 0xc0, 0x32, 0xe7, 0xff, 0xff, 0xf, + 0xa8, 0x80, 0x28, 0xa3, 0x0, 0xff, 0xe1, 0x8a, + 0xea, 0x2, 0x38, 0x7, 0xff, 0x22, 0xc7, 0xc0, + 0x3, 0x57, 0x7f, 0xfc, 0x28, 0x0, 0x99, 0x40, + 0x10, 0xa8, 0x9f, 0xfc, 0x27, 0x40, 0x6, 0x88, + 0x0, 0xc0, 0x3f, 0xf8, 0x82, 0x0, 0x10, 0xf, + 0xff, 0xf8, 0x7, 0xff, 0xfc, 0x3, 0xff, 0xfe, + 0x1, 0xff, 0xf3, 0x10, 0x1, 0x80, 0x7f, 0xf1, + 0x4, 0x0, 0x2a, 0x0, 0x85, 0x44, 0xff, 0xe1, + 0x3a, 0x0, 0x37, 0x80, 0x3, 0x57, 0x7f, 0xfc, + 0x28, 0x0, 0x99, 0x5c, 0x3, 0xff, 0x91, 0x62, + 0x11, 0x46, 0x1, 0xff, 0xc3, 0x15, 0xd4, 0x0, + + /* U+00F6 "ö" */ + 0x0, 0xf4, 0x7f, 0xb0, 0x2, 0x3f, 0xf8, 0x80, + 0x3f, 0xfe, 0x15, 0x76, 0xf0, 0x8, 0xee, 0xe2, + 0x0, 0xff, 0xe0, 0x12, 0x20, 0xc0, 0x32, 0x24, + 0x1, 0xff, 0xd5, 0x5c, 0xff, 0xff, 0xe1, 0xf5, + 0x10, 0x5, 0x14, 0x60, 0x1f, 0xfc, 0x31, 0x5d, + 0x40, 0x47, 0x0, 0xff, 0xe4, 0x58, 0xf8, 0x0, + 0x6a, 0xef, 0xff, 0x85, 0x0, 0x13, 0x28, 0x2, + 0x15, 0x13, 0xff, 0x84, 0xe8, 0x0, 0xd1, 0x0, + 0x18, 0x7, 0xff, 0x10, 0x40, 0x2, 0x1, 0xff, + 0xff, 0x0, 0xff, 0xff, 0x80, 0x7f, 0xff, 0xc0, + 0x3f, 0xfe, 0x62, 0x0, 0x30, 0xf, 0xfe, 0x20, + 0x80, 0x5, 0x40, 0x10, 0xa8, 0x9f, 0xfc, 0x27, + 0x40, 0x6, 0xf0, 0x0, 0x6a, 0xef, 0xff, 0x85, + 0x0, 0x13, 0x2b, 0x80, 0x7f, 0xf2, 0x2c, 0x42, + 0x28, 0xc0, 0x3f, 0xf8, 0x62, 0xba, 0x80, + + /* U+00F7 "÷" */ + 0x0, 0xfc, 0xdf, 0xee, 0x0, 0xff, 0xf6, 0x3d, + 0xdb, 0x40, 0x3f, 0xf8, 0x82, 0x88, 0x30, 0xf, + 0xff, 0xf8, 0x7, 0xff, 0x4, 0x51, 0x3f, 0xf8, + 0xc4, 0xb7, 0x7f, 0xfc, 0x69, 0x0, 0xff, 0xee, + 0x27, 0xff, 0xff, 0x1a, 0xc0, 0x3f, 0xff, 0xe0, + 0x1f, 0xfd, 0x51, 0x44, 0x18, 0x7, 0xff, 0x11, + 0xee, 0xda, 0x1, 0xff, 0xe4, + + /* U+00F9 "ù" */ + 0x0, 0xfe, 0xaf, 0xf4, 0x80, 0x7f, 0xf2, 0x30, + 0x0, 0x80, 0x1f, 0xfc, 0x84, 0x0, 0x10, 0x80, + 0x7f, 0xf1, 0xc8, 0x80, 0x4, 0x0, 0xff, 0xe4, + 0x20, 0x3, 0x0, 0x3f, 0xf9, 0x14, 0xee, 0xf0, + 0xf, 0xfe, 0x41, 0xc4, 0x18, 0x3, 0xff, 0xad, + 0xdf, 0xe6, 0x0, 0xff, 0xe2, 0x47, 0xfb, 0x0, + 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0xf, 0xff, + 0xf8, 0x7, 0xff, 0xfc, 0x3, 0xff, 0xfe, 0x1, + 0xff, 0xd5, 0x30, 0x0, 0x80, 0x7f, 0xf1, 0x4, + 0x0, 0xee, 0x0, 0x4a, 0xa2, 0x7f, 0xf0, 0x99, + 0x80, 0x3, 0x90, 0x0, 0xd5, 0xdf, 0xff, 0xa, + 0x40, 0x2a, 0x38, 0x0, 0xff, 0xe4, 0x51, 0x3, + 0xda, 0x0, 0x7f, 0xf0, 0xc5, 0x31, 0x40, + + /* U+00FA "ú" */ + 0x0, 0xff, 0xab, 0xfd, 0x0, 0x1f, 0xfc, 0x84, + 0x0, 0x60, 0x7, 0xff, 0x1c, 0x84, 0x0, 0x80, + 0x1f, 0xfc, 0x74, 0x0, 0x18, 0x7, 0xff, 0x23, + 0x0, 0x8, 0x1, 0xff, 0xc8, 0xd7, 0x75, 0x0, + 0x7f, 0xf2, 0x1e, 0x20, 0x40, 0x1f, 0xfd, 0x6e, + 0xff, 0x30, 0x7, 0xff, 0x12, 0x3f, 0xd8, 0x1, + 0xff, 0xff, 0x0, 0xff, 0xff, 0x80, 0x7f, 0xff, + 0xc0, 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0xf, + 0xfe, 0xa9, 0x80, 0x4, 0x3, 0xff, 0x88, 0x20, + 0x7, 0x70, 0x2, 0x55, 0x13, 0xff, 0x84, 0xcc, + 0x0, 0x1c, 0x80, 0x6, 0xae, 0xff, 0xf8, 0x52, + 0x1, 0x51, 0xc0, 0x7, 0xff, 0x22, 0x88, 0x1e, + 0xd0, 0x3, 0xff, 0x86, 0x29, 0x8a, 0x0, + + /* U+00FB "û" */ + 0x0, 0xff, 0x6f, 0xfb, 0x40, 0x3f, 0xf8, 0xee, + 0x45, 0x9c, 0x3, 0xff, 0x8a, 0x30, 0x13, 0x1, + 0x2, 0x1, 0xff, 0xc4, 0xb0, 0x44, 0x29, 0x84, + 0x0, 0x7f, 0xf0, 0xd1, 0x42, 0x40, 0x1e, 0xc, + 0x60, 0x1f, 0xfc, 0x21, 0x7a, 0x20, 0x1, 0xcb, + 0xb0, 0x7, 0xff, 0x9, 0x61, 0x40, 0x33, 0x42, + 0x80, 0x7f, 0xfa, 0xfb, 0xfc, 0xc0, 0x1f, 0xfc, + 0x48, 0xff, 0x60, 0x7, 0xff, 0xfc, 0x3, 0xff, + 0xfe, 0x1, 0xff, 0xff, 0x0, 0xff, 0xff, 0x80, + 0x7f, 0xff, 0xc0, 0x3f, 0xfa, 0xa6, 0x0, 0x10, + 0xf, 0xfe, 0x20, 0x80, 0x1d, 0xc0, 0x9, 0x54, + 0x4f, 0xfe, 0x13, 0x30, 0x0, 0x72, 0x0, 0x1a, + 0xbb, 0xff, 0xe1, 0x48, 0x5, 0x47, 0x0, 0x1f, + 0xfc, 0x8a, 0x20, 0x7b, 0x40, 0xf, 0xfe, 0x18, + 0xa6, 0x28, 0x0, + + /* U+00FC "ü" */ + 0x0, 0xf2, 0xff, 0xc0, 0x1b, 0xfe, 0x40, 0xf, + 0xff, 0x82, 0x5d, 0xb0, 0x3, 0x5d, 0xca, 0x1, + 0xff, 0xc0, 0x14, 0x41, 0x80, 0x64, 0x48, 0x40, + 0x3f, 0xfa, 0x5d, 0xfe, 0x60, 0xf, 0xfe, 0x24, + 0x7f, 0xb0, 0x3, 0xff, 0xfe, 0x1, 0xff, 0xff, + 0x0, 0xff, 0xff, 0x80, 0x7f, 0xff, 0xc0, 0x3f, + 0xff, 0xe0, 0x1f, 0xfd, 0x53, 0x0, 0x8, 0x7, + 0xff, 0x10, 0x40, 0xe, 0xe0, 0x4, 0xaa, 0x27, + 0xff, 0x9, 0x98, 0x0, 0x39, 0x0, 0xd, 0x5d, + 0xff, 0xf0, 0xa4, 0x2, 0xa3, 0x80, 0xf, 0xfe, + 0x45, 0x10, 0x3d, 0xa0, 0x7, 0xff, 0xc, 0x53, + 0x14, 0x0, + + /* U+00FD "ý" */ + 0x0, 0xff, 0x8b, 0xfe, 0x0, 0xff, 0xe4, 0xa0, + 0x0, 0xc0, 0x3f, 0xf9, 0x38, 0x0, 0x40, 0xf, + 0xfe, 0x4a, 0x0, 0x30, 0x3, 0xff, 0x90, 0x42, + 0x0, 0x40, 0xf, 0xfe, 0x4b, 0xba, 0x0, 0x3f, + 0xf9, 0x25, 0x10, 0x70, 0xf, 0xfe, 0xc2, 0xff, + 0xc0, 0x1f, 0xfc, 0x5f, 0xf9, 0x0, 0x3f, 0xff, + 0xe0, 0x1f, 0xff, 0xf0, 0xf, 0xff, 0xf8, 0x7, + 0xff, 0xfc, 0x3, 0xff, 0xfe, 0x1, 0xff, 0xe6, + 0x10, 0xf, 0xfe, 0x38, 0x80, 0x65, 0x0, 0xa9, + 0x13, 0xff, 0x87, 0x40, 0x18, 0x54, 0x0, 0xb7, + 0x7f, 0xfc, 0x34, 0x0, 0xeb, 0x20, 0xf, 0xfe, + 0x58, 0xeb, 0x90, 0x7, 0xff, 0x2e, 0x37, 0xff, + 0xff, 0x88, 0x1, 0xff, 0xff, 0x0, 0xff, 0xe6, + 0x8, 0x4, 0xe0, 0x1c, 0x89, 0xff, 0xc2, 0x5a, + 0x0, 0x88, 0x3, 0xae, 0xff, 0xf8, 0x54, 0x80, + 0x5, 0x10, 0xf, 0xfe, 0x51, 0x58, 0x7, 0xff, + 0x24, 0x9f, 0x44, 0x0, + + /* U+00FF "ÿ" */ + 0x0, 0xfb, 0x7f, 0xce, 0x1, 0x47, 0xfb, 0x0, + 0x3f, 0xfe, 0x9d, 0x76, 0x60, 0xa, 0xae, 0xde, + 0x1, 0xff, 0xc2, 0x34, 0x40, 0x80, 0x44, 0x88, + 0x30, 0xf, 0xfe, 0xa2, 0xff, 0xc0, 0x1f, 0xfc, + 0x5f, 0xf9, 0x0, 0x3f, 0xff, 0xe0, 0x1f, 0xff, + 0xf0, 0xf, 0xff, 0xf8, 0x7, 0xff, 0xfc, 0x3, + 0xff, 0xfe, 0x1, 0xff, 0xe6, 0x10, 0xf, 0xfe, + 0x38, 0x80, 0x65, 0x0, 0xa9, 0x13, 0xff, 0x87, + 0x40, 0x18, 0x54, 0x0, 0xb7, 0x7f, 0xfc, 0x34, + 0x0, 0xeb, 0x20, 0xf, 0xfe, 0x58, 0xeb, 0x90, + 0x7, 0xff, 0x2e, 0x37, 0xff, 0xff, 0x88, 0x1, + 0xff, 0xff, 0x0, 0xff, 0xe6, 0x8, 0x4, 0xe0, + 0x1c, 0x89, 0xff, 0xc2, 0x5a, 0x0, 0x88, 0x3, + 0xae, 0xff, 0xf8, 0x54, 0x80, 0x5, 0x10, 0xf, + 0xfe, 0x51, 0x58, 0x7, 0xff, 0x24, 0x9f, 0x44, + 0x0, + + /* U+0131 "ı" */ + 0x5f, 0xf7, 0x0, 0x7f, 0xff, 0xc0, 0x3f, 0xf8, + 0x20, + + /* U+0152 "Œ" */ + 0x0, 0x2e, 0x7f, 0xff, 0xf9, 0x2, 0x28, 0xc0, + 0x3f, 0xfc, 0x86, 0xe0, 0x1f, 0xfe, 0x69, 0x0, + 0xd, 0x66, 0x7f, 0xf1, 0xec, 0x40, 0x2c, 0xcf, + 0xfe, 0x5b, 0x80, 0x21, 0x4c, 0xff, 0xf8, 0xe9, + 0x0, 0x1, 0x33, 0xff, 0xe6, 0x0, 0xc, 0x3, + 0xff, 0x92, 0x60, 0x1f, 0xff, 0xf0, 0xf, 0xff, + 0xf8, 0x7, 0xff, 0xfc, 0x3, 0xff, 0xfe, 0x1, + 0xff, 0xff, 0x0, 0xff, 0xef, 0x8f, 0xff, 0xff, + 0x19, 0xc0, 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0x2b, + 0xbf, 0xfe, 0x33, 0x0, 0x7f, 0xf5, 0x5, 0x13, + 0xff, 0x8c, 0x20, 0x1f, 0xff, 0xf0, 0xf, 0xff, + 0xf8, 0x7, 0xff, 0xfc, 0x3, 0xff, 0xfe, 0x1, + 0xff, 0xc4, 0x30, 0x1, 0x80, 0x7f, 0xf2, 0x4c, + 0x3, 0xff, 0x9c, 0xe0, 0x8, 0x53, 0x3f, 0xfe, + 0x3a, 0x40, 0x0, 0x4c, 0xff, 0xf9, 0x7c, 0x0, + 0x1a, 0xcc, 0xff, 0xe3, 0xd8, 0x80, 0x59, 0x9f, + 0xfc, 0xb4, 0x70, 0xf, 0xff, 0x3c, 0x51, 0x80, + 0x7f, 0xf9, 0x0, + + /* U+0153 "œ" */ + 0x0, 0x2e, 0x7f, 0xff, 0xf6, 0x31, 0x40, 0x28, + 0xa3, 0x0, 0xff, 0xec, 0x1d, 0x48, 0x23, 0x80, + 0x7f, 0xf7, 0x19, 0x7c, 0x0, 0x35, 0x77, 0xff, + 0xc2, 0x80, 0xc, 0xf7, 0x7f, 0xfc, 0x2a, 0x10, + 0x4, 0xa8, 0x2, 0x15, 0x13, 0xff, 0x84, 0xea, + 0x0, 0x38, 0x44, 0xff, 0xe1, 0x2d, 0x80, 0xc, + 0x2, 0x30, 0xf, 0xfe, 0x21, 0x80, 0x1c, 0x3, + 0xff, 0x88, 0x20, 0x1f, 0xff, 0xf0, 0xf, 0xff, + 0xf8, 0x7, 0xff, 0xa1, 0x3f, 0xff, 0xf8, 0x96, + 0x1, 0xff, 0xff, 0x0, 0xff, 0xed, 0x9d, 0xdf, + 0xff, 0x20, 0x3, 0xff, 0x96, 0xe8, 0x9f, 0xfc, + 0x80, 0xf, 0xff, 0xf8, 0x7, 0xff, 0xc0, 0xc0, + 0x3f, 0xf8, 0x86, 0x0, 0x70, 0xf, 0xfe, 0x42, + 0x80, 0x21, 0x51, 0x3f, 0xf8, 0x4e, 0xa0, 0x3, + 0x74, 0x4f, 0xfe, 0x3f, 0x80, 0x6, 0xae, 0xff, + 0xf8, 0x50, 0x1, 0xa2, 0xef, 0xff, 0x8e, 0x8e, + 0x1, 0xff, 0xdf, 0x8a, 0x30, 0xf, 0xfe, 0xe0, + + /* U+0160 "Š" */ + 0x0, 0xff, 0x14, 0x18, 0x4, 0x50, 0x60, 0x1f, + 0xfc, 0x82, 0x5e, 0x0, 0xb9, 0x8c, 0x3, 0xff, + 0x93, 0xc5, 0x41, 0x7, 0x60, 0x1f, 0xfc, 0xa3, + 0x75, 0x97, 0x45, 0x0, 0xff, 0xe5, 0xc1, 0x30, + 0x50, 0x7, 0xff, 0x37, 0x7f, 0xc2, 0x1, 0xff, + 0xdf, 0x5c, 0xff, 0xff, 0xe4, 0xda, 0x0, 0x53, + 0x46, 0x1, 0xff, 0xc9, 0x4b, 0x70, 0x56, 0x0, + 0xff, 0xe6, 0xc1, 0xf0, 0x0, 0xaf, 0x33, 0xff, + 0x8f, 0x42, 0x0, 0x95, 0x0, 0x42, 0x19, 0xff, + 0xf1, 0xd6, 0x40, 0xc, 0x20, 0x1, 0x0, 0xff, + 0xe4, 0x88, 0x0, 0x40, 0x3f, 0xf9, 0xad, 0xfe, + 0xd0, 0xf, 0xff, 0xf8, 0x7, 0xff, 0x74, 0x40, + 0x3f, 0xfa, 0x4, 0x0, 0xf1, 0x0, 0xff, 0xe6, + 0x58, 0x1, 0x3b, 0xff, 0xff, 0x91, 0x68, 0x0, + 0x73, 0x0, 0xff, 0xe5, 0xa5, 0xb8, 0x3, 0x18, + 0x3, 0xff, 0x99, 0x6, 0x0, 0x9e, 0xcb, 0xbf, + 0xfe, 0x3c, 0x88, 0x2, 0x40, 0x21, 0x34, 0x4f, + 0xfe, 0x3b, 0x48, 0x1, 0x80, 0x3f, 0xf9, 0xa2, + 0x0, 0x10, 0xf, 0xff, 0xf8, 0x7, 0xff, 0x77, + 0x7b, 0x88, 0x1, 0xff, 0xcd, 0x31, 0x18, 0x3, + 0xff, 0x92, 0x20, 0x1, 0x20, 0x4, 0x21, 0x9f, + 0xff, 0x1d, 0x64, 0x0, 0xd2, 0x0, 0x2b, 0xcc, + 0xff, 0xe3, 0xd0, 0x80, 0x25, 0x54, 0x1, 0xff, + 0xcd, 0x73, 0xa, 0x93, 0x0, 0xff, 0xe4, 0x9d, + 0xc0, 0x0, + + /* U+0161 "š" */ + 0x0, 0xfc, 0x5f, 0x40, 0x12, 0xfa, 0x80, 0x7f, + 0xf1, 0xa, 0x15, 0x80, 0x69, 0x14, 0x3, 0xff, + 0x8a, 0xe7, 0x25, 0xa1, 0x20, 0x1f, 0xfc, 0x7e, + 0xd, 0x38, 0x20, 0xf, 0xfe, 0x39, 0x53, 0xc3, + 0x80, 0x7f, 0xf2, 0x56, 0x1c, 0x3, 0xff, 0xb6, + 0xd9, 0xff, 0xff, 0xc3, 0xe9, 0x10, 0xd, 0x52, + 0x60, 0x1f, 0xfc, 0x31, 0x6f, 0x30, 0x3, 0x28, + 0x7, 0xff, 0x1c, 0x78, 0x1, 0x60, 0x2, 0xab, + 0xbf, 0xfe, 0x13, 0x0, 0x9, 0x40, 0x80, 0x12, + 0xa8, 0x9f, 0xfc, 0x29, 0x20, 0x6, 0x88, 0x4, + 0xe0, 0x1f, 0xfc, 0x4c, 0xdd, 0x48, 0x7, 0xff, + 0x24, 0x48, 0x82, 0x1, 0xff, 0xcd, 0x10, 0xf, + 0xfe, 0x68, 0x80, 0x24, 0x3, 0xff, 0x92, 0xc0, + 0x5, 0xff, 0xff, 0xe1, 0xf4, 0x90, 0x5, 0x4, + 0x1, 0xff, 0xc5, 0x16, 0xc3, 0x0, 0xb1, 0x40, + 0x3f, 0xf8, 0xc3, 0xa0, 0x10, 0xd7, 0x65, 0xdf, + 0xff, 0x9, 0xc0, 0x2, 0xa0, 0x18, 0x4d, 0x13, + 0xff, 0x85, 0x4, 0x0, 0xd0, 0xf, 0xfe, 0x48, + 0x80, 0x4, 0x3, 0xff, 0x9c, 0x23, 0x0, 0x7f, + 0xf2, 0xbb, 0xa4, 0x0, 0xff, 0xe2, 0x8, 0x0, + 0x40, 0x80, 0x12, 0x89, 0xff, 0xc3, 0x82, 0x0, + 0x68, 0x50, 0x0, 0xae, 0xff, 0xf8, 0x6e, 0x0, + 0x15, 0x7, 0x50, 0xf, 0xfe, 0x46, 0x80, 0x55, + 0x26, 0x1, 0xff, 0xc3, 0x15, 0xd3, 0x0, + + /* U+0178 "Ÿ" */ + 0x0, 0xff, 0x22, 0x40, 0x18, 0xd1, 0x2, 0x1, + 0xff, 0xc6, 0x3b, 0xb8, 0x40, 0x2d, 0xbb, 0x38, + 0x7, 0xff, 0xfc, 0x2, 0x3f, 0xf8, 0x40, 0x2e, + 0xff, 0x30, 0x7, 0xff, 0x6d, 0x7f, 0xe6, 0x0, + 0xff, 0xe4, 0x1f, 0xfd, 0x4a, 0xc0, 0x9, 0x30, + 0xf, 0xfe, 0x30, 0xe8, 0x0, 0xa8, 0x24, 0x80, + 0x1a, 0x20, 0x1f, 0xfc, 0x5a, 0x10, 0x7, 0x0, + 0x5e, 0x0, 0x1a, 0x0, 0xff, 0xe2, 0x42, 0x0, + 0x24, 0xc0, 0x22, 0x90, 0x2, 0x40, 0x7, 0xff, + 0x9, 0x1c, 0x0, 0x8c, 0x1, 0xcc, 0x80, 0x7, + 0x50, 0xf, 0xfe, 0x1, 0x58, 0x0, 0x68, 0x3, + 0xea, 0x10, 0x5, 0x10, 0x7, 0xfd, 0xe0, 0x15, + 0x8, 0x7, 0xc3, 0xa0, 0x17, 0x80, 0x7f, 0xa8, + 0x80, 0xe, 0x80, 0x1f, 0xc7, 0x0, 0x2, 0xa0, + 0xf, 0xcc, 0xa0, 0x3, 0x80, 0xf, 0xf9, 0xcc, + 0x0, 0xae, 0x1, 0xe3, 0x90, 0x0, 0xe8, 0x7, + 0xff, 0x7, 0x44, 0x1, 0x8, 0x1, 0x87, 0x40, + 0x2b, 0x10, 0xf, 0xfe, 0x8, 0xd0, 0x5, 0x44, + 0x1, 0x50, 0x80, 0x19, 0x40, 0x3f, 0xf8, 0x68, + 0xc0, 0x1, 0xe0, 0x4, 0x20, 0x0, 0xa4, 0x3, + 0xff, 0x8b, 0x26, 0x0, 0x39, 0x57, 0x0, 0xbc, + 0x3, 0xff, 0x8f, 0xc0, 0x13, 0x50, 0x5, 0x24, + 0x1, 0xff, 0xc7, 0x2a, 0x0, 0xf2, 0xb0, 0x7, + 0xff, 0x25, 0x54, 0x1, 0x8a, 0x80, 0x3f, 0xf9, + 0x74, 0x40, 0x17, 0x0, 0x7f, 0xf3, 0x68, 0x0, + 0x66, 0x0, 0xff, 0xff, 0x80, 0x7f, 0xff, 0xc0, + 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0xf, 0xfe, + 0x0, + + /* U+017D "Ž" */ + 0x0, 0xff, 0x1c, 0x10, 0x4, 0x70, 0x40, 0x1f, + 0xfc, 0x83, 0x6e, 0x0, 0xb9, 0x8, 0x3, 0xff, + 0x93, 0x67, 0x1, 0x25, 0xe0, 0x1f, 0xfc, 0xa5, + 0x53, 0xcb, 0x30, 0xc0, 0x3f, 0xf9, 0x76, 0xc, + 0x32, 0x1, 0xff, 0xcc, 0x1f, 0xf7, 0x0, 0x7f, + 0xf7, 0xbf, 0xff, 0xfa, 0x1a, 0x1, 0xff, 0xee, + 0xcc, 0xff, 0xe6, 0x40, 0x6, 0x33, 0xff, 0xe5, + 0xac, 0x0, 0x4e, 0x1, 0xff, 0xca, 0x89, 0x0, + 0x9a, 0x80, 0x3f, 0xf9, 0x34, 0xe0, 0x14, 0xc8, + 0x3, 0xff, 0x90, 0x38, 0xa0, 0x15, 0xb0, 0x7, + 0xff, 0x20, 0xbc, 0xc0, 0x3, 0xa8, 0x1, 0xff, + 0xc8, 0x4d, 0x10, 0x1, 0xf9, 0x0, 0x7f, 0xf2, + 0x1a, 0xc0, 0x25, 0xc1, 0x0, 0xff, 0xe4, 0x4c, + 0x80, 0x27, 0xa0, 0xf, 0xfe, 0x4d, 0xb0, 0x5, + 0x50, 0x1, 0xff, 0xc8, 0x1d, 0x40, 0x0, 0xe2, + 0x80, 0x7f, 0xf2, 0xf, 0xc8, 0x0, 0x58, 0x60, + 0x1f, 0xfc, 0x85, 0xc1, 0x0, 0x26, 0x10, 0x7, + 0xff, 0x21, 0xe8, 0x2, 0x6b, 0x10, 0xf, 0xfe, + 0x45, 0x40, 0x5, 0x12, 0x1, 0xff, 0xc8, 0x1c, + 0x50, 0xa, 0x9c, 0x3, 0xff, 0x90, 0x58, 0x60, + 0x1, 0xc5, 0x0, 0xff, 0xe4, 0x1e, 0x10, 0x0, + 0xbc, 0xc0, 0x3f, 0xf9, 0xd, 0x82, 0x0, 0x4d, + 0x10, 0xf, 0xfe, 0x44, 0x48, 0x4, 0xd6, 0x1, + 0xff, 0xc9, 0xa7, 0x0, 0xa6, 0x40, 0x1f, 0xfc, + 0x9c, 0x50, 0xa, 0xd8, 0x3, 0xff, 0x94, 0x60, + 0x14, 0xb9, 0x9f, 0xff, 0x28, 0x80, 0x34, 0xe6, + 0x7f, 0xf2, 0xfc, 0x3, 0xff, 0xdc, + + /* U+017E "ž" */ + 0x0, 0xf8, 0x7e, 0x80, 0x24, 0xf5, 0x0, 0xff, + 0xe1, 0x8c, 0xab, 0x0, 0xd2, 0x28, 0x7, 0xff, + 0x11, 0x8e, 0x4e, 0x86, 0x40, 0x3f, 0xf8, 0xdc, + 0x18, 0x90, 0x40, 0x1f, 0xfc, 0x62, 0xa7, 0x87, + 0x0, 0xff, 0xe4, 0x2c, 0x38, 0x7, 0xff, 0x5f, + 0x7f, 0xff, 0xf9, 0x58, 0x1, 0xff, 0xe4, 0xeb, + 0xbf, 0xfe, 0x34, 0x0, 0x63, 0x44, 0xff, 0xe2, + 0x9c, 0x0, 0x4a, 0x1, 0xff, 0xc5, 0xa8, 0x0, + 0xa6, 0x40, 0x1f, 0xfc, 0x33, 0xd5, 0x0, 0xe, + 0x30, 0x7, 0xff, 0xd, 0xb0, 0x80, 0x9, 0xe6, + 0x1, 0xff, 0xc3, 0xa9, 0x0, 0xa2, 0xc4, 0x3, + 0xff, 0x84, 0x5a, 0xa0, 0x1, 0xb7, 0x0, 0xff, + 0xe1, 0xae, 0x90, 0x0, 0xfd, 0x0, 0x3f, 0xf8, + 0x73, 0x40, 0x13, 0xe0, 0x80, 0x7f, 0xf0, 0x87, + 0x18, 0x0, 0x37, 0x0, 0x1f, 0xfc, 0x34, 0xf3, + 0x0, 0x1e, 0x20, 0x7, 0xff, 0xe, 0x2c, 0x40, + 0xd, 0x84, 0x1, 0xff, 0xc2, 0x1b, 0x70, 0xa, + 0xa4, 0x3, 0xff, 0x86, 0x7e, 0x80, 0x2, 0xd5, + 0x0, 0xff, 0xe1, 0xbe, 0x8, 0x1, 0x74, 0x80, + 0x3f, 0xf8, 0x75, 0x0, 0x14, 0xd0, 0x7, 0xff, + 0x15, 0xc0, 0x28, 0x24, 0x4f, 0xfe, 0x29, 0x80, + 0x68, 0xbb, 0xff, 0xe3, 0x78, 0x7, 0xff, 0x90, + + /* U+2013 "–" */ + 0x34, 0x4f, 0xfe, 0x58, 0x75, 0xdf, 0xff, 0x2c, + 0xc0, 0x3f, 0xfc, 0xc0, + + /* U+2014 "—" */ + 0x34, 0x4f, 0xfe, 0x87, 0x5d, 0xff, 0xf4, 0x0, + 0x3f, 0xfd, 0xc0, + + /* U+2018 "‘" */ + 0x0, 0x8c, 0xb, 0x64, 0x3c, 0x81, 0x88, 0x1, + 0x80, 0x1f, 0x8, 0x7, 0xfc, + + /* U+2019 "’" */ + 0xdf, 0xf3, 0x80, 0x7f, 0xc2, 0x1, 0xf2, 0x0, + 0x14, 0x81, 0x28, 0x3a, 0xc0, 0x0, + + /* U+201C "“" */ + 0x0, 0x88, 0x40, 0x38, 0x48, 0x0, 0xfa, 0x40, + 0x19, 0x39, 0x1, 0x60, 0x3, 0x8a, 0xc0, 0x28, + 0x0, 0xf5, 0x80, 0x42, 0x40, 0x1e, 0x40, 0xf, + 0xfe, 0x19, 0x80, 0x7c, 0x60, 0x1f, 0xfc, 0xf0, + + /* U+201D "”" */ + 0xdf, 0xf3, 0x80, 0x53, 0xfe, 0xb0, 0xf, 0xfe, + 0xa0, 0x80, 0x42, 0x1, 0xfe, 0x50, 0xf, 0x60, + 0x1, 0xc, 0x3, 0x89, 0x80, 0xec, 0x3, 0x8b, + 0xc3, 0xb0, 0x3, 0xab, 0x48, 0x0, + + /* U+2022 "•" */ + 0x0, 0x8, 0x0, 0x7f, 0xb4, 0x8, 0x0, 0x40, + 0x42, 0x6, 0x0, + + /* U+2026 "…" */ + 0x34, 0x40, 0x80, 0x64, 0x48, 0x3, 0x12, 0x20, + 0xba, 0xec, 0xc0, 0x11, 0xdd, 0xc0, 0x1a, 0x6e, + 0xe0, 0xf, 0xfe, 0xd0 +}; + + +/*--------------------- + * GLYPH DESCRIPTION + *--------------------*/ + +static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = { + {.bitmap_index = 0, .adv_w = 0, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0} /* id = 0 reserved */, + {.bitmap_index = 0, .adv_w = 174, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 0, .adv_w = 141, .box_w = 4, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 19, .adv_w = 238, .box_w = 10, .box_h = 6, .ofs_x = 2, .ofs_y = 23}, + {.bitmap_index = 33, .adv_w = 510, .box_w = 30, .box_h = 29, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 285, .adv_w = 504, .box_w = 30, .box_h = 38, .ofs_x = 1, .ofs_y = -5}, + {.bitmap_index = 493, .adv_w = 618, .box_w = 37, .box_h = 30, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 810, .adv_w = 600, .box_w = 35, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 1049, .adv_w = 143, .box_w = 4, .box_h = 6, .ofs_x = 2, .ofs_y = 23}, + {.bitmap_index = 1057, .adv_w = 177, .box_w = 7, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 1099, .adv_w = 178, .box_w = 7, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 1139, .adv_w = 314, .box_w = 17, .box_h = 17, .ofs_x = 1, .ofs_y = 12}, + {.bitmap_index = 1234, .adv_w = 277, .box_w = 17, .box_h = 16, .ofs_x = 0, .ofs_y = 4}, + {.bitmap_index = 1272, .adv_w = 124, .box_w = 4, .box_h = 9, .ofs_x = 2, .ofs_y = -6}, + {.bitmap_index = 1287, .adv_w = 331, .box_w = 16, .box_h = 4, .ofs_x = 2, .ofs_y = 10}, + {.bitmap_index = 1299, .adv_w = 137, .box_w = 4, .box_h = 4, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 1306, .adv_w = 333, .box_w = 21, .box_h = 29, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 1467, .adv_w = 534, .box_w = 30, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 1675, .adv_w = 250, .box_w = 13, .box_h = 29, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 1732, .adv_w = 531, .box_w = 30, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 1860, .adv_w = 529, .box_w = 30, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 2010, .adv_w = 467, .box_w = 29, .box_h = 29, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 2165, .adv_w = 531, .box_w = 30, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 2281, .adv_w = 525, .box_w = 30, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 2411, .adv_w = 422, .box_w = 25, .box_h = 29, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 2469, .adv_w = 534, .box_w = 30, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 2619, .adv_w = 530, .box_w = 30, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 2750, .adv_w = 137, .box_w = 4, .box_h = 23, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 2768, .adv_w = 124, .box_w = 4, .box_h = 29, .ofs_x = 2, .ofs_y = -6}, + {.bitmap_index = 2794, .adv_w = 303, .box_w = 17, .box_h = 23, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 2926, .adv_w = 408, .box_w = 22, .box_h = 12, .ofs_x = 2, .ofs_y = 6}, + {.bitmap_index = 2955, .adv_w = 304, .box_w = 17, .box_h = 23, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 3087, .adv_w = 434, .box_w = 26, .box_h = 29, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 3200, .adv_w = 532, .box_w = 30, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 3364, .adv_w = 535, .box_w = 30, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 3452, .adv_w = 532, .box_w = 30, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 3582, .adv_w = 526, .box_w = 29, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 3662, .adv_w = 534, .box_w = 30, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 3756, .adv_w = 490, .box_w = 27, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 3820, .adv_w = 463, .box_w = 27, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 3876, .adv_w = 531, .box_w = 30, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 3998, .adv_w = 545, .box_w = 30, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 4054, .adv_w = 137, .box_w = 4, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 4063, .adv_w = 499, .box_w = 29, .box_h = 29, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 4139, .adv_w = 510, .box_w = 28, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 4325, .adv_w = 499, .box_w = 30, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 4371, .adv_w = 594, .box_w = 34, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 4566, .adv_w = 532, .box_w = 30, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 4737, .adv_w = 530, .box_w = 29, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 4839, .adv_w = 506, .box_w = 30, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 4936, .adv_w = 566, .box_w = 33, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 5046, .adv_w = 528, .box_w = 30, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 5193, .adv_w = 526, .box_w = 29, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 5347, .adv_w = 486, .box_w = 30, .box_h = 29, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 5404, .adv_w = 530, .box_w = 29, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 5476, .adv_w = 642, .box_w = 38, .box_h = 29, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 5764, .adv_w = 755, .box_w = 45, .box_h = 29, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 6205, .adv_w = 520, .box_w = 30, .box_h = 29, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 6489, .adv_w = 516, .box_w = 30, .box_h = 29, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 6677, .adv_w = 525, .box_w = 29, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 6850, .adv_w = 176, .box_w = 7, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 6876, .adv_w = 333, .box_w = 21, .box_h = 29, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 7032, .adv_w = 177, .box_w = 7, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 7056, .adv_w = 530, .box_w = 30, .box_h = 4, .ofs_x = 2, .ofs_y = -3}, + {.bitmap_index = 7068, .adv_w = 202, .box_w = 5, .box_h = 6, .ofs_x = 4, .ofs_y = 25}, + {.bitmap_index = 7084, .adv_w = 444, .box_w = 24, .box_h = 23, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 7171, .adv_w = 427, .box_w = 24, .box_h = 31, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 7260, .adv_w = 445, .box_w = 24, .box_h = 23, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 7330, .adv_w = 427, .box_w = 25, .box_h = 31, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 7427, .adv_w = 443, .box_w = 24, .box_h = 23, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 7524, .adv_w = 260, .box_w = 14, .box_h = 31, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 7578, .adv_w = 437, .box_w = 25, .box_h = 32, .ofs_x = 1, .ofs_y = -9}, + {.bitmap_index = 7701, .adv_w = 428, .box_w = 24, .box_h = 31, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 7767, .adv_w = 133, .box_w = 4, .box_h = 31, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 7787, .adv_w = 153, .box_w = 16, .box_h = 41, .ofs_x = -8, .ofs_y = -10}, + {.bitmap_index = 7851, .adv_w = 413, .box_w = 24, .box_h = 31, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 8006, .adv_w = 193, .box_w = 9, .box_h = 31, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 8038, .adv_w = 626, .box_w = 36, .box_h = 23, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 8117, .adv_w = 445, .box_w = 24, .box_h = 23, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 8171, .adv_w = 443, .box_w = 24, .box_h = 23, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 8264, .adv_w = 425, .box_w = 24, .box_h = 32, .ofs_x = 2, .ofs_y = -9}, + {.bitmap_index = 8357, .adv_w = 425, .box_w = 25, .box_h = 32, .ofs_x = 0, .ofs_y = -9}, + {.bitmap_index = 8452, .adv_w = 328, .box_w = 18, .box_h = 23, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 8497, .adv_w = 439, .box_w = 25, .box_h = 23, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 8649, .adv_w = 262, .box_w = 14, .box_h = 31, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 8703, .adv_w = 445, .box_w = 24, .box_h = 23, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 8766, .adv_w = 506, .box_w = 31, .box_h = 23, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 8983, .adv_w = 685, .box_w = 41, .box_h = 23, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 9315, .adv_w = 443, .box_w = 25, .box_h = 23, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 9520, .adv_w = 438, .box_w = 25, .box_h = 32, .ofs_x = 1, .ofs_y = -9}, + {.bitmap_index = 9613, .adv_w = 447, .box_w = 24, .box_h = 23, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 9749, .adv_w = 185, .box_w = 9, .box_h = 29, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 9823, .adv_w = 137, .box_w = 4, .box_h = 38, .ofs_x = 2, .ofs_y = -5}, + {.bitmap_index = 9832, .adv_w = 185, .box_w = 8, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 9899, .adv_w = 259, .box_w = 15, .box_h = 5, .ofs_x = 0, .ofs_y = 10}, + {.bitmap_index = 9930, .adv_w = 174, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 9930, .adv_w = 134, .box_w = 4, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 9950, .adv_w = 407, .box_w = 24, .box_h = 34, .ofs_x = 1, .ofs_y = -5}, + {.bitmap_index = 10060, .adv_w = 470, .box_w = 27, .box_h = 29, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 10180, .adv_w = 298, .box_w = 11, .box_h = 4, .ofs_x = 4, .ofs_y = 25}, + {.bitmap_index = 10194, .adv_w = 320, .box_w = 14, .box_h = 4, .ofs_x = 3, .ofs_y = 25}, + {.bitmap_index = 10206, .adv_w = 281, .box_w = 15, .box_h = 13, .ofs_x = 1, .ofs_y = 16}, + {.bitmap_index = 10274, .adv_w = 136, .box_w = 5, .box_h = 6, .ofs_x = 2, .ofs_y = 25}, + {.bitmap_index = 10290, .adv_w = 533, .box_w = 30, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 10399, .adv_w = 136, .box_w = 6, .box_h = 7, .ofs_x = 1, .ofs_y = -6}, + {.bitmap_index = 10422, .adv_w = 432, .box_w = 27, .box_h = 29, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 10539, .adv_w = 535, .box_w = 30, .box_h = 37, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 10666, .adv_w = 535, .box_w = 30, .box_h = 37, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 10793, .adv_w = 535, .box_w = 30, .box_h = 38, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 10932, .adv_w = 535, .box_w = 30, .box_h = 39, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 11074, .adv_w = 535, .box_w = 30, .box_h = 35, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 11189, .adv_w = 535, .box_w = 30, .box_h = 38, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 11318, .adv_w = 880, .box_w = 52, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 11448, .adv_w = 526, .box_w = 29, .box_h = 35, .ofs_x = 2, .ofs_y = -6}, + {.bitmap_index = 11567, .adv_w = 490, .box_w = 27, .box_h = 37, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 11671, .adv_w = 490, .box_w = 27, .box_h = 37, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 11776, .adv_w = 490, .box_w = 27, .box_h = 38, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 11892, .adv_w = 490, .box_w = 27, .box_h = 35, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 11984, .adv_w = 137, .box_w = 6, .box_h = 37, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 12020, .adv_w = 137, .box_w = 6, .box_h = 37, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 12054, .adv_w = 137, .box_w = 11, .box_h = 38, .ofs_x = -1, .ofs_y = 0}, + {.bitmap_index = 12110, .adv_w = 137, .box_w = 11, .box_h = 35, .ofs_x = -1, .ofs_y = 0}, + {.bitmap_index = 12151, .adv_w = 532, .box_w = 30, .box_h = 39, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 12376, .adv_w = 530, .box_w = 29, .box_h = 37, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 12517, .adv_w = 530, .box_w = 29, .box_h = 37, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 12658, .adv_w = 530, .box_w = 29, .box_h = 38, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 12810, .adv_w = 530, .box_w = 29, .box_h = 39, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 12967, .adv_w = 530, .box_w = 29, .box_h = 35, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 13096, .adv_w = 349, .box_w = 18, .box_h = 17, .ofs_x = 2, .ofs_y = 4}, + {.bitmap_index = 13220, .adv_w = 530, .box_w = 29, .box_h = 37, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 13332, .adv_w = 530, .box_w = 29, .box_h = 37, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 13444, .adv_w = 530, .box_w = 29, .box_h = 38, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 13567, .adv_w = 530, .box_w = 29, .box_h = 35, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 13667, .adv_w = 516, .box_w = 30, .box_h = 37, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 13896, .adv_w = 533, .box_w = 30, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 14026, .adv_w = 444, .box_w = 24, .box_h = 31, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 14153, .adv_w = 444, .box_w = 24, .box_h = 31, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 14281, .adv_w = 444, .box_w = 24, .box_h = 32, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 14419, .adv_w = 444, .box_w = 24, .box_h = 33, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 14558, .adv_w = 444, .box_w = 24, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 14672, .adv_w = 444, .box_w = 24, .box_h = 33, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 14806, .adv_w = 754, .box_w = 45, .box_h = 23, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 14946, .adv_w = 445, .box_w = 24, .box_h = 29, .ofs_x = 2, .ofs_y = -6}, + {.bitmap_index = 15051, .adv_w = 443, .box_w = 24, .box_h = 31, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 15186, .adv_w = 443, .box_w = 24, .box_h = 31, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 15322, .adv_w = 443, .box_w = 24, .box_h = 32, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 15470, .adv_w = 443, .box_w = 24, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 15593, .adv_w = 137, .box_w = 6, .box_h = 31, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 15625, .adv_w = 137, .box_w = 5, .box_h = 31, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 15654, .adv_w = 137, .box_w = 11, .box_h = 32, .ofs_x = -1, .ofs_y = 0}, + {.bitmap_index = 15706, .adv_w = 137, .box_w = 12, .box_h = 29, .ofs_x = -2, .ofs_y = 0}, + {.bitmap_index = 15743, .adv_w = 445, .box_w = 24, .box_h = 33, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 15849, .adv_w = 443, .box_w = 24, .box_h = 31, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 15981, .adv_w = 443, .box_w = 24, .box_h = 31, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 16113, .adv_w = 443, .box_w = 24, .box_h = 32, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 16256, .adv_w = 443, .box_w = 24, .box_h = 33, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 16400, .adv_w = 443, .box_w = 24, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 16519, .adv_w = 325, .box_w = 20, .box_h = 23, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 16572, .adv_w = 445, .box_w = 24, .box_h = 31, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 16675, .adv_w = 445, .box_w = 24, .box_h = 31, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 16778, .adv_w = 445, .box_w = 24, .box_h = 32, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 16893, .adv_w = 445, .box_w = 24, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 16983, .adv_w = 438, .box_w = 25, .box_h = 40, .ofs_x = 1, .ofs_y = -9}, + {.bitmap_index = 17115, .adv_w = 438, .box_w = 25, .box_h = 38, .ofs_x = 1, .ofs_y = -9}, + {.bitmap_index = 17236, .adv_w = 137, .box_w = 4, .box_h = 23, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 17245, .adv_w = 879, .box_w = 52, .box_h = 29, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 17384, .adv_w = 753, .box_w = 44, .box_h = 23, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 17520, .adv_w = 526, .box_w = 29, .box_h = 36, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 17714, .adv_w = 439, .box_w = 25, .box_h = 30, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 17905, .adv_w = 516, .box_w = 30, .box_h = 35, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 18122, .adv_w = 525, .box_w = 29, .box_h = 36, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 18336, .adv_w = 447, .box_w = 24, .box_h = 30, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 18512, .adv_w = 453, .box_w = 25, .box_h = 4, .ofs_x = 2, .ofs_y = 10}, + {.bitmap_index = 18524, .adv_w = 526, .box_w = 29, .box_h = 4, .ofs_x = 2, .ofs_y = 10}, + {.bitmap_index = 18535, .adv_w = 108, .box_w = 4, .box_h = 9, .ofs_x = 1, .ofs_y = 20}, + {.bitmap_index = 18548, .adv_w = 106, .box_w = 4, .box_h = 9, .ofs_x = 2, .ofs_y = 20}, + {.bitmap_index = 18562, .adv_w = 232, .box_w = 12, .box_h = 9, .ofs_x = 1, .ofs_y = 20}, + {.bitmap_index = 18594, .adv_w = 232, .box_w = 11, .box_h = 9, .ofs_x = 2, .ofs_y = 20}, + {.bitmap_index = 18624, .adv_w = 237, .box_w = 5, .box_h = 4, .ofs_x = 5, .ofs_y = 13}, + {.bitmap_index = 18635, .adv_w = 367, .box_w = 19, .box_h = 4, .ofs_x = 2, .ofs_y = 0} +}; + +/*--------------------- + * CHARACTER MAPPING + *--------------------*/ + +static const uint8_t glyph_id_ofs_list_2[] = { + 0, 1, 2, 3, 0, 0, 0, 0, + 4, 0, 0, 0, 0, 0, 0, 5, + 6, 0, 0, 0, 7, 0, 8, 0, + 9, 0, 0, 0, 0, 0, 0, 10, + 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, + 0, 27, 28, 29, 30, 31, 32, 33, + 0, 34, 35, 36, 37, 38, 0, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 0, 56, 57, 58, 59, 60, 61, 62, + 0, 63, 64, 65, 66, 67 +}; + +static const uint16_t unicode_list_3[] = { + 0x0, 0x32, 0x53, 0x54, 0x61, 0x62, 0x79, 0x7e, + 0x7f, 0x1f14, 0x1f15, 0x1f19, 0x1f1a, 0x1f1d, 0x1f1e, 0x1f23, + 0x1f27 +}; + +/*Collect the unicode lists and glyph_id offsets*/ +static const lv_font_fmt_txt_cmap_t cmaps[] = +{ + { + .range_start = 32, .range_length = 62, .glyph_id_start = 1, + .unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY + }, + { + .range_start = 95, .range_length = 32, .glyph_id_start = 63, + .unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY + }, + { + .range_start = 160, .range_length = 94, .glyph_id_start = 95, + .unicode_list = NULL, .glyph_id_ofs_list = glyph_id_ofs_list_2, .list_length = 94, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL + }, + { + .range_start = 255, .range_length = 7976, .glyph_id_start = 163, + .unicode_list = unicode_list_3, .glyph_id_ofs_list = NULL, .list_length = 17, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY + } +}; + +/*----------------- + * KERNING + *----------------*/ + + +/*Pair left and right glyphs for kerning*/ +static const uint8_t kern_pair_glyph_ids[] = +{ + 32, 86, + 34, 56, + 34, 58, + 35, 55, + 35, 58, + 37, 55, + 37, 59, + 38, 46, + 38, 48, + 39, 15, + 39, 43, + 39, 51, + 39, 53, + 39, 89, + 40, 56, + 44, 34, + 44, 41, + 45, 55, + 45, 56, + 45, 58, + 46, 67, + 48, 55, + 48, 56, + 48, 57, + 49, 34, + 49, 43, + 49, 68, + 49, 86, + 51, 55, + 51, 56, + 52, 47, + 52, 56, + 52, 58, + 53, 79, + 53, 83, + 53, 85, + 53, 87, + 53, 89, + 53, 90, + 55, 34, + 55, 48, + 55, 52, + 55, 79, + 55, 83, + 56, 34, + 56, 48, + 56, 65, + 56, 69, + 56, 73, + 56, 79, + 56, 85, + 57, 35, + 58, 52, + 58, 65, + 58, 69, + 58, 73, + 58, 79, + 58, 80, + 58, 83, + 58, 85, + 59, 58, + 65, 66, + 65, 67, + 65, 76, + 65, 77, + 65, 78, + 65, 82, + 65, 83, + 65, 86, + 65, 87, + 65, 88, + 65, 90, + 66, 68, + 67, 69, + 67, 72, + 67, 75, + 67, 84, + 67, 85, + 67, 90, + 68, 69, + 68, 79, + 68, 85, + 69, 67, + 69, 69, + 69, 76, + 69, 82, + 69, 86, + 69, 87, + 69, 88, + 70, 13, + 70, 84, + 70, 85, + 71, 65, + 71, 67, + 71, 69, + 71, 79, + 71, 82, + 71, 83, + 71, 85, + 71, 87, + 72, 65, + 72, 70, + 72, 73, + 73, 65, + 73, 67, + 73, 70, + 73, 78, + 73, 80, + 73, 81, + 73, 86, + 73, 88, + 74, 69, + 75, 69, + 76, 65, + 76, 68, + 76, 69, + 76, 77, + 76, 79, + 76, 86, + 76, 87, + 76, 175, + 77, 75, + 77, 77, + 77, 87, + 78, 66, + 78, 67, + 78, 79, + 78, 80, + 78, 88, + 79, 65, + 79, 76, + 79, 78, + 79, 79, + 79, 82, + 79, 83, + 79, 84, + 79, 85, + 79, 86, + 79, 87, + 79, 88, + 80, 68, + 80, 73, + 80, 83, + 81, 85, + 82, 13, + 82, 15, + 82, 79, + 83, 67, + 83, 69, + 83, 75, + 83, 78, + 83, 85, + 83, 86, + 83, 88, + 83, 90, + 84, 66, + 84, 68, + 84, 72, + 84, 73, + 84, 90, + 85, 66, + 85, 67, + 85, 68, + 85, 69, + 85, 70, + 85, 77, + 85, 78, + 85, 80, + 85, 83, + 85, 84, + 85, 88, + 85, 90, + 86, 69, + 86, 79, + 86, 83, + 87, 69, + 87, 79, + 87, 82, + 87, 83, + 88, 69, + 89, 65, + 89, 67, + 89, 69, + 89, 76, + 89, 78, + 89, 79, + 90, 65, + 90, 69, + 90, 76, + 90, 84, + 90, 90 +}; + +/* Kerning between the respective left and right glyphs + * 4.4 format which needs to scaled with `kern_scale`*/ +static const int8_t kern_pair_values[] = +{ + 0, -15, -6, -24, -15, -14, -13, -14, + -15, -62, -117, 2, 5, -10, -13, -15, + -14, -113, -63, -87, -5, -13, -13, -19, + 5, -91, 5, 0, -9, -9, -9, -12, + -5, -60, -59, -49, -40, -44, -50, -18, + -16, -10, -15, -20, -13, -11, -7, -17, + -5, -12, -10, -17, -17, -47, -58, -9, + -58, -48, -57, -47, -5, -12, -5, -5, + -10, -7, -2, -10, -10, -15, -7, -12, + 15, -12, -7, -10, -12, -10, -10, -10, + -11, -11, -10, -7, -10, -2, -10, -15, + -8, -88, 2, 5, -7, -7, -7, -10, + -7, -10, -12, -10, 2, 5, 2, -17, + -7, -23, -7, -12, -7, -2, -20, -10, + -2, -5, 2, -7, -5, -5, -29, -32, + -42, -6, -6, -12, -10, -10, -7, -5, + -12, -5, -10, -7, -10, -5, -10, -11, + -11, -6, -17, -17, 12, 7, 5, -12, + -118, -10, -2, -9, -7, -7, -11, -10, + -10, -18, -11, 2, 10, 2, 2, 2, + -7, -10, 5, -7, -5, -10, 2, -7, + -7, -7, -7, -7, -15, -14, -12, -16, + -10, -10, -7, -24, -11, -10, -10, 5, + -7, -11, -12, -12, -10, -10, -10 +}; + +/*Collect the kern pair's data in one place*/ +static const lv_font_fmt_txt_kern_pair_t kern_pairs = +{ + .glyph_ids = kern_pair_glyph_ids, + .values = kern_pair_values, + .pair_cnt = 191, + .glyph_ids_size = 0 +}; + +/*-------------------- + * ALL CUSTOM DATA + *--------------------*/ + +#if LVGL_VERSION_MAJOR == 8 +/*Store all the custom data of the font*/ +static lv_font_fmt_txt_glyph_cache_t cache; +#endif + +#if LVGL_VERSION_MAJOR >= 8 +static const lv_font_fmt_txt_dsc_t font_dsc = { +#else +static lv_font_fmt_txt_dsc_t font_dsc = { +#endif + .glyph_bitmap = glyph_bitmap, + .glyph_dsc = glyph_dsc, + .cmaps = cmaps, + .kern_dsc = &kern_pairs, + .kern_scale = 21, + .cmap_num = 4, + .bpp = 4, + .kern_classes = 0, + .bitmap_format = 1, +#if LVGL_VERSION_MAJOR == 8 + .cache = &cache +#endif +}; + + + +/*----------------- + * PUBLIC FONT + *----------------*/ + +/*Initialize a public general font descriptor*/ +#if LVGL_VERSION_MAJOR >= 8 +const lv_font_t lv_font_orbitron_40 = { +#else +lv_font_t lv_font_orbitron_40 = { +#endif + .get_glyph_dsc = lv_font_get_glyph_dsc_fmt_txt, /*Function pointer to get glyph's data*/ + .get_glyph_bitmap = lv_font_get_bitmap_fmt_txt, /*Function pointer to get glyph's bitmap*/ + .line_height = 49, /*The maximum line height required by the font*/ + .base_line = 10, /*Baseline measured from the bottom of the line*/ +#if !(LVGL_VERSION_MAJOR == 6 && LVGL_VERSION_MINOR == 0) + .subpx = LV_FONT_SUBPX_NONE, +#endif +#if LV_VERSION_CHECK(7, 4, 0) || LVGL_VERSION_MAJOR >= 8 + .underline_position = -4, + .underline_thickness = 2, +#endif + .dsc = &font_dsc, /*The custom font data. Will be accessed by `get_glyph_bitmap/dsc` */ +#if LV_VERSION_CHECK(8, 2, 0) || LVGL_VERSION_MAJOR >= 9 + .fallback = NULL, +#endif + .user_data = NULL, +}; + + + +#endif /*#if LV_FONT_ORBITRON_40*/ + diff --git a/idf_zacus/components/display_ui/idf_component.yml b/idf_zacus/components/display_ui/idf_component.yml new file mode 100644 index 0000000..3777f55 --- /dev/null +++ b/idf_zacus/components/display_ui/idf_component.yml @@ -0,0 +1,21 @@ +## display_ui managed-component manifest. +## +## LovyanGFX is not published on the Espressif component registry; it is +## distributed as a standard ESP-IDF component on GitHub. The IDF component +## manager supports git: sources — on the first `idf.py build` it clones the +## repo at the requested tag into managed_components/. +## +## Trust note: lovyan03/LovyanGFX is the SAME upstream the original Arduino +## firmware already depends on (platformio.ini line 34: lovyan03/LovyanGFX@1.2.7). +## Pinned below to the exact commit SHA of release tag 1.2.21 so the dependency +## is reproducible and tamper-evident (a moved tag cannot change what we build). + +dependencies: + lovyan03/LovyanGFX: + git: https://github.com/lovyan03/LovyanGFX.git + version: "4e689dba65135c2d91b180dc0a27a3cedebcfb5e" # tag 1.2.21 + # LVGL from the Espressif registry (checksummed). Pinned to the same + # 8.4.x line as the original firmware (platformio.ini:35 lvgl/lvgl@8.4.0) + # so the original screens port unchanged in later phases. + lvgl/lvgl: + version: "~8.4.0" diff --git a/idf_zacus/components/display_ui/include/buttons_input.h b/idf_zacus/components/display_ui/include/buttons_input.h new file mode 100644 index 0000000..1fcfd42 --- /dev/null +++ b/idf_zacus/components/display_ui/include/buttons_input.h @@ -0,0 +1,22 @@ +// buttons_input.h — 5-way analog ladder on GPIO19 (FNK0102H), IDF port of +// ui_freenove_allinone's ButtonManager (same thresholds, debounce, semantics). +// +// Key numbers follow the original firmware: 1=SELECT 2=DOWN 3=MENU +// 4=LEFT/RIGHT 5=UP. Events are delivered to display_ui_handle_key() from a +// dedicated scan task (50 Hz, 30 ms debounce). +// +// GPIO19 is ADC2 on the ESP32-S3 — concurrent Wi-Fi can make individual +// reads fail with ESP_ERR_TIMEOUT; those samples are silently skipped (the +// 50 Hz scan re-samples immediately after). +#pragma once +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +esp_err_t buttons_input_init(void); + +#ifdef __cplusplus +} +#endif diff --git a/idf_zacus/components/display_ui/include/display_ui.h b/idf_zacus/components/display_ui/include/display_ui.h new file mode 100644 index 0000000..2a64173 --- /dev/null +++ b/idf_zacus/components/display_ui/include/display_ui.h @@ -0,0 +1,93 @@ +#pragma once +#include +#include +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + char step_id[64]; // current scenario step ("" = none) + char armed[8]; // "qr" | "sound" | "none" + char code[16]; // assembled puzzle code + uint8_t solved_count; + char ip[16]; // STA IP ("" = none) + bool wake_active; // wake-word detector up + + // Scene metadata (from the step's optional `scene` IR object; empty + // strings = fall back to step_id / armed / code). Sizes mirror + // puzzle_binding.h SB_MAX_*. + char scene_title[48]; + char scene_subtitle[64]; + char scene_symbol[16]; + uint8_t scene_effect; // scene_effect_t value (0=pulse 1=glitch 2=gyro 3=none) +} display_status_t; + +/** + * @brief Initialise the ST7796 panel via LovyanGFX and show a splash screen. + * + * Must be called once from app_main after NVS init but before the idle loop. + * Non-fatal: a failure is logged and the rest of the firmware continues. + * The internal refresh task is spawned here; it polls for status changes + * every 250 ms. + * + * @return ESP_OK on success, or a driver error code. + */ +esp_err_t display_ui_init(void); + +/** + * @brief Copy a new status snapshot and request an async redraw. + * + * Thread-safe. The copy is protected by an internal mutex; the actual + * rendering happens on the display task (LovyanGFX is not thread-safe + * across tasks). + * + * @param s Pointer to the caller's status struct. May be NULL (no-op). + */ +void display_ui_set_status(const display_status_t *s); + +/** + * @brief Set the backlight brightness (LEDC PWM on LEDC_TIMER_1/CHANNEL_1). + * + * @param pct 0..100 (values above 100 are clamped). Default after init: 100. + */ +void display_ui_set_brightness(uint8_t pct); + +/** + * @brief Push a camera frame for the scene-view viewfinder (QR aiming aid). + * + * Thread-safe; intended as the qr_puzzle preview callback (wired in main.c). + * Copies the grayscale QVGA buffer; the display task converts it to RGB565 + * and refreshes the canvas at ~5 fps. Frames are shown only while the scene + * view is active with the QR puzzle armed. Non-QVGA frames are ignored. + */ +void display_ui_camera_frame(const uint8_t *gray, int width, int height); + +/** + * @brief Handle a 5-way key press (called from the buttons scan task). + * + * Original key numbering: 1=SELECT 2=DOWN 3=MENU 4=LEFT/RIGHT 5=UP. + * Shell closed: SELECT / LEFT-RIGHT toggle scene↔status; MENU opens the + * Workbench shell; UP/DOWN step the backlight brightness. + * Shell open: grid navigation per the original ui_amiga_shell semantics + * (SELECT launches the tile — Statut / Scene / Auto / Lumiere; MENU closes). + * Thread-safe (atomic request flags consumed by the display task; the + * brightness path drives LEDC directly, which has its own locking). + */ +void display_ui_handle_key(uint8_t key); + +/** + * @brief Pop the pending shell-app launch request, if any. + * + * Dynamic shell tiles (from /littlefs/apps//) set a pending launch on + * SELECT; the main loop polls this and performs the app action (e.g. read + * the app's step.txt and call game_endpoint_apply_step). One-shot: returns + * true at most once per launch. + */ +bool display_ui_take_pending_launch(char *id_out, size_t cap); + +#ifdef __cplusplus +} +#endif diff --git a/idf_zacus/components/display_ui/include/intro_fx3d.h b/idf_zacus/components/display_ui/include/intro_fx3d.h new file mode 100644 index 0000000..cf40b52 --- /dev/null +++ b/idf_zacus/components/display_ui/include/intro_fx3d.h @@ -0,0 +1,37 @@ +// Intro cracktro — 3D FX phases B/C/D (rotozoom, dot sphere, ray corridor). +// Faithful per-pixel ports of ui_freenove_allinone/src/ui/fx/fx_engine.cpp +// (renderMidRotoZoom, renderDotSphere3D, renderRayCorridor) rendered at +// 240x160 in a PSRAM buffer then pixel-doubled to the 480x320 LVGL canvas. +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define FX3D_W 240 +#define FX3D_H 160 + +typedef enum { + FX3D_ROTOZOOM = 0, + FX3D_DOTSPHERE = 1, + FX3D_CORRIDOR = 2, + FX3D_STARFIELD = 3, // renderStarfield3D — z-flight + rotation + trails + FX3D_VOXEL = 4, // renderVoxelLandscape — raycast heightfield + FX3D_WIRECUBE = 5, // v9 WireCubeFx — Bresenham wireframe cube + FX3D_MODE_COUNT = 6, +} fx3d_mode_t; + +// Allocate the low-res buffer + LUTs/textures (PSRAM). Idempotent. +bool fx3d_init(void); + +// Render `mode` at time t_ms into dst (RGB565, dst_w x dst_h) with 2x pixel +// doubling. dst must be exactly FX3D_W*2 x FX3D_H*2; anything else is a no-op. +void fx3d_render(fx3d_mode_t mode, uint32_t t_ms, uint16_t *dst, + int dst_w, int dst_h); + +#ifdef __cplusplus +} +#endif diff --git a/idf_zacus/components/display_ui/intro_fx3d.cpp b/idf_zacus/components/display_ui/intro_fx3d.cpp new file mode 100644 index 0000000..c66a534 --- /dev/null +++ b/idf_zacus/components/display_ui/intro_fx3d.cpp @@ -0,0 +1,528 @@ +// Intro cracktro 3D FX — ports of the original Arduino FxEngine renderers +// (ui_freenove_allinone/src/ui/fx/fx_engine.cpp): renderMidRotoZoom, +// renderDotSphere3D (+ kDotSphere3D init), renderRayCorridor (+ kRayCorridor +// init). Only the render loops are ported — no CapsAllocator, timelines or +// DMA line buffers. Everything renders into a 240x160 RGB565 low-res buffer +// (PSRAM) then gets pixel-doubled into the caller's 480x320 canvas. +#include "intro_fx3d.h" + +#include +#include + +#include "esp_heap_caps.h" + +namespace { + +constexpr int kRotoTexSize = 128; // FxEngine kRotoTexSize +constexpr int kRayTexSize = 64; // FxEngine kRayTexSize +constexpr int kDotCount = 360; // ~W*H/75 clamped (original formula) +constexpr int kDotRadius = 72; // min_dim/2 - 8 clamped to [24,72] +constexpr int kDotBlobR = 2; +constexpr int kStar3DCount = 400; // ~W*H/50, capped for the 10 ms tick +constexpr int kVoxelMaxDist = 96; // FxEngine voxel_max_dist_ + +struct DotPt { int16_t x, y, z; }; +struct Star3D { int16_t x, y; uint16_t z; }; + +uint16_t *s_lowres; // FX3D_W * FX3D_H +uint16_t *s_roto_tex; // 128 * 128 +uint16_t *s_ray_tex; // 64 * 64 +DotPt *s_dots; +Star3D *s_stars3d; +uint16_t s_dot_shade[256]; +int8_t s_ray_col_off[FX3D_W]; +uint16_t s_ray_floor_q12[FX3D_H]; +int16_t s_sin_q15[256]; +uint8_t s_voxel_height[256]; +uint16_t s_voxel_pal[256]; +uint16_t s_voxel_proj_q8[kVoxelMaxDist + 1]; +uint32_t s_rng = 0x5EED1234u; +bool s_ready = false; + +uint32_t next_rand(void) { + s_rng ^= s_rng << 13; s_rng ^= s_rng >> 17; s_rng ^= s_rng << 5; + return s_rng; +} + +// ---- helpers (FxEngine::rgb565 / mul565_u8 / addSat565 / sin8) ---- + +uint16_t rgb565(uint8_t r, uint8_t g, uint8_t b) { + return (uint16_t) (((r & 0xF8u) << 8) | ((g & 0xFCu) << 3) | (b >> 3)); +} + +uint16_t mul565_u8(uint16_t c, uint8_t v) { + uint16_t r = (uint16_t) ((c >> 11) & 31u); + uint16_t g = (uint16_t) ((c >> 5) & 63u); + uint16_t b = (uint16_t) (c & 31u); + r = (uint16_t) ((r * v + 128u) >> 8); + g = (uint16_t) ((g * v + 128u) >> 8); + b = (uint16_t) ((b * v + 128u) >> 8); + return (uint16_t) ((r << 11) | (g << 5) | b); +} + +uint16_t add_sat565(uint16_t a, uint16_t b) { + uint16_t ar = (uint16_t) ((a >> 11) & 31u), ag = (uint16_t) ((a >> 5) & 63u), ab = (uint16_t) (a & 31u); + uint16_t br = (uint16_t) ((b >> 11) & 31u), bg = (uint16_t) ((b >> 5) & 63u), bb = (uint16_t) (b & 31u); + const uint16_t rr = (uint16_t) ((ar + br > 31u) ? 31u : (ar + br)); + const uint16_t gg = (uint16_t) ((ag + bg > 63u) ? 63u : (ag + bg)); + const uint16_t b2 = (uint16_t) ((ab + bb > 31u) ? 31u : (ab + bb)); + return (uint16_t) ((rr << 11) | (gg << 5) | b2); +} + +int16_t sin_q15(uint8_t a) { return s_sin_q15[a]; } +int16_t cos_q15(uint8_t a) { return s_sin_q15[(uint8_t) (a + 64u)]; } +// fx_sin8/fx_cos8 equivalents: amplitude -128..127. +int16_t sin8(uint8_t a) { return (int16_t) (s_sin_q15[a] >> 8); } +int16_t cos8(uint8_t a) { return (int16_t) (s_sin_q15[(uint8_t) (a + 64u)] >> 8); } + +template +T clampv(T v, T lo, T hi) { return (v < lo) ? lo : (v > hi) ? hi : v; } + +void fill_lowres(uint16_t color) { + // 32-bit fill (buffer is 4-byte aligned, FX3D_W*FX3D_H even) + const uint32_t packed = (uint32_t) color | ((uint32_t) color << 16); + uint32_t *dst32 = (uint32_t *) s_lowres; + for (size_t i = 0; i < (size_t) FX3D_W * FX3D_H / 2; i++) dst32[i] = packed; +} + +void add_pixel(int x, int y, uint16_t color) { + if (x < 0 || y < 0 || x >= FX3D_W || y >= FX3D_H) return; + const size_t idx = (size_t) y * FX3D_W + (size_t) x; + s_lowres[idx] = add_sat565(s_lowres[idx], color); +} + +void set_pixel(int x, int y, uint16_t color) { + if (x < 0 || y < 0 || x >= FX3D_W || y >= FX3D_H) return; + s_lowres[(size_t) y * FX3D_W + (size_t) x] = color; +} + +// Bresenham, additive (v9 WireCubeFx::line_, max-blend approximated by add). +void add_line(int x0, int y0, int x1, int y1, uint16_t color) { + int dx = (x1 > x0) ? (x1 - x0) : (x0 - x1); + const int sx = (x0 < x1) ? 1 : -1; + int dy = (y1 > y0) ? (y0 - y1) : (y1 - y0); // -abs + const int sy = (y0 < y1) ? 1 : -1; + int err = dx + dy; + for (;;) { + add_pixel(x0, y0, color); + if (x0 == x1 && y0 == y1) break; + const int e2 = err << 1; + if (e2 >= dy) { err += dy; x0 += sx; } + if (e2 <= dx) { err += dx; y0 += sy; } + } +} + +void *psram_alloc(size_t bytes) { + void *p = heap_caps_malloc(bytes, MALLOC_CAP_SPIRAM); + if (!p) p = heap_caps_malloc(bytes, MALLOC_CAP_DEFAULT); + return p; +} + +// ---- renderers (faithful ports) ---- + +// FxEngine::renderMidRotoZoom — additive rotozoom of the radial-checker +// texture generated in FxEngine::begin(). +void render_rotozoom(uint32_t now_ms) { + fill_lowres(rgb565(4u, 8u, 16u)); + const int cx = FX3D_W / 2, cy = FX3D_H / 2; + const uint8_t phase = (uint8_t) ((now_ms / 10u) & 0xFFu); + const int16_t s = sin8(phase); + const int16_t c = cos8(phase); + const int16_t pulse = (int16_t) (sin8((uint8_t) (phase * 2u)) >> 1); + const int16_t zoom_q8 = (int16_t) (256 + pulse); + + for (int y = 0; y < FX3D_H; y++) { + const int16_t dy = (int16_t) (y - cy); + const size_t row = (size_t) y * FX3D_W; + for (int x = 0; x < FX3D_W; x++) { + const int16_t dx = (int16_t) (x - cx); + int32_t u = (c * dx - s * dy); + int32_t v = (s * dx + c * dy); + u = (u * zoom_q8) >> 8; + v = (v * zoom_q8) >> 8; + const int tx = (int) ((u + kRotoTexSize / 2) & (kRotoTexSize - 1)); + const int ty = (int) ((v + kRotoTexSize / 2) & (kRotoTexSize - 1)); + const uint16_t tex = s_roto_tex[(size_t) ty * kRotoTexSize + (size_t) tx]; + s_lowres[row + x] = add_sat565(s_lowres[row + x], mul565_u8(tex, 180u)); + } + } +} + +// FxEngine::renderDotSphere3D — lit point sphere, Q15 LUT rotations. +void render_dotsphere(uint32_t now_ms) { + fill_lowres(0x0000u); + const uint8_t ax = (uint8_t) (now_ms >> 4); + const uint8_t ay = (uint8_t) (now_ms >> 5); + const uint8_t az = (uint8_t) (now_ms >> 6); + const int16_t cx_r = cos_q15(ax), sx_r = sin_q15(ax); + const int16_t cy_r = cos_q15(ay), sy_r = sin_q15(ay); + const int16_t cz_r = cos_q15(az), sz_r = sin_q15(az); + const int16_t lx = (int16_t) (0.30f * 32767.0f); + const int16_t ly = (int16_t) (-0.20f * 32767.0f); + const int16_t lz = (int16_t) (0.93f * 32767.0f); + const int center_x = FX3D_W / 2, center_y = FX3D_H / 2; + const int fov = (FX3D_W < FX3D_H) ? FX3D_W : FX3D_H; + + for (int i = 0; i < kDotCount; i++) { + const DotPt &dot = s_dots[i]; + const int32_t x = ((int32_t) dot.x * kDotRadius) >> 7; + const int32_t y = ((int32_t) dot.y * kDotRadius) >> 7; + const int32_t z = ((int32_t) dot.z * kDotRadius) >> 7; + + const int32_t y1 = (y * cx_r - z * sx_r) >> 15; + const int32_t z1 = (y * sx_r + z * cx_r) >> 15; + const int32_t x2 = (x * cy_r + z1 * sy_r) >> 15; + const int32_t z2 = (-x * sy_r + z1 * cy_r) >> 15; + const int32_t x3 = (x2 * cz_r - y1 * sz_r) >> 15; + const int32_t y3 = (x2 * sz_r + y1 * cz_r) >> 15; + + const int32_t depth = z2 + (kDotRadius * 3); + if (depth <= 1) continue; + + const int sxp = center_x + (int) ((x3 * fov) / depth); + const int syp = center_y + (int) ((y3 * fov) / depth); + if (sxp < 0 || syp < 0 || sxp >= FX3D_W || syp >= FX3D_H) continue; + + const int32_t nd = (x3 * lx + y3 * ly + z2 * lz) >> 15; + const int32_t ndotl = clampv((nd * 128) / kDotRadius + 128, 0, 255); + const uint16_t base = s_dot_shade[ndotl]; + + for (int yy = -kDotBlobR; yy <= kDotBlobR; yy++) { + for (int xx = -kDotBlobR; xx <= kDotBlobR; xx++) { + const int d2 = xx * xx + yy * yy; + if (d2 > kDotBlobR * kDotBlobR) continue; + const int atten = clampv(255 - d2 * 28, 0, 255); + add_pixel(sxp + xx, syp + yy, mul565_u8(base, (uint8_t) atten)); + } + } + } +} + +// FxEngine::renderRayCorridor — textured tunnel walls + scrolling floor. +void render_corridor(uint32_t now_ms) { + fill_lowres(0x0000u); + const int horizon = FX3D_H / 2; + const uint32_t zscroll = now_ms >> 3; + const uint8_t camera_angle = (uint8_t) (now_ms >> 6); + + for (int x = 0; x < FX3D_W; x++) { + const int8_t off = s_ray_col_off[x]; + const uint8_t ray_angle = (uint8_t) (camera_angle + (uint8_t) off); + const int16_t dir_x = sin_q15(ray_angle); + const int16_t dir_z = cos_q15(ray_angle); + const int16_t abs_dir_x = (int16_t) ((dir_x < 0) ? -dir_x : dir_x); + if (abs_dir_x < 64) { + for (int y = horizon; y < FX3D_H; y++) { + const int dy = y - horizon; + const uint8_t shade = (uint8_t) (120 + dy * 2); + s_lowres[(size_t) y * FX3D_W + x] = mul565_u8(rgb565(6u, 5u, 2u), shade); + } + continue; + } + + const uint32_t t_q15 = (1ul << 30) / (uint32_t) abs_dir_x; + const uint16_t corr = (uint16_t) cos_q15((uint8_t) off); + uint32_t dist_q15 = (uint32_t) (((uint64_t) t_q15 * corr) >> 15); + if (dist_q15 == 0u) dist_q15 = 1u; + int slice = (int) (((uint32_t) FX3D_H << 15) / dist_q15); + slice = clampv(slice, 1, FX3D_H); + int y0 = horizon - slice / 2; + int y1 = y0 + slice - 1; + y0 = clampv(y0, 0, FX3D_H - 1); + y1 = clampv(y1, 0, FX3D_H - 1); + + const int32_t zhit_q15 = (int32_t) (((int64_t) dir_z * (int64_t) t_q15) >> 15); + int u = (int) (((zhit_q15 >> 9) + (int32_t) zscroll) & 63); + if (dir_x < 0) u ^= 63; + const int shade = clampv(255 - (int) (dist_q15 >> 9), 0, 255); + for (int y = y0; y <= y1; y++) { + const int v = ((y - y0) * 64) / ((slice == 0) ? 1 : slice); + uint16_t color = s_ray_tex[(size_t) (v & 63) * kRayTexSize + (size_t) (u & 63)]; + s_lowres[(size_t) y * FX3D_W + x] = mul565_u8(color, (uint8_t) shade); + } + for (int y = y1 + 1; y < FX3D_H; y++) { + const uint16_t k = s_ray_floor_q12[y]; + if (k == 0u) continue; + const int32_t uu_q12 = (int32_t) (((int64_t) dir_x * k) >> 15); + const int32_t vv_q12 = (int32_t) (((int64_t) dir_z * k) >> 15); + const int uf = (int) (((uu_q12 >> 6) + (int32_t) zscroll) & 63); + const int vf = (int) (((vv_q12 >> 6) + (int32_t) (zscroll >> 1)) & 63); + uint16_t color = s_ray_tex[(size_t) (vf & 63) * kRayTexSize + (size_t) (uf & 63)]; + const int dy = y - horizon; + const int fade = clampv(255 - dy * 2, 0, 255); + s_lowres[(size_t) y * FX3D_W + x] = mul565_u8(color, (uint8_t) fade); + } + } +} + +// FxEngine::renderStarfield3D — z-flight starfield, slow roll, motion trails. +void render_starfield3d(uint32_t now_ms) { + fill_lowres(rgb565(2u, 4u, 10u)); + const uint8_t angle = (uint8_t) (now_ms >> 4); + const int16_t cs = cos_q15(angle); + const int16_t sn = sin_q15(angle); + const int fov = ((FX3D_W < FX3D_H) ? FX3D_W : FX3D_H) + 24; + const uint16_t z_min = 32u; + const int dz = (int) (10u + ((now_ms >> 6) & 7u)); + const int cx = FX3D_W / 2, cy = FX3D_H / 2; + const uint16_t base = rgb565(240u, 248u, 255u); + + for (int i = 0; i < kStar3DCount; i++) { + Star3D &star = s_stars3d[i]; + const uint16_t z_prev = star.z; + const int z_next = (int) star.z - dz; + if (z_next < (int) z_min) { + star.x = (int16_t) ((int32_t) (next_rand() & 511u) - 256); + star.y = (int16_t) ((int32_t) ((next_rand() >> 9) & 511u) - 256); + star.z = (uint16_t) (256u + (next_rand() % 768u)); + continue; + } + star.z = (uint16_t) z_next; + + const int xr = (int) (((int32_t) star.x * cs - (int32_t) star.y * sn) >> 15); + const int yr = (int) (((int32_t) star.x * sn + (int32_t) star.y * cs) >> 15); + const int sx = cx + (xr * fov) / (int) star.z; + const int sy = cy + (yr * fov) / (int) star.z; + const int sx0 = cx + (xr * fov) / (int) z_prev; + const int sy0 = cy + (yr * fov) / (int) z_prev; + if (sx < 0 || sy < 0 || sx >= FX3D_W || sy >= FX3D_H) continue; + + uint8_t brightness = (uint8_t) (255u - (star.z >> 2)); + if (brightness < 40u) brightness = 40u; + set_pixel(sx, sy, mul565_u8(base, brightness)); + + const int dx = sx - sx0, dy = sy - sy0; + int steps = ((dx < 0 ? -dx : dx) > (dy < 0 ? -dy : dy)) + ? (dx < 0 ? -dx : dx) : (dy < 0 ? -dy : dy); + steps = clampv(steps, 0, 10); + for (int s = 1; s <= steps; s++) { + const int x = sx0 + (dx * s) / steps; + const int y = sy0 + (dy * s) / steps; + const uint8_t fade = (uint8_t) ((brightness * (steps - s)) / (steps + 1)); + add_pixel(x, y, mul565_u8(base, fade)); + } + } +} + +// FxEngine::renderVoxelLandscape — per-column raycast heightfield over a +// vertical sky gradient, camera drifting with time. +void render_voxel(uint32_t now_ms) { + for (int y = 0; y < FX3D_H; y++) { + const uint8_t t = (uint8_t) ((y * 255) / FX3D_H); + const uint16_t color = rgb565((uint8_t) ((8 * (255 - t)) >> 8), + (uint8_t) ((12 * (255 - t)) >> 8), + (uint8_t) ((32 * (255 - t)) >> 8)); + const size_t row = (size_t) y * FX3D_W; + for (int x = 0; x < FX3D_W; x++) s_lowres[row + x] = color; + } + + const int horizon = FX3D_H / 2; + const uint8_t angle = (uint8_t) (now_ms >> 6); + const uint16_t cam_x = (uint16_t) ((now_ms >> 5) & 255u); + const uint16_t cam_y = (uint16_t) ((now_ms >> 6) & 255u); + const int half = FX3D_W / 2; + + for (int x = 0; x < FX3D_W; x++) { + const int dx = x - half; + const uint8_t ray_angle = (uint8_t) (angle + (uint8_t) ((dx * 24) / half)); + const int16_t dir_x = cos_q15(ray_angle); + const int16_t dir_y = sin_q15(ray_angle); + int max_y = FX3D_H - 1; + for (int z = 1; z <= kVoxelMaxDist; z++) { + const int map_x = ((int) cam_x + ((dir_x * z) >> 15)) & 255; + const int map_y = ((int) cam_y + ((dir_y * z) >> 15)) & 255; + const uint8_t hh = s_voxel_height[(uint8_t) ((map_x + map_y * 3) & 255)]; + const uint16_t proj = s_voxel_proj_q8[z]; + int y = horizon - (int) (((unsigned) hh * proj) >> 8); + if (y < 0) y = 0; + if (y > max_y) continue; + const uint8_t shade = (uint8_t) ((z * 3 < 255) ? (255 - z * 3) : 0); + const uint16_t color = s_voxel_pal[shade]; + for (int yy = y; yy <= max_y; yy++) + s_lowres[(size_t) yy * FX3D_W + x] = color; + max_y = y - 1; + if (max_y < 0) break; + } + } +} + +// v9 WireCubeFx — wireframe cube, float math (8 vertices/frame, FPU is fine), +// rotation speeds and projection from the v9 defaults feel. +void render_wirecube(uint32_t now_ms) { + fill_lowres(rgb565(2u, 3u, 8u)); + static const float V[8][3] = { + {-1, -1, -1}, {+1, -1, -1}, {+1, +1, -1}, {-1, +1, -1}, + {-1, -1, +1}, {+1, -1, +1}, {+1, +1, +1}, {-1, +1, +1}, + }; + static const uint8_t E[12][2] = { + {0, 1}, {1, 2}, {2, 3}, {3, 0}, + {4, 5}, {5, 6}, {6, 7}, {7, 4}, + {0, 4}, {1, 5}, {2, 6}, {3, 7}, + }; + const float t = (float) now_ms * 0.001f; + const float ax = t * 0.9f, ay = t * 1.3f, az = t * 0.5f; + const float sx = sinf(ax), cxr = cosf(ax); + const float sy = sinf(ay), cyr = cosf(ay); + const float sz = sinf(az), czr = cosf(az); + const int cx = FX3D_W / 2, cy = FX3D_H / 2; + const float pulse = 0.5f + 0.5f * sinf(t * 2.4f); + const float scale = (float) FX3D_H * 0.30f * (1.0f + 0.20f * pulse); + const float fov = 2.2f, z_offset = 3.0f; + + int px[8], py[8]; + for (int i = 0; i < 8; i++) { + const float x = V[i][0], y = V[i][1], z = V[i][2]; + const float x1 = x * cyr + z * sy; + const float z1 = -x * sy + z * cyr; + const float y2 = y * cxr - z1 * sx; + const float z2 = y * sx + z1 * cxr; + const float x3 = x1 * czr - y2 * sz; + const float y3 = x1 * sz + y2 * czr; + float zz = z2 + z_offset; + if (zz < 0.3f) zz = 0.3f; + const float inv = fov / zz; + px[i] = cx + (int) lroundf(x3 * inv * scale); + py[i] = cy + (int) lroundf(y3 * inv * scale); + } + const uint16_t edge = mul565_u8(rgb565(120u, 255u, 220u), + (uint8_t) (200 + (int) (55.0f * pulse))); + for (int e = 0; e < 12; e++) + add_line(px[E[e][0]], py[E[e][0]], px[E[e][1]], py[E[e][1]], edge); +} + +} // namespace + +extern "C" bool fx3d_init(void) { + if (s_ready) return true; + + s_lowres = (uint16_t *) psram_alloc((size_t) FX3D_W * FX3D_H * sizeof(uint16_t)); + s_roto_tex = (uint16_t *) psram_alloc((size_t) kRotoTexSize * kRotoTexSize * sizeof(uint16_t)); + s_ray_tex = (uint16_t *) psram_alloc((size_t) kRayTexSize * kRayTexSize * sizeof(uint16_t)); + s_dots = (DotPt *) psram_alloc((size_t) kDotCount * sizeof(DotPt)); + s_stars3d = (Star3D *) psram_alloc((size_t) kStar3DCount * sizeof(Star3D)); + if (!s_lowres || !s_roto_tex || !s_ray_tex || !s_dots || !s_stars3d) { + heap_caps_free(s_lowres); s_lowres = nullptr; + heap_caps_free(s_roto_tex); s_roto_tex = nullptr; + heap_caps_free(s_ray_tex); s_ray_tex = nullptr; + heap_caps_free(s_dots); s_dots = nullptr; + heap_caps_free(s_stars3d); s_stars3d = nullptr; + return false; + } + + for (int i = 0; i < 256; i++) { + s_sin_q15[i] = (int16_t) (sinf((float) i * (6.28318530f / 256.0f)) * 32767.0f); + } + + // Rotozoom texture — radial-shaded checker (FxEngine::begin). + for (int y = 0; y < kRotoTexSize; y++) { + for (int x = 0; x < kRotoTexSize; x++) { + const float cx = (float) x - kRotoTexSize * 0.5f; + const float cy = (float) y - kRotoTexSize * 0.5f; + float rr = sqrtf(cx * cx + cy * cy) / (kRotoTexSize * 0.5f); + if (rr > 1.0f) rr = 1.0f; + const bool checker = (((x >> 4) ^ (y >> 4)) & 1) != 0; + const uint8_t r = checker ? (uint8_t) (40 + (uint8_t) (200.0f * (1.0f - rr))) : 200u; + const uint8_t g = checker ? (uint8_t) (60 + (uint8_t) (160.0f * (1.0f - rr))) + : (uint8_t) (50 + (uint8_t) (120.0f * (1.0f - rr))); + const uint8_t b = checker ? 200u : (uint8_t) (60 + (uint8_t) (120.0f * (1.0f - rr))); + s_roto_tex[(size_t) y * kRotoTexSize + x] = rgb565(r, g, b); + } + } + + // Dot sphere — shade LUT + random unit sphere (initModeState, same seed). + { + const uint16_t base = rgb565(40u, 80u, 240u); + const uint16_t high = rgb565(255u, 255u, 255u); + for (int i = 0; i < 256; i++) { + const uint8_t diffuse = (uint8_t) i; + const int spec_i = (i > 220) ? (i - 220) * 7 : 0; + const uint8_t spec = (uint8_t) clampv(spec_i, 0, 255); + s_dot_shade[i] = add_sat565(mul565_u8(base, diffuse), mul565_u8(high, spec)); + } + uint32_t rng = 0xBADC0FFEul ^ (uint32_t) FX3D_W ^ ((uint32_t) FX3D_H << 16); + for (int i = 0; i < kDotCount; i++) { + rng ^= rng << 13; rng ^= rng >> 17; rng ^= rng << 5; + const uint8_t a = (uint8_t) (rng & 0xFFu); + rng ^= rng << 13; rng ^= rng >> 17; rng ^= rng << 5; + const uint8_t b = (uint8_t) ((rng >> 8) & 0xFFu); + const int16_t ca = cos_q15(a), sa = sin_q15(a); + const int16_t cb = cos_q15(b), sb = sin_q15(b); + s_dots[i].x = (int16_t) ((((int32_t) ca * cb) >> 15) >> 8); + s_dots[i].y = (int16_t) ((int32_t) sb >> 8); + s_dots[i].z = (int16_t) ((((int32_t) sa * cb) >> 15) >> 8); + } + } + + // Ray corridor — wall/floor texture, per-column angle offsets, floor LUT + // (initModeState kRayCorridor). + for (int y = 0; y < kRayTexSize; y++) { + for (int x = 0; x < kRayTexSize; x++) { + const bool checker = (((x >> 3) ^ (y >> 3)) & 1) != 0; + int c = checker ? 190 : 70; + if ((y & 7) == 0) c = 40; + s_ray_tex[(size_t) y * kRayTexSize + x] = + rgb565((uint8_t) c, (uint8_t) (c / 2), (uint8_t) (c / 3)); + } + } + for (int x = 0; x < FX3D_W; x++) { + const int dx = x - FX3D_W / 2; + s_ray_col_off[x] = (int8_t) clampv((dx * 24) / (FX3D_W / 2), -64, 64); + } + for (int y = 0; y < FX3D_H; y++) { + const int dy = y - FX3D_H / 2; + if (dy <= 0) { s_ray_floor_q12[y] = 0u; continue; } + uint32_t value = (64ul << 12) / (uint32_t) dy; + if (value > 65535ul) value = 65535ul; + s_ray_floor_q12[y] = (uint16_t) value; + } + + // Starfield 3D — initModeState kStarfield3D. + for (int i = 0; i < kStar3DCount; i++) { + s_stars3d[i].x = (int16_t) ((int32_t) (next_rand() & 511u) - 256); + s_stars3d[i].y = (int16_t) ((int32_t) ((next_rand() >> 9) & 511u) - 256); + s_stars3d[i].z = (uint16_t) (128u + (next_rand() % 896u)); + } + + // Voxel landscape — initModeState kVoxelLandscape (sine heightfield, + // shaded green palette, perspective projection table). + for (int i = 0; i < 256; i++) { + const int16_t s1 = sin_q15((uint8_t) i); + const int16_t s2 = sin_q15((uint8_t) (i * 3)); + s_voxel_height[i] = (uint8_t) clampv(s1 / 512 + s2 / 1024 + 128, 0, 255); + s_voxel_pal[i] = mul565_u8(rgb565(30u, 220u, 80u), (uint8_t) i); + } + for (int z = 1; z <= kVoxelMaxDist; z++) { + s_voxel_proj_q8[z] = (uint16_t) clampv((70 * 256) / (z + 8), 0, 65535); + } + s_voxel_proj_q8[0] = 0u; + + s_ready = true; + return true; +} + +extern "C" void fx3d_render(fx3d_mode_t mode, uint32_t t_ms, uint16_t *dst, + int dst_w, int dst_h) { + if (!s_ready || dst == nullptr || dst_w != FX3D_W * 2 || dst_h != FX3D_H * 2) { + return; + } + switch (mode) { + case FX3D_ROTOZOOM: render_rotozoom(t_ms); break; + case FX3D_DOTSPHERE: render_dotsphere(t_ms); break; + case FX3D_CORRIDOR: render_corridor(t_ms); break; + case FX3D_STARFIELD: render_starfield3d(t_ms); break; + case FX3D_VOXEL: render_voxel(t_ms); break; + case FX3D_WIRECUBE: render_wirecube(t_ms); break; + default: return; + } + // 2x pixel doubling, two 32-bit writes per source pixel, row duplicated. + for (int y = 0; y < FX3D_H; y++) { + const uint16_t *src = &s_lowres[(size_t) y * FX3D_W]; + uint32_t *out0 = (uint32_t *) (dst + (size_t) (y * 2) * dst_w); + uint32_t *out1 = (uint32_t *) (dst + (size_t) (y * 2 + 1) * dst_w); + for (int x = 0; x < FX3D_W; x++) { + const uint32_t px = (uint32_t) src[x] | ((uint32_t) src[x] << 16); + out0[x] = px; + out1[x] = px; + } + } +} diff --git a/idf_zacus/components/game_endpoint/CMakeLists.txt b/idf_zacus/components/game_endpoint/CMakeLists.txt index 827a2f6..e3b3f0c 100644 --- a/idf_zacus/components/game_endpoint/CMakeLists.txt +++ b/idf_zacus/components/game_endpoint/CMakeLists.txt @@ -1,6 +1,7 @@ idf_component_register( SRCS "game_endpoint.c" + "puzzle_binding.c" INCLUDE_DIRS "include" REQUIRES @@ -13,4 +14,7 @@ idf_component_register( freertos log joltwallet__littlefs + puzzle_state + PRIV_REQUIRES + local_puzzles ) diff --git a/idf_zacus/components/game_endpoint/README.md b/idf_zacus/components/game_endpoint/README.md index 83dba56..59722b5 100644 --- a/idf_zacus/components/game_endpoint/README.md +++ b/idf_zacus/components/game_endpoint/README.md @@ -1,30 +1,57 @@ # game_endpoint -REST surface for **runtime game configuration**. Slice 12 of the IDF -migration. Currently exposes a single resource — the hints-engine -group profile — but the component is the natural home for additional -runtime tunables (cooldowns, voice persona, etc.) as scenarios grow. +Surface **REST de jeu** du firmware. Le composant n'ouvre **aucun socket +propre** : il greffe ses handlers sur l'instance `esp_http_server` +(port **80**) détenue par `ota_server` — même pattern que +`voice_hook_endpoint`, même pool de workers. `game_endpoint_init(httpd)` +est appelé après `ota_server_init()`, avec le handle rendu par +`ota_server_get_handle()`. -## Why +Né en slice 12 pour le seul profil de groupe des indices, le composant a +beaucoup grandi. Il couvre aujourd'hui quatre domaines : -The hints engine adapts its policy to the audience: `TECH`, -`NON_TECH`, `MIXED`, or `BOTH`. Until this slice the value was baked -into NVS via `idf.py nvs-partition-gen` or the dashboard's flash -helper — both require a power cycle. The game master needs to be able -to change profile mid-session if the actual room composition differs -from the booking, so we expose a REST endpoint that updates both the -in-RAM hints client and the persistent NVS slot. +- **configuration runtime** — profil de groupe du moteur d'indices ; +- **scénario** — chargement à chaud de l'IR Runtime 3 (LittleFS + reboot), + et relais du même IR vers d'autres boîtiers en ESP-NOW ; +- **provisioning mesh** — registre des pairs ESP-NOW (alias → MAC) ; +- **énigmes locales** — armement d'une énigme par step, lecture de l'état + agrégé, et dépôt de fichiers d'apps pour le shell d'affichage. -## Routes +## Table des routes -Listener: existing `esp_http_server` instance on port **80** (shared -with `ota_server` and `voice_hook_endpoint` — same TCP socket, no -second httpd). +| Méthode | Chemin | Corps attendu | Réponse (succès) | +|---|---|---|---| +| GET | `/game/group_profile` | — | `200 {group_profile}` | +| POST | `/game/group_profile` | `{group_profile}` | `200 {status,group_profile}` | +| POST | `/game/scenario` | IR Runtime 3 (JSON brut, ≤64 KiB) | `200 {status,steps_count,entry_step_id,bytes,reload}` + reboot | +| POST | `/game/scenario/relay` | `{peers:[…], ir:{…}}` | `200 {relayed:[…],skipped:[…]}` | +| GET | `/game/peers` | — | `200 {peers:[{alias,mac}]}` | +| POST | `/game/peers` | `{alias, mac}` | `200 {ok,alias,live}` | +| POST | `/game/step` | `{step_id}` | `200 {step_id,armed}` | +| GET | `/game/puzzle_state` | — | `200 {step_id,solved,code}` | +| POST | `/game/file?path=apps//` | corps brut du fichier (≤256 KiB) | `200 {status,path,bytes}` | + +Toutes les réponses sont `application/json` avec l'en-tête +`Access-Control-Allow-Origin: *`. Les corps d'erreur ont la forme +`{"error":""}` (sauf le cas `500 runtime_only` de +`POST /game/group_profile`, qui renvoie un objet structuré). + +`POST /game/scenario/relay` n'est enregistrée **que** si +`scenario_mesh_init()` a réussi au boot ; sinon elle est absente et le log +le signale. `/game/peers`, `/game/step`, `/game/puzzle_state` et +`/game/file` sont enregistrées en best-effort (un échec d'enregistrement +est loggué mais non fatal). `GET /game/puzzle_state` et `POST /game/step` +ne fonctionnent qu'après l'appel à `game_endpoint_set_puzzle_state()` ; +avant cela elles répondent `503 not_ready`. + +--- + +## Configuration runtime ### `GET /game/group_profile` -Returns the live profile (whatever `hints_client_group_profile()` -reports — defaults to `MIXED` after init). +Renvoie le profil vivant rapporté par `hints_client_group_profile()` +(`MIXED` par défaut après init). ```json { "group_profile": "MIXED" } @@ -32,65 +59,387 @@ reports — defaults to `MIXED` after init). ### `POST /game/group_profile` -Body (JSON, max 256 bytes): +Corps JSON, **1..256 octets** : ```json { "group_profile": "NON_TECH" } ``` -| Status | Body | Meaning | -|--------|------|---------| -| 200 | `{"status":"ok","group_profile":"NON_TECH"}` | accepted, NVS persisted | -| 400 | `{"error":"missing 'group_profile'"}` | empty / non-string field | -| 400 | `{"error":"invalid group_profile, must be one of [TECH, NON_TECH, MIXED, BOTH]"}` | rejected by hints client whitelist | -| 400 | `{"error":"malformed json"}` | body did not parse | -| 413 | `{"error":"body must be 1..256 bytes"}` | body too large | -| 500 | `{"status":"runtime_only","group_profile":"NON_TECH","warning":"nvs write failed: …"}` | hints client updated but NVS commit failed (will not survive reboot) | +La validation est déléguée à `hints_client_set_group_profile()` +(whitelist `TECH` / `NON_TECH` / `MIXED` / `BOTH`). En cas d'acceptation +la valeur est ensuite persistée en NVS (namespace `zacus`, clé +`group_profile`) — le même slot que `main.c` relit au boot, donc le +changement survit au reboot sans flash. -## curl examples +| Statut | Corps | Sens | +|---|---|---| +| 200 | `{"status":"ok","group_profile":"NON_TECH"}` | accepté, NVS persisté | +| 400 | `{"error":"missing 'group_profile'"}` | champ vide / non-string | +| 400 | `{"error":"invalid group_profile, must be one of [TECH, NON_TECH, MIXED, BOTH]"}` | rejeté par la whitelist | +| 400 | `{"error":"malformed json"}` | corps non parsable | +| 413 | `{"error":"body must be 1..256 bytes"}` | corps hors bornes | +| 500 | `{"status":"runtime_only","group_profile":"NON_TECH","warning":"nvs write failed: …"}` | hints client mis à jour mais commit NVS échoué (ne survivra pas au reboot) | -```bash -# Set the profile (uses mDNS — see main.c slice 12 wire-up) -curl -X POST http://zacus-master.local/game/group_profile \ - -H "Content-Type: application/json" \ - -d '{"group_profile":"NON_TECH"}' +--- -# Probe current state -curl http://zacus-master.local/game/group_profile +## Scénario -# Static-IP fallback -curl -X POST http://192.168.0./game/group_profile \ - -H "Content-Type: application/json" \ - -d '{"group_profile":"MIXED"}' +### `POST /game/scenario` + +Charge à chaud un IR **Runtime 3** complet. Corps = JSON brut, +**1..65536 octets**. Validation firmware **minimale** (le validateur +strict est `runtime3_common.py`, côté gateway) : + +- JSON parsable ; +- `schema_version == "zacus.runtime3.v1"` ; +- `steps` = tableau **non vide**. + +Sur succès : l'ancien `scenario.json` est tourné en `scenario.bak`, le +nouveau blob est écrit atomiquement sur LittleFS +(`/littlefs/scenario.json`), toute énigme en cours est désarmée, puis un +**reboot différé de ~800 ms** est planifié (le temps que la réponse HTTP +soit flushée). Un write court est rollback depuis `.bak`. + +```json +{ "status": "ok", "steps_count": 12, "entry_step_id": "STEP_INTRO", + "bytes": 4096, "reload": "reboot_pending" } ``` -## Persistence +| Statut | Corps | Sens | +|---|---|---| +| 200 | `{…,"reload":"reboot_pending"}` | accepté, écrit, reboot planifié | +| 400 | `{"error":"malformed json"}` | JSON invalide | +| 400 | `{"error":"schema_version must be zacus.runtime3.v1"}` | mauvais schéma | +| 400 | `{"error":"steps must be a non-empty array"}` | `steps` absent/vide | +| 400 | `{"error":"recv failed"}` | erreur de réception socket | +| 413 | `{"error":"body must be 1..65536 bytes"}` | corps hors bornes | +| 500 | `{"error":"littlefs mount failed"}` / `"scenario write open failed"` / `"scenario write short"` / `"out of memory"` | échec stockage | -Successful POSTs write to NVS namespace `zacus`, key `group_profile`. -This is the same slot `main.c` reads at boot to seed the hints client, -so a successful POST survives reboot without a flash step. The -write/commit pair runs on the httpd worker task; expect ~5–15 ms of -flash latency. +```bash +curl -X POST http://zacus-master.local/game/scenario \ + -H "Content-Type: application/json" \ + --data-binary @scenario.runtime3.json +``` -If the hints client validation passes but `nvs_set_str` / -`nvs_commit` fails (e.g. NVS partition full), the response is `500` -with `status: runtime_only` so the operator can decide whether to -keep going or force a reboot. +### `POST /game/scenario/relay` -## Component dependencies +(Présente uniquement si le mesh ESP-NOW est monté.) Diffuse un IR vers +d'autres boîtiers. Corps **1..65536 octets** : + +```json +{ "peers": ["box3", "plip"], "ir": { /* IR Runtime 3 */ } } +``` + +L'objet `ir` est re-sérialisé compact puis chunké/envoyé en ESP-NOW à +chaque alias résolu via le registre des pairs. Un échec sur un pair +(alias inconnu, timeout) n'interrompt pas les autres : il atterrit dans +`skipped` avec sa raison. Côté récepteur, l'IR repasse par exactement le +même chemin que `POST /game/scenario` (validation + write + reboot). + +```json +{ "relayed": ["box3"], + "skipped": [ { "name": "plip", "reason": "unknown_peer" } ] } +``` + +Raisons de `skipped` : `unknown_peer` (alias absent du registre), +`timeout` (pas d'ack ESP-NOW), ou tout autre `esp_err_to_name`. + +| Statut | Corps | Sens | +|---|---|---| +| 200 | `{"relayed":[…],"skipped":[…]}` | traité (succès partiel possible) | +| 400 | `{"error":"malformed json"}` | JSON invalide | +| 400 | `{"error":"'peers' must be a non-empty array"}` | `peers` absent/vide | +| 400 | `{"error":"'ir' must be an object"}` | `ir` non objet | +| 413 | `{"error":"body must be 1..65536 bytes"}` | corps hors bornes | +| 500 | `{"error":"ir serialize failed"}` / `"response serialize failed"` | échec interne cJSON | + +--- + +## Provisioning mesh + +Remplace le round-trip desktop `NvsConfigurator` pour le provisioning +ESP-NOW. + +### `POST /game/peers` + +Corps **1..256 octets** : + +```json +{ "alias": "plip", "mac": "AA:BB:CC:DD:EE:FF" } +``` + +- `alias` : **1..15 caractères** (limite de clé NVS) ; +- `mac` : format `AA:BB:CC:DD:EE:FF` (6 octets hex). + +Persiste en NVS (namespace `peers`, clé = alias, blob = MAC 6 octets — le +format exact que `main.c` reseed au boot) **et** enregistre le pair en +direct dans la table `scenario_mesh` (sans reboot). Si l'enregistrement +live échoue, le pair est tout de même en NVS (`live:false`, effectif au +prochain reboot). + +```json +{ "ok": true, "alias": "plip", "live": true } +``` + +| Statut | Corps | Sens | +|---|---|---| +| 200 | `{"ok":true,"alias":"plip","live":true|false}` | persisté (`live` = enregistré à chaud) | +| 400 | `{"error":"invalid JSON"}` | corps non parsable | +| 400 | `{"error":"alias and mac (string) required"}` | champ manquant/non-string | +| 400 | `{"error":"alias must be 1..15 chars (NVS key)"}` | alias hors bornes | +| 400 | `{"error":"mac must be AA:BB:CC:DD:EE:FF"}` | MAC mal formé | +| 413 | `{"error":"body must be 1..256 bytes"}` | corps hors bornes | +| 500 | `{"error":"NVS write failed"}` | échec NVS | + +### `GET /game/peers` + +Itère le namespace NVS `peers` et liste les pairs persistés : + +```json +{ "peers": [ { "alias": "plip", "mac": "AA:BB:CC:DD:EE:FF" } ] } +``` + +```bash +curl -X POST http://zacus-master.local/game/peers \ + -H "Content-Type: application/json" \ + -d '{"alias":"plip","mac":"AA:BB:CC:DD:EE:FF"}' + +curl http://zacus-master.local/game/peers +``` + +--- + +## Énigmes locales + +### `POST /game/step` + +Arme l'énigme locale attachée à un step de l'IR stocké. Corps +**1..512 octets** : + +```json +{ "step_id": "STEP_3" } +``` + +Le handler relit `/littlefs/scenario.json`, en extrait le binding du step +via `puzzle_binding_from_ir` (+ la `scene` d'affichage, parse lenient), +désarme l'énigme courante, puis arme la nouvelle. Même logique exposée en +interne via `game_endpoint_apply_step()` (lancement d'app sur l'écran du +boîtier). + +`armed` indique ce qui a été armé : `"qr"`, `"sound"`, ou `"none"` (step +trouvé mais sans objet `puzzle`). + +```json +{ "step_id": "STEP_3", "armed": "qr" } +``` + +| Statut | Corps | Sens | +|---|---|---| +| 200 | `{"step_id":"STEP_3","armed":"qr|sound|none"}` | armé | +| 400 | `{"error":"missing or empty step_id"}` | champ absent/vide | +| 400 | `{"error":"malformed json"}` | corps non parsable | +| 400 | `{"error":"body must be 1..512 bytes"}` | corps hors bornes | +| 404 | `{"error":"unknown_step"}` | aucun step de cet id dans l'IR | +| 409 | `{"error":"no_scenario"}` | aucun scénario stocké / stockage indispo | +| 422 | `{"error":"invalid_puzzle"}` | objet `puzzle` invalide dans l'IR | +| 503 | `{"error":"puzzle_busy"}` | énigme occupée (fenêtre de teardown QR, après un retry) | +| 503 | `{"error":"not_ready"}` | `puzzle_state` pas encore câblé | + +```bash +curl -X POST http://zacus-master.local/game/step \ + -H "Content-Type: application/json" \ + -d '{"step_id":"STEP_3"}' +``` + +### `GET /game/puzzle_state` + +État agrégé des énigmes résolues, pour le polling du dashboard GM. + +```json +{ "step_id": "STEP_3", "solved": [1, 3], "code": "125" } +``` + +- `step_id` : dernier step armé, ou `null` si rien n'a encore été armé ; +- `solved` : ids (1..8) des énigmes résolues ; +- `code` : concaténation des fragments des énigmes résolues. + +`503 {"error":"not_ready"}` tant que `puzzle_state` n'est pas câblé. + +```bash +curl http://zacus-master.local/game/puzzle_state +``` + +### `POST /game/file?path=apps//` + +Provisionne un fichier d'app du shell d'affichage (icône, `step.txt`…) +sans reflasher l'image LittleFS. Le chemin est passé en **query param**, +le corps est le **contenu brut** du fichier (**1..262144 octets**, soit +256 KiB). + +Contraintes de sécurité sur `path` : + +- doit commencer par `apps/` (whitelist) ; +- ne doit contenir aucun `..` (anti-traversal) ; +- ne doit pas finir par `/`. + +Toute violation → `403 {"error":"path must be under apps/"}`. Les +répertoires intermédiaires sont créés (`mkdir -p`). Le fichier final est +écrit sous `/littlefs/apps/…` ; un échec d'écriture supprime le fichier +partiel. + +```json +{ "status": "ok", "path": "apps/clock/icon.png", "bytes": 1873 } +``` + +| Statut | Corps | Sens | +|---|---|---| +| 200 | `{"status":"ok","path":"…","bytes":N}` | écrit | +| 400 | `{"error":"missing path param"}` | query `path` absent | +| 400 | `{"error":"size 1..262144 bytes"}` | corps hors bornes | +| 400 | `{"error":"recv failed"}` | erreur réception socket | +| 403 | `{"error":"path must be under apps/"}` | hors whitelist / traversal | +| 500 | `{"error":"open failed"}` / `"write failed"` | échec stockage | +| 503 | `{"error":"storage_unavailable"}` | mount LittleFS échoué | + +```bash +curl -X POST "http://zacus-master.local/game/file?path=apps/clock/icon.png" \ + --data-binary @icon.png +``` + +--- + +## Format IR : objets `puzzle` et `scene` + +Les deux objets sont **optionnels** par step de l'IR Runtime 3, lus à +`POST /game/step` (et `game_endpoint_apply_step`). Les contraintes +ci-dessous sont celles de `puzzle_binding.c` / `puzzle_binding.h`. + +### `puzzle` (validation **stricte**) + +Un objet `puzzle` invalide bloque le step (`422 invalid_puzzle`). +Absent ou `null` → `armed:"none"`, pas d'erreur. + +| Champ | Type | Contrainte | +|---|---|---| +| `id` | nombre | **1..8** (entier) | +| `type` | string | `"qr"` ou `"sound"` (rien d'autre) | +| `fragment` | tableau d'entiers | **1..4** valeurs, chacune **0..9** (chiffres) | +| `codes` | tableau de strings | *(qr)* **1..16** labels, chacun < 32 chars | +| `melody` | tableau d'entiers | *(sound)* **1..32** notes | +| `tolerance` | nombre | *(sound, optionnel)* entier **≥ 0**, défaut **1** | + +Énigme QR : + +```json +{ + "id": 1, + "type": "qr", + "codes": ["BADGE_ROUGE", "BADGE_BLEU"], + "fragment": [1, 2] +} +``` + +Énigme sonore (mélodie + tolérance) : + +```json +{ + "id": 4, + "type": "sound", + "melody": [262, 294, 330, 349], + "tolerance": 2, + "fragment": [5] +} +``` + +### `scene` (parse **lenient**) + +Décoration d'affichage : ne bloque **jamais** un changement de step. Les +strings trop longues sont **silencieusement tronquées**, un `effect` +inconnu retombe sur `pulse`. Seul un JSON globalement non parsable échoue. + +| Champ | Type | Contrainte | +|---|---|---| +| `title` | string | tronqué à 48 chars | +| `subtitle` | string | tronqué à 64 chars | +| `symbol` | string | tronqué à 16 chars | +| `effect` | string | `pulse` (défaut) \| `glitch` \| `gyro` \| `none` ; inconnu → `pulse` | + +```json +{ + "title": "Salle des machines", + "subtitle": "Trouvez la séquence", + "symbol": "⚙", + "effect": "glitch" +} +``` + +Un step complet combinant les deux : + +```json +{ + "id": "STEP_3", + "puzzle": { "id": 1, "type": "qr", "codes": ["BADGE_ROUGE"], "fragment": [1, 2] }, + "scene": { "title": "Le coffre", "effect": "pulse" } +} +``` + +--- + +## Modèle de concurrence + +La lecture de `GET /game/puzzle_state` (et les snapshots +`game_endpoint_get_puzzle_status` / `game_endpoint_get_scene`) est +**lock-free**, par choix assumé pour ce jeu : + +- l'armement d'un step (writer) se fait uniquement sur la tâche httpd ; + les tâches d'énigmes n'écrivent que via `puzzle_state_report` (flags + `solved[]` montones) ; +- le dashboard GM ne fait que poller ; +- un read concurrent peut donc voir un `solved[id]` à `true` alors que le + fragment correspondant est en cours d'écriture (lecture torn). De même + un torn read du texte de `scene` pendant la copie est cosmétique et + s'auto-corrige au poll suivant. + +**Note honnête** : c'est acceptable ici (un seul step armé à la fois, +affichage informatif, polling), mais ce n'est pas thread-safe au sens +strict — pas de mutex sur `puzzle_state` ni sur les statiques +`s_current_*`. À ne pas réutiliser tel quel dans un contexte +multi-writer. + +--- + +## Tests hôte du parser + +Le parser `puzzle_binding` a une suite de **10 tests `[pbind]`** qui +tourne sur l'hôte (pas de cible ESP requise), via Unity + cJSON : + +```bash +make -C idf_zacus/components/game_endpoint/test/host test +``` + +Le `Makefile` attend Unity et cJSON dans l'arbre ESP-IDF +(`UNITY_DIR` / `CJSON_DIR`, par défaut sous `~/esp/esp-idf/components/`, +surchargeables en variables d'environnement). + +--- + +## Dépendances du composant ```cmake REQUIRES - esp_http_server # the shared httpd_handle_t - json # cJSON for body parsing - nvs_flash # nvs_open / nvs_set_str / nvs_commit - hints_client # validation + push to in-RAM state - ota_server # supplies httpd_handle via ota_server_get_handle() + esp_http_server # le httpd_handle_t partagé + json # cJSON pour le parsing + nvs_flash # group_profile + registre des pairs + hints_client # validation + push du profil en RAM + ota_server # fournit le handle via ota_server_get_handle() + scenario_mesh # transport ESP-NOW (relay + provisioning) + local_puzzles # armement QR / sonore + esp_littlefs # stockage scénario + fichiers d'apps freertos log ``` -Init pattern in `main.c` (slice 12): +Câblage dans `main.c` : ```c esp_err_t ota_err = ota_server_init(); @@ -98,5 +447,6 @@ if (ota_err == ESP_OK) { httpd_handle_t httpd = ota_server_get_handle(); voice_hook_endpoint_init(httpd); game_endpoint_init(httpd); + game_endpoint_set_puzzle_state(&g_puzzle_state); // active /game/step + /game/puzzle_state } ``` diff --git a/idf_zacus/components/game_endpoint/game_endpoint.c b/idf_zacus/components/game_endpoint/game_endpoint.c index 9f55881..26b7d31 100644 --- a/idf_zacus/components/game_endpoint/game_endpoint.c +++ b/idf_zacus/components/game_endpoint/game_endpoint.c @@ -26,9 +26,21 @@ #include "hints_client.h" #include "scenario_mesh.h" +#include "puzzle_binding.h" +#include "local_puzzles.h" static const char *TAG = "game_endpoint"; +// ─── puzzle state + step tracking (Task C) ────────────────────────────────── +// Pointer supplied by game_endpoint_set_puzzle_state(); NULL until then. +static puzzle_state_t *s_pstate = NULL; +// Last step id armed via POST /game/step. Empty string = none. +static char s_current_step_id[64] = {0}; +// Last armed puzzle type: "qr" | "sound" | "none" | "" (empty = nothing armed yet). +static char s_current_armed[8] = {0}; +// Display scene metadata of the current step (lenient parse; defaults safe). +static scene_binding_t s_current_scene = {0}; + // Whitelist mirrored in the 4xx error message so the operator can // recover without grepping the source. Keep aligned with // hints_client_set_group_profile() validation. @@ -175,6 +187,13 @@ static bool s_storage_mounted = false; static esp_err_t mount_storage_lazy(void) { if (s_storage_mounted) return ESP_OK; + // Already mounted by main.c (or media_manager)? Checking first avoids + // esp_littlefs's own "Partition already used" ERROR logs — the register + // call below would still succeed-as-INVALID_STATE, but noisily. + if (esp_littlefs_mounted(GAME_ENDPOINT_STORAGE_LABEL)) { + s_storage_mounted = true; + return ESP_OK; + } esp_vfs_littlefs_conf_t conf = { .base_path = GAME_ENDPOINT_STORAGE_BASE, .partition_label = GAME_ENDPOINT_STORAGE_LABEL, @@ -309,6 +328,11 @@ static esp_err_t scenario_apply_buffer(const char *body, size_t len, ESP_LOGI(TAG, "scenario hot-load OK: %zu bytes, %d steps, entry=%s", len, steps_count, (entry_out && entry_cap) ? entry_out : ""); + // New scenario supersedes any live puzzle — disarm and clear remembered step. + local_puzzles_disarm(); + s_current_step_id[0] = '\0'; + memset(&s_current_scene, 0, sizeof(s_current_scene)); + // Hot-reload-via-reboot until scenario_engine_reload() lands (Phase 3). schedule_restart(); return ESP_OK; @@ -571,12 +595,13 @@ static esp_err_t handle_peers_post(httpd_req_t *req) { ESP_LOGI(TAG, "peers: \"%s\" -> %02X:%02X:%02X:%02X:%02X:%02X (%s)", alias, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], reg == ESP_OK ? "live" : "reboot needed"); - cJSON_Delete(root); - + // Build the response BEFORE deleting root: `alias` points into the cJSON + // tree (use-after-free observed on-device as a garbled alias echo). char resp[128]; snprintf(resp, sizeof(resp), "{\"ok\":true,\"alias\":\"%s\",\"live\":%s}", alias, reg == ESP_OK ? "true" : "false"); + cJSON_Delete(root); return send_json(req, "200 OK", resp); } @@ -620,6 +645,376 @@ static esp_err_t handle_peers_get(httpd_req_t *req) { return ret; } +// ─── POST /game/espnow/cmd — debug/manual CMD injection ───────────────────── +// +// Body: {"peer":"alias","command":"ping"} or {"broadcast":true,"command":"x"}. +// Sends one CMD text frame (spec 2026-06-11) and reports the radio status. +// The scenario-driven executor will share this exact send path. + +static const uint8_t kEspnowBroadcastMac[6] = + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + +// Receive side: log every CMD/EVT so two-board bring-up is observable on the +// serial console. The step executor will hook waits in here later. +static void master_text_cb(uint8_t kind, const uint8_t src[6], + const char *text) { + ESP_LOGI(TAG, "espnow %s \"%s\" from %02x:%02x:%02x:%02x:%02x:%02x", + kind == SCENARIO_MESH_TEXT_CMD ? "CMD" : "EVT", text, + src[0], src[1], src[2], src[3], src[4], src[5]); +} + +static esp_err_t handle_espnow_cmd_post(httpd_req_t *req) { + char buf[256]; + int total = req->content_len; + if (total <= 0 || total >= (int) sizeof(buf)) { + return send_error(req, "400 Bad Request", "bad body length"); + } + int got = httpd_req_recv(req, buf, total); + if (got <= 0) { + return send_error(req, "400 Bad Request", "body read failed"); + } + buf[got] = '\0'; + + cJSON *root = cJSON_Parse(buf); + if (!root) { + return send_error(req, "400 Bad Request", "invalid JSON"); + } + const cJSON *cmd_j = cJSON_GetObjectItemCaseSensitive(root, "command"); + const cJSON *peer_j = cJSON_GetObjectItemCaseSensitive(root, "peer"); + const cJSON *bc_j = cJSON_GetObjectItemCaseSensitive(root, "broadcast"); + if (!cJSON_IsString(cmd_j) || !cmd_j->valuestring[0]) { + cJSON_Delete(root); + return send_error(req, "400 Bad Request", "'command' required"); + } + + uint8_t mac[6]; + if (cJSON_IsTrue(bc_j)) { + memcpy(mac, kEspnowBroadcastMac, 6); + } else if (cJSON_IsString(peer_j) && peer_j->valuestring[0]) { + if (scenario_mesh_mac_for_alias(peer_j->valuestring, mac) != ESP_OK) { + cJSON_Delete(root); + return send_error(req, "404 Not Found", "unknown peer alias"); + } + } else { + cJSON_Delete(root); + return send_error(req, "400 Bad Request", + "'peer' or 'broadcast':true required"); + } + + esp_err_t serr = scenario_mesh_send_text(mac, SCENARIO_MESH_TEXT_CMD, + cmd_j->valuestring); + char resp[96]; + snprintf(resp, sizeof(resp), "{\"ok\":%s,\"status\":\"%s\"}", + serr == ESP_OK ? "true" : "false", esp_err_to_name(serr)); + cJSON_Delete(root); + return send_json(req, serr == ESP_OK ? "200 OK" : "502 Bad Gateway", resp); +} + +// ─── POST /game/step — arm a puzzle for the given step id ─────────────────── +// +// Body: {"step_id":"STEP_X"} +// Reads /littlefs/scenario.json, calls puzzle_binding_from_ir, then +// disarms whatever is running and arms the new puzzle (if any). +// Concurrency: httpd task vs. puzzle solved-callbacks writing puzzle_state — +// the arm/disarm calls here are the only writers to local_puzzles state; puzzle +// tasks only call puzzle_state_report. No mutex needed for this slice. + +// Core step-change logic, shared by the HTTP handler and internal callers +// (e.g. shell app launch on the local display). Error contract: +// ESP_OK armed (armed_out = "qr"|"sound"|"none") +// ESP_ERR_INVALID_STATE not ready (no puzzle_state wired) +// ESP_ERR_NOT_SUPPORTED no scenario stored / storage unavailable +// ESP_ERR_NOT_FOUND unknown step id +// ESP_ERR_INVALID_ARG invalid puzzle object in the IR +// ESP_ERR_TIMEOUT puzzle busy (QR teardown window, after one retry) +// others internal failure +esp_err_t game_endpoint_apply_step(const char *step_id, + char *armed_out, size_t armed_cap) { + if (armed_out && armed_cap) armed_out[0] = '\0'; + if (!step_id || !step_id[0]) return ESP_ERR_INVALID_ARG; + if (s_pstate == NULL) return ESP_ERR_INVALID_STATE; + + if (mount_storage_lazy() != ESP_OK) return ESP_ERR_NOT_SUPPORTED; + FILE *f = fopen(GAME_ENDPOINT_SCENARIO_PATH, "rb"); + if (!f) return ESP_ERR_NOT_SUPPORTED; + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + rewind(f); + if (fsize <= 0 || fsize > GAME_ENDPOINT_MAX_SCENARIO_BYTES) { + fclose(f); + return ESP_ERR_NOT_SUPPORTED; + } + char *ir_json = (char *) malloc((size_t) fsize + 1); + if (!ir_json) { fclose(f); return ESP_ERR_NO_MEM; } + size_t nread = fread(ir_json, 1, (size_t) fsize, f); + fclose(f); + ir_json[nread] = '\0'; + + // Disarm current puzzle before re-arming. + local_puzzles_disarm(); + + // Parse binding for this step (+ display scene metadata, lenient). + puzzle_binding_t binding; + esp_err_t berr = puzzle_binding_from_ir(ir_json, step_id, &binding); + if (berr == ESP_OK) { + (void) scene_binding_from_ir(ir_json, step_id, &s_current_scene); + } + free(ir_json); + if (berr != ESP_OK) return berr; // NOT_FOUND / INVALID_ARG / other + + strncpy(s_current_step_id, step_id, sizeof(s_current_step_id) - 1); + s_current_step_id[sizeof(s_current_step_id) - 1] = '\0'; + + const char *armed = "none"; + if (binding.type == PB_QR) { + const char *ptrs[PB_MAX_CODES]; + for (size_t i = 0; i < binding.code_count; i++) ptrs[i] = binding.codes[i]; + esp_err_t aerr = local_puzzles_arm_qr(binding.id, ptrs, binding.code_count, + binding.fragment, binding.fragment_len); + if (aerr == ESP_ERR_INVALID_STATE) { + vTaskDelay(pdMS_TO_TICKS(250)); // async QR teardown — retry once + aerr = local_puzzles_arm_qr(binding.id, ptrs, binding.code_count, + binding.fragment, binding.fragment_len); + } + if (aerr == ESP_ERR_INVALID_STATE) return ESP_ERR_TIMEOUT; + if (aerr != ESP_OK) return aerr; + armed = "qr"; + } else if (binding.type == PB_SOUND) { + esp_err_t aerr = local_puzzles_arm_sound(binding.id, binding.melody, + binding.note_count, binding.tolerance, + binding.fragment, binding.fragment_len); + if (aerr == ESP_ERR_INVALID_STATE) { + vTaskDelay(pdMS_TO_TICKS(250)); + aerr = local_puzzles_arm_sound(binding.id, binding.melody, + binding.note_count, binding.tolerance, + binding.fragment, binding.fragment_len); + } + if (aerr == ESP_ERR_INVALID_STATE) return ESP_ERR_TIMEOUT; + if (aerr != ESP_OK) return aerr; + armed = "sound"; + } + + strncpy(s_current_armed, armed, sizeof(s_current_armed) - 1); + s_current_armed[sizeof(s_current_armed) - 1] = '\0'; + if (armed_out && armed_cap) { + strncpy(armed_out, armed, armed_cap - 1); + armed_out[armed_cap - 1] = '\0'; + } + return ESP_OK; +} + +static esp_err_t handle_step_post(httpd_req_t *req) { + if (s_pstate == NULL) { + return send_error(req, "503 Service Unavailable", "not_ready"); + } + + // Read body (≤512 bytes is sufficient for {"step_id":"..."}). + char body[513] = {0}; + if (req->content_len <= 0 || req->content_len > 512) { + return send_error(req, "400 Bad Request", "body must be 1..512 bytes"); + } + int total = 0; + while (total < (int) req->content_len) { + int got = httpd_req_recv(req, body + total, req->content_len - total); + if (got <= 0) { + if (got == HTTPD_SOCK_ERR_TIMEOUT) continue; + return send_error(req, "400 Bad Request", "recv failed"); + } + total += got; + } + body[total] = '\0'; + + cJSON *root = cJSON_Parse(body); + if (!root) { + return send_error(req, "400 Bad Request", "malformed json"); + } + const cJSON *step_id_j = cJSON_GetObjectItemCaseSensitive(root, "step_id"); + if (!cJSON_IsString(step_id_j) || !step_id_j->valuestring || + step_id_j->valuestring[0] == '\0') { + cJSON_Delete(root); + return send_error(req, "400 Bad Request", "missing or empty step_id"); + } + char step_id[64]; + strncpy(step_id, step_id_j->valuestring, sizeof(step_id) - 1); + step_id[sizeof(step_id) - 1] = '\0'; + cJSON_Delete(root); + + // Delegate to the shared core; map its error contract onto the SAME + // HTTP statuses/messages as before (REST contract unchanged). + char armed[8]; + esp_err_t aerr = game_endpoint_apply_step(step_id, armed, sizeof(armed)); + switch (aerr) { + case ESP_OK: { + char buf[128]; + snprintf(buf, sizeof(buf), + "{\"step_id\":\"%s\",\"armed\":\"%s\"}", step_id, armed); + return send_json(req, "200 OK", buf); + } + case ESP_ERR_NOT_SUPPORTED: + return send_error(req, "409 Conflict", "no_scenario"); + case ESP_ERR_NOT_FOUND: + return send_error(req, "404 Not Found", "unknown_step"); + case ESP_ERR_INVALID_ARG: + return send_error(req, "422 Unprocessable Entity", "invalid_puzzle"); + case ESP_ERR_TIMEOUT: + return send_error(req, "503 Service Unavailable", "puzzle_busy"); + case ESP_ERR_INVALID_STATE: + return send_error(req, "503 Service Unavailable", "not_ready"); + default: + return send_error(req, "500 Internal Server Error", + esp_err_to_name(aerr)); + } +} + +// ─── GET /game/puzzle_state ────────────────────────────────────────────────── +// +// Returns {"step_id":"STEP_X"|null, "solved":[1,3], "code":"125"}. +// NOTE: reads puzzle_state fields without a mutex — acceptable for this game +// (GM dashboard polling). solved[] flags are written monotonically by puzzle +// tasks; a concurrent read may see solved=true with fragment mid-write. + +static esp_err_t handle_puzzle_state_get(httpd_req_t *req) { + if (s_pstate == NULL) { + return send_error(req, "503 Service Unavailable", "not_ready"); + } + + // Build "solved" array of ids where solved[id] == true (ids 1..PUZZLE_MAX_ID). + char solved_buf[64] = "["; + int solved_len = 1; + bool first = true; + for (int id = 1; id <= PUZZLE_MAX_ID; id++) { + if (s_pstate->solved[id]) { + int n = snprintf(solved_buf + solved_len, + sizeof(solved_buf) - (size_t) solved_len - 1, + "%s%d", first ? "" : ",", id); + if (n > 0) solved_len += n; + first = false; + } + } + if (solved_len < (int) sizeof(solved_buf) - 1) { + solved_buf[solved_len++] = ']'; + solved_buf[solved_len] = '\0'; + } + + char code[PUZZLE_MAX_ID * PUZZLE_MAX_FRAG + 1] = {0}; + puzzle_state_code(s_pstate, code, sizeof(code)); + + // Build response. step_id is null when no step has been armed yet. + char resp[256]; + if (s_current_step_id[0] != '\0') { + snprintf(resp, sizeof(resp), + "{\"step_id\":\"%s\",\"solved\":%s,\"code\":\"%s\"}", + s_current_step_id, solved_buf, code); + } else { + snprintf(resp, sizeof(resp), + "{\"step_id\":null,\"solved\":%s,\"code\":\"%s\"}", + solved_buf, code); + } + return send_json(req, "200 OK", resp); +} + +// ─── public setter (Task C) ────────────────────────────────────────────────── + +void game_endpoint_set_puzzle_state(puzzle_state_t *state) { + s_pstate = state; + ESP_LOGI(TAG, "puzzle_state registered (GET /game/puzzle_state + POST /game/step active)"); +} + +// ─── public getter for display_ui (Phase 1) ────────────────────────────────── +// +// Copies the statics written by the step handler. Reading without a mutex is +// acceptable here — the game master arms one step at a time and the display is +// informational; a torn read produces at worst a one-cycle stale value. + +void game_endpoint_get_puzzle_status(char *step_id, size_t step_cap, + char *armed, size_t armed_cap) { + if (step_id && step_cap > 0) { + strncpy(step_id, s_current_step_id, step_cap - 1); + step_id[step_cap - 1] = '\0'; + } + if (armed && armed_cap > 0) { + strncpy(armed, s_current_armed, armed_cap - 1); + armed[armed_cap - 1] = '\0'; + } +} + +// ─── POST /game/file?path=apps// — provision shell apps ──────────── +// Raw body → file under /littlefs/apps/ ONLY (whitelist + no traversal). +// Lets the GM push icon.png / step.txt for the display shell's dynamic tiles +// without reflashing a littlefs image. +#define GAME_ENDPOINT_MAX_FILE_BYTES (256 * 1024) + +static esp_err_t handle_file_post(httpd_req_t *req) { + char query[160], path_param[96]; + if (httpd_req_get_url_query_str(req, query, sizeof(query)) != ESP_OK || + httpd_query_key_value(query, "path", path_param, + sizeof(path_param)) != ESP_OK) { + return send_error(req, "400 Bad Request", "missing path param"); + } + if (strncmp(path_param, "apps/", 5) != 0 || strstr(path_param, "..") || + path_param[strlen(path_param) - 1] == '/') { + return send_error(req, "403 Forbidden", "path must be under apps/"); + } + if (req->content_len <= 0 || + req->content_len > GAME_ENDPOINT_MAX_FILE_BYTES) { + return send_error(req, "400 Bad Request", "size 1..262144 bytes"); + } + if (mount_storage_lazy() != ESP_OK) { + return send_error(req, "503 Service Unavailable", "storage_unavailable"); + } + + char full[160]; + snprintf(full, sizeof(full), "/littlefs/%s", path_param); + // mkdir -p for intermediate directories. + for (char *p = full + strlen("/littlefs/"); *p; p++) { + if (*p == '/') { + *p = '\0'; + mkdir(full, 0775); // EEXIST is fine + *p = '/'; + } + } + + FILE *f = fopen(full, "wb"); + if (!f) { + return send_error(req, "500 Internal Server Error", "open failed"); + } + char buf[1024]; + int remaining = (int) req->content_len; + while (remaining > 0) { + const int want = remaining < (int) sizeof(buf) ? remaining + : (int) sizeof(buf); + int got = httpd_req_recv(req, buf, want); + if (got <= 0) { + if (got == HTTPD_SOCK_ERR_TIMEOUT) continue; + fclose(f); + unlink(full); + return send_error(req, "400 Bad Request", "recv failed"); + } + if (fwrite(buf, 1, (size_t) got, f) != (size_t) got) { + fclose(f); + unlink(full); + return send_error(req, "500 Internal Server Error", "write failed"); + } + remaining -= got; + } + fclose(f); + + char resp[192]; + snprintf(resp, sizeof(resp), + "{\"status\":\"ok\",\"path\":\"%s\",\"bytes\":%d}", + path_param, (int) req->content_len); + ESP_LOGI(TAG, "file stored: %s (%d B)", full, (int) req->content_len); + return send_json(req, "200 OK", resp); +} + +void game_endpoint_get_scene(scene_binding_t *out) { + if (!out) return; + // Lock-free snapshot, same class as get_puzzle_status: written on the + // httpd task per step change, polled by main's status loop. A torn read + // of display text during the copy is cosmetic and self-heals next poll. + memcpy(out, &s_current_scene, sizeof(*out)); +} + // ─── public init ──────────────────────────────────────────────────────────── esp_err_t game_endpoint_init(httpd_handle_t server) { @@ -665,6 +1060,30 @@ esp_err_t game_endpoint_init(httpd_handle_t server) { .handler = handle_peers_post, .user_ctx = NULL, }; + static const httpd_uri_t uri_step_post = { + .uri = "/game/step", + .method = HTTP_POST, + .handler = handle_step_post, + .user_ctx = NULL, + }; + static const httpd_uri_t uri_puzzle_state_get = { + .uri = "/game/puzzle_state", + .method = HTTP_GET, + .handler = handle_puzzle_state_get, + .user_ctx = NULL, + }; + static const httpd_uri_t uri_file_post = { + .uri = "/game/file", + .method = HTTP_POST, + .handler = handle_file_post, + .user_ctx = NULL, + }; + static const httpd_uri_t uri_espnow_cmd_post = { + .uri = "/game/espnow/cmd", + .method = HTTP_POST, + .handler = handle_espnow_cmd_post, + .user_ctx = NULL, + }; // Bring up the ESP-NOW mesh transport. The master is primarily a sender // (the relay handler) but we also register the apply adapter so a peer @@ -702,6 +1121,17 @@ esp_err_t game_endpoint_init(httpd_handle_t server) { ESP_LOGW(TAG, "register POST /game/scenario/relay: %s", esp_err_to_name(err)); } + // CMD/EVT text channel: log inbound traffic + manual CMD injection. + esp_err_t terr = scenario_mesh_set_text_cb(master_text_cb); + if (terr != ESP_OK) { + ESP_LOGW(TAG, "scenario_mesh_set_text_cb: %s", + esp_err_to_name(terr)); + } + err = httpd_register_uri_handler(server, &uri_espnow_cmd_post); + if (err != ESP_OK) { + ESP_LOGW(TAG, "register POST /game/espnow/cmd: %s", + esp_err_to_name(err)); + } } // Peer registry management — non-fatal if registration fails. err = httpd_register_uri_handler(server, &uri_peers_get); @@ -713,9 +1143,23 @@ esp_err_t game_endpoint_init(httpd_handle_t server) { ESP_LOGW(TAG, "register POST /game/peers: %s", esp_err_to_name(err)); } + // Task C: puzzle arming + state readout — non-fatal. + err = httpd_register_uri_handler(server, &uri_step_post); + if (err != ESP_OK) { + ESP_LOGW(TAG, "register POST /game/step: %s", esp_err_to_name(err)); + } + err = httpd_register_uri_handler(server, &uri_file_post); + if (err != ESP_OK) { + ESP_LOGW(TAG, "register /game/file: %s", esp_err_to_name(err)); + } + err = httpd_register_uri_handler(server, &uri_puzzle_state_get); + if (err != ESP_OK) { + ESP_LOGW(TAG, "register GET /game/puzzle_state: %s", esp_err_to_name(err)); + } + ESP_LOGI(TAG, "game endpoint registered " "(GET+POST /game/group_profile, POST /game/scenario%s, " - "GET+POST /game/peers)", + "GET+POST /game/peers, POST /game/step, GET /game/puzzle_state)", mesh_err == ESP_OK ? ", POST /game/scenario/relay" : ""); return ESP_OK; } diff --git a/idf_zacus/components/game_endpoint/include/game_endpoint.h b/idf_zacus/components/game_endpoint/include/game_endpoint.h index 41f4db1..da2ccba 100644 --- a/idf_zacus/components/game_endpoint/include/game_endpoint.h +++ b/idf_zacus/components/game_endpoint/include/game_endpoint.h @@ -24,6 +24,8 @@ #include "esp_err.h" #include "esp_http_server.h" +#include "puzzle_state.h" +#include "puzzle_binding.h" // scene_binding_t (display scene metadata) #ifdef __cplusplus extern "C" { @@ -57,6 +59,8 @@ extern "C" { * Registers: * - GET/POST /game/group_profile (slice 12, runtime hints profile) * - POST /game/scenario (slice 13, Runtime 3 IR hot-load) + * - POST /game/step (Task C, REST-driven puzzle arming) + * - GET /game/puzzle_state (Task C, puzzle state readout) * * Pass the handle returned by `ota_server_get_handle()`. Returns * ESP_ERR_INVALID_ARG if `server` is NULL, or any error propagated @@ -64,6 +68,54 @@ extern "C" { */ esp_err_t game_endpoint_init(httpd_handle_t server); +/** + * @brief Give the endpoint read access to the master's puzzle aggregation + * state and enable GET /game/puzzle_state + POST /game/step. + * Call once at boot, right after game_endpoint_init succeeds. + */ +void game_endpoint_set_puzzle_state(puzzle_state_t *state); + +/** + * @brief Thread-safe snapshot of the current step id and armed puzzle type. + * + * Copies the last step armed via POST /game/step into caller-supplied buffers. + * Both buffers are NUL-terminated on return. Either pointer may be NULL if + * the caller does not need that field. + * + * Returns "" / "" when no step has been armed yet. + * + * @param step_id Output buffer for the current step id. + * @param step_cap Capacity of step_id buffer (recommend 64). + * @param armed Output buffer: "qr" | "sound" | "none" (or "" = nothing yet). + * @param armed_cap Capacity of armed buffer (recommend 8). + */ +void game_endpoint_get_puzzle_status(char *step_id, size_t step_cap, + char *armed, size_t armed_cap); + +/** + * @brief Snapshot the display scene metadata of the current step. + * + * Filled from the step's optional `scene` IR object at POST /game/step time + * (lenient parse — see puzzle_binding.h). Zeroed when no step is armed or a + * new scenario is pushed. Lock-free copy; cosmetic torn reads possible. + */ +void game_endpoint_get_scene(scene_binding_t *out); + +/** + * @brief Apply a scenario step internally (same logic as POST /game/step). + * + * Used by local triggers (e.g. shell app launch on the device display). + * May block up to ~250 ms (QR re-arm retry). Error contract documented at + * the definition (NOT_SUPPORTED=no scenario, NOT_FOUND=unknown step, + * TIMEOUT=puzzle busy, INVALID_STATE=not ready, INVALID_ARG=bad IR puzzle). + * + * @param step_id Step to arm (non-empty). + * @param armed_out Optional out: "qr"|"sound"|"none". + * @param armed_cap Capacity of armed_out. + */ +esp_err_t game_endpoint_apply_step(const char *step_id, + char *armed_out, size_t armed_cap); + #ifdef __cplusplus } #endif diff --git a/idf_zacus/components/game_endpoint/include/puzzle_binding.h b/idf_zacus/components/game_endpoint/include/puzzle_binding.h new file mode 100644 index 0000000..2b16c88 --- /dev/null +++ b/idf_zacus/components/game_endpoint/include/puzzle_binding.h @@ -0,0 +1,73 @@ +// puzzle_binding.h — extract a step's optional puzzle binding from Runtime 3 IR. +#pragma once +#include +#include +#include +#include "esp_err.h" + +#define PB_MAX_CODES 16 // = SEQ_MAX_STEPS +#define PB_MAX_LABEL 32 // = SEQ_MAX_LABEL +#define PB_MAX_NOTES 32 // = MELODY_MAX_NOTES +#define PB_MAX_FRAG 4 // = PUZZLE_MAX_FRAG +#define PB_MAX_PUZZLE_ID 8 // = PUZZLE_MAX_ID + +typedef enum { PB_NONE = 0, PB_QR, PB_SOUND } puzzle_binding_type_t; + +typedef struct { + puzzle_binding_type_t type; // PB_NONE if the step has no puzzle + uint8_t id; // 1..8 + // qr + char codes[PB_MAX_CODES][PB_MAX_LABEL]; + size_t code_count; + // sound + int melody[PB_MAX_NOTES]; + size_t note_count; + int tolerance; // default 1 + // common + uint8_t fragment[PB_MAX_FRAG]; + uint8_t fragment_len; +} puzzle_binding_t; + +// Parse `ir_json` (full Runtime 3 IR), locate step `step_id`, fill `out`. +// ESP_OK: step found ('out->type' PB_NONE if it has no/empty puzzle object). +// ESP_ERR_NOT_FOUND: no step with that id (or no steps array). +// ESP_ERR_INVALID_ARG: malformed JSON, or puzzle object violating constraints +// (bad type string, id out of range, too many/zero codes|notes, label too +// long, fragment missing/empty/too long/non-digit values, negative tolerance). +// NOTE: Parsing a full 64KB IR allocates transient heap via cJSON — call +// sparingly (e.g. per step change, not per frame). +// Tolerance defaults to 1 when absent (sound only). +esp_err_t puzzle_binding_from_ir(const char *ir_json, const char *step_id, + puzzle_binding_t *out); + +// ─── Scene metadata (display) ──────────────────────────────────────────────── +// Optional per-step `scene` object mirroring the original ui_manager scene +// frame: {"title": "...", "subtitle": "...", "symbol": "...", +// "effect": "pulse"|"glitch"|"gyro"|"none"}. +// LENIENT parsing (unlike the strict puzzle constraints): scene fields are +// display decoration and must never block a step change — over-long strings +// are silently TRUNCATED, an unknown effect falls back to pulse. + +#define SB_MAX_TITLE 48 +#define SB_MAX_SUBTITLE 64 +#define SB_MAX_SYMBOL 16 + +typedef enum { + SB_FX_PULSE = 0, // default (original SceneEffect::kPulse) + SB_FX_GLITCH, + SB_FX_GYRO, + SB_FX_NONE, +} scene_effect_t; + +typedef struct { + bool present; // step has a scene object + char title[SB_MAX_TITLE]; + char subtitle[SB_MAX_SUBTITLE]; + char symbol[SB_MAX_SYMBOL]; + scene_effect_t effect; +} scene_binding_t; + +// Same return contract as puzzle_binding_from_ir for ESP_OK/NOT_FOUND/ +// INVALID_ARG(malformed JSON only — scene content itself never fails). +esp_err_t scene_binding_from_ir(const char *ir_json, const char *step_id, + scene_binding_t *out); diff --git a/idf_zacus/components/game_endpoint/puzzle_binding.c b/idf_zacus/components/game_endpoint/puzzle_binding.c new file mode 100644 index 0000000..3a56079 --- /dev/null +++ b/idf_zacus/components/game_endpoint/puzzle_binding.c @@ -0,0 +1,232 @@ +// puzzle_binding.c — parse a step's optional puzzle binding from Runtime 3 IR. +#include "puzzle_binding.h" +#include "cJSON.h" +#include + +esp_err_t puzzle_binding_from_ir(const char *ir_json, const char *step_id, + puzzle_binding_t *out) +{ + if (!ir_json || !step_id || !out) return ESP_ERR_INVALID_ARG; + + memset(out, 0, sizeof(*out)); + + cJSON *root = cJSON_Parse(ir_json); + if (!root) return ESP_ERR_INVALID_ARG; + if (!cJSON_IsObject(root)) { cJSON_Delete(root); return ESP_ERR_INVALID_ARG; } + + const cJSON *steps = cJSON_GetObjectItemCaseSensitive(root, "steps"); + if (!cJSON_IsArray(steps) || cJSON_GetArraySize(steps) == 0) { + cJSON_Delete(root); + return ESP_ERR_NOT_FOUND; + } + + // Find the step with matching id. + const cJSON *step = NULL; + const cJSON *item = NULL; + cJSON_ArrayForEach(item, steps) { + const cJSON *sid = cJSON_GetObjectItemCaseSensitive(item, "id"); + if (cJSON_IsString(sid) && strcmp(sid->valuestring, step_id) == 0) { + step = item; + break; + } + } + if (!step) { cJSON_Delete(root); return ESP_ERR_NOT_FOUND; } + + // Step found. Check for puzzle member. + const cJSON *puzzle = cJSON_GetObjectItemCaseSensitive(step, "puzzle"); + if (!puzzle || cJSON_IsNull(puzzle)) { + // No puzzle — out->type stays PB_NONE, ESP_OK. + cJSON_Delete(root); + return ESP_OK; + } + + // --- puzzle.id --- + const cJSON *jid = cJSON_GetObjectItemCaseSensitive(puzzle, "id"); + if (!cJSON_IsNumber(jid) || jid->valueint < 1 || jid->valueint > PB_MAX_PUZZLE_ID) { + cJSON_Delete(root); + memset(out, 0, sizeof(*out)); + return ESP_ERR_INVALID_ARG; + } + + // --- puzzle.type --- + const cJSON *jtype = cJSON_GetObjectItemCaseSensitive(puzzle, "type"); + if (!cJSON_IsString(jtype)) { + cJSON_Delete(root); + memset(out, 0, sizeof(*out)); + return ESP_ERR_INVALID_ARG; + } + puzzle_binding_type_t ptype; + if (strcmp(jtype->valuestring, "qr") == 0) { + ptype = PB_QR; + } else if (strcmp(jtype->valuestring, "sound") == 0) { + ptype = PB_SOUND; + } else { + cJSON_Delete(root); + memset(out, 0, sizeof(*out)); + return ESP_ERR_INVALID_ARG; + } + + // --- fragment (required) --- + const cJSON *jfrag = cJSON_GetObjectItemCaseSensitive(puzzle, "fragment"); + if (!cJSON_IsArray(jfrag)) { + cJSON_Delete(root); + memset(out, 0, sizeof(*out)); + return ESP_ERR_INVALID_ARG; + } + int frag_len = cJSON_GetArraySize(jfrag); + if (frag_len < 1 || frag_len > PB_MAX_FRAG) { + cJSON_Delete(root); + memset(out, 0, sizeof(*out)); + return ESP_ERR_INVALID_ARG; + } + for (int i = 0; i < frag_len; i++) { + const cJSON *fv = cJSON_GetArrayItem(jfrag, i); + if (!cJSON_IsNumber(fv) || fv->valueint < 0 || fv->valueint > 9) { + cJSON_Delete(root); + memset(out, 0, sizeof(*out)); + return ESP_ERR_INVALID_ARG; + } + out->fragment[i] = (uint8_t)fv->valueint; + } + out->fragment_len = (uint8_t)frag_len; + + // --- type-specific fields --- + if (ptype == PB_QR) { + const cJSON *jcodes = cJSON_GetObjectItemCaseSensitive(puzzle, "codes"); + if (!cJSON_IsArray(jcodes)) { + cJSON_Delete(root); + memset(out, 0, sizeof(*out)); + return ESP_ERR_INVALID_ARG; + } + int n = cJSON_GetArraySize(jcodes); + if (n < 1 || n > PB_MAX_CODES) { + cJSON_Delete(root); + memset(out, 0, sizeof(*out)); + return ESP_ERR_INVALID_ARG; + } + for (int i = 0; i < n; i++) { + const cJSON *cv = cJSON_GetArrayItem(jcodes, i); + if (!cJSON_IsString(cv)) { + cJSON_Delete(root); + memset(out, 0, sizeof(*out)); + return ESP_ERR_INVALID_ARG; + } + if (strlen(cv->valuestring) >= PB_MAX_LABEL) { + cJSON_Delete(root); + memset(out, 0, sizeof(*out)); + return ESP_ERR_INVALID_ARG; + } + strncpy(out->codes[i], cv->valuestring, PB_MAX_LABEL - 1); + out->codes[i][PB_MAX_LABEL - 1] = '\0'; + } + out->code_count = (size_t)n; + } else { // PB_SOUND + const cJSON *jmelody = cJSON_GetObjectItemCaseSensitive(puzzle, "melody"); + if (!cJSON_IsArray(jmelody)) { + cJSON_Delete(root); + memset(out, 0, sizeof(*out)); + return ESP_ERR_INVALID_ARG; + } + int n = cJSON_GetArraySize(jmelody); + if (n < 1 || n > PB_MAX_NOTES) { + cJSON_Delete(root); + memset(out, 0, sizeof(*out)); + return ESP_ERR_INVALID_ARG; + } + for (int i = 0; i < n; i++) { + const cJSON *nv = cJSON_GetArrayItem(jmelody, i); + if (!cJSON_IsNumber(nv)) { + cJSON_Delete(root); + memset(out, 0, sizeof(*out)); + return ESP_ERR_INVALID_ARG; + } + out->melody[i] = nv->valueint; + } + out->note_count = (size_t)n; + + // tolerance: optional, default 1, must be >= 0 + const cJSON *jtol = cJSON_GetObjectItemCaseSensitive(puzzle, "tolerance"); + if (jtol) { + if (!cJSON_IsNumber(jtol) || jtol->valueint < 0) { + cJSON_Delete(root); + memset(out, 0, sizeof(*out)); + return ESP_ERR_INVALID_ARG; + } + out->tolerance = jtol->valueint; + } else { + out->tolerance = 1; + } + } + + // id/type written last — all error exits above leave *out zeroed + out->id = (uint8_t)jid->valueint; + out->type = ptype; + + cJSON_Delete(root); + return ESP_OK; +} + +// ─── Scene metadata (lenient — see header) ─────────────────────────────────── + +static void copy_scene_str(char *dst, size_t cap, const cJSON *node) { + if (cJSON_IsString(node) && node->valuestring) { + strncpy(dst, node->valuestring, cap - 1); // silent truncation OK + dst[cap - 1] = '\0'; + } +} + +esp_err_t scene_binding_from_ir(const char *ir_json, const char *step_id, + scene_binding_t *out) +{ + if (!ir_json || !step_id || !out) return ESP_ERR_INVALID_ARG; + + memset(out, 0, sizeof(*out)); + out->effect = SB_FX_PULSE; + + cJSON *root = cJSON_Parse(ir_json); + if (!root) return ESP_ERR_INVALID_ARG; + if (!cJSON_IsObject(root)) { cJSON_Delete(root); return ESP_ERR_INVALID_ARG; } + + const cJSON *steps = cJSON_GetObjectItemCaseSensitive(root, "steps"); + if (!cJSON_IsArray(steps) || cJSON_GetArraySize(steps) == 0) { + cJSON_Delete(root); + return ESP_ERR_NOT_FOUND; + } + + const cJSON *step = NULL; + const cJSON *item = NULL; + cJSON_ArrayForEach(item, steps) { + const cJSON *sid = cJSON_GetObjectItemCaseSensitive(item, "id"); + if (cJSON_IsString(sid) && strcmp(sid->valuestring, step_id) == 0) { + step = item; + break; + } + } + if (!step) { cJSON_Delete(root); return ESP_ERR_NOT_FOUND; } + + const cJSON *scene = cJSON_GetObjectItemCaseSensitive(step, "scene"); + if (!scene || !cJSON_IsObject(scene)) { + // No scene — present stays false, ESP_OK. + cJSON_Delete(root); + return ESP_OK; + } + + out->present = true; + copy_scene_str(out->title, sizeof(out->title), + cJSON_GetObjectItemCaseSensitive(scene, "title")); + copy_scene_str(out->subtitle, sizeof(out->subtitle), + cJSON_GetObjectItemCaseSensitive(scene, "subtitle")); + copy_scene_str(out->symbol, sizeof(out->symbol), + cJSON_GetObjectItemCaseSensitive(scene, "symbol")); + + const cJSON *fx = cJSON_GetObjectItemCaseSensitive(scene, "effect"); + if (cJSON_IsString(fx) && fx->valuestring) { + if (strcmp(fx->valuestring, "glitch") == 0) out->effect = SB_FX_GLITCH; + else if (strcmp(fx->valuestring, "gyro") == 0) out->effect = SB_FX_GYRO; + else if (strcmp(fx->valuestring, "none") == 0) out->effect = SB_FX_NONE; + else out->effect = SB_FX_PULSE; // unknown → default, never an error + } + + cJSON_Delete(root); + return ESP_OK; +} diff --git a/idf_zacus/components/game_endpoint/test/host/.gitignore b/idf_zacus/components/game_endpoint/test/host/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/idf_zacus/components/game_endpoint/test/host/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/idf_zacus/components/game_endpoint/test/host/Makefile b/idf_zacus/components/game_endpoint/test/host/Makefile new file mode 100644 index 0000000..5e216e4 --- /dev/null +++ b/idf_zacus/components/game_endpoint/test/host/Makefile @@ -0,0 +1,57 @@ +# Host test harness Makefile — puzzle_binding parser +UNITY_DIR ?= $(HOME)/esp/esp-idf/components/unity/unity/src +CJSON_DIR ?= $(HOME)/esp/esp-idf/components/json/cJSON +CC ?= cc + +COMPONENT_DIR := ../.. +TEST_DIR := .. +BUILD_DIR := build + +TEST_BIN := $(BUILD_DIR)/test_runner + +SRCS := \ + $(COMPONENT_DIR)/puzzle_binding.c \ + $(CJSON_DIR)/cJSON.c \ + $(UNITY_DIR)/unity.c \ + ../../../local_puzzles/test/host/host_runner.c + +TEST_SRCS := \ + $(TEST_DIR)/test_puzzle_binding.c + +ALL_SRCS := $(SRCS) $(TEST_SRCS) + +INCS := \ + -I$(COMPONENT_DIR)/include \ + -I$(COMPONENT_DIR) \ + -Iesp_err_shim \ + -I$(TEST_DIR)/host \ + -I../../../local_puzzles/test/host \ + -I$(CJSON_DIR) \ + -I$(UNITY_DIR) + +CFLAGS := -std=c11 -Wall -Wextra -Werror $(INCS) -DUNITY_INCLUDE_CONFIG_H + +TEST_CFLAGS := $(CFLAGS) -include ../../../local_puzzles/test/host/unity_test_case.h + +.PHONY: all test clean + +all: $(TEST_BIN) + +$(BUILD_DIR): + mkdir -p $(BUILD_DIR) + $(if $(wildcard $(UNITY_DIR)/unity.c),,$(error UNITY_DIR not found: $(UNITY_DIR) — set UNITY_DIR=...)) + $(if $(wildcard $(CJSON_DIR)/cJSON.c),,$(error CJSON_DIR not found: $(CJSON_DIR) — set CJSON_DIR=...)) + +$(TEST_BIN): $(ALL_SRCS) | $(BUILD_DIR) + $(if $(wildcard $(UNITY_DIR)/unity.c),,$(error UNITY_DIR not found: $(UNITY_DIR) — set UNITY_DIR=...)) + $(if $(wildcard $(CJSON_DIR)/cJSON.c),,$(error CJSON_DIR not found: $(CJSON_DIR) — set CJSON_DIR=...)) + $(CC) $(TEST_CFLAGS) -c -o $(BUILD_DIR)/test_puzzle_binding.o $(TEST_DIR)/test_puzzle_binding.c + $(CC) $(CFLAGS) -o $@ $(SRCS) $(BUILD_DIR)/test_puzzle_binding.o + +test: $(TEST_BIN) + ./$(TEST_BIN) + +clean: + rm -rf $(BUILD_DIR) + +.PRECIOUS: $(TEST_BIN) diff --git a/idf_zacus/components/game_endpoint/test/host/esp_err_shim/esp_err.h b/idf_zacus/components/game_endpoint/test/host/esp_err_shim/esp_err.h new file mode 100644 index 0000000..376bb97 --- /dev/null +++ b/idf_zacus/components/game_endpoint/test/host/esp_err_shim/esp_err.h @@ -0,0 +1,9 @@ +// esp_err.h — HOST-ONLY shim. Mirrors IDF values for unit tests outside ESP-IDF. +// Do NOT ship this to firmware; the real esp_err.h is used there. +#pragma once +typedef int esp_err_t; +#define ESP_OK 0 +#define ESP_FAIL (-1) +#define ESP_ERR_INVALID_ARG 0x102 +#define ESP_ERR_INVALID_STATE 0x103 +#define ESP_ERR_NOT_FOUND 0x105 diff --git a/idf_zacus/components/game_endpoint/test/test_puzzle_binding.c b/idf_zacus/components/game_endpoint/test/test_puzzle_binding.c new file mode 100644 index 0000000..fececd6 --- /dev/null +++ b/idf_zacus/components/game_endpoint/test/test_puzzle_binding.c @@ -0,0 +1,154 @@ +// test_puzzle_binding.c — IDF-style host tests for puzzle_binding_from_ir. +#include "unity.h" +#include "puzzle_binding.h" +#include + +// ── 1. QR step parses fully ────────────────────────────────────────────────── +TEST_CASE("pbind: qr step parses fully", "[pbind]") +{ + const char *ir = + "{\"steps\":[" + "{\"id\":\"STEP_QR_DETECTOR\"," + "\"puzzle\":{\"id\":3,\"type\":\"qr\"," + "\"codes\":[\"zacus-qr-1\",\"zacus-qr-2\",\"zacus-qr-3\"]," + "\"fragment\":[5]}}" + "]}"; + puzzle_binding_t b; + TEST_ASSERT_EQUAL(ESP_OK, puzzle_binding_from_ir(ir, "STEP_QR_DETECTOR", &b)); + TEST_ASSERT_EQUAL(PB_QR, b.type); + TEST_ASSERT_EQUAL(3, b.id); + TEST_ASSERT_EQUAL(3, b.code_count); + TEST_ASSERT_EQUAL_STRING("zacus-qr-1", b.codes[0]); + TEST_ASSERT_EQUAL_STRING("zacus-qr-2", b.codes[1]); + TEST_ASSERT_EQUAL_STRING("zacus-qr-3", b.codes[2]); + TEST_ASSERT_EQUAL(1, b.fragment_len); + TEST_ASSERT_EQUAL(5, b.fragment[0]); +} + +// ── 2. Sound step parses; tolerance defaults to 1 when absent ──────────────── +TEST_CASE("pbind: sound step parses with default tolerance", "[pbind]") +{ + const char *ir = + "{\"steps\":[" + "{\"id\":\"STEP_LA_DETECTOR\"," + "\"puzzle\":{\"id\":1,\"type\":\"sound\"," + "\"melody\":[60,62,64,65]," + "\"fragment\":[1,2]}}" + "]}"; + puzzle_binding_t b; + TEST_ASSERT_EQUAL(ESP_OK, puzzle_binding_from_ir(ir, "STEP_LA_DETECTOR", &b)); + TEST_ASSERT_EQUAL(PB_SOUND, b.type); + TEST_ASSERT_EQUAL(1, b.id); + TEST_ASSERT_EQUAL(4, b.note_count); + TEST_ASSERT_EQUAL(60, b.melody[0]); + TEST_ASSERT_EQUAL(65, b.melody[3]); + TEST_ASSERT_EQUAL(1, b.tolerance); // default + TEST_ASSERT_EQUAL(2, b.fragment_len); + TEST_ASSERT_EQUAL(1, b.fragment[0]); + TEST_ASSERT_EQUAL(2, b.fragment[1]); +} + +// ── 3. Step without puzzle → ESP_OK + PB_NONE ──────────────────────────────── +TEST_CASE("pbind: step without puzzle yields PB_NONE", "[pbind]") +{ + const char *ir = + "{\"steps\":[" + "{\"id\":\"STEP_PLAIN\"}" + "]}"; + puzzle_binding_t b; + TEST_ASSERT_EQUAL(ESP_OK, puzzle_binding_from_ir(ir, "STEP_PLAIN", &b)); + TEST_ASSERT_EQUAL(PB_NONE, b.type); +} + +// ── 4. Unknown step id → ESP_ERR_NOT_FOUND ─────────────────────────────────── +TEST_CASE("pbind: unknown step id yields NOT_FOUND", "[pbind]") +{ + const char *ir = + "{\"steps\":[" + "{\"id\":\"STEP_A\"}" + "]}"; + puzzle_binding_t b; + TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, + puzzle_binding_from_ir(ir, "STEP_MISSING", &b)); + TEST_ASSERT_EQUAL(PB_NONE, b.type); +} + +// ── 5. Malformed JSON → ESP_ERR_INVALID_ARG ────────────────────────────────── +TEST_CASE("pbind: malformed JSON yields INVALID_ARG", "[pbind]") +{ + puzzle_binding_t b; + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, + puzzle_binding_from_ir("{not valid json", "STEP_A", &b)); + TEST_ASSERT_EQUAL(PB_NONE, b.type); +} + +// ── 6a. Puzzle id out of range (0) → ESP_ERR_INVALID_ARG ───────────────────── +TEST_CASE("pbind: puzzle id 0 yields INVALID_ARG", "[pbind]") +{ + const char *ir = + "{\"steps\":[" + "{\"id\":\"STEP_X\"," + "\"puzzle\":{\"id\":0,\"type\":\"qr\"," + "\"codes\":[\"c1\"],\"fragment\":[1]}}" + "]}"; + puzzle_binding_t b; + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, + puzzle_binding_from_ir(ir, "STEP_X", &b)); + TEST_ASSERT_EQUAL(PB_NONE, b.type); +} + +// ── 6b. Fragment value 12 (non-single-digit) → ESP_ERR_INVALID_ARG ─────────── +TEST_CASE("pbind: fragment value 12 yields INVALID_ARG", "[pbind]") +{ + const char *ir = + "{\"steps\":[" + "{\"id\":\"STEP_X\"," + "\"puzzle\":{\"id\":2,\"type\":\"qr\"," + "\"codes\":[\"c1\"],\"fragment\":[12]}}" + "]}"; + puzzle_binding_t b; + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, + puzzle_binding_from_ir(ir, "STEP_X", &b)); + TEST_ASSERT_EQUAL(PB_NONE, b.type); +} + +// ── 8-10. Scene metadata (lenient parser) ──────────────────────────────────── +TEST_CASE("scene parses title/subtitle/symbol/effect", "[pbind]") +{ + const char *ir = + "{\"steps\":[" + "{\"id\":\"STEP_X\",\"scene\":{\"title\":\"MISSION\"," + "\"subtitle\":\"trouvez les QR\",\"symbol\":\"RUN\"," + "\"effect\":\"gyro\"}}" + "]}"; + scene_binding_t s; + TEST_ASSERT_EQUAL(ESP_OK, scene_binding_from_ir(ir, "STEP_X", &s)); + TEST_ASSERT_TRUE(s.present); + TEST_ASSERT_EQUAL_STRING("MISSION", s.title); + TEST_ASSERT_EQUAL_STRING("trouvez les QR", s.subtitle); + TEST_ASSERT_EQUAL_STRING("RUN", s.symbol); + TEST_ASSERT_EQUAL(SB_FX_GYRO, s.effect); +} + +TEST_CASE("scene absent leaves present=false, defaults intact", "[pbind]") +{ + const char *ir = "{\"steps\":[{\"id\":\"STEP_X\"}]}"; + scene_binding_t s; + TEST_ASSERT_EQUAL(ESP_OK, scene_binding_from_ir(ir, "STEP_X", &s)); + TEST_ASSERT_FALSE(s.present); + TEST_ASSERT_EQUAL(SB_FX_PULSE, s.effect); +} + +TEST_CASE("scene unknown effect -> pulse; long title truncated", "[pbind]") +{ + const char *ir = + "{\"steps\":[" + "{\"id\":\"STEP_X\",\"scene\":{\"effect\":\"discoball\"," + "\"title\":\"0123456789012345678901234567890123456789012345678901234\"}}" + "]}"; + scene_binding_t s; + TEST_ASSERT_EQUAL(ESP_OK, scene_binding_from_ir(ir, "STEP_X", &s)); + TEST_ASSERT_TRUE(s.present); + TEST_ASSERT_EQUAL(SB_FX_PULSE, s.effect); + TEST_ASSERT_EQUAL(SB_MAX_TITLE - 1, (int) strlen(s.title)); +} diff --git a/idf_zacus/components/local_puzzles/CMakeLists.txt b/idf_zacus/components/local_puzzles/CMakeLists.txt new file mode 100644 index 0000000..49acf0b --- /dev/null +++ b/idf_zacus/components/local_puzzles/CMakeLists.txt @@ -0,0 +1,7 @@ +idf_component_register( + SRCS "mic_broker.c" "seq_validator.c" "melody_validator.c" "qr_puzzle.c" "sound_puzzle.c" + "local_puzzles.c" + INCLUDE_DIRS "include" "." + REQUIRES puzzle_state + PRIV_REQUIRES driver +) diff --git a/idf_zacus/components/local_puzzles/README.md b/idf_zacus/components/local_puzzles/README.md new file mode 100644 index 0000000..8fd022b --- /dev/null +++ b/idf_zacus/components/local_puzzles/README.md @@ -0,0 +1,55 @@ +# local_puzzles + +Énigmes locales du master Freenove (FNK0102H Media Kit) : la séquence QR (P3) +et la mélodie au son (P1). Le composant câble la capture matérielle (caméra, +micro) à une logique de validation pure, et reporte les fragments de code +résolus dans `puzzle_state`. + +## Sous-modules + +- **`board_pins_mediakit.h`** — pin-map de la carte Freenove FNK0102H (gelée le + 2026-06-10, source de vérité). Caméra, micro I2S IN, haut-parleur I2S OUT et + écran TFT ST7796, sans collision GPIO. À noter : les défauts de + `voice_pipeline` ciblent un autre câblage et **entrent en collision** avec les + pins caméra — ils doivent être surchargés par les valeurs FNK0102H (micro + 3/14/46, HP 42/41/1) à l'init de l'application. +- **`seq_validator`** / **`melody_validator`** — logique pure, sans I/O, testée + sur hôte. `seq_validator` valide une séquence de codes QR dans l'ordre (labels + distincts ; un mauvais scan réinitialise, un doublon du dernier code correct + est ignoré). `melody_validator` valide une séquence de notes MIDI avec + tolérance en demi-tons (notes répétées permises). +- **`mic_broker`** — propriétaire unique du micro I2S RX, partagé avec + `voice_pipeline`. Route les trames PCM16 mono vers **un seul** consommateur + actif selon le mode : `MIC_IDLE` (aucune trame), `MIC_NPC_LISTEN` (dialogue + PNJ), `MIC_P1_SOUND` (énigme son). Init unique ; un second `mic_broker_init()` + renvoie `ESP_ERR_INVALID_STATE`. +- **`qr_puzzle`** — caméra OV3660 en QVGA 320×240 grayscale (esp32-camera) + + décodage **quirc**. La tâche de scan ne passe la trame à quirc que si sa + géométrie correspond à la taille configurée (garde adaptatif à la géométrie du + capteur) ; hook viewfinder optionnel avant décodage. Teardown caméra + **asynchrone** après `stop` — `start` renvoie `ESP_ERR_INVALID_STATE` jusqu'à + la fin du teardown (réessayer après ~100 ms). +- **`sound_puzzle`** — consomme les trames `MIC_P1_SOUND` et détecte les notes + par taux de **passages par zéro** (zero-crossing → fréquence fondamentale → + note MIDI), avant validation par `melody_validator`. +- **`local_puzzles`** — couche de câblage : `local_puzzles_init` (cible + d'agrégation), `arm_qr` / `arm_sound` (armer une énigme avec sa séquence + attendue et le fragment à reporter), `disarm` (arrêter les deux). Les callbacks + de résolution ne doivent pas ré-armer ni désarmer directement — différer à une + autre tâche. + +## Tests hôte + +```bash +make -C idf_zacus/components/local_puzzles/test/host test +``` + +6 tests (3 `seq_validator`, 3 `melody_validator`) compilés et exécutés sur hôte, +sans matériel. Nécessite Unity (`UNITY_DIR`, défaut `~/esp/esp-idf/...`). + +## Notes / incohérences + +- **Capteur** : le matériel est un **OV3660**. Le code et certains commentaires + (ex. en-tête `qr_puzzle.h`) disent parfois OV2640 à tort. +- **QR sur LCD** : le décodage par la caméra d'un QR affiché sur un écran LCD + (émissif) n'est pas fiable — préférer un QR imprimé pour P3. diff --git a/idf_zacus/components/local_puzzles/idf_component.yml b/idf_zacus/components/local_puzzles/idf_component.yml new file mode 100644 index 0000000..c6a949a --- /dev/null +++ b/idf_zacus/components/local_puzzles/idf_component.yml @@ -0,0 +1,7 @@ +## Managed dependencies for local_puzzles component. +## Both libs exist on the ESP Component Registry — no vendoring needed. +dependencies: + espressif/esp32-camera: + version: ">=2.0.0" + espressif/quirc: + version: ">=1.2.0" diff --git a/idf_zacus/components/local_puzzles/include/board_pins_mediakit.h b/idf_zacus/components/local_puzzles/include/board_pins_mediakit.h new file mode 100644 index 0000000..b0e4481 --- /dev/null +++ b/idf_zacus/components/local_puzzles/include/board_pins_mediakit.h @@ -0,0 +1,125 @@ +#pragma once + +/** + * @file board_pins_mediakit.h + * + * Freenove FNK0102H Media Kit v1.2 pin map. + * + * Board: Freenove ESP32-S3 WROOM N16R8 (FNK0102H) + * Sources: + * - docs/FNK0102H_SOURCE_OF_TRUTH.md (canonical pin tables) + * - ui_freenove_allinone/include/ui_freenove_config.h (validated firmware) + * + * Date frozen: 2026-06-10 + * + * CRITICAL NOTE (voice_pipeline defaults): + * The voice_pipeline component defaults (in idf_zacus/components/voice_pipeline/voice_pipeline.c) + * are configured for a different wiring: + * - Mic: 14 (BCLK), 15 (WS), 22 (DIN) + * - Speaker: 11 (BCLK), 12 (LRC), 13 (DOUT) + * + * These defaults COLLIDE with the FNK0102H camera pins: + * - 15 = CAM_PIN_XCLK + * - 13 = CAM_PIN_PCLK + * - 11 = CAM_PIN_D0 (Y2) + * - 12 = CAM_PIN_D4 (Y6) + * + * When initializing voice_pipeline on the Media Kit, the defaults MUST be overridden + * with the FNK0102H values defined below (mic: 3/14/46, speaker: 42/41/1). + * This override is performed in the main application initialization (separate task). + */ + +/* Camera pins (8-bit parallel interface + control) */ +#define CAM_PIN_PWDN -1 /* Power down: not wired */ +#define CAM_PIN_RESET -1 /* Reset: not wired */ +#define CAM_PIN_XCLK 15 /* Master clock */ +#define CAM_PIN_SIOD 4 /* I2C SDA (camera config) */ +#define CAM_PIN_SIOC 5 /* I2C SCL (camera config) */ +#define CAM_PIN_VSYNC 6 /* Vertical sync */ +#define CAM_PIN_HREF 7 /* Horizontal ref / data enable */ +#define CAM_PIN_PCLK 13 /* Pixel clock */ + +/* Camera data pins (D0..D7 = Y2..Y9 per esp32-camera convention) */ +#define CAM_PIN_D0 11 /* Y2 */ +#define CAM_PIN_D1 9 /* Y3 */ +#define CAM_PIN_D2 8 /* Y4 */ +#define CAM_PIN_D3 10 /* Y5 */ +#define CAM_PIN_D4 12 /* Y6 */ +#define CAM_PIN_D5 18 /* Y7 */ +#define CAM_PIN_D6 17 /* Y8 */ +#define CAM_PIN_D7 16 /* Y9 */ + +/* Microphone I2S IN (PDM or PCM, input path) */ +#define MIC_PIN_BCLK 3 /* Bit clock (I2S_IN_SCK) */ +#define MIC_PIN_WS 14 /* Word select (I2S_IN_WS) */ +#define MIC_PIN_DIN 46 /* Data in (I2S_IN_DIN) */ + +/* Speaker I2S OUT (output path) */ +#define SPK_PIN_BCLK 42 /* Bit clock (I2S_BCK) */ +#define SPK_PIN_LRC 41 /* Left-right clock / word select (I2S_WS) */ +#define SPK_PIN_DIN 1 /* Data out (I2S_DOUT) */ + +/** + * GPIO Coexistence Table + * + * All GPIO pins used across camera, microphone, and speaker: + * + * GPIO │ Device │ Function + * ──────┼──────────┼──────────────────── + * 1 │ Speaker │ DOUT (I2S OUT) + * 3 │ Mic │ BCLK (I2S IN) + * 4 │ Camera │ SIOD (I2C SDA) + * 5 │ Camera │ SIOC (I2C SCL) + * 6 │ Camera │ VSYNC + * 7 │ Camera │ HREF + * 8 │ Camera │ D2 (Y4) + * 9 │ Camera │ D1 (Y3) + * 10 │ Camera │ D3 (Y5) + * 11 │ Camera │ D0 (Y2) + * 12 │ Camera │ D4 (Y6) + * 13 │ Camera │ PCLK + * 14 │ Mic │ WS (I2S IN) + * 15 │ Camera │ XCLK + * 16 │ Camera │ D7 (Y9) + * 17 │ Camera │ D6 (Y8) + * 18 │ Camera │ D5 (Y7) + * 41 │ Speaker │ LRC (I2S OUT) + * 42 │ Speaker │ BCLK (I2S OUT) + * 46 │ Mic │ DIN (I2S IN) + * + * Result: ZERO collision. Each GPIO assigned to exactly one device. + * + * Note: GPIO -1 (PWDN, RESET) indicates "not wired" and is not allocated. + */ + +/* ── TFT display (FNK0102H ST7796 320×480) ───────────────────────────────── + * + * Source: ui_freenove_allinone/include/ui_freenove_config.h (validated firmware) + * docs/FNK0102H_SOURCE_OF_TRUTH.md (canonical pin tables) + * + * SPI host: SPI2_HOST (FSPI) — FREENOVE_LCD_USE_HSPI == 0 in the reference + * firmware, so the default FSPI host is used (not HSPI/SPI3_HOST). + * Write frequency: 80 MHz. Read frequency: 20 MHz. + * CS pin: -1 (not wired — the panel is the only device on this SPI bus). + * + * Coexistence with camera/mic/speaker: + * GPIO 20 (TFT_PIN_RST): not present in the camera/mic/speaker tables above. + * GPIO 21 (TFT_PIN_MOSI): not present in the camera/mic/speaker tables above. + * GPIO 45 (TFT_PIN_DC): not present in the camera/mic/speaker tables above. + * GPIO 47 (TFT_PIN_SCK): not present in the camera/mic/speaker tables above. + * GPIO 2 (TFT_PIN_BL): not present in the camera/mic/speaker tables above. + * RESULT: ZERO collision with any already-mapped GPIO. + * + * BACKLIGHT / LEDC NOTE (Phase 1): + * The reference firmware uses Light_PWM (LEDC) on GPIO 2. In Phase 1 we + * drive the backlight as plain GPIO (full-on) because qr_puzzle already + * claims LEDC_TIMER_0 / LEDC_CHANNEL_0 for the camera XCLK. Phase 2 will + * switch to Light_PWM on a free LEDC channel/timer once the XCLK is moved + * to the esp32-camera driver's own LEDC allocation. + */ +#define TFT_PIN_SCK 47 /* SPI clock */ +#define TFT_PIN_MOSI 21 /* SPI MOSI */ +#define TFT_PIN_MISO -1 /* not wired */ +#define TFT_PIN_DC 45 /* D/C (register select) */ +#define TFT_PIN_RST 20 /* hardware reset */ +#define TFT_PIN_BL 2 /* backlight enable (Phase 1: plain GPIO, full-on) */ diff --git a/idf_zacus/components/local_puzzles/include/local_puzzles.h b/idf_zacus/components/local_puzzles/include/local_puzzles.h new file mode 100644 index 0000000..1985da9 --- /dev/null +++ b/idf_zacus/components/local_puzzles/include/local_puzzles.h @@ -0,0 +1,40 @@ +// local_puzzles.h — wiring layer: QR and sound puzzles → puzzle_state. +#pragma once +#include +#include +#include "esp_err.h" +#include "puzzle_state.h" + +// Store the aggregation target. Must be called before arm_*. +void local_puzzles_init(puzzle_state_t *state); + +// Arm the QR sequence puzzle. expected[0..count-1] are the QR strings to +// match in order. On solve, reports fragment[0..frag_len) for puzzle_id into +// puzzle_state. Caller buffers may be transient — contents are copied. +// Returns ESP_ERR_INVALID_STATE if init not called OR if the QR puzzle is +// still running (including the async teardown window after disarm) — retry +// after ~100 ms. +// Returns ESP_ERR_INVALID_ARG if puzzle_id out of range [1,PUZZLE_MAX_ID], +// frag_len > PUZZLE_MAX_FRAG, or fragment is NULL when frag_len > 0. +// CALLBACK SAFETY: do not call arm_qr or disarm from the solved callback; +// defer to another task (e.g. via task notification or event group). +esp_err_t local_puzzles_arm_qr(uint8_t puzzle_id, + const char *const *expected, size_t count, + const uint8_t *fragment, uint8_t frag_len); + +// Arm the sound/melody puzzle. expected[0..count-1] are MIDI note numbers; +// tol is the accepted semitone deviation. On solve, reports fragment[0..frag_len) +// for puzzle_id into puzzle_state. Caller buffers may be transient — copied. +// mic_broker_init() must have been called first. +// Returns ESP_ERR_INVALID_STATE if init not called OR if the sound puzzle is +// still running (broker is in MIC_P1_SOUND mode). +// Returns ESP_ERR_INVALID_ARG if puzzle_id out of range [1,PUZZLE_MAX_ID], +// frag_len > PUZZLE_MAX_FRAG, or fragment is NULL when frag_len > 0. +// CALLBACK SAFETY: do not call arm_sound or disarm from the solved callback; +// defer to another task. +esp_err_t local_puzzles_arm_sound(uint8_t puzzle_id, + const int *expected, size_t count, int tol, + const uint8_t *fragment, uint8_t frag_len); + +// Stop both puzzles. QR teardown is asynchronous (see qr_puzzle.h). +void local_puzzles_disarm(void); diff --git a/idf_zacus/components/local_puzzles/include/qr_puzzle.h b/idf_zacus/components/local_puzzles/include/qr_puzzle.h new file mode 100644 index 0000000..3395485 --- /dev/null +++ b/idf_zacus/components/local_puzzles/include/qr_puzzle.h @@ -0,0 +1,34 @@ +// qr_puzzle.h — camera-based QR sequence puzzle for the OV3660 on the +// Freenove ESP32-S3 Media Kit. Uses esp32-camera (QVGA grayscale) + quirc. +// +// qr_puzzle_start: initialise the camera, spawn the scan task, and register +// the sequence to match. Returns ESP_ERR_INVALID_STATE if already running. +// NOTE: after qr_puzzle_stop() the scan task deinits the camera +// asynchronously — qr_puzzle_start returns ESP_ERR_INVALID_STATE until +// teardown completes; callers should retry after a short delay (e.g. 100 ms). +// qr_puzzle_stop: request shutdown; the camera is deinitialized by the task +// itself before it exits (frees PSRAM and powers down the sensor). +// +// CALLBACK SAFETY: the solved callback runs in the scan task context and MUST +// NOT call qr_puzzle_start/qr_puzzle_stop directly — defer re-arming to +// another task (e.g. via task notification or event group). +#pragma once +#include +#include +#include +#include "esp_err.h" + +typedef void (*qr_solved_cb_t)(void); + +// Optional frame-mirror hook (e.g. live viewfinder on the local display). +// Called from the scan task for every captured frame BEFORE decode, with the +// raw grayscale QVGA buffer. The callee must COPY the data and return fast +// (it blocks scanning); it runs in the scan task context — same restrictions +// as the solved callback. Pass NULL to disable. Survives start/stop cycles. +typedef void (*qr_preview_cb_t)(const uint8_t *gray, int width, int height); +void qr_puzzle_set_preview_cb(qr_preview_cb_t cb); + +esp_err_t qr_puzzle_start(const char *const *expected, size_t count, qr_solved_cb_t cb); +void qr_puzzle_stop(void); +// True while the scan task exists, including the async teardown window after stop. +bool qr_puzzle_is_running(void); diff --git a/idf_zacus/components/local_puzzles/include/sound_puzzle.h b/idf_zacus/components/local_puzzles/include/sound_puzzle.h new file mode 100644 index 0000000..72d1a96 --- /dev/null +++ b/idf_zacus/components/local_puzzles/include/sound_puzzle.h @@ -0,0 +1,37 @@ +// sound_puzzle.h — melody-recognition puzzle consuming MIC_P1_SOUND frames +// from the mic broker. +// +// Prerequisites: mic_broker_init() must have been called before +// sound_puzzle_start(). +// +// Callback contract: the solved callback runs on the mic_broker task +// (8 KiB stack). It must not block nor allocate large buffers. It must not +// call sound_puzzle_start(). sound_puzzle_stop() is safe to call from the +// callback (it is a thin alias for mic_broker_set_mode(MIC_IDLE), which is +// a safe volatile write from the broker task). Note: the puzzle self-stops +// on solve (MIC_IDLE is set before the callback fires), so calling +// sound_puzzle_stop() from the callback is redundant but harmless. +// +// Note on repeated notes: melodies with immediately repeated notes require +// an intervening silence between them (the per-frame debounce suppresses +// consecutive identical notes). +// +// Concurrency: sound_puzzle_start() must be called only while the broker is +// MIC_IDLE; calling it while MIC_P1_SOUND is active is undefined behaviour. +#pragma once +#include +#include + +typedef void (*sound_solved_cb_t)(void); + +// Register on the mic broker (MIC_P1_SOUND mode) and start routing frames to +// the melody validator. expected[0..count-1] are MIDI note numbers; tol is the +// accepted semitone deviation. cb is invoked once the full melody is matched. +void sound_puzzle_start(const int *expected, size_t count, int tol, + sound_solved_cb_t cb); + +// Return the broker to MIC_IDLE, stopping frame delivery. Safe to call from +// the solved callback; the puzzle self-stops on solve so this is redundant then. +void sound_puzzle_stop(void); +// True while the broker is in MIC_P1_SOUND mode. +bool sound_puzzle_is_running(void); diff --git a/idf_zacus/components/local_puzzles/local_puzzles.c b/idf_zacus/components/local_puzzles/local_puzzles.c new file mode 100644 index 0000000..46b6e49 --- /dev/null +++ b/idf_zacus/components/local_puzzles/local_puzzles.c @@ -0,0 +1,73 @@ +// local_puzzles.c — wires QR/sound to puzzle_state and arms by puzzle id. +#include "local_puzzles.h" +#include "qr_puzzle.h" +#include "sound_puzzle.h" +#include + +static puzzle_state_t *s_state; + +// Per-type static contexts — copied at arm time so caller buffers can be +// transient (e.g. a REST handler's stack frame). +typedef struct { + uint8_t id; + uint8_t frag[PUZZLE_MAX_FRAG]; + uint8_t frag_len; +} puzzle_ctx_t; + +static puzzle_ctx_t s_qr_ctx; +static puzzle_ctx_t s_sound_ctx; + +static void on_qr_solved(void) { + puzzle_state_report(s_state, s_qr_ctx.id, s_qr_ctx.frag, s_qr_ctx.frag_len); +} + +static void on_sound_solved(void) { + puzzle_state_report(s_state, s_sound_ctx.id, s_sound_ctx.frag, s_sound_ctx.frag_len); +} + +void local_puzzles_init(puzzle_state_t *state) { s_state = state; } + +esp_err_t local_puzzles_arm_qr(uint8_t puzzle_id, + const char *const *expected, size_t count, + const uint8_t *fragment, uint8_t frag_len) { + if (!s_state) return ESP_ERR_INVALID_STATE; + if (puzzle_id < 1 || puzzle_id > PUZZLE_MAX_ID) return ESP_ERR_INVALID_ARG; + if (frag_len > PUZZLE_MAX_FRAG) return ESP_ERR_INVALID_ARG; + if (frag_len > 0 && fragment == NULL) return ESP_ERR_INVALID_ARG; + + if (qr_puzzle_is_running()) return ESP_ERR_INVALID_STATE; + + puzzle_ctx_t prev_qr = s_qr_ctx; + s_qr_ctx.id = puzzle_id; + s_qr_ctx.frag_len = frag_len; + if (frag_len > 0) { + memcpy(s_qr_ctx.frag, fragment, frag_len); + } + esp_err_t e = qr_puzzle_start(expected, count, on_qr_solved); + if (e != ESP_OK) { s_qr_ctx = prev_qr; } + return e; +} + +esp_err_t local_puzzles_arm_sound(uint8_t puzzle_id, + const int *expected, size_t count, int tol, + const uint8_t *fragment, uint8_t frag_len) { + if (!s_state) return ESP_ERR_INVALID_STATE; + if (puzzle_id < 1 || puzzle_id > PUZZLE_MAX_ID) return ESP_ERR_INVALID_ARG; + if (frag_len > PUZZLE_MAX_FRAG) return ESP_ERR_INVALID_ARG; + if (frag_len > 0 && fragment == NULL) return ESP_ERR_INVALID_ARG; + + if (sound_puzzle_is_running()) return ESP_ERR_INVALID_STATE; + + s_sound_ctx.id = puzzle_id; + s_sound_ctx.frag_len = frag_len; + if (frag_len > 0) { + memcpy(s_sound_ctx.frag, fragment, frag_len); + } + sound_puzzle_start(expected, count, tol, on_sound_solved); + return ESP_OK; +} + +void local_puzzles_disarm(void) { + qr_puzzle_stop(); + sound_puzzle_stop(); +} diff --git a/idf_zacus/components/local_puzzles/melody_validator.c b/idf_zacus/components/local_puzzles/melody_validator.c new file mode 100644 index 0000000..58cf1e4 --- /dev/null +++ b/idf_zacus/components/local_puzzles/melody_validator.c @@ -0,0 +1,28 @@ +// melody_validator.c +#include "melody_validator.h" +#include +#include + +void melody_validator_init(melody_validator_t *m, const int *expected, + size_t count, int tolerance) +{ + memset(m, 0, sizeof(*m)); + if (count > MELODY_MAX_NOTES) count = MELODY_MAX_NOTES; + if (tolerance < 0) tolerance = 0; + m->count = count; + m->tolerance = tolerance; + for (size_t i = 0; i < count; i++) m->expected[i] = expected[i]; +} + +bool melody_validator_feed(melody_validator_t *m, int note) +{ + if (m->count == 0 || m->pos >= m->count) return false; + if (abs(note - m->expected[m->pos]) <= m->tolerance) { + m->pos++; + return m->pos == m->count; + } + // out of tolerance: reset, but let this note seed index 0 if it fits + m->pos = 0; + if (abs(note - m->expected[0]) <= m->tolerance) m->pos = 1; + return false; +} diff --git a/idf_zacus/components/local_puzzles/melody_validator.h b/idf_zacus/components/local_puzzles/melody_validator.h new file mode 100644 index 0000000..2eb8b8c --- /dev/null +++ b/idf_zacus/components/local_puzzles/melody_validator.h @@ -0,0 +1,24 @@ +// melody_validator.h — note-sequence validator (P1). Pure logic, no I/O. +#pragma once +#include +#include + +#define MELODY_MAX_NOTES 32 + +typedef struct { + int expected[MELODY_MAX_NOTES]; // MIDI note numbers (0-127) + size_t count; + int tolerance; // accepted deviation in semitones (>=0; negative clamped to 0) + size_t pos; +} melody_validator_t; + +// expected: array of `count` MIDI note numbers (0-127, caller-bounded). +// tolerance: maximum deviation in semitones from an expected note (>=0; negative clamped to 0). +// Repeated notes ARE permitted in the expected sequence (unlike seq_validator). +// m and expected must be non-NULL; no internal NULL checks are performed. +void melody_validator_init(melody_validator_t *m, const int *expected, + size_t count, int tolerance); + +// Feed one detected note (MIDI number 0-127). True when the full melody is matched. +// On mismatch, resets and allows the current note to seed index 0 if it fits. +bool melody_validator_feed(melody_validator_t *m, int note); diff --git a/idf_zacus/components/local_puzzles/mic_broker.c b/idf_zacus/components/local_puzzles/mic_broker.c new file mode 100644 index 0000000..4277e5b --- /dev/null +++ b/idf_zacus/components/local_puzzles/mic_broker.c @@ -0,0 +1,114 @@ +// mic_broker.c — single I2S RX owner; delivers PCM16 mono frames to one +// active consumer at a time. +// +// I2S configuration mirrors the working voice_pipeline setup exactly: +// port : I2S_NUM_0 (RX only; voice_pipeline TX uses I2S_NUM_1 — no clash) +// mode : Philips std, 16-bit, mono +// rate : caller-supplied (voice_pipeline uses 16 000 Hz) +// The broker's capture_task reads MB_FRAME-sample chunks (20 ms @ 16 kHz) +// and invokes the registered callback for the current mode on every frame. +// MIC_IDLE suppresses delivery without stopping the I2S clock, keeping DMA +// buffers drained. + +#include "mic_broker.h" + +#include "driver/i2s_std.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +static const char *TAG = "mic_broker"; + +// 20 ms @ 16 kHz mono 16-bit +#define MB_FRAME 320 +// NPC callback runs AFE feed/fetch + WS send on this task; +// sized like the old voice_pipeline capture task (CAPTURE_TASK_STACK = 8192). +#define MB_TASK_STACK 8192 + +static i2s_chan_handle_t s_rx; +static volatile mic_mode_t s_mode = MIC_IDLE; +static struct { + mic_frame_cb_t cb; + void *ctx; +} s_consumers[3]; // indexed by mic_mode_t value (0=IDLE unused, 1, 2) + +static void capture_task(void *arg) { + int16_t buf[MB_FRAME]; + size_t got; + for (;;) { + esp_err_t err = i2s_channel_read(s_rx, buf, sizeof(buf), &got, + portMAX_DELAY); + if (err != ESP_OK) { + ESP_LOGW(TAG, "i2s read err: %s", esp_err_to_name(err)); + continue; + } + mic_mode_t m = s_mode; + if (m != MIC_IDLE && s_consumers[m].cb) { + s_consumers[m].cb(buf, got / sizeof(int16_t), s_consumers[m].ctx); + } + } +} + +esp_err_t mic_broker_init(int bclk, int ws, int din, int sr) { + // Guard double init: return a distinctive error so callers can treat + // it as benign ("already up") rather than a hard failure. + if (s_rx) return ESP_ERR_INVALID_STATE; + + i2s_chan_config_t cc = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER); + esp_err_t e = i2s_new_channel(&cc, NULL, &s_rx); + if (e != ESP_OK) return e; + + // Philips mono 16-bit — matches voice_pipeline's working i2s_setup(). + i2s_std_config_t std = { + .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(sr), + .slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, + I2S_SLOT_MODE_MONO), + .gpio_cfg = { + .mclk = I2S_GPIO_UNUSED, + .bclk = bclk, + .ws = ws, + .din = din, + .dout = I2S_GPIO_UNUSED, + .invert_flags = { .mclk_inv = false, + .bclk_inv = false, + .ws_inv = false }, + }, + }; + + e = i2s_channel_init_std_mode(s_rx, &std); + if (e != ESP_OK) { + i2s_del_channel(s_rx); + s_rx = NULL; + return e; + } + + e = i2s_channel_enable(s_rx); + if (e != ESP_OK) { + i2s_del_channel(s_rx); + s_rx = NULL; + return e; + } + + if (xTaskCreate(capture_task, "mic_broker", MB_TASK_STACK, NULL, 5, NULL) != pdPASS) { + i2s_channel_disable(s_rx); + i2s_del_channel(s_rx); + s_rx = NULL; + return ESP_ERR_NO_MEM; + } + ESP_LOGI(TAG, "init OK (BCLK=%d WS=%d DIN=%d @%d Hz)", bclk, ws, din, sr); + return ESP_OK; +} + +void mic_broker_register(mic_mode_t mode, mic_frame_cb_t cb, void *ctx) { + if (mode == MIC_IDLE) return; // IDLE slot is never called + s_consumers[mode].cb = cb; + s_consumers[mode].ctx = ctx; +} + +void mic_broker_set_mode(mic_mode_t mode) { + s_mode = mode; +} + +mic_mode_t mic_broker_mode(void) { + return s_mode; +} diff --git a/idf_zacus/components/local_puzzles/mic_broker.h b/idf_zacus/components/local_puzzles/mic_broker.h new file mode 100644 index 0000000..4ae9ee9 --- /dev/null +++ b/idf_zacus/components/local_puzzles/mic_broker.h @@ -0,0 +1,20 @@ +// mic_broker.h — single owner of the I2S RX mic; routes frames to one +// active consumer chosen by mode. No frame is delivered in MIC_IDLE. +// +// A second mic_broker_init() call returns ESP_ERR_INVALID_STATE (single +// init; callers may treat that as benign if the broker is already up). +#pragma once +#include +#include +#include +#include "esp_err.h" + +typedef enum { MIC_IDLE, MIC_NPC_LISTEN, MIC_P1_SOUND } mic_mode_t; + +// cb receives PCM16 mono frames (count = samples) when its mode is active. +typedef void (*mic_frame_cb_t)(const int16_t *pcm, size_t samples, void *ctx); + +esp_err_t mic_broker_init(int bclk_pin, int ws_pin, int din_pin, int sample_rate_hz); +void mic_broker_register(mic_mode_t mode, mic_frame_cb_t cb, void *ctx); +void mic_broker_set_mode(mic_mode_t mode); +mic_mode_t mic_broker_mode(void); diff --git a/idf_zacus/components/local_puzzles/qr_puzzle.c b/idf_zacus/components/local_puzzles/qr_puzzle.c new file mode 100644 index 0000000..67ef1a2 --- /dev/null +++ b/idf_zacus/components/local_puzzles/qr_puzzle.c @@ -0,0 +1,181 @@ +// qr_puzzle.c — camera capture + quirc decode; validates QR order via seq_validator. +// Pin map: board_pins_mediakit.h (frozen source of truth for CAM_PIN_* macros). +#include "qr_puzzle.h" +#include "seq_validator.h" +#include "board_pins_mediakit.h" +#include "esp_camera.h" +#include "quirc.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include + +#define TAG "qr_puzzle" + +// QVGA dimensions — must match frame_size = FRAMESIZE_QVGA in cam_cfg(). +#define QR_WIDTH 320 +#define QR_HEIGHT 240 + +static seq_validator_t s_seq; +static qr_solved_cb_t s_cb; +static TaskHandle_t s_task; +static volatile bool s_run; +static qr_preview_cb_t s_preview_cb; // optional viewfinder mirror + +void qr_puzzle_set_preview_cb(qr_preview_cb_t cb) { s_preview_cb = cb; } + +static camera_config_t cam_cfg(void) { + camera_config_t c = { + .pin_pwdn = CAM_PIN_PWDN, + .pin_reset = CAM_PIN_RESET, + .pin_xclk = CAM_PIN_XCLK, + .pin_sccb_sda = CAM_PIN_SIOD, + .pin_sccb_scl = CAM_PIN_SIOC, + .pin_d7 = CAM_PIN_D7, + .pin_d6 = CAM_PIN_D6, + .pin_d5 = CAM_PIN_D5, + .pin_d4 = CAM_PIN_D4, + .pin_d3 = CAM_PIN_D3, + .pin_d2 = CAM_PIN_D2, + .pin_d1 = CAM_PIN_D1, + .pin_d0 = CAM_PIN_D0, + .pin_vsync = CAM_PIN_VSYNC, + .pin_href = CAM_PIN_HREF, + .pin_pclk = CAM_PIN_PCLK, + .xclk_freq_hz = 20000000, + .ledc_timer = LEDC_TIMER_0, + .ledc_channel = LEDC_CHANNEL_0, + .pixel_format = PIXFORMAT_GRAYSCALE, + // QVGA: matches the on-device viewfinder canvas (320x240) so the live + // preview keeps working. VGA was tried for QR decode but quirc still + // found zero finder patterns (the LCD-emissive contrast is the real + // blocker, not resolution), and it broke the viewfinder — so revert. + .frame_size = FRAMESIZE_QVGA, // 320x240 + .fb_count = 1, + .fb_location = CAMERA_FB_IN_PSRAM, + .grab_mode = CAMERA_GRAB_LATEST, + }; + return c; +} + +static void scan_task(void *arg) { + // CONFIG_SPIRAM_USE_MALLOC=y: quirc_new's ~76 KB internal buffer lands in PSRAM via malloc. + struct quirc *q = quirc_new(); + if (!q) { + ESP_LOGE(TAG, "quirc_new failed (out of memory)"); + esp_camera_deinit(); + s_task = NULL; + vTaskDelete(NULL); + return; + } + // quirc is (re)sized to the camera's ACTUAL frame geometry. The OV3660 + // on this board does not always deliver exactly QVGA — adapt at runtime + // instead of dropping every off-size frame (the old fixed 320x240 guard + // silently starved quirc when the sensor returned another size). + int q_w = 0, q_h = 0; + + while (s_run) { + camera_fb_t *fb = esp_camera_fb_get(); + if (!fb) { vTaskDelay(pdMS_TO_TICKS(50)); continue; } + + if (fb->width != q_w || fb->height != q_h) { + if (quirc_resize(q, fb->width, fb->height) < 0) { + ESP_LOGE(TAG, "quirc_resize(%d,%d) failed", fb->width, fb->height); + esp_camera_fb_return(fb); + vTaskDelay(pdMS_TO_TICKS(50)); + continue; + } + q_w = fb->width; + q_h = fb->height; + ESP_LOGI(TAG, "scanning at sensor geometry %dx%d", q_w, q_h); + } + + if (s_preview_cb) s_preview_cb(fb->buf, q_w, q_h); + + uint8_t *img = quirc_begin(q, NULL, NULL); + memcpy(img, fb->buf, (size_t)q_w * q_h); + quirc_end(q); + + int n = quirc_count(q); + // Diagnostic: a code IDENTIFIED but not decoded points at quiet-zone / + // image-quality issues rather than framing. Throttled to ~1/sec. + static int s_diag_ticks; + if (n > 0 && (s_diag_ticks++ % 33) == 0) { + ESP_LOGI(TAG, "quirc identified %d candidate(s)", n); + } + for (int i = 0; i < n; i++) { + // static: ~12.5 KB total — too large for the task stack; single-instance task. + static struct quirc_code code; + static struct quirc_data data; + quirc_extract(q, i, &code); + if (quirc_decode(&code, &data) == 0) { + // data.payload is a NUL-terminated uint8_t string for text QRs. + ESP_LOGI(TAG, "QR decoded: %s", (const char *)data.payload); + if (seq_validator_feed(&s_seq, (const char *)data.payload)) { + if (s_cb) s_cb(); + s_run = false; + } + } + } + + esp_camera_fb_return(fb); // always return the frame + vTaskDelay(pdMS_TO_TICKS(30)); + } + + quirc_destroy(q); + esp_camera_deinit(); // free PSRAM + power down sensor + s_task = NULL; + vTaskDelete(NULL); +} + +esp_err_t qr_puzzle_start(const char *const *expected, size_t count, qr_solved_cb_t cb) { + if (s_task) { + ESP_LOGW(TAG, "busy (running or still stopping)"); + return ESP_ERR_INVALID_STATE; + } + + camera_config_t cfg = cam_cfg(); + esp_err_t e = esp_camera_init(&cfg); + if (e != ESP_OK) { + ESP_LOGE(TAG, "camera init failed: 0x%x (%s)", e, esp_err_to_name(e)); + return e; + } + + // Tune the OV3660 for QR decoding off a backlit LCD: max contrast + + // sharpness, and cap the gain/exposure so the bright screen does not + // bloom and wash out the QR modules. + sensor_t *s = esp_camera_sensor_get(); + if (s) { + if (s->set_contrast) s->set_contrast(s, 2); + if (s->set_sharpness) s->set_sharpness(s, 2); + if (s->set_gainceiling) s->set_gainceiling(s, GAINCEILING_2X); + if (s->set_whitebal) s->set_whitebal(s, 1); + if (s->set_aec2) s->set_aec2(s, 1); + // The OV3660 mounts mirrored on this board: un-mirror so the captured + // image matches reality (the viewfinder showed a mirror image, and a + // flipped frame defeats QR finder-pattern detection). + if (s->set_hmirror) s->set_hmirror(s, 1); + if (s->set_vflip) s->set_vflip(s, 1); + } + + seq_validator_init(&s_seq, expected, count); + s_cb = cb; + s_run = true; + + if (xTaskCreate(scan_task, "qr_scan", 8192, NULL, 5, &s_task) != pdPASS) { + ESP_LOGE(TAG, "xTaskCreate failed"); + esp_camera_deinit(); + s_task = NULL; + s_run = false; + return ESP_ERR_NO_MEM; + } + + return ESP_OK; +} + +void qr_puzzle_stop(void) { + if (!s_task) return; // no-op when idle + s_run = false; +} + +bool qr_puzzle_is_running(void) { return s_task != NULL; } diff --git a/idf_zacus/components/local_puzzles/seq_validator.c b/idf_zacus/components/local_puzzles/seq_validator.c new file mode 100644 index 0000000..0603bad --- /dev/null +++ b/idf_zacus/components/local_puzzles/seq_validator.c @@ -0,0 +1,29 @@ +// seq_validator.c +#include "seq_validator.h" +#include + +void seq_validator_init(seq_validator_t *v, const char *const *expected, size_t count) +{ + memset(v, 0, sizeof(*v)); + if (count > SEQ_MAX_STEPS) count = SEQ_MAX_STEPS; + v->count = count; + for (size_t i = 0; i < count; i++) { + strncpy(v->expected[i], expected[i], SEQ_MAX_LABEL - 1); + } +} + +bool seq_validator_feed(seq_validator_t *v, const char *code) +{ + if (v->count == 0 || v->pos >= v->count) return false; + // duplicate of the last matched code: ignore (no progress, no reset) + if (v->pos > 0 && strncmp(code, v->expected[v->pos - 1], SEQ_MAX_LABEL) == 0) + return false; + if (strncmp(code, v->expected[v->pos], SEQ_MAX_LABEL) == 0) { + v->pos++; + return v->pos == v->count; + } + v->pos = 0; // wrong scan -> reset + // allow the wrong scan to itself start a new run at index 0 + if (strncmp(code, v->expected[0], SEQ_MAX_LABEL) == 0) v->pos = 1; + return false; +} diff --git a/idf_zacus/components/local_puzzles/seq_validator.h b/idf_zacus/components/local_puzzles/seq_validator.h new file mode 100644 index 0000000..c08750a --- /dev/null +++ b/idf_zacus/components/local_puzzles/seq_validator.h @@ -0,0 +1,22 @@ +// seq_validator.h — ordered-scan validator (QR P3). Pure logic, no I/O. +#pragma once +#include +#include + +#define SEQ_MAX_STEPS 16 +#define SEQ_MAX_LABEL 32 + +typedef struct { + char expected[SEQ_MAX_STEPS][SEQ_MAX_LABEL]; + size_t count; + size_t pos; // next index to match +} seq_validator_t; + +// expected: array of `count` C-strings (<= SEQ_MAX_STEPS, each < SEQ_MAX_LABEL). +// All expected labels must be distinct: duplicates make the sequence uncompletable. +// v and expected must be non-NULL; no internal NULL checks are performed. +void seq_validator_init(seq_validator_t *v, const char *const *expected, size_t count); + +// Feed one scanned code. Returns true exactly when the full sequence is done. +// A wrong code resets progress; a duplicate of the last matched code is ignored. +bool seq_validator_feed(seq_validator_t *v, const char *code); diff --git a/idf_zacus/components/local_puzzles/sound_puzzle.c b/idf_zacus/components/local_puzzles/sound_puzzle.c new file mode 100644 index 0000000..bc42896 --- /dev/null +++ b/idf_zacus/components/local_puzzles/sound_puzzle.c @@ -0,0 +1,60 @@ +// sound_puzzle.c — consumes MIC_P1_SOUND frames, estimates note, validates melody. +// +// Pitch estimation: zero-crossing rate -> fundamental -> MIDI note number. +// Known limit: ZCR conflates harmonics with the fundamental; accuracy is +// acceptable for well-separated notes in a quiet room. Future upgrade path: +// autocorrelation or esp-dsp FFT for more reliable fundamental detection. +#include "sound_puzzle.h" +#include "melody_validator.h" +#include "mic_broker.h" +#include "esp_log.h" +#include +#include +#include + +static const char *TAG = "sound_puzzle"; + +static melody_validator_t s_mel; +static sound_solved_cb_t s_cb; +static int s_last_note = -1000; + +// Crude pitch: zero-crossing rate -> fundamental -> MIDI. Replaced by an +// autocorrelation estimator if accuracy is insufficient (documented limit). +static int frame_to_midi(const int16_t *pcm, size_t n, int sr) { + size_t zc = 0; + for (size_t i = 1; i < n; i++) if ((pcm[i-1] < 0) != (pcm[i] < 0)) zc++; + if (zc < 2) return -1000; // silence + float freq = (float)zc * sr / (2.0f * n); + if (freq < 80.0f || freq > 2000.0f) return -1000; + return (int)lroundf(69.0f + 12.0f * log2f(freq / 440.0f)); +} + +static void on_frame(const int16_t *pcm, size_t n, void *ctx) { + int note = frame_to_midi(pcm, n, 16000); + if (note <= -1000) { s_last_note = -1000; return; } + if (note == s_last_note) return; // debounce sustained note + s_last_note = note; + const size_t before = s_mel.pos; + if (melody_validator_feed(&s_mel, note)) { + ESP_LOGI(TAG, "note=%d -> melodie complete (%u notes)", + note, (unsigned) s_mel.count); + mic_broker_set_mode(MIC_IDLE); // self-stop: safe volatile mode write from broker task + if (s_cb) s_cb(); + } else { + // Trace bout-en-bout du test physique : note entendue + progression + // (un retour a 0/N signale un reset du validateur sur fausse note). + ESP_LOGI(TAG, "note=%d progression=%u/%u%s", note, + (unsigned) s_mel.pos, (unsigned) s_mel.count, + (s_mel.pos > before) ? "" : " (reset)"); + } +} + +void sound_puzzle_start(const int *expected, size_t count, int tol, sound_solved_cb_t cb) { + melody_validator_init(&s_mel, expected, count, tol); + s_cb = cb; s_last_note = -1000; + mic_broker_register(MIC_P1_SOUND, on_frame, NULL); + mic_broker_set_mode(MIC_P1_SOUND); +} +void sound_puzzle_stop(void) { mic_broker_set_mode(MIC_IDLE); } + +bool sound_puzzle_is_running(void) { return mic_broker_mode() == MIC_P1_SOUND; } diff --git a/idf_zacus/components/local_puzzles/test/host/.gitignore b/idf_zacus/components/local_puzzles/test/host/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/idf_zacus/components/local_puzzles/test/host/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/idf_zacus/components/local_puzzles/test/host/Makefile b/idf_zacus/components/local_puzzles/test/host/Makefile new file mode 100644 index 0000000..7bdcdaf --- /dev/null +++ b/idf_zacus/components/local_puzzles/test/host/Makefile @@ -0,0 +1,63 @@ +# Host test harness Makefile — covers the component's pure-logic units (seq_validator, melody_validator) +UNITY_DIR ?= $(HOME)/esp/esp-idf/components/unity/unity/src +CC ?= cc + +# Source paths +COMPONENT_DIR := ../.. +TEST_DIR := .. + +# Build output +BUILD_DIR := build + +# Test executable +TEST_BIN := $(BUILD_DIR)/test_runner + +# Component + harness sources +SRCS := \ + $(COMPONENT_DIR)/seq_validator.c \ + $(COMPONENT_DIR)/melody_validator.c \ + $(UNITY_DIR)/unity.c \ + host_runner.c + +# Canonical IDF-style test file (single source of truth for assertions) +TEST_SRCS := \ + $(TEST_DIR)/test_validators.c + +ALL_SRCS := $(SRCS) $(TEST_SRCS) + +# Include directories +INCS := \ + -I$(COMPONENT_DIR)/include \ + -I$(COMPONENT_DIR) \ + -I$(TEST_DIR)/host \ + -I$(UNITY_DIR) + +# Compiler flags +CFLAGS := -std=c11 -Wall -Wextra -Werror +CFLAGS += $(INCS) +CFLAGS += -DUNITY_INCLUDE_CONFIG_H + +# Inject the TEST_CASE shim before each IDF-style test source +TEST_CFLAGS := $(CFLAGS) -include unity_test_case.h + +.PHONY: all test clean + +all: $(TEST_BIN) + +$(BUILD_DIR): + mkdir -p $(BUILD_DIR) + $(if $(wildcard $(UNITY_DIR)/unity.c),,$(error UNITY_DIR not found: $(UNITY_DIR) — set UNITY_DIR=...)) + +# Compile test sources with the shim injected, then link everything in one shot +$(TEST_BIN): $(ALL_SRCS) | $(BUILD_DIR) + $(if $(wildcard $(UNITY_DIR)/unity.c),,$(error UNITY_DIR not found: $(UNITY_DIR) — set UNITY_DIR=...)) + $(CC) $(TEST_CFLAGS) -c -o $(BUILD_DIR)/test_validators.o $(TEST_DIR)/test_validators.c + $(CC) $(CFLAGS) -o $@ $(SRCS) $(BUILD_DIR)/test_validators.o + +test: $(TEST_BIN) + ./$(TEST_BIN) + +clean: + rm -rf $(BUILD_DIR) + +.PRECIOUS: $(TEST_BIN) diff --git a/idf_zacus/components/local_puzzles/test/host/host_runner.c b/idf_zacus/components/local_puzzles/test/host/host_runner.c new file mode 100644 index 0000000..81fd5b8 --- /dev/null +++ b/idf_zacus/components/local_puzzles/test/host/host_runner.c @@ -0,0 +1,36 @@ +// host_runner.c — dynamic registration runner for Unity on host. +// Tests self-register via constructor attributes defined in unity_test_case.h. +#include "unity.h" +#include "unity_test_case.h" +#include +#include + +#define HOST_TEST_TABLE_CAPACITY 64 + +static host_test_fn_t s_fns[HOST_TEST_TABLE_CAPACITY]; +static const char *s_names[HOST_TEST_TABLE_CAPACITY]; +static int s_count = 0; + +void host_register_test(host_test_fn_t fn, const char *name) +{ + if (s_count >= HOST_TEST_TABLE_CAPACITY) { + fprintf(stderr, "host_runner: test table full (capacity %d) — cannot register \"%s\"\n", + HOST_TEST_TABLE_CAPACITY, name); + exit(1); + } + s_fns[s_count] = fn; + s_names[s_count] = name; + s_count++; +} + +void setUp(void) {} +void tearDown(void) {} + +int main(void) +{ + UNITY_BEGIN(); + for (int i = 0; i < s_count; i++) { + UnityDefaultTestRun(s_fns[i], s_names[i], 0); + } + return UNITY_END(); +} diff --git a/idf_zacus/components/local_puzzles/test/host/unity_config.h b/idf_zacus/components/local_puzzles/test/host/unity_config.h new file mode 100644 index 0000000..1b37848 --- /dev/null +++ b/idf_zacus/components/local_puzzles/test/host/unity_config.h @@ -0,0 +1,6 @@ +// unity_config.h — minimal Unity configuration for host builds +#pragma once + +// For host build, we use standard libc, not platform-specific memory layouts +#define UNITY_INT_WIDTH 32 +#define UNITY_LONG_WIDTH 64 diff --git a/idf_zacus/components/local_puzzles/test/host/unity_test_case.h b/idf_zacus/components/local_puzzles/test/host/unity_test_case.h new file mode 100644 index 0000000..d746a43 --- /dev/null +++ b/idf_zacus/components/local_puzzles/test/host/unity_test_case.h @@ -0,0 +1,20 @@ +// unity_test_case.h — host shim for ESP-IDF's TEST_CASE macro. +// Injected via `-include` before each IDF-style test file; the file's own +// `#include "unity.h"` is harmless after this. +#pragma once +#include "unity.h" + +typedef void (*host_test_fn_t)(void); +void host_register_test(host_test_fn_t fn, const char *name); + +#define HOST_CAT2(a, b) a##b +#define HOST_CAT(a, b) HOST_CAT2(a, b) + +#define HOST_TEST_CASE_IMPL(fn, name_str) \ + static void fn(void); \ + __attribute__((constructor)) static void HOST_CAT(fn, _register)(void) \ + { host_register_test(fn, name_str); } \ + static void fn(void) + +#define TEST_CASE(name_str, tags) \ + HOST_TEST_CASE_IMPL(HOST_CAT(host_test_line_, __LINE__), name_str) diff --git a/idf_zacus/components/local_puzzles/test/test_validators.c b/idf_zacus/components/local_puzzles/test/test_validators.c new file mode 100644 index 0000000..bf7038b --- /dev/null +++ b/idf_zacus/components/local_puzzles/test/test_validators.c @@ -0,0 +1,65 @@ +#include "unity.h" +#include "seq_validator.h" +#include "melody_validator.h" + +TEST_CASE("seq accepts codes scanned in the expected order", "[seq]") +{ + const char *expected[] = {"qr3", "qr1", "qr4"}; + seq_validator_t v; + seq_validator_init(&v, expected, 3); + TEST_ASSERT_FALSE(seq_validator_feed(&v, "qr3")); // 1/3 + TEST_ASSERT_FALSE(seq_validator_feed(&v, "qr1")); // 2/3 + TEST_ASSERT_TRUE (seq_validator_feed(&v, "qr4")); // complete +} + +TEST_CASE("seq resets on a wrong scan", "[seq]") +{ + const char *expected[] = {"a", "b"}; + seq_validator_t v; + seq_validator_init(&v, expected, 2); + TEST_ASSERT_FALSE(seq_validator_feed(&v, "a")); // 1/2 + TEST_ASSERT_FALSE(seq_validator_feed(&v, "x")); // wrong -> reset + TEST_ASSERT_FALSE(seq_validator_feed(&v, "a")); // 1/2 again + TEST_ASSERT_TRUE (seq_validator_feed(&v, "b")); // complete +} + +TEST_CASE("seq ignores a duplicate of the last correct scan", "[seq]") +{ + const char *expected[] = {"a", "b"}; + seq_validator_t v; + seq_validator_init(&v, expected, 2); + TEST_ASSERT_FALSE(seq_validator_feed(&v, "a")); + TEST_ASSERT_FALSE(seq_validator_feed(&v, "a")); // duplicate, no progress, no reset + TEST_ASSERT_TRUE (seq_validator_feed(&v, "b")); +} + +TEST_CASE("melody accepts the exact expected note sequence", "[melody]") +{ + const int expected[] = {60, 62, 64, 65}; // do re mi fa + melody_validator_t m; + melody_validator_init(&m, expected, 4, /*tolerance=*/1); + TEST_ASSERT_FALSE(melody_validator_feed(&m, 60)); + TEST_ASSERT_FALSE(melody_validator_feed(&m, 62)); + TEST_ASSERT_FALSE(melody_validator_feed(&m, 64)); + TEST_ASSERT_TRUE (melody_validator_feed(&m, 65)); +} + +TEST_CASE("melody accepts notes within tolerance", "[melody]") +{ + const int expected[] = {60, 62}; + melody_validator_t m; + melody_validator_init(&m, expected, 2, 1); + TEST_ASSERT_FALSE(melody_validator_feed(&m, 61)); // 60 +/-1 ok + TEST_ASSERT_TRUE (melody_validator_feed(&m, 62)); +} + +TEST_CASE("melody resets on an out-of-tolerance note", "[melody]") +{ + const int expected[] = {60, 62}; + melody_validator_t m; + melody_validator_init(&m, expected, 2, 1); + TEST_ASSERT_FALSE(melody_validator_feed(&m, 60)); + TEST_ASSERT_FALSE(melody_validator_feed(&m, 70)); // way off -> reset + TEST_ASSERT_FALSE(melody_validator_feed(&m, 60)); + TEST_ASSERT_TRUE (melody_validator_feed(&m, 62)); +} diff --git a/idf_zacus/components/ota_server/ota_server.c b/idf_zacus/components/ota_server/ota_server.c index aeae175..0754dd2 100644 --- a/idf_zacus/components/ota_server/ota_server.c +++ b/idf_zacus/components/ota_server/ota_server.c @@ -310,7 +310,9 @@ esp_err_t ota_server_init(void) { // HTTP server config httpd_config_t config = HTTPD_DEFAULT_CONFIG(); config.server_port = OTA_SERVER_PORT; - config.max_uri_handlers = 16; // ota (3) + voice_hook (2) + game (3) + headroom + config.max_uri_handlers = 20; // ota + voice_hook + game (incl. /game/step, + // /game/puzzle_state, /game/file, relay) + // + headroom config.uri_match_fn = httpd_uri_match_wildcard; config.stack_size = 8192; diff --git a/idf_zacus/components/puzzle_state/CMakeLists.txt b/idf_zacus/components/puzzle_state/CMakeLists.txt new file mode 100644 index 0000000..0174c3a --- /dev/null +++ b/idf_zacus/components/puzzle_state/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + SRCS "puzzle_state.c" + INCLUDE_DIRS "include" +) diff --git a/idf_zacus/components/puzzle_state/README.md b/idf_zacus/components/puzzle_state/README.md new file mode 100644 index 0000000..c254cd9 --- /dev/null +++ b/idf_zacus/components/puzzle_state/README.md @@ -0,0 +1,30 @@ +# puzzle_state + +Agrégation côté master des énigmes résolues. Chaque énigme reporte un fragment +de code ; `puzzle_state` assemble le **code final** en concaténant les fragments +de toutes les énigmes résolues, **par id croissant**. + +Logique pure, sans I/O — testée sur hôte. + +## API (`puzzle_state.h`) + +- **`puzzle_state_init(s)`** — remet l'état à zéro. +- **`puzzle_state_report(s, id, fragment, len)`** — enregistre une énigme + résolue. **Idempotent par id** : reporter deux fois le même id ne duplique pas + ses chiffres. `len <= PUZZLE_MAX_FRAG` ; `fragment` non-NULL si `len > 0`. Un + fragment de `len == 0` est valide (énigme marquée résolue, aucun chiffre). +- **`puzzle_state_code(s, out, cap)`** — écrit le code assemblé (chiffres de + toutes les énigmes résolues, id croissant) en chaîne NUL-terminée ; renvoie le + nombre de chiffres écrits. Chaque octet de fragment est pris **modulo 10** pour + donner un chiffre décimal. `cap == 0` n'écrit rien et renvoie 0. + +Constantes : `PUZZLE_MAX_ID` = 8, `PUZZLE_MAX_FRAG` = 4. + +## Tests hôte + +```bash +make -C idf_zacus/components/puzzle_state/test/host test +``` + +2 tests : assemblage du code à partir des fragments reportés, et idempotence +(une énigme reportée deux fois ne duplique pas ses chiffres). Nécessite Unity. diff --git a/idf_zacus/components/puzzle_state/include/puzzle_state.h b/idf_zacus/components/puzzle_state/include/puzzle_state.h new file mode 100644 index 0000000..bfda3a3 --- /dev/null +++ b/idf_zacus/components/puzzle_state/include/puzzle_state.h @@ -0,0 +1,28 @@ +// puzzle_state.h — master-side aggregation of solved puzzles + assembled code. +#pragma once +#include +#include +#include + +#define PUZZLE_MAX_ID 8 +#define PUZZLE_MAX_FRAG 4 + +typedef struct { + bool solved[PUZZLE_MAX_ID + 1]; + uint8_t frag[PUZZLE_MAX_ID + 1][PUZZLE_MAX_FRAG]; + uint8_t frag_len[PUZZLE_MAX_ID + 1]; +} puzzle_state_t; + +void puzzle_state_init(puzzle_state_t *s); + +// Record a solved puzzle. Idempotent per id. `len` <= PUZZLE_MAX_FRAG. +// `fragment` pointer must be non-NULL when len > 0. No internal NULL checks. +// A len == 0 fragment is valid — the puzzle is marked solved and contributes no digits. +void puzzle_state_report(puzzle_state_t *s, uint8_t id, + const uint8_t *fragment, uint8_t len); + +// Write the assembled code (digits of all solved puzzles, by ascending id) +// as a NUL-terminated string. Returns the number of digits written. +// Each fragment byte is taken modulo 10 to produce a decimal digit. +// If cap == 0, nothing is written and 0 is returned. +int puzzle_state_code(const puzzle_state_t *s, char *out, size_t cap); diff --git a/idf_zacus/components/puzzle_state/puzzle_state.c b/idf_zacus/components/puzzle_state/puzzle_state.c new file mode 100644 index 0000000..205ab7f --- /dev/null +++ b/idf_zacus/components/puzzle_state/puzzle_state.c @@ -0,0 +1,29 @@ +// puzzle_state.c +#include "puzzle_state.h" +#include + +void puzzle_state_init(puzzle_state_t *s) { memset(s, 0, sizeof(*s)); } + +void puzzle_state_report(puzzle_state_t *s, uint8_t id, + const uint8_t *fragment, uint8_t len) +{ + if (id == 0 || id > PUZZLE_MAX_ID) return; + if (len > PUZZLE_MAX_FRAG) len = PUZZLE_MAX_FRAG; + if (s->solved[id]) return; // idempotent + s->solved[id] = true; + s->frag_len[id] = len; + memcpy(s->frag[id], fragment, len); +} + +int puzzle_state_code(const puzzle_state_t *s, char *out, size_t cap) +{ + if (cap == 0) return 0; + int n = 0; + for (uint8_t id = 1; id <= PUZZLE_MAX_ID; id++) { + if (!s->solved[id]) continue; + for (uint8_t k = 0; k < s->frag_len[id] && (size_t)(n + 1) < cap; k++) + out[n++] = (char)('0' + (s->frag[id][k] % 10)); + } + if ((size_t)n < cap) out[n] = '\0'; + return n; +} diff --git a/idf_zacus/components/puzzle_state/test/host/.gitignore b/idf_zacus/components/puzzle_state/test/host/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/idf_zacus/components/puzzle_state/test/host/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/idf_zacus/components/puzzle_state/test/host/Makefile b/idf_zacus/components/puzzle_state/test/host/Makefile new file mode 100644 index 0000000..25f2491 --- /dev/null +++ b/idf_zacus/components/puzzle_state/test/host/Makefile @@ -0,0 +1,63 @@ +# Host test harness Makefile — covers puzzle_state aggregation logic +UNITY_DIR ?= $(HOME)/esp/esp-idf/components/unity/unity/src +CC ?= cc + +# Source paths +COMPONENT_DIR := ../.. +TEST_DIR := .. + +# Build output +BUILD_DIR := build + +# Test executable +TEST_BIN := $(BUILD_DIR)/test_runner + +# Component + harness sources +SRCS := \ + $(COMPONENT_DIR)/puzzle_state.c \ + $(UNITY_DIR)/unity.c \ + ../../../local_puzzles/test/host/host_runner.c + +# Canonical IDF-style test file (single source of truth for assertions) +TEST_SRCS := \ + $(TEST_DIR)/test_puzzle_state.c + +ALL_SRCS := $(SRCS) $(TEST_SRCS) + +# Include directories +INCS := \ + -I$(COMPONENT_DIR)/include \ + -I$(COMPONENT_DIR) \ + -I$(TEST_DIR)/host \ + -I../../../local_puzzles/test/host \ + -I$(UNITY_DIR) + +# Compiler flags +CFLAGS := -std=c11 -Wall -Wextra -Werror +CFLAGS += $(INCS) +CFLAGS += -DUNITY_INCLUDE_CONFIG_H + +# Inject the TEST_CASE shim before each IDF-style test source +TEST_CFLAGS := $(CFLAGS) -include ../../../local_puzzles/test/host/unity_test_case.h + +.PHONY: all test clean + +all: $(TEST_BIN) + +$(BUILD_DIR): + mkdir -p $(BUILD_DIR) + $(if $(wildcard $(UNITY_DIR)/unity.c),,$(error UNITY_DIR not found: $(UNITY_DIR) — set UNITY_DIR=...)) + +# Compile test sources with the shim injected, then link everything in one shot +$(TEST_BIN): $(ALL_SRCS) | $(BUILD_DIR) + $(if $(wildcard $(UNITY_DIR)/unity.c),,$(error UNITY_DIR not found: $(UNITY_DIR) — set UNITY_DIR=...)) + $(CC) $(TEST_CFLAGS) -c -o $(BUILD_DIR)/test_puzzle_state.o $(TEST_DIR)/test_puzzle_state.c + $(CC) $(CFLAGS) -o $@ $(SRCS) $(BUILD_DIR)/test_puzzle_state.o + +test: $(TEST_BIN) + ./$(TEST_BIN) + +clean: + rm -rf $(BUILD_DIR) + +.PRECIOUS: $(TEST_BIN) diff --git a/idf_zacus/components/puzzle_state/test/test_puzzle_state.c b/idf_zacus/components/puzzle_state/test/test_puzzle_state.c new file mode 100644 index 0000000..a1e5b2c --- /dev/null +++ b/idf_zacus/components/puzzle_state/test/test_puzzle_state.c @@ -0,0 +1,24 @@ +#include "unity.h" +#include "puzzle_state.h" + +TEST_CASE("assembles the code from reported fragments", "[pstate]") +{ + puzzle_state_t s; + puzzle_state_init(&s); + puzzle_state_report(&s, /*id=*/1, (const uint8_t[]){1,2,0,0}, 2); + puzzle_state_report(&s, /*id=*/3, (const uint8_t[]){3,4,0,0}, 2); + char code[16]; + TEST_ASSERT_EQUAL_INT(4, puzzle_state_code(&s, code, sizeof(code))); + TEST_ASSERT_EQUAL_STRING("1234", code); +} + +TEST_CASE("a puzzle reported twice does not duplicate its digits", "[pstate]") +{ + puzzle_state_t s; + puzzle_state_init(&s); + puzzle_state_report(&s, 1, (const uint8_t[]){1,2,0,0}, 2); + puzzle_state_report(&s, 1, (const uint8_t[]){1,2,0,0}, 2); + char code[16]; + TEST_ASSERT_EQUAL_INT(2, puzzle_state_code(&s, code, sizeof(code))); + TEST_ASSERT_EQUAL_STRING("12", code); +} diff --git a/idf_zacus/components/voice_pipeline/CMakeLists.txt b/idf_zacus/components/voice_pipeline/CMakeLists.txt index 4f30fa0..aec1a79 100644 --- a/idf_zacus/components/voice_pipeline/CMakeLists.txt +++ b/idf_zacus/components/voice_pipeline/CMakeLists.txt @@ -8,6 +8,7 @@ idf_component_register( PRIV_INCLUDE_DIRS "." REQUIRES + local_puzzles esp_driver_i2s esp_timer esp_system diff --git a/idf_zacus/components/voice_pipeline/voice_pipeline.c b/idf_zacus/components/voice_pipeline/voice_pipeline.c index 7a220b3..64fee7d 100644 --- a/idf_zacus/components/voice_pipeline/voice_pipeline.c +++ b/idf_zacus/components/voice_pipeline/voice_pipeline.c @@ -3,6 +3,12 @@ // `cfg.enable_wake_word`; if init fails (PSRAM exhausted, model // partition absent, etc.) we log + degrade silently to the slice-5 // I2S-only capture path so the rest of the firmware still boots. +// +// Slice (task 4): I2S RX ownership transferred to mic_broker. The +// voice_pipeline registers on_npc_frame() for MIC_NPC_LISTEN and the broker +// delivers PCM16 mono 320-sample frames; voice_pipeline accumulates them +// into the AFE feed buffer (or counts heartbeat bytes in stub mode). +// The I2S TX path (speaker / MAX98357A on I2S_NUM_1) is unchanged. #include "voice_pipeline.h" #include "voice_pipeline_ws.h" @@ -23,6 +29,8 @@ #include "esp_wn_models.h" #include "model_path.h" +#include "mic_broker.h" + static const char *TAG = "voice_pipeline"; // Slice 6 placeholder wake word. Standard Espressif WakeNet9 model @@ -50,15 +58,15 @@ static const char *TAG = "voice_pipeline"; static struct { bool ready; voice_pipeline_config_t cfg; - i2s_chan_handle_t rx_chan; + // rx_chan removed: I2S RX is now owned by mic_broker (task 4). // Slice 9: I2S TX channel for TTS playback (MAX98357A DAC). NULL // if `enable_tts_playback == false` or alloc/init failed. i2s_chan_handle_t tx_chan; bool tx_enabled; // channel currently enabled uint32_t tx_sample_rate; // current configured rate voice_state_t state; - TaskHandle_t capture_task; - bool capture_run; + // capture_task / capture_run removed: mic_broker's task delivers frames + // via on_npc_frame(); broker mode (MIC_NPC_LISTEN/MIC_IDLE) gates it. // Wake-word callback (set independently of init, may be NULL). voice_wake_callback_t wake_cb; @@ -75,13 +83,18 @@ static struct { int afe_fetch_chunk_samples; // post-AFE, what we stream // Slice 7: streaming state. `stream_active` mirrors voice_ws_is_streaming - // but is owned by the capture task so we don't race with the WS event - // loop on transitions. `silence_chunks` counts sustained AFE_VAD_SILENCE - // fetches; reaching VAD_SILENCE_CHUNKS_TO_END closes the upload. + // but is owned by the on_npc_frame callback so we don't race with the WS + // event loop on transitions. `silence_chunks` counts sustained + // AFE_VAD_SILENCE fetches; reaching END_SILENCE_CHUNKS closes the upload. bool stream_active; uint32_t silence_chunks; uint32_t streamed_chunks; char session_id[32]; + + // AFE feed accumulator: broker delivers 320-sample chunks; the AFE may + // require a larger feed_chunk_samples. We accumulate here before feeding. + int16_t *afe_accum; // PSRAM alloc; size = feed_bytes + size_t afe_accum_fill; // samples already buffered } s_pipe = { .state = VOICE_STATE_IDLE, }; @@ -186,36 +199,8 @@ esp_err_t voice_pipeline_stop_streaming(void) { return ESP_OK; } -static esp_err_t i2s_setup(void) { - i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER); - esp_err_t err = i2s_new_channel(&chan_cfg, NULL, &s_pipe.rx_chan); - if (err != ESP_OK) return err; - - i2s_std_config_t std_cfg = { - .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(s_pipe.cfg.sample_rate_hz), - .slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, - I2S_SLOT_MODE_MONO), - .gpio_cfg = { - .mclk = I2S_GPIO_UNUSED, - .bclk = s_pipe.cfg.i2s_bclk_pin, - .ws = s_pipe.cfg.i2s_ws_pin, - .dout = I2S_GPIO_UNUSED, - .din = s_pipe.cfg.i2s_din_pin, - .invert_flags = { - .mclk_inv = false, - .bclk_inv = false, - .ws_inv = false, - }, - }, - }; - err = i2s_channel_init_std_mode(s_pipe.rx_chan, &std_cfg); - if (err != ESP_OK) { - i2s_del_channel(s_pipe.rx_chan); - s_pipe.rx_chan = NULL; - return err; - } - return ESP_OK; -} +// i2s_setup() removed: I2S RX is now owned by mic_broker (task 4). +// mic_broker_init() is called from voice_pipeline_init() instead. // Slice 9: bring up the I2S TX channel for TTS playback on a separate // I2S port (I2S_NUM_1) so the mic capture on I2S_NUM_0 keeps running @@ -343,59 +328,49 @@ static void wake_word_teardown(void) { } } -// Capture task. Two modes: -// * AFE active (esp-sr loaded) : feed I2S into AFE, fetch results, -// detect wake → fire callback, -// stream post-AFE PCM until VAD silence. -// * AFE inactive (slice-5 stub) : log a heartbeat every ~1.6 s. -static void capture_task(void *pv) { - if (voice_pipeline_wake_word_active()) { - const int chunk_samples = s_pipe.afe_feed_chunk_samples; - const int chunk_channels = s_pipe.afe_feed_channel_num; - const size_t feed_bytes = (size_t) chunk_samples * chunk_channels * sizeof(int16_t); +// NPC mic callback — invoked by mic_broker's capture task for every +// MB_FRAME-sample (320 samples = 20 ms) frame while MIC_NPC_LISTEN is active. +// +// Two paths: +// * AFE active : accumulate frames into s_pipe.afe_accum until we have a +// full afe_feed_chunk_samples worth, then feed+fetch+react. +// * Stub (no esp-sr): count bytes for heartbeat logging. +// +// Called from mic_broker's task context (stack 4096); must not block long. +static void on_npc_frame(const int16_t *pcm, size_t samples, void *ctx) { + (void)ctx; - int16_t *feed_buf = heap_caps_malloc(feed_bytes, - MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); - if (!feed_buf) { - ESP_LOGE(TAG, "PSRAM alloc for AFE feed buffer failed (%u B) — " - "stopping capture", (unsigned) feed_bytes); - s_pipe.capture_task = NULL; - vTaskDelete(NULL); + if (voice_pipeline_wake_word_active()) { + // ── AFE path ───────────────────────────────────────────────────── + const int need = s_pipe.afe_feed_chunk_samples * s_pipe.afe_feed_channel_num; + if (!s_pipe.afe_accum) return; // alloc failed at init; silently skip + + // Slice 9: mute-during-TTS gate — drain the mic but do not feed AFE. + if (s_pipe.state == VOICE_STATE_SPEAKING) { + s_pipe.afe_accum_fill = 0; // also reset accumulator on mute return; } - ESP_LOGI(TAG, "capture_task: AFE mode — chunk=%d samples × %d ch (%u B)", - chunk_samples, chunk_channels, (unsigned) feed_bytes); + size_t copied = 0; + while (copied < samples) { + size_t space = (size_t)need - s_pipe.afe_accum_fill; + size_t take = samples - copied; + if (take > space) take = space; + memcpy(s_pipe.afe_accum + s_pipe.afe_accum_fill, pcm + copied, + take * sizeof(int16_t)); + s_pipe.afe_accum_fill += take; + copied += take; - size_t bytes_read = 0; - uint32_t feeds = 0; - while (s_pipe.capture_run) { - // INMP441 mono I2S: read directly into the feed buffer (1 ch). - // If chunk_channels > 1 (e.g. with reference), this would need - // interleaving — slice-6 single-mic path keeps it simple. - esp_err_t err = i2s_channel_read(s_pipe.rx_chan, feed_buf, - feed_bytes, &bytes_read, - pdMS_TO_TICKS(200)); - if (err == ESP_ERR_TIMEOUT) { - continue; - } - if (err != ESP_OK) { - ESP_LOGW(TAG, "i2s read err: %s", esp_err_to_name(err)); - vTaskDelay(pdMS_TO_TICKS(200)); - continue; - } - if (bytes_read == 0) continue; + if ((int)s_pipe.afe_accum_fill < need) break; // not full yet - // Slice 9: mute-during-TTS gate. While the pipeline is in - // SPEAKING state we keep draining I2S so the DMA buffers - // don't overflow, but we do NOT feed AFE — this prevents - // the speaker output (which leaks back into the mic) from - // re-triggering the wake word during a TTS reply. - if (s_pipe.state == VOICE_STATE_SPEAKING) continue; + // Buffer full — feed AFE. + // INMP441 single-mic: afe_feed_channel_num == 1, so accum IS + // the feed buffer directly (no interleaving needed). + s_pipe.afe_iface->feed(s_pipe.afe_data, s_pipe.afe_accum); + s_pipe.afe_accum_fill = 0; - s_pipe.afe_iface->feed(s_pipe.afe_data, feed_buf); - - // Drain anything available without blocking the feed cadence. + // Fetch result (non-blocking). + static uint32_t s_feeds = 0; afe_fetch_result_t *res = s_pipe.afe_iface->fetch_with_delay( s_pipe.afe_data, 0); if (res && res->ret_value == ESP_OK) { @@ -407,8 +382,8 @@ static void capture_task(void *pv) { if (s_pipe.wake_cb) { s_pipe.wake_cb(word, s_pipe.wake_cb_ctx); } - // Slice 7: open the WS stream as soon as the wake - // fires so the player's first words make it across. + // Slice 7: open WS stream immediately on wake so + // the player's first words are captured. if (voice_ws_is_configured() && !s_pipe.stream_active) { if (streaming_begin() != ESP_OK) { ESP_LOGW(TAG, "streaming_begin failed at wake — " @@ -418,10 +393,7 @@ static void capture_task(void *pv) { } } - // Slice 7: while streaming, push the post-AFE PCM out - // and watch the VAD for end-of-speech. `res->data` is - // the cleaned, single-channel int16 buffer of length - // `afe_fetch_chunk_samples`. + // Slice 7: push post-AFE PCM and watch VAD. if (s_pipe.stream_active && res->data && res->data_size > 0) { esp_err_t serr = voice_ws_send_chunk( res->data, res->data_size / sizeof(int16_t)); @@ -431,16 +403,12 @@ static void capture_task(void *pv) { streaming_end("send_error"); } else { s_pipe.streamed_chunks++; - - // res->vad_state is a `vad_state_t` (VAD_SILENCE = 0, - // VAD_SPEECH = 1). The legacy `AFE_VAD_*` enum is - // marked deprecated in esp_afe_sr_iface.h. + // res->vad_state: VAD_SILENCE=0, VAD_SPEECH=1 if (res->vad_state == VAD_SILENCE) { s_pipe.silence_chunks++; } else { s_pipe.silence_chunks = 0; } - if (s_pipe.silence_chunks >= END_SILENCE_CHUNKS) { streaming_end("vad_silence"); } else if (s_pipe.streamed_chunks >= STREAMING_MAX_CHUNKS) { @@ -450,39 +418,19 @@ static void capture_task(void *pv) { } } - if (++feeds % 100 == 0) { - ESP_LOGD(TAG, "AFE feed heartbeat: %u chunks", (unsigned) feeds); + if (++s_feeds % 100 == 0) { + ESP_LOGD(TAG, "AFE feed heartbeat: %u chunks", (unsigned)s_feeds); } } - // Make sure we don't leak an open WS if capture is being torn down. - if (s_pipe.stream_active) { - streaming_end("capture_stop"); - } - free(feed_buf); } else { - // Slice-5 fallback: dumb capture, no detection. - static uint8_t buf[CAPTURE_CHUNK_BYTES]; - size_t bytes_read = 0; - uint32_t total = 0; - uint32_t ticks = 0; - ESP_LOGI(TAG, "capture_task: stub mode (no esp-sr)"); - while (s_pipe.capture_run) { - esp_err_t err = i2s_channel_read(s_pipe.rx_chan, buf, sizeof(buf), - &bytes_read, pdMS_TO_TICKS(100)); - if (err == ESP_OK) { - total += bytes_read; - if (++ticks % 50 == 0) { - ESP_LOGI(TAG, "capture heartbeat: %u bytes total", - (unsigned) total); - } - } else if (err != ESP_ERR_TIMEOUT) { - ESP_LOGW(TAG, "i2s read err: %s", esp_err_to_name(err)); - vTaskDelay(pdMS_TO_TICKS(200)); - } + // ── Stub path (no esp-sr) ───────────────────────────────────────── + static uint32_t s_total = 0; + static uint32_t s_ticks = 0; + s_total += (uint32_t)(samples * sizeof(int16_t)); + if (++s_ticks % 80 == 0) { // ~80 × 20 ms ≈ 1.6 s + ESP_LOGI(TAG, "capture heartbeat: %u bytes total", (unsigned)s_total); } } - s_pipe.capture_task = NULL; - vTaskDelete(NULL); } esp_err_t voice_pipeline_init(const voice_pipeline_config_t *config) { @@ -497,16 +445,20 @@ esp_err_t voice_pipeline_init(const voice_pipeline_config_t *config) { session_id_init(); - esp_err_t err = i2s_setup(); - if (err != ESP_OK) { - ESP_LOGW(TAG, "i2s setup failed: %s — staying idle without capture", + // Bring up mic_broker (single I2S RX owner). + // ESP_ERR_INVALID_STATE means the broker is already running (another + // caller initialised it first) — treat as success. + esp_err_t err = mic_broker_init(cfg.i2s_bclk_pin, cfg.i2s_ws_pin, + cfg.i2s_din_pin, 16000); + if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) { + ESP_LOGW(TAG, "mic_broker_init failed: %s — staying idle without capture", esp_err_to_name(err)); - // Don't fail init: we still want the state machine to be usable so - // the rest of the system (npc_engine, REST surface) can wire calls. + // Don't fail init: state machine stays usable. s_pipe.ready = true; s_pipe.state = VOICE_STATE_IDLE; return ESP_OK; } + mic_broker_register(MIC_NPC_LISTEN, on_npc_frame, &s_pipe); // Slice 9: optional I2S TX bring-up for TTS playback. Failure is // non-fatal — the rest of the voice loop still works. @@ -528,8 +480,29 @@ esp_err_t voice_pipeline_init(const voice_pipeline_config_t *config) { ESP_LOGW(TAG, "wake_word_setup failed: %s — degrading to stub capture", esp_err_to_name(we)); // Don't fail init — the rest of the firmware should still - // come up. The capture task will run in slice-5 stub mode. + // come up. The callback will run in slice-5 stub mode. wake_word_teardown(); + } else { + // Broker delivers linear mono; AFE must be configured for 1 channel. + if (s_pipe.afe_feed_channel_num != 1) { + ESP_LOGE(TAG, "AFE feed_channel_num=%d but broker delivers mono (1) — " + "configuration mismatch", s_pipe.afe_feed_channel_num); + wake_word_teardown(); + return ESP_ERR_NOT_SUPPORTED; + } + // Allocate the AFE accumulator now that we know chunk sizes. + size_t feed_bytes = (size_t)s_pipe.afe_feed_chunk_samples + * s_pipe.afe_feed_channel_num * sizeof(int16_t); + s_pipe.afe_accum = heap_caps_malloc(feed_bytes, + MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (!s_pipe.afe_accum) { + ESP_LOGW(TAG, "PSRAM alloc for AFE accum failed (%u B) — " + "degrading to stub capture", (unsigned)feed_bytes); + wake_word_teardown(); + } else { + s_pipe.afe_accum_fill = 0; + ESP_LOGI(TAG, "AFE accum alloc OK (%u B)", (unsigned)feed_bytes); + } } } @@ -565,28 +538,21 @@ esp_err_t voice_pipeline_init(const voice_pipeline_config_t *config) { esp_err_t voice_pipeline_start_capture(void) { if (!s_pipe.ready) return ESP_ERR_INVALID_STATE; - if (!s_pipe.rx_chan) return ESP_ERR_INVALID_STATE; - if (s_pipe.capture_task) return ESP_OK; // already running - esp_err_t err = i2s_channel_enable(s_pipe.rx_chan); - if (err != ESP_OK) return err; - s_pipe.capture_run = true; - if (xTaskCreate(capture_task, "voice_capture", CAPTURE_TASK_STACK, NULL, - CAPTURE_TASK_PRIO, &s_pipe.capture_task) != pdPASS) { - s_pipe.capture_run = false; - i2s_channel_disable(s_pipe.rx_chan); - return ESP_ERR_NO_MEM; - } + // Reset stale partial accumulator from a previous stop before new session. + s_pipe.afe_accum_fill = 0; + // Route mic frames to the NPC callback via the broker. + mic_broker_set_mode(MIC_NPC_LISTEN); voice_pipeline_set_state(VOICE_STATE_LISTENING); return ESP_OK; } esp_err_t voice_pipeline_stop_capture(void) { if (!s_pipe.ready) return ESP_ERR_INVALID_STATE; - if (!s_pipe.capture_task) return ESP_OK; - s_pipe.capture_run = false; - // Task observes the flag and self-deletes on its next tick. - if (s_pipe.rx_chan) { - i2s_channel_disable(s_pipe.rx_chan); + // Stop delivery to the NPC callback. + mic_broker_set_mode(MIC_IDLE); + // Close any in-flight stream so we don't leak a WS connection. + if (s_pipe.stream_active) { + streaming_end("capture_stop"); } voice_pipeline_set_state(VOICE_STATE_IDLE); return ESP_OK; diff --git a/idf_zacus/main/CMakeLists.txt b/idf_zacus/main/CMakeLists.txt index 4af2a99..0d9a88c 100644 --- a/idf_zacus/main/CMakeLists.txt +++ b/idf_zacus/main/CMakeLists.txt @@ -13,6 +13,7 @@ idf_component_register( voice_hook_endpoint game_endpoint scenario_mesh + local_puzzles nvs_flash esp_timer esp_system @@ -20,4 +21,6 @@ idf_component_register( esp_netif esp_event espressif__mdns + display_ui + puzzle_state ) diff --git a/idf_zacus/main/main.c b/idf_zacus/main/main.c index d0f21c7..519446e 100644 --- a/idf_zacus/main/main.c +++ b/idf_zacus/main/main.c @@ -47,6 +47,13 @@ #include "voice_hook_endpoint.h" #include "game_endpoint.h" #include "scenario_mesh.h" +#include "puzzle_state.h" +#include "local_puzzles.h" +#include "qr_puzzle.h" +#include "buttons_input.h" +#include "mic_broker.h" +#include "board_pins_mediakit.h" +#include "display_ui.h" // Hints engine endpoint (slice 5). Hardcoded for now — slice 7 will move // this to NVS so the field operator can repoint the firmware without a flash. @@ -60,6 +67,9 @@ static const char *TAG = "zacus_main"; +// ─── Puzzle aggregation state ───────────────────────────────────────────────── +static puzzle_state_t s_pstate; + // Soft-AP fallback when no creds in NVS yet. #define ZACUS_FALLBACK_AP_SSID "zacus-setup" #define ZACUS_FALLBACK_AP_CHAN 6 @@ -79,7 +89,7 @@ int puzzle_get_battery_pct(void) { } int puzzle_get_espnow_peer_count(void) { - return 0; + return scenario_mesh_peer_count(); } // Slice 6: wake-word callback. Runs on the voice_pipeline capture task, @@ -402,6 +412,22 @@ void app_main(void) { ESP_ERROR_CHECK(nvs_err); ESP_LOGI(TAG, "NVS initialized"); + // Phase 1 display: bring up the ST7796 TFT early so the splash is visible + // while the rest of the subsystems initialise. Non-fatal on failure. + esp_err_t disp_err = display_ui_init(); + if (disp_err != ESP_OK) { + ESP_LOGW(TAG, "display_ui_init: %s (continuing without display)", + esp_err_to_name(disp_err)); + } else { + // Phase 3: mirror QR camera frames to the scene-view viewfinder. + qr_puzzle_set_preview_cb(display_ui_camera_frame); + // 5-way buttons (view toggle + brightness). Non-fatal. + esp_err_t btn_err = buttons_input_init(); + if (btn_err != ESP_OK) { + ESP_LOGW(TAG, "buttons_input_init: %s", esp_err_to_name(btn_err)); + } + } + bool sta_ok = wifi_bring_up(); ESP_LOGI(TAG, "Wi-Fi up (mode=%s)", sta_ok ? "STA" : "AP-fallback"); @@ -448,6 +474,13 @@ void app_main(void) { // relay peer registry from NVS so /game/scenario/relay can resolve // aliases to MACs without a reflash. seed_relay_peers_from_nvs(); + // Task C: give the endpoint access to the master puzzle state so + // POST /game/step and GET /game/puzzle_state become live. + // Called here so s_pstate is valid (puzzle_state_init runs below). + // game_endpoint_set_puzzle_state tolerates being called before + // puzzle_state_init — it only stores the pointer; actual reads + // happen on-demand in the httpd task after boot completes. + game_endpoint_set_puzzle_state(&s_pstate); } } @@ -541,6 +574,30 @@ void app_main(void) { esp_err_to_name(disp_err)); } + // Task 7: init puzzle aggregation + local puzzle wiring. + puzzle_state_init(&s_pstate); + local_puzzles_init(&s_pstate); + ESP_LOGI(TAG, "puzzle_state + local_puzzles initialised"); + + // Task 7: mic_broker takes ownership of the Media Kit I2S IN + // pins (3/14/46 per board_pins_mediakit.h) BEFORE + // voice_pipeline_init — the pipeline's own init call then + // no-ops with ESP_ERR_INVALID_STATE, which it tolerates. + // Note (a): a HARD failure here (anything other than + // ESP_ERR_INVALID_STATE) will surface again inside + // voice_pipeline_init — same root cause, two log warnings. + // Note (b): the readout path is also deferred — nothing + // consumes puzzle_state_code yet; that lands with the + // scenario hook slice. + esp_err_t mic_err = mic_broker_init(MIC_PIN_BCLK, MIC_PIN_WS, + MIC_PIN_DIN, 16000); + if (mic_err != ESP_OK && mic_err != ESP_ERR_INVALID_STATE) { + ESP_LOGW(TAG, "mic_broker_init: %s", esp_err_to_name(mic_err)); + } else { + ESP_LOGI(TAG, "mic_broker initialised (bclk=%d ws=%d din=%d 16kHz)", + MIC_PIN_BCLK, MIC_PIN_WS, MIC_PIN_DIN); + } + voice_pipeline_config_t voice_cfg; voice_pipeline_default_config(&voice_cfg); // Slice 6: bring up esp-sr AFE + WakeNet (placeholder @@ -558,6 +615,16 @@ void app_main(void) { // sessions, but "hi esp" remains a backup if the phone is // unplugged or its hook switch fails. voice_cfg.enable_tts_playback = true; + // Task 7 / Task 0 pin alignment: override voice_pipeline + // defaults (mic 14/15/22, spk 11/12/13) which collide with + // the FNK0102H camera pins. Use Media Kit values from + // board_pins_mediakit.h (mic: 3/14/46, spk: 42/41/1). + voice_cfg.i2s_bclk_pin = MIC_PIN_BCLK; // 3 + voice_cfg.i2s_ws_pin = MIC_PIN_WS; // 14 + voice_cfg.i2s_din_pin = MIC_PIN_DIN; // 46 + voice_cfg.i2s_out_bclk_pin = SPK_PIN_BCLK; // 42 + voice_cfg.i2s_out_lrc_pin = SPK_PIN_LRC; // 41 + voice_cfg.i2s_out_din_pin = SPK_PIN_DIN; // 1 voice_pipeline_set_wake_callback(on_voice_wake, NULL); voice_pipeline_set_stt_callback(on_voice_stt, NULL); esp_err_t voice_err = voice_pipeline_init(&voice_cfg); @@ -571,6 +638,9 @@ void app_main(void) { ESP_LOGW(TAG, "voice: wake-word inactive — running " "in slice-5 stub mode"); } + + // Arming is driven by POST /game/step (game_endpoint); + // readout via GET /game/puzzle_state. } } @@ -580,19 +650,98 @@ void app_main(void) { ota_server_mark_valid(); ESP_LOGI(TAG, "entering idle loop (heartbeat every 60 s)"); - uint32_t tick = 0; + uint32_t tick = 0; + uint32_t disp_sub = 0; // sub-counter: display refresh every 2 s for (;;) { - vTaskDelay(pdMS_TO_TICKS(60000)); - tick++; - const uint32_t uptime_ms = (uint32_t) esp_log_timestamp(); - ESP_LOGI(TAG, "heartbeat #%u — uptime=%llu s", - (unsigned) tick, - (unsigned long long) (uptime_ms / 1000)); - // Drive the slice-3/4 subsystems from the heartbeat. Once we have - // a real game loop these will move to a dedicated FreeRTOS task - // running at ~5 Hz; for now 60 s is enough to keep mood + media - // simulation state coherent without spamming the log. - media_manager_update(uptime_ms); - npc_engine_update(uptime_ms); + // Sleep in 2 s increments so the display stays responsive. + // Every 30 sub-ticks (~60 s) we do the full heartbeat log. + // 500 ms so scene changes (POST /game/step) reach the display fast. + vTaskDelay(pdMS_TO_TICKS(500)); + disp_sub++; + + // ── Display status refresh (every 500 ms) ─────────────────────── + if (disp_err == ESP_OK) { + display_status_t ds; + memset(&ds, 0, sizeof(ds)); + + // IP address (STA only; empty when in AP mode) + esp_netif_t *sta_if = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"); + if (sta_if) { + esp_netif_ip_info_t ip_info; + if (esp_netif_get_ip_info(sta_if, &ip_info) == ESP_OK && + ip_info.ip.addr != 0) { + snprintf(ds.ip, sizeof(ds.ip), IPSTR, + IP2STR(&ip_info.ip)); + } + } + + // Wake-word detector state + ds.wake_active = voice_pipeline_wake_word_active(); + + // Step + armed type from game_endpoint + game_endpoint_get_puzzle_status(ds.step_id, sizeof(ds.step_id), + ds.armed, sizeof(ds.armed)); + + // Solved count + assembled code from puzzle_state + ds.solved_count = 0; + for (int i = 1; i <= PUZZLE_MAX_ID; i++) { + if (s_pstate.solved[i]) { + ds.solved_count++; + } + } + puzzle_state_code(&s_pstate, ds.code, sizeof(ds.code)); + + // Scene metadata (title/subtitle/symbol/effect) of the step. + scene_binding_t sb; + game_endpoint_get_scene(&sb); + if (sb.present) { + memcpy(ds.scene_title, sb.title, sizeof(ds.scene_title)); + memcpy(ds.scene_subtitle, sb.subtitle, sizeof(ds.scene_subtitle)); + memcpy(ds.scene_symbol, sb.symbol, sizeof(ds.scene_symbol)); + } + ds.scene_effect = (uint8_t) sb.effect; + + display_ui_set_status(&ds); + + // Shell app launch (dynamic tiles from /littlefs/apps//): + // the app's step.txt names the scenario step to arm. + char app_id[32]; + if (display_ui_take_pending_launch(app_id, sizeof(app_id))) { + char path[80], step[64] = {0}; + snprintf(path, sizeof(path), "/littlefs/apps/%s/step.txt", + app_id); + FILE *af = fopen(path, "r"); + if (af) { + if (fgets(step, sizeof(step), af)) { + step[strcspn(step, "\r\n")] = '\0'; + } + fclose(af); + } + if (step[0]) { + char armed[8]; + esp_err_t lerr = game_endpoint_apply_step(step, armed, + sizeof(armed)); + ESP_LOGI(TAG, "app '%s' -> step '%s': %s (armed=%s)", + app_id, step, esp_err_to_name(lerr), + (lerr == ESP_OK) ? armed : "-"); + } else { + ESP_LOGW(TAG, "app '%s': no step.txt — nothing to do", + app_id); + } + } + } + + // ── 60 s heartbeat (every 120 × 500 ms sub-ticks) ─────────────── + if (disp_sub >= 120) { + disp_sub = 0; + tick++; + const uint32_t uptime_ms = (uint32_t) esp_log_timestamp(); + ESP_LOGI(TAG, "heartbeat #%u — uptime=%llu s", + (unsigned) tick, + (unsigned long long) (uptime_ms / 1000)); + // Drive the slice-3/4 subsystems from the heartbeat. + media_manager_update(uptime_ms); + npc_engine_update(uptime_ms); + } } } diff --git a/idf_zacus/partitions.csv b/idf_zacus/partitions.csv index 6107bf0..9c45714 100644 --- a/idf_zacus/partitions.csv +++ b/idf_zacus/partitions.csv @@ -5,21 +5,27 @@ # esp-sr component CMake glue). # # Total target: 16 MB flash (Freenove ESP32-S3 N16R8). -# Layout (rough): 0x000000-0x020000 system, 0x020000-0x620000 apps (3×2 MB), -# 0x620000-0x720000 model (SPIFFS 1 MB, esp-sr models), -# 0x720000-0xC20000 storage (LittleFS 5 MB), -# 0xC20000-0x1000000 unused (~3.87 MB headroom). +# Layout (rough): 0x000000-0x020000 system, 0x020000-0x920000 apps (3×3 MB), +# 0x920000-0xA20000 model (SPIFFS 1 MB, esp-sr models), +# 0xA20000-0xF20000 storage (LittleFS 5 MB), +# 0xF20000-0x1000000 unused (~0.87 MB headroom). # # App partition bumped 1.5 MB → 2 MB (2026-05-03) to give voice/STT/hints # slices and esp-sr integration ~25 % free space margin. OTA dual-bank # preserved (factory + ota_0 + ota_1 all 2 MB → rollback symmetry intact). # +# Bumped 2 MB → 3 MB (2026-06-10, display_ui phase 2b): LVGL + LovyanGFX + +# the original UI fonts left only 5 % app headroom; later UI phases (camera +# preview, more fonts, effects) need room. NVS/otadata/phy offsets unchanged +# (Wi-Fi creds survive); ota_0/ota_1/model/storage shift — LittleFS content +# is lost on reflash (auto-reformats; re-push scenario via POST /game/scenario). +# # Name, Type, SubType, Offset, Size, Flags nvs, data, nvs, 0x9000, 0x6000, otadata, data, ota, 0xf000, 0x2000, phy_init, data, phy, 0x11000, 0x1000, -factory, app, factory, 0x20000, 0x200000, -ota_0, app, ota_0, , 0x200000, -ota_1, app, ota_1, , 0x200000, +factory, app, factory, 0x20000, 0x300000, +ota_0, app, ota_0, , 0x300000, +ota_1, app, ota_1, , 0x300000, model, data, spiffs, , 0x100000, storage, data, littlefs,, 0x500000, diff --git a/idf_zacus/sdkconfig.defaults b/idf_zacus/sdkconfig.defaults index dc30aeb..c4d92ae 100644 --- a/idf_zacus/sdkconfig.defaults +++ b/idf_zacus/sdkconfig.defaults @@ -58,3 +58,15 @@ CONFIG_SR_MN_CN_NONE=y CONFIG_SR_MN_EN_NONE=y CONFIG_MODEL_IN_FLASH=y CONFIG_AFE_INTERFACE_V1=y + +# LVGL (display_ui phase 2a) — mirrors original ui_freenove_allinone lv_conf.h: +# depth 16 + swap 0 are lvgl defaults; Montserrat 24/28 enabled. Mem pool 48KB +CONFIG_LV_MEM_SIZE_KILOBYTES=48 +CONFIG_LV_FONT_MONTSERRAT_24=y +CONFIG_LV_FONT_MONTSERRAT_28=y + +# LVGL file access + PNG icons (shell apps from littlefs) +CONFIG_LV_USE_FS_STDIO=y +CONFIG_LV_FS_STDIO_LETTER=76 +CONFIG_LV_FS_STDIO_PATH="/littlefs" +CONFIG_LV_USE_PNG=y diff --git a/lib/scenario_mesh/include/scenario_mesh.h b/lib/scenario_mesh/include/scenario_mesh.h index 2db86d4..abca984 100644 --- a/lib/scenario_mesh/include/scenario_mesh.h +++ b/lib/scenario_mesh/include/scenario_mesh.h @@ -73,6 +73,10 @@ esp_err_t scenario_mesh_register_peer(const char *alias, const uint8_t mac[6]); // ESP_ERR_NOT_FOUND otherwise. esp_err_t scenario_mesh_mac_for_alias(const char *alias, uint8_t mac_out[6]); +// Number of peers currently registered in the live table (broadcast peer +// excluded). Backs the espnow_peers health counter. +int scenario_mesh_peer_count(void); + // Chunk `data` (len bytes) into frames and send them all sequentially to // `dest_mac`, awaiting the per-frame ack. Returns ESP_OK only if every frame // was acked; ESP_ERR_TIMEOUT if any frame ack timed out, or the underlying @@ -81,6 +85,33 @@ esp_err_t scenario_mesh_mac_for_alias(const char *alias, uint8_t mac_out[6]); esp_err_t scenario_mesh_send(const uint8_t dest_mac[6], const char *data, size_t len); +// ─── short text frames: commands & events (spec 2026-06-11) ──────────────── +// +// Single-frame messages riding the same ESP-NOW link as IR transfers. Wire +// compat: header seq=0xFFFF (sentinel) + total=kind — old receivers drop +// them via their `seq >= total` malformed check. Payload: UTF-8 text, +// NUL excluded, <= SCENARIO_MESH_TEXT_MAX bytes. + +#define SCENARIO_MESH_TEXT_CMD 1 // master -> annex: do something +#define SCENARIO_MESH_TEXT_EVT 2 // annex -> master: something happened +#define SCENARIO_MESH_TEXT_MAX 200 + +// kind, sender MAC, NUL-terminated text. Runs on a dedicated worker task +// (never the Wi-Fi callback); keep it reasonably short anyway. +typedef void (*scenario_mesh_text_cb_t)(uint8_t kind, + const uint8_t src_mac[6], + const char *text); + +// Send one CMD/EVT frame (kind = SCENARIO_MESH_TEXT_*). Unicast awaits the +// radio ack (ESP_ERR_TIMEOUT if the peer is silent); broadcast returns once +// transmitted. dest_mac may be the broadcast address FF:FF:FF:FF:FF:FF. +esp_err_t scenario_mesh_send_text(const uint8_t dest_mac[6], uint8_t kind, + const char *text); + +// Install (or replace, NULL to clear) the receive handler for CMD/EVT +// frames. Spawns the dispatch worker on first use. +esp_err_t scenario_mesh_set_text_cb(scenario_mesh_text_cb_t cb); + #ifdef __cplusplus } #endif diff --git a/lib/scenario_mesh/scenario_mesh.c b/lib/scenario_mesh/scenario_mesh.c index 89d10e3..2cbbddf 100644 --- a/lib/scenario_mesh/scenario_mesh.c +++ b/lib/scenario_mesh/scenario_mesh.c @@ -72,6 +72,30 @@ typedef struct { static QueueHandle_t s_apply_queue; +// ─── CMD/EVT text frames (header sentinel seq=0xFFFF, total=kind) ─────────── + +#define MESH_TEXT_SEQ_SENTINEL 0xFFFFu + +typedef struct { + uint8_t kind; + uint8_t src[6]; + char text[SCENARIO_MESH_TEXT_MAX + 1]; +} mesh_text_job_t; + +static scenario_mesh_text_cb_t s_text_cb; +static QueueHandle_t s_text_queue; + +static void text_worker_task(void *arg) { + (void) arg; + mesh_text_job_t job; + for (;;) { + if (xQueueReceive(s_text_queue, &job, portMAX_DELAY) == pdTRUE) { + scenario_mesh_text_cb_t cb = s_text_cb; + if (cb) cb(job.kind, job.src, job.text); + } + } +} + static void apply_worker_task(void *arg) { (void) arg; mesh_apply_job_t job; @@ -93,6 +117,14 @@ static void apply_worker_task(void *arg) { // ─── peer table helpers ───────────────────────────────────────────────────── +int scenario_mesh_peer_count(void) { + int n = 0; + for (int i = 0; i < MESH_PEERS_MAX; i++) { + if (s_peers[i].used) n++; + } + return n; +} + esp_err_t scenario_mesh_register_peer(const char *alias, const uint8_t mac[6]) { if (!alias || !mac) return ESP_ERR_INVALID_ARG; @@ -212,6 +244,22 @@ static void on_recv(const esp_now_recv_info_t *info, uint16_t total = (uint16_t) (data[2] | (data[3] << 8)); const uint8_t *payload = data + SCENARIO_MESH_HEADER_BYTES; int payload_len = len - SCENARIO_MESH_HEADER_BYTES; + + // CMD/EVT text frame: sentinel seq + kind in `total`. Old receivers fall + // through to the malformed check below and drop it silently. + if (seq == MESH_TEXT_SEQ_SENTINEL && + (total == SCENARIO_MESH_TEXT_CMD || total == SCENARIO_MESH_TEXT_EVT)) { + if (!s_text_queue || payload_len > SCENARIO_MESH_TEXT_MAX) return; + mesh_text_job_t job; + job.kind = (uint8_t) total; + memcpy(job.src, info->src_addr, 6); + memcpy(job.text, payload, payload_len); + job.text[payload_len] = '\0'; + BaseType_t hp = pdFALSE; + xQueueSendFromISR(s_text_queue, &job, &hp); + if (hp) portYIELD_FROM_ISR(); + return; + } if (seq >= total) return; // malformed if (!s_reasm_lock) return; @@ -387,3 +435,67 @@ esp_err_t scenario_mesh_send(const uint8_t dest_mac[6], } return result; } + +// ─── CMD/EVT text API ─────────────────────────────────────────────────────── + +esp_err_t scenario_mesh_set_text_cb(scenario_mesh_text_cb_t cb) { + if (!s_text_queue) { + s_text_queue = xQueueCreate(8, sizeof(mesh_text_job_t)); + if (!s_text_queue) return ESP_ERR_NO_MEM; + BaseType_t ok = xTaskCreate(text_worker_task, "scn_mesh_text", + 3072, NULL, tskIDLE_PRIORITY + 2, NULL); + if (ok != pdPASS) { + vQueueDelete(s_text_queue); + s_text_queue = NULL; + return ESP_ERR_NO_MEM; + } + } + s_text_cb = cb; + return ESP_OK; +} + +esp_err_t scenario_mesh_send_text(const uint8_t dest_mac[6], uint8_t kind, + const char *text) { + if (!dest_mac || !text || + (kind != SCENARIO_MESH_TEXT_CMD && kind != SCENARIO_MESH_TEXT_EVT)) { + return ESP_ERR_INVALID_ARG; + } + size_t len = strlen(text); + if (len == 0 || len > SCENARIO_MESH_TEXT_MAX) return ESP_ERR_INVALID_SIZE; + + const bool broadcast = (memcmp(dest_mac, kBroadcast, 6) == 0); + + if (xSemaphoreTake(s_send_lock, portMAX_DELAY) != pdTRUE) return ESP_FAIL; + + uint8_t frame[SCENARIO_MESH_FRAME_MAX]; + frame[0] = (uint8_t) (MESH_TEXT_SEQ_SENTINEL & 0xFF); + frame[1] = (uint8_t) ((MESH_TEXT_SEQ_SENTINEL >> 8) & 0xFF); + frame[2] = kind; + frame[3] = 0; + memcpy(frame + SCENARIO_MESH_HEADER_BYTES, text, len); + + xSemaphoreTake(s_send_done, 0); // drain stale ack + s_last_send_status = ESP_NOW_SEND_FAIL; + + esp_err_t result = esp_now_send(dest_mac, frame, + SCENARIO_MESH_HEADER_BYTES + len); + if (result == ESP_OK) { + if (xSemaphoreTake(s_send_done, + pdMS_TO_TICKS(SCENARIO_MESH_ACK_TIMEOUT_MS)) + != pdTRUE) { + result = ESP_ERR_TIMEOUT; + } else if (!broadcast && + s_last_send_status != ESP_NOW_SEND_SUCCESS) { + result = ESP_ERR_TIMEOUT; // unicast: peer silent + } + } + + xSemaphoreGive(s_send_lock); + + ESP_LOGI(TAG, "text %s \"%s\" -> %02x:%02x:%02x:%02x:%02x:%02x: %s", + kind == SCENARIO_MESH_TEXT_CMD ? "CMD" : "EVT", text, + dest_mac[0], dest_mac[1], dest_mac[2], + dest_mac[3], dest_mac[4], dest_mac[5], + esp_err_to_name(result)); + return result; +}